#include #include #include #define SCREEN_WIDTH 128 #define SCREEN_HEIGHT 64 #define OLED_RESET -1 Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET); // Eye config #define EYE_WIDTH 40 #define EYE_HEIGHT 25 #define BASE_EYE_Y 20 #define EYE1_X 15 #define EYE2_X 78 // Emotion constants #define EMOTION_DEFAULT 0 #define EMOTION_HAPPY 1 #define EMOTION_SAD 2 #define EMOTION_SILLY 3 #define EMOTION_HOWAREYOU 4 int currentX = 0, currentY = 0; int targetX = 0, targetY = 0; bool eyesOn = false; int currentEmotion = EMOTION_DEFAULT; unsigned long lastMoveTime = 0; unsigned long moveInterval = 1500; unsigned long lastBlinkTime = 0; unsigned long blinkInterval = 7000; // Function declarations void drawEyes(int eyelidHeight, int dx, int dy); void blinkEyes(int dx, int dy); void eyeWakeupAnimation(); void eyeShutdownAnimation(); void setup() { Serial.begin(9600); display.begin(SSD1306_SWITCHCAPVCC, 0x3C); display.clearDisplay(); display.display(); randomSeed(analogRead(0)); } void loop() { if (Serial.available()) { String command = Serial.readStringUntil('\n'); command.trim(); if (command == "WAKE" && !eyesOn) { eyeWakeupAnimation(); drawEyes(0, currentX, currentY); eyesOn = true; } else if (command == "SLEEP" && eyesOn) { eyeShutdownAnimation(); display.clearDisplay(); display.display(); eyesOn = false; } else if (command == "HAPPY") { currentEmotion = EMOTION_HAPPY; } else if (command == "SAD") { currentEmotion = EMOTION_SAD; } else if (command == "SILLY"){ currentEmotion = EMOTION_SILLY; } else if ( command == "HOWAREYOU"){ currentEmotion = EMOTION_HOWAREYOU; } else if (command == "DEFAULT") { currentEmotion = EMOTION_DEFAULT; } } if (!eyesOn) return; unsigned long currentTime = millis(); if (currentTime - lastMoveTime > moveInterval) { targetX = random(-8, 12); targetY = random(-5, 8); lastMoveTime = currentTime; } if (currentX != targetX || currentY != targetY) { if (currentX < targetX) currentX++; else if (currentX > targetX) currentX--; if (currentY < targetY) currentY++; else if (currentY > targetY) currentY--; drawEyes(0, currentX, currentY); delay(30); } if (currentTime - lastBlinkTime > blinkInterval) { blinkEyes(currentX, currentY); lastBlinkTime = currentTime; blinkInterval = 7000 + random(0, 3000); } } void drawEyes(int eyelidHeight, int dx, int dy) { display.clearDisplay(); // Base white eyes display.fillRoundRect(EYE1_X + dx, BASE_EYE_Y + dy, EYE_WIDTH, EYE_HEIGHT, 12, SSD1306_WHITE); display.fillRoundRect(EYE2_X + dx, BASE_EYE_Y + dy, EYE_WIDTH, EYE_HEIGHT, 12, SSD1306_WHITE); // Blinking (eyelid) if (eyelidHeight > 0) { display.fillRect(EYE1_X + dx, BASE_EYE_Y + dy, EYE_WIDTH, eyelidHeight, SSD1306_BLACK); display.fillRect(EYE2_X + dx, BASE_EYE_Y + dy, EYE_WIDTH, eyelidHeight, SSD1306_BLACK); } // Emotion overlay if (currentEmotion == EMOTION_HAPPY) { // Happy: curved "smiling" eyes from bottom display.fillRoundRect(EYE1_X + dx , BASE_EYE_Y + dy + 8, EYE_WIDTH, EYE_HEIGHT, 12, SSD1306_BLACK); display.fillRoundRect(EYE2_X + dx , BASE_EYE_Y + dy + 8, EYE_WIDTH, EYE_HEIGHT, 12, SSD1306_BLACK); } else if (currentEmotion == EMOTION_SAD) { // Show sad eyes first display.fillRoundRect(EYE1_X + dx , BASE_EYE_Y + dy - 12, EYE_WIDTH, EYE_HEIGHT, 12, SSD1306_BLACK); display.fillRoundRect(EYE2_X + dx , BASE_EYE_Y + dy - 12, EYE_WIDTH, EYE_HEIGHT, 12, SSD1306_BLACK); display.display(); delay(5000); // Show sad eyes for 5 seconds // Clear eyes display.clearDisplay(); display.display(); // Display hope verse number in center int index = random(HOPE_COUNT); display.setTextSize(2); // Slightly bigger display.setTextColor(SSD1306_WHITE); display.setCursor((SCREEN_WIDTH - 60) / 2, (SCREEN_HEIGHT - 16) / 2); display.println(hope_verses[index]); // Just the verse number display.display(); delay(5000); // Show verse number for 5 seconds // Clear screen and return to default display.clearDisplay(); display.display(); currentEmotion = EMOTION_DEFAULT; } // how are you questions else if (currentEmotion == EMOTION_HOWAREYOU) { // Show happy eyes display.fillRoundRect(EYE1_X + dx , BASE_EYE_Y + dy + 8, EYE_WIDTH, EYE_HEIGHT, 12, SSD1306_BLACK); display.fillRoundRect(EYE2_X + dx , BASE_EYE_Y + dy + 8, EYE_WIDTH, EYE_HEIGHT, 12, SSD1306_BLACK); display.display(); delay(3000); // Keep happy eyes for 2 seconds // Show text: "I'm okay" display.clearDisplay(); display.setTextSize(2); display.setTextColor(SSD1306_WHITE); display.setCursor((SCREEN_WIDTH - 80) / 2, (SCREEN_HEIGHT - 16) / 2); display.println("I'm okay"); display.display(); delay(3000); // Clear screen and return to default display.clearDisplay(); display.display(); currentEmotion = EMOTION_DEFAULT; } else if (currentEmotion == EMOTION_SILLY){ display.fillRoundRect(EYE1_X + dx , BASE_EYE_Y + dy + 8, EYE_WIDTH, EYE_HEIGHT, 12, SSD1306_BLACK); //display.fillRoundRect(EYE2_X + dx , BASE_EYE_Y + dy, EYE_WIDTH, EYE_HEIGHT, 12, SSD1306_WHITE); } display.display(); } void blinkEyes(int dx, int dy) { for (int i = 0; i <= EYE_HEIGHT; i += 5) { drawEyes(i, dx, dy); delay(20); } for (int i = EYE_HEIGHT; i >= 0; i -= 5) { drawEyes(i, dx, dy); delay(20); } } void eyeWakeupAnimation() { for (int i = 0; i <= EYE_HEIGHT; i += 3) { display.clearDisplay(); int eyeY = BASE_EYE_Y + currentY; int eyeX1 = EYE1_X + currentX; int eyeX2 = EYE2_X + currentX; display.fillRoundRect(eyeX1, eyeY + EYE_HEIGHT / 2 - i / 2, EYE_WIDTH, i, 12, SSD1306_WHITE); display.fillRoundRect(eyeX2, eyeY + EYE_HEIGHT / 2 - i / 2, EYE_WIDTH, i, 12, SSD1306_WHITE); display.display(); delay(30); } } void eyeShutdownAnimation() { for (int i = EYE_HEIGHT; i >= 0; i -= 3) { display.clearDisplay(); int eyeY = BASE_EYE_Y + currentY; int eyeX1 = EYE1_X + currentX; int eyeX2 = EYE2_X + currentX; display.fillRoundRect(eyeX1, eyeY + EYE_HEIGHT / 2 - i / 2, EYE_WIDTH, i, 12, SSD1306_WHITE); display.fillRoundRect(eyeX2, eyeY + EYE_HEIGHT / 2 - i / 2, EYE_WIDTH, i, 12, SSD1306_WHITE); display.display(); delay(30); } }