#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
//#include <Quran.h>
#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);
void blinkEyes();
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()) {
// note: readuntil has a timout of 1 second
String command = Serial.readStringUntil('\n');
command.trim();
Serial.println(command);
if (command == "WAKE" && !eyesOn) {
eyeWakeupAnimation();
drawEyes(0);
eyesOn = true;
}
if (command == "SLEEP" && eyesOn) {
eyeShutdownAnimation();
display.clearDisplay();
display.display();
eyesOn = false;
}
if (command == "HAPPY") {
currentEmotion = EMOTION_HAPPY;
}
if (command == "SAD") {
currentEmotion = EMOTION_SAD;
}
if (command == "SILLY"){
currentEmotion = EMOTION_SILLY;
}
if ( command == "HOWAREYOU"){
currentEmotion = EMOTION_HOWAREYOU;
}
if (command == "DEFAULT") {
currentEmotion = EMOTION_DEFAULT;
}
if (command == "") {
currentEmotion = EMOTION_DEFAULT;
}
Serial.print("currentEmotion=");
Serial.println(currentEmotion);
}
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);
delay(30);
}
if (currentTime - lastBlinkTime > blinkInterval) {
currentX= currentX;
currentY = currentY;
blinkEyes();
lastBlinkTime = currentTime;
blinkInterval = 7000 + random(0, 3000);
}
}
void drawEyes(int eyelidHeight) {
display.clearDisplay();
// Base white eyes
display.fillRoundRect(EYE1_X + currentX, BASE_EYE_Y + currentY, EYE_WIDTH, EYE_HEIGHT, 12, SSD1306_WHITE);
display.fillRoundRect(EYE2_X + currentX, BASE_EYE_Y + currentY, EYE_WIDTH, EYE_HEIGHT, 12, SSD1306_WHITE);
// Blinking (eyelid)
if (eyelidHeight > 0) {
display.fillRect(EYE1_X + currentX, BASE_EYE_Y + currentY, EYE_WIDTH, eyelidHeight, SSD1306_BLACK);
display.fillRect(EYE2_X + currentX, BASE_EYE_Y + currentY, EYE_WIDTH, eyelidHeight, SSD1306_BLACK);
}
switch (currentEmotion) {
// Emotion overlay
case EMOTION_HAPPY:
happyeyes();
break;
case EMOTION_SAD:
sadeyes();
currentEmotion = EMOTION_DEFAULT;
break;
// how are you questions
case EMOTION_HOWAREYOU:
happyeyes();
delay(3000); // Keep happy eyes for 2 seconds
howareyoueyes();
currentEmotion = EMOTION_DEFAULT;
break;
case EMOTION_SILLY:
sillyeyes();
break;
}
}
void blinkEyes() {
for (int i = 0; i <= EYE_HEIGHT; i += 5) {
drawEyes(i);
delay(20);
}
for (int i = EYE_HEIGHT; i >= 0; i -= 5) {
drawEyes(i);
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);
}
}
void happyeyes()
{
// Happy: curved "smiling" eyes from bottom
display.fillRoundRect(EYE1_X + currentX , BASE_EYE_Y + currentY + 8, EYE_WIDTH, EYE_HEIGHT, 12, SSD1306_BLACK);
display.fillRoundRect(EYE2_X + currentX , BASE_EYE_Y + currentY + 8, EYE_WIDTH, EYE_HEIGHT, 12, SSD1306_BLACK);
display.display();
}
void sillyeyes()
{ display.fillRoundRect(EYE1_X + currentX , BASE_EYE_Y + currentY + 8, EYE_WIDTH, EYE_HEIGHT, 12, SSD1306_BLACK);
//display.fillRoundRect(EYE2_X + currentX , BASE_EYE_Y + currentY, EYE_WIDTH, EYE_HEIGHT, 12, SSD1306_WHITE);
display.display();
}
void sadeyes()
{
// Show sad eyes first
display.fillRoundRect(EYE1_X + currentX , BASE_EYE_Y + currentY - 12, EYE_WIDTH, EYE_HEIGHT, 12, SSD1306_BLACK);
display.fillRoundRect(EYE2_X + currentX , BASE_EYE_Y + currentY - 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();
}
void howareyoueyes()
{
// 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();
}