#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <Adafruit_NeoPixel.h>
// ===== OLED Setup =====
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_RESET -1
Adafruit_SSD1306 display1(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET); // 0x3C
Adafruit_SSD1306 display2(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET); // 0x3D
// === Eye Size and Center Position ===
int eyeX = 44;
int eyeY = 12;
int eyeWidth = 40;
int eyeHeight = 40;
// === Eye Movement Variables ===
int targetOffsetX = 0;
int targetOffsetY = 0;
int moveSpeed = 3;
// === Blinking Logic ===
int blinkState = 0;
int blinkDelay = 4000;
unsigned long lastBlinkTime = 0;
unsigned long moveTime = 0;
// Smooth movement tracking
static int offsetX = 0;
static int offsetY = 0;
// ===== WS2812 Setup =====
#define LED1_PIN 12
#define LED2_PIN 13
#define NUMPIXELS 16 // Number of LEDs in each ring
Adafruit_NeoPixel ring1(NUMPIXELS, LED1_PIN, NEO_GRB + NEO_KHZ800);
Adafruit_NeoPixel ring2(NUMPIXELS, LED2_PIN, NEO_GRB + NEO_KHZ800);
// =======================
// === SETUP FUNCTION ===
// =======================
void setup() {
Serial.begin(115200);
Wire.begin();
// Init both displays
if (!display1.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
Serial.println(F("Display 1 failed"));
while (1);
}
if (!display2.begin(SSD1306_SWITCHCAPVCC, 0x3D)) {
Serial.println(F("Display 2 failed"));
while (1);
}
display1.clearDisplay();
display2.clearDisplay();
display1.display();
display2.display();
updateEyes();
// Init LED Rings
ring1.begin();
ring2.begin();
// === Run eyes + wakeup only once ===
// === Blink LEDs once at startup ===
setRingColor(ring1, 255, 255, 0); // Yellow
setRingColor(ring2, 0, 0, 255); // Blue
delay(500);
setRingColor(ring1, 0, 0, 0); // OFF
setRingColor(ring2, 0, 0, 0); // OFF
delay(500);
setRingColor(ring1, 255, 255, 0); // Back to Yellow
setRingColor(ring2, 0, 0, 255); // Back to Blue
// === Run eyes + wakeup only once ===
// updateEyes();
// playWakeUpSequence();
}
// ============================
// === Wake-Up Sequence Code ===
// ============================
void playWakeUpSequence() {
}
delay(500);
}
// ==========================
// === Full Eye Function ===
// ==========================
void updateEyes() {
unsigned long currentTime = millis();
// === Blink Start ===
if (currentTime - lastBlinkTime > blinkDelay && blinkState == 0) {
blinkState = 1;
lastBlinkTime = currentTime;
}
// === Blink End ===
if (currentTime - lastBlinkTime > 150 && blinkState == 1) {
blinkState = 0;
lastBlinkTime = currentTime;
}
// === Random Movement Trigger ===
if (currentTime - moveTime > random(1500, 3000) && blinkState == 0) {
int direction = random(0, 6);
if (direction == 0) { targetOffsetX = -8; targetOffsetY = 0; }
else if (direction == 1) { targetOffsetX = 8; targetOffsetY = 0; }
else if (direction == 2) { targetOffsetX = 0; targetOffsetY = -6; }
else if (direction == 3) { targetOffsetX = 0; targetOffsetY = 6; }
else { targetOffsetX = 0; targetOffsetY = 0; }
moveTime = currentTime;
}
// === Smooth Movement ===
offsetX += (targetOffsetX - offsetX) / moveSpeed;
offsetY += (targetOffsetY - offsetY) / moveSpeed;
// === Drawing Both Eyes ===
display1.clearDisplay();
display2.clearDisplay();
if (blinkState == 0) {
drawEye(display1, eyeX + offsetX, eyeY + offsetY, eyeWidth, eyeHeight);
drawEye(display2, eyeX + offsetX, eyeY + offsetY, eyeWidth, eyeHeight);
} else {
display1.fillRect(eyeX + offsetX, eyeY + offsetY + eyeHeight / 2 - 2, eyeWidth, 4, WHITE);
display2.fillRect(eyeX + offsetX, eyeY + offsetY + eyeHeight / 2 - 2, eyeWidth, 4, WHITE);
}
display1.display();
display2.display();
}
void drawEye(Adafruit_SSD1306 &disp, int x, int y, int w, int h) {
disp.fillRoundRect(x, y, w, h, 8, WHITE);
}
// ==========================
// === LED Ring Function ===
// ==========================
void setRingColor(Adafruit_NeoPixel &ring, uint8_t r, uint8_t g, uint8_t b) {
for (int i = 0; i < NUMPIXELS; i++) {
ring.setPixelColor(i, ring.Color(r, g, b));
}
ring.show();
}
// ==================
// === MAIN LOOP ===
// ==================
void loop() {
// Nothing here — everything runs once
}