/*
  【花雕动手做】为 Arduino 机器人创建迷人的 OLED 眼睛动画！
   实验项目之一：基于Intellar开源框架的OLED眼部动画实现
*/

#include <Wire.h>              // I2C通信库
#include <Adafruit_GFX.h>      // Adafruit图形库
#include <Adafruit_SSD1306.h>  // SSD1306 OLED驱动库

// 定义OLED屏幕的宽度和高度（像素）
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64

// 声明SSD1306显示屏对象，连接到I2C（SDA, SCL引脚）
// OLED_RESET为复位引脚，-1表示与Arduino共享复位引脚
#define OLED_RESET -1
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

#include "eyes.h"  // 包含自定义的眼睛动画数据

void setup() {
    // 初始化代码，运行一次
    pinMode(8, INPUT_PULLUP);  // 设置引脚8为输入，启用内部上拉电阻
    pinMode(9, INPUT_PULLUP);  // 设置引脚9为输入，启用内部上拉电阻
    pinMode(10, INPUT_PULLUP); // 设置引脚10为输入，启用内部上拉电阻
    pinMode(11, INPUT_PULLUP); // 设置引脚11为输入，启用内部上拉电阻

    Serial.begin(115200);      // 初始化串口通信，波特率115200
    Serial.println("Booting"); // 打印启动信息

    // 初始化SSD1306显示屏，I2C地址为0x3C
    if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
        Serial.println(F("SSD1306 allocation failed")); // 初始化失败提示
        for (;;); // 停止程序，进入死循环
    }

    // 显示初始缓冲区内容（Adafruit启动画面）
    display.display();
    display.clearDisplay(); // 清空显示缓冲区
    display.display();      // 更新显示
}

// 读取按键输入的函数
unsigned char readkey(void) {
    unsigned char ret = 0;
    if (digitalRead(8) == 0) ret = 1;   // 左键按下
    if (digitalRead(9) == 0) ret += 2;  // 上键按下
    if (digitalRead(10) == 0) ret += 4; // 下键按下
    if (digitalRead(11) == 0) ret += 8; // 开火键按下
    return ret;
}

int xp = 16; // 眼睛的水平位置
int mood = 1; // 当前情绪状态

void loop() {
    int n;
    static int xd = 0;    // 眼睛的水平偏移量
    static int espera = 0; // 等待时间计数器
    static int step = 0;   // 当前动画步骤
    int x1, x2;

    if (espera > 0) {
        espera--; // 减少等待时间
        delay(1); // 延迟1毫秒
    } else {
        // 计算眼睛的显示位置
        x1 = xd + (xp > 16 ? (16 + 2 * (xp - 16)) : xp);
        x2 = 64 + xd + (xp < 16 ? (-16 + (xp * 2)) : xp);

        switch (step) {
            case 0: // 正常显示眼睛
                display.clearDisplay(); // 清空显示缓冲区
                if (xp < 6) {
                    // 显示向左看的眼睛
                    display.drawBitmap(x1, 8, peyes[mood][2][0], 32, 32, WHITE);
                    display.drawBitmap(x2, 8, peyes[mood][1][1], 32, 32, WHITE);
                } else if (xp < 26) {
                    // 显示正前方的眼睛
                    display.drawBitmap(x1, 8, peyes[mood][0][0], 32, 32, WHITE);
                    display.drawBitmap(x2, 8, peyes[mood][0][1], 32, 32, WHITE);
                } else {
                    // 显示向右看的眼睛
                    display.drawBitmap(x1, 8, peyes[mood][1][0], 32, 32, WHITE);
                    display.drawBitmap(x2, 8, peyes[mood][2][1], 32, 32, WHITE);
                }
                display.display(); // 更新显示
                espera = random(250, 1000); // 随机等待时间
                n = random(0, 7); // 随机选择下一步
                if (n == 6) {
                    step = 1; // 进入眨眼状态
                } else {
                    step = 2; // 进入眼睛移动状态
                }
                break;

            case 1: // 眨眼动画
                display.clearDisplay();
                display.drawBitmap(x1, 8, eye0, 32, 32, WHITE); // 显示闭眼
                display.drawBitmap(x2, 8, eye0, 32, 32, WHITE);
                display.display();
                espera = 100; // 眨眼持续时间
                step = 0;     // 返回正常状态
                break;

            case 2: // 眼睛随机移动
                n = random(0, 10);
                if (n < 5) xd--; // 向左移动
                if (n > 5) xd++; // 向右移动
                if (xd < -4) xd = -3; // 限制左移范围
                if (xd > 4) xd = 3;    // 限制右移范围
                espera = 0;
                step = 0;
                break;
        }
    }

    // 读取按键输入并更新眼睛位置和情绪
    n = readkey();
    if (n == 2) xp = (xp <= 0 ? 0 : xp - 1); // 左键：眼睛向左移动
    if (n == 4) xp = (xp >= 32 ? 32 : xp + 1); // 右键：眼睛向右移动
    if (n == 1) {
        mood = (mood >= 5 ? 0 : mood + 1); // 切换情绪状态
        do {} while (readkey() != 0); // 等待按键释放
    }
    if (n != 0) {
        espera = 0; // 重置等待时间
        step = 0;   // 重置动画步骤
    }
}
