Every time I compile this is what I experience
/home/gonit/.arduino15/packages/esp32/tools/esp-x32/2302/bin/../lib/gcc/xtensa-esp32-elf/12.2.0/../../../../xtensa-esp32-elf/bin/ld: sketch/CIMOL.ino.cpp.o:(.literal._Z17handleMoodChangesv+0x10): undefined reference to `_Z13setExpressioni'
/home/gonit/.arduino15/packages/esp32/tools/esp-x32/2302/bin/../lib/gcc/xtensa-esp32-elf/12.2.0/../../../../xtensa-esp32-elf/bin/ld: sketch/CIMOL.ino.cpp.o:(.literal._Z15handleSleepModev+0x10): undefined reference to `_Z15enterLightSleepv'
/home/gonit/.arduino15/packages/esp32/tools/esp-x32/2302/bin/../lib/gcc/xtensa-esp32-elf/12.2.0/../../../../xtensa-esp32-elf/bin/ld: sketch/CIMOL.ino.cpp.o:(.literal._Z4loopv+0xc): undefined reference to `_Z13updateDisplayv'
/home/gonit/.arduino15/packages/esp32/tools/esp-x32/2302/bin/../lib/gcc/xtensa-esp32-elf/12.2.0/../../../../xtensa-esp32-elf/bin/ld: sketch/CIMOL.ino.cpp.o: in function `_Z17handleMoodChangesv':
/home/gonit/Dokumen/Project GONIT/CIMOL/CIMOL.ino:247: undefined reference to `_Z13setExpressioni'
/home/gonit/.arduino15/packages/esp32/tools/esp-x32/2302/bin/../lib/gcc/xtensa-esp32-elf/12.2.0/../../../../xtensa-esp32-elf/bin/ld: sketch/CIMOL.ino.cpp.o: in function `_Z11handleShockv':
/home/gonit/Dokumen/Project GONIT/CIMOL/CIMOL.ino:303: undefined reference to `_Z13setExpressioni'
/home/gonit/.arduino15/packages/esp32/tools/esp-x32/2302/bin/../lib/gcc/xtensa-esp32-elf/12.2.0/../../../../xtensa-esp32-elf/bin/ld: sketch/CIMOL.ino.cpp.o: in function `_Z14enterDeepSleepv':
/home/gonit/Dokumen/Project GONIT/CIMOL/CIMOL.ino:405: undefined reference to `_Z13setExpressioni'
/home/gonit/.arduino15/packages/esp32/tools/esp-x32/2302/bin/../lib/gcc/xtensa-esp32-elf/12.2.0/../../../../xtensa-esp32-elf/bin/ld: sketch/CIMOL.ino.cpp.o: in function `_Z13handleBatteryv':
/home/gonit/Dokumen/Project GONIT/CIMOL/CIMOL.ino:200: undefined reference to `_Z13setExpressioni'
/home/gonit/.arduino15/packages/esp32/tools/esp-x32/2302/bin/../lib/gcc/xtensa-esp32-elf/12.2.0/../../../../xtensa-esp32-elf/bin/ld: /home/gonit/Dokumen/Project GONIT/CIMOL/CIMOL.ino:192: undefined reference to `_Z13setExpressioni'
/home/gonit/.arduino15/packages/esp32/tools/esp-x32/2302/bin/../lib/gcc/xtensa-esp32-elf/12.2.0/../../../../xtensa-esp32-elf/bin/ld: sketch/CIMOL.ino.cpp.o:/home/gonit/Dokumen/Project GONIT/CIMOL/CIMOL.ino:209: more undefined references to `_Z13setExpressioni' follow
/home/gonit/.arduino15/packages/esp32/tools/esp-x32/2302/bin/../lib/gcc/xtensa-esp32-elf/12.2.0/../../../../xtensa-esp32-elf/bin/ld: sketch/CIMOL.ino.cpp.o: in function `_Z15handleSleepModev':
/home/gonit/Dokumen/Project GONIT/CIMOL/CIMOL.ino:220: undefined reference to `_Z15enterLightSleepv'
/home/gonit/.arduino15/packages/esp32/tools/esp-x32/2302/bin/../lib/gcc/xtensa-esp32-elf/12.2.0/../../../../xtensa-esp32-elf/bin/ld: sketch/CIMOL.ino.cpp.o: in function `_Z18wakeFromLightSleepv':
/home/gonit/Dokumen/Project GONIT/CIMOL/CIMOL.ino:223: undefined reference to `_Z13setExpressioni'
/home/gonit/.arduino15/packages/esp32/tools/esp-x32/2302/bin/../lib/gcc/xtensa-esp32-elf/12.2.0/../../../../xtensa-esp32-elf/bin/ld: sketch/CIMOL.ino.cpp.o: in function `_Z11handleTouchv':
/home/gonit/Dokumen/Project GONIT/CIMOL/CIMOL.ino:266: undefined reference to `_Z13setExpressioni'
/home/gonit/.arduino15/packages/esp32/tools/esp-x32/2302/bin/../lib/gcc/xtensa-esp32-elf/12.2.0/../../../../xtensa-esp32-elf/bin/ld: sketch/CIMOL.ino.cpp.o: in function `_Z5setupv':
/home/gonit/Dokumen/Project GONIT/CIMOL/CIMOL.ino:507: undefined reference to `_Z13setExpressioni'
/home/gonit/.arduino15/packages/esp32/tools/esp-x32/2302/bin/../lib/gcc/xtensa-esp32-elf/12.2.0/../../../../xtensa-esp32-elf/bin/ld: sketch/CIMOL.ino.cpp.o: in function `_Z4loopv':
/home/gonit/Dokumen/Project GONIT/CIMOL/CIMOL.ino:535: undefined reference to `_Z13updateDisplayv'
/home/gonit/.arduino15/packages/esp32/tools/esp-x32/2302/bin/../lib/gcc/xtensa-esp32-elf/12.2.0/../../../../xtensa-esp32-elf/bin/ld: /home/gonit/Dokumen/Project GONIT/CIMOL/CIMOL.ino:542: undefined reference to `_Z13setExpressioni'
/home/gonit/.arduino15/packages/esp32/tools/esp-x32/2302/bin/../lib/gcc/xtensa-esp32-elf/12.2.0/../../../../xtensa-esp32-elf/bin/ld: /home/gonit/Dokumen/Project GONIT/CIMOL/CIMOL.ino:548: undefined reference to `_Z13setExpressioni'
collect2: error: ld returned 1 exit status
exit status 1
#include "Arduino.h"
#include "TFT_eSPI.h"
#include "SPI.h"
#include "SPIFFS.h"
#include "AudioFileSourceSPIFFS.h"
#include "AudioGeneratorWAV.h"
#include "AudioOutputI2S.h"
#include <ESP32Time.h>
#include <Preferences.h>
// Pin Definitions
#define TOUCH_PIN 4
#define SHOCK_PIN 16
#define VIBRATION_PIN 17
#define SPEAKER_PIN 25
#define TFT_DC 2
#define TFT_CS 5
#define TFT_RST 4
#define BATTERY_PIN 35 // ADC pin for battery monitoring
// Constants
#define BATTERY_READ_INTERVAL 60000 // Check battery every minute
#define SLEEP_TIME_START 22 // Sleep time start (24-hour format)
#define SLEEP_TIME_END 7 // Sleep time end (24-hour format)
#define DEEP_SLEEP_TIMEOUT 300000 // 5 minutes without interaction
#define LIGHT_SLEEP_TIMEOUT 60000 // 1 minute without interaction
#define TFT_GREY 0x7BEF
#define TFT_DARKGREY 0x4208
#define TFT_LIGHTGREY 0xC618
#define TFT_GOLD 0xFEA0
#define TFT_RAINBOW 0xFE40 // You might want to implement a rainbow effect separately
#define TFT_PINK 0xFBB4
// Display setup
TFT_eSPI tft = TFT_eSPI();
AudioOutputI2S audio;
ESP32Time rtc;
// Structures
struct EyeParams {
int width;
int height;
int angle;
bool isDotted;
int sparkleSize;
bool isBlinking;
};
struct MouthParams {
int width;
int height;
int curve;
bool isOpen;
int style; // 0=curve, 1=straight, 2=zigzag, 3=dotted, 4=pixel
};
struct Expression {
const char* name;
EyeParams leftEye;
EyeParams rightEye;
MouthParams mouth;
uint16_t primaryColor;
uint16_t accentColor;
int blinkRate;
const char* soundFile;
bool canInterrupt; // Can this expression be interrupted by other events
int duration; // How long to show this expression (ms), 0 for indefinite
int priority; // Priority level (1-5, 5 being highest)
};
// Global Variables
unsigned long lastInteractionTime = 0;
unsigned long lastBatteryCheck = 0;
unsigned long lastBlinkTime = 0;
unsigned long expressionStartTime = 0;
int currentExpression = 0;
int batteryLevel = 100;
bool isInSleepMode = false;
bool isInLightSleep = false;
bool isCharging = false; // Add this line
int moodLevel = 50; // 0-100, affects expression selection
int energyLevel = 100; // 0-100, affects activity level
int interactionCount = 0; // Counts interactions for mood changes
// Interaction tracking
struct InteractionHistory {
unsigned long timestamp;
int type; // 1=touch, 2=shock, 3=other
} lastInteractions[10];
int interactionIndex = 0;
// Expression Definitions (61 different expressions)
Expression expressions[] = {
// Basic States (1-10)
{"normal", {20,20,0,false,0,false}, {20,20,0,false,0,false}, {40,10,0,false,0}, TFT_WHITE, TFT_BLUE, 3000, "normal.wav", true, 0, 1},
{"happy", {25,20,0,false,2,false}, {25,20,0,false,2,false}, {50,15,10,false,0}, TFT_YELLOW, TFT_WHITE, 2000, "happy.wav", true, 3000, 3},
{"sad", {15,20,-10,false,0,false}, {15,20,-10,false,0,false}, {30,10,-10,false,0}, TFT_BLUE, TFT_CYAN, 4000, "sad.wav", true, 4000, 3},
{"angry", {30,15,-20,false,0,false}, {30,15,-20,false,0,false}, {40,5,-5,false,1}, TFT_RED, TFT_ORANGE, 1000, "angry.wav", true, 3000, 4},
{"surprised", {35,35,0,false,3,false}, {35,35,0,false,3,false}, {30,30,0,true,0}, TFT_MAGENTA, TFT_PINK, 1500, "surprise.wav", true, 2000, 4},
// Sleep States (11-15)
{"sleepy", {10,5,0,false,0,false}, {10,5,0,false,0,false}, {20,5,0,false,0}, TFT_LIGHTGREY, TFT_GREY, 5000, "sleepy.wav", false, 0, 2},
{"sleeping", {2,15,0,false,0,false}, {2,15,0,false,0,false}, {20,5,0,false,0}, TFT_DARKGREY, TFT_GREY, 6000, "sleep.wav", false, 0, 1},
{"deep_sleep", {1,10,0,false,0,true}, {1,10,0,false,0,true}, {15,3,0,false,0}, TFT_DARKGREY, TFT_BLACK, 8000, "deepsleep.wav", false, 0, 1},
{"waking", {5,25,0,false,0,false}, {5,25,0,false,0,false}, {40,40,0,true,0}, TFT_LIGHTGREY, TFT_GREY, 4000, "wake.wav", true, 3000, 4},
{"yawning", {5,25,0,false,0,false}, {5,25,0,false,0,false}, {40,40,0,true,0}, TFT_LIGHTGREY, TFT_GREY, 4000, "yawn.wav", true, 2000, 3},
// Battery States (16-20)
{"low_battery",{15,15,-5,false,0,true}, {15,15,-5,false,0,true}, {30,8,-5,false,0}, TFT_RED, TFT_ORANGE, 5000, "lowbat.wav", false, 0, 5},
{"charging", {20,20,0,true,2,false}, {20,20,0,true,2,false}, {35,10,5,false,0}, TFT_GREEN, TFT_YELLOW, 2000, "charge.wav", true, 0, 4},
{"charged", {25,25,0,false,3,false}, {25,25,0,false,3,false}, {45,15,10,false,0}, TFT_GREEN, TFT_CYAN, 2000, "charged.wav", true, 3000, 4},
{"power_saving",{10,10,-5,false,0,true}, {10,10,-5,false,0,true}, {25,5,-5,false,0}, TFT_ORANGE, TFT_RED, 6000, "powersave.wav", false, 0, 4},
{"shutting_down",{5,5,0,false,0,true}, {5,5,0,false,0,true}, {20,5,-10,false,0}, TFT_RED, TFT_DARKGREY, 8000, "shutdown.wav", false, 5000, 5},
// Interactive States (21-30)
{"pet_happy", {25,20,0,false,3,false}, {25,20,0,false,3,false}, {50,20,15,false,0}, TFT_YELLOW, TFT_WHITE, 1500, "pethappy.wav", true, 2000, 4},
{"tickled", {20,20,45,true,2,false}, {20,20,45,true,2,false}, {45,15,10,true,2}, TFT_CYAN, TFT_WHITE, 1000, "tickle.wav", true, 1500, 4},
{"shocked", {35,35,0,false,0,false}, {35,35,0,false,0,false}, {30,30,0,true,3}, TFT_WHITE, TFT_YELLOW, 1000, "shock.wav", true, 1000, 5},
{"dizzy", {30,30,45,true,0,false}, {30,30,-45,true,0,false}, {35,10,0,false,3}, TFT_PURPLE, TFT_BLUE, 2000, "dizzy.wav", true, 3000, 4},
{"attention", {25,25,0,false,2,false}, {25,25,0,false,2,false}, {30,10,0,false,0}, TFT_CYAN, TFT_WHITE, 2000, "attention.wav", true, 2000, 3},
// Emotional States (31-40)
{"excited", {30,25,0,true,3,false}, {30,25,0,true,3,false}, {45,20,15,true,0}, TFT_YELLOW, TFT_WHITE, 1000, "excited.wav", true, 2500, 4},
{"curious", {25,25,10,false,2,false}, {25,25,-10,false,2,false}, {25,10,0,false,0}, TFT_CYAN, TFT_WHITE, 2500, "curious.wav", true, 2000, 3},
{"confused", {20,20,30,false,0,false}, {20,20,-30,false,0,false}, {30,5,0,true,3}, TFT_MAGENTA, TFT_BLUE, 2000, "confused.wav", true, 2500, 3},
{"worried", {25,15,-5,false,0,false}, {25,15,-5,false,0,false}, {35,5,-5,false,1}, TFT_ORANGE, TFT_YELLOW, 3000, "worried.wav", true, 3000, 3},
{"skeptical", {20,15,15,false,0,false}, {20,15,-15,false,0,false}, {30,5,-10,false,1}, TFT_CYAN, TFT_BLUE, 2500, "skeptical.wav", true, 2000, 3},
{"love", {25,25,0,true,3,false}, {25,25,0,true,3,false}, {40,15,20,false,0}, TFT_RED, TFT_PINK, 1500, "love.wav", true, 2500, 4},
{"proud", {25,20,0,false,2,false}, {25,20,0,false,2,false}, {45,10,15,false,0}, TFT_GREEN, TFT_CYAN, 2000, "proud.wav", true, 3000, 3},
{"bored", {15,10,0,false,0,true}, {15,10,0,false,0,true}, {30,5,-5,false,1}, TFT_GREY, TFT_DARKGREY, 4000, "bored.wav", true, 3000, 2},
{"mischievous",{20,20,15,false,2,false}, {20,20,15,false,2,false}, {40,10,10,false,2}, TFT_PURPLE, TFT_MAGENTA, 2000, "mischief.wav", true, 2500, 3},
{"embarrassed",{15,15,-10,false,0,false}, {15,15,-10,false,0,false}, {25,5,-5,true,0}, TFT_PINK, TFT_RED, 2500, "embarrassed.wav", true, 2000, 3},
// Action States (41-50)
{"thinking", {20,15,30,false,0,false}, {20,15,30,false,0,false}, {20,5,0,false,1}, TFT_BLUE, TFT_CYAN, 3000, "thinking.wav", true, 4000, 2},
{"processing", {25,25,0,true,1,false}, {25,25,0,true,1,false}, {30,10,0,true,4}, TFT_CYAN, TFT_BLUE, 1000, "process.wav", true, 3000, 3},
{"calculating",{20,20,0,true,2,true}, {20,20,0,true,2,true}, {25,5,0,false,4}, TFT_GREEN, TFT_CYAN, 1500, "calculate.wav", true, 3000, 3},
{"searching", {30,15,45,true,1,false}, {30,15,-45,true,1,false}, {20,5,0,true,3}, TFT_YELLOW, TFT_WHITE, 1000, "search.wav", true, 2500, 3},
{"listening", {25,25,0,false,2,false}, {25,25,0,false,2,false}, {20,5,0,false,0}, TFT_BLUE, TFT_CYAN, 2000, "listen.wav", true, 2000, 3},
{"working", {20,20,0,true,1,false}, {20,20,0,true,1,false}, {30,10,5,false,1}, TFT_GREEN, TFT_YELLOW, 2000, "working.wav", true, 3000, 3},
{"dancing", {25,25,45,true,3,false}, {25,25,-45,true,3,false}, {40,15,15,true,2}, TFT_MAGENTA, TFT_PINK, 1000, "dance.wav", true, 2000, 4},
{"singing", {20,20,0,false,2,false}, {20,20,0,false,2,false}, {35,30,0,true,0}, TFT_CYAN, TFT_WHITE, 1500, "sing.wav", true, 2500, 3},
{"exercising", {25,15,30,false,1,false}, {25,15,30,false,1,false}, {35,10,10,false,2}, TFT_GREEN, TFT_YELLOW, 1500, "exercise.wav", true, 2000, 3},
{"resting", {15,10,0,false,0,true}, {15,10,0,false,0,true}, {25,5,0,false,0}, TFT_LIGHTGREY, TFT_GREY, 4000, "rest.wav", true, 3000, 2},
// Special States (51-61)
{"party", {30,30,0,true,3,false}, {30,30,0,true,3,false}, {50,20,20,true,2}, TFT_RAINBOW, TFT_WHITE, 1000, "party.wav", true, 3000, 4},
{"birthday", {25,25,0,true,3,false}, {25,25,0,true,3,false}, {45,15,15,false,0}, TFT_RAINBOW, TFT_YELLOW, 1500, "birthday.wav", true, 3000, 4},
{"holiday", {25,25,0,false,3,false}, {25,25,0,false,3,false}, {40,15,10,false,0}, TFT_GREEN, TFT_RED, 2000, "holiday.wav", true, 3000, 4},
{"winning", {30,25,0,true,3,false}, {30,25,0,true,3,false}, {50,20,20,false,0}, TFT_GOLD, TFT_YELLOW, 1500, "win.wav", true, 3000, 4},
{"losing", {20,15,-10,false,0,false}, {20,15,-10,false,0,false}, {30,5,-10,false,1}, TFT_BLUE, TFT_LIGHTGREY, 3000, "lose.wav", true, 2500, 3},
{"error", {25,25,45,true,0,false}, {25,25,-45,true,0,false}, {35,10,-5,true,3}, TFT_RED, TFT_ORANGE, 1000, "error.wav", true, 2000, 5},
{"debug", {20,20,0,true,1,false}, {20,20,0,true,1,false}, {30,5,0,true,4}, TFT_GREEN, TFT_CYAN, 1500, "debug.wav", true, 2500, 3},
{"update", {25,25,0,true,2,false}, {25,25,0,true,2,false}, {35,10,5,false,4}, TFT_BLUE, TFT_CYAN, 2000, "update.wav", true, 3000, 4},
{"overheating",{30,20,-10,true,0,false}, {30,20,-10,true,0,false}, {40,15,-5,true,2}, TFT_RED, TFT_ORANGE, 1500, "overheat.wav", true, 2500, 5},
{"cooling", {20,20,0,false,1,false}, {20,20,0,false,1,false}, {30,10,0,false,0}, TFT_CYAN, TFT_BLUE, 2500, "cooling.wav", true, 3000, 4},
{"system_check",{25,25,0,true,2,true}, {25,25,0,true,2,true}, {25,5,0,true,4}, TFT_GREEN, TFT_CYAN, 2000, "syscheck.wav", true, 4000, 4}
};
// Function Declarations
void handleBattery();
void handleSleepMode();
void handleMoodChanges();
void playSound(const char* filename);
void setExpression(int index);
void handleTouch();
void handleShock();
void updateDisplay();
void enterLightSleep();
void enterDeepSleep();
void recordInteraction(int type);
// Battery Management
void handleBattery() {
if (millis() - lastBatteryCheck >= BATTERY_READ_INTERVAL) {
// Read battery voltage
int rawValue = analogRead(BATTERY_PIN);
batteryLevel = map(rawValue, 0, 4095, 0, 100);
// Check charging status
bool wasCharging = isCharging;
isCharging = rawValue > 4000; // Assuming voltage above 4V indicates charging
// Handle battery states
if (isCharging && !wasCharging) {
setExpression(16); // charging expression
} else if (batteryLevel <= 10 && !isCharging) {
setExpression(15); // low battery expression
if (batteryLevel <= 5) {
enterDeepSleep();
}
} else if (batteryLevel <= 20 && !isCharging) {
setExpression(18); // power saving expression
}
lastBatteryCheck = millis();
}
}
// Sleep Mode Management
void handleSleepMode() {
int currentHour = rtc.getHour(true);
bool shouldBeSleeping = (currentHour >= SLEEP_TIME_START || currentHour < SLEEP_TIME_END);
if (shouldBeSleeping && !isInSleepMode) {
isInSleepMode = true;
setExpression(12); // deep sleep expression
energyLevel = max(energyLevel - 20, 0);
} else if (!shouldBeSleeping && isInSleepMode) {
isInSleepMode = false;
setExpression(13); // waking expression
energyLevel = min(energyLevel + 30, 100);
}
// Handle sleep timeouts
if (millis() - lastInteractionTime > DEEP_SLEEP_TIMEOUT) {
enterDeepSleep();
} else if (millis() - lastInteractionTime > LIGHT_SLEEP_TIMEOUT) {
enterLightSleep();
}
}
// Mood Management
void handleMoodChanges() {
// Analyze recent interactions
int recentInteractions = 0;
unsigned long currentTime = millis();
for (int i = 0; i < 10; i++) {
if (currentTime - lastInteractions[i].timestamp < 300000) { // Last 5 minutes
recentInteractions++;
}
}
// Adjust mood based on interaction frequency
if (recentInteractions > 5) {
moodLevel = min(moodLevel + 10, 100);
} else if (recentInteractions < 2) {
moodLevel = max(moodLevel - 5, 0);
}
// Energy level affects available expressions
if (energyLevel < 30) {
// Limit to low energy expressions
setExpression(15); // low energy state
}
}
// Touch Interaction Handler
void handleTouch() {
// Membaca status tombol sentuh
if (digitalRead(TOUCH_PIN) == HIGH) { // Sesuaikan dengan logika tombol Anda (HIGH/LOW)
if (isInLightSleep) {
wakeFromLightSleep();
return;
}
recordInteraction(1);
// Respons berbeda berdasarkan mood dan energyLevel
if (moodLevel > 80) {
// Respons sangat senang
int happyResponses[] = {1, 20, 21, 31, 35, 36};
setExpression(happyResponses[random(6)]);
} else if (moodLevel > 50) {
// Respons netral
int neutralResponses[] = {0, 32, 33, 47, 48};
setExpression(neutralResponses[random(5)]);
} else {
// Respons mood rendah
int lowMoodResponses[] = {2, 37, 38, 39};
setExpression(lowMoodResponses[random(4)]);
}
// Haptic feedback jika menggunakan vibration motor
digitalWrite(VIBRATION_PIN, HIGH);
delay(50);
digitalWrite(VIBRATION_PIN, LOW);
lastInteractionTime = millis();
energyLevel = min(energyLevel + 5, 100);
// Debounce delay
delay(200); // Mencegah multiple trigger
}
}
// Shock Interaction Handler
void handleShock() {
if (digitalRead(SHOCK_PIN)) {
int shockValue = analogRead(SHOCK_PIN);
if (shockValue > 3000) {
int strongResponses[] = {22, 3, 4, 23}; // shocked, angry, surprised, dizzy
setExpression(strongResponses[random(4)]);
} else if (shockValue > 2000) {
int mediumResponses[] = {33, 34, 24}; // confused, worried, attention
setExpression(mediumResponses[random(3)]);
} else {
int lightResponses[] = {32, 35, 39}; // curious, love, embarrassed
setExpression(lightResponses[random(3)]);
}
}
}
void handleTimeBasedExpressions() {
int currentHour = rtc.getHour(true);
// Pagi hari
if (currentHour >= 6 && currentHour < 10) {
int morningExpr[] = {13, 47, 48}; // waking, dancing, singing
setExpression(morningExpr[random(3)]);
}
// Siang hari
else if (currentHour >= 10 && currentHour < 15) {
int dayExpr[] = {41, 42, 46}; // thinking, processing, working
setExpression(dayExpr[random(3)]);
}
// Malam hari
else if (currentHour >= 20 && currentHour < 22) {
int nightExpr[] = {11, 49, 50}; // sleepy, exercising, resting
setExpression(nightExpr[random(3)]);
}
}
void handleBatteryExpressions() {
if (batteryLevel > 90) {
int highBatteryExpr[] = {1, 31, 36}; // happy, excited, proud
setExpression(highBatteryExpr[random(3)]);
} else if (batteryLevel < 20) {
int lowBatteryExpr[] = {15, 18, 34}; // low_battery, power_saving, worried
setExpression(lowBatteryExpr[random(3)]);
}
}
// Display Functions
void drawEye(int x, int y, EyeParams params, uint16_t color, uint16_t accentColor) {
// Sesuaikan ukuran mata dengan layar yang lebih kecil
int scaledWidth = params.width * 0.6; // Skala lebih kecil
int scaledHeight = params.height * 0.6;
if (params.isBlinking) {
tft.drawLine(x - scaledWidth, y, x + scaledWidth, y, color);
return;
}
if (params.isDotted) {
for (int i = 0; i < scaledWidth; i += 2) { // Kurangi jarak antar titik
tft.fillCircle(x + i - scaledWidth/2, y, 1, color); // Ukuran titik lebih kecil
}
} else {
tft.fillEllipse(x, y, scaledWidth, scaledHeight, color);
if (params.sparkleSize > 0) {
tft.fillCircle(x + 1, y - 1, params.sparkleSize/2, accentColor); // Sparkle lebih kecil
tft.fillCircle(x - 1, y + 1, params.sparkleSize/4, TFT_WHITE);
}
}
}
void drawMouth(int x, int y, MouthParams params, uint16_t color) {
// Sesuaikan ukuran mulut
int scaledWidth = params.width * 0.6;
int scaledHeight = params.height * 0.6;
switch(params.style) {
case 0: // Curved mouth
for(int i = -scaledWidth/2; i <= scaledWidth/2; i++) {
int yOffset = (params.curve * i * i) / (scaledWidth/2);
tft.drawPixel(x + i, y + yOffset, color);
if (scaledHeight > 1) {
tft.drawPixel(x + i, y + yOffset + 1, color);
}
}
break;
case 1: // Straight line
tft.drawLine(x - scaledWidth/2, y, x + scaledWidth/2, y, color);
break;
case 2: // Zigzag
for(int i = -scaledWidth/2; i < scaledWidth/2; i += 2) {
tft.drawLine(x + i, y - 1, x + i + 1, y + 1, color);
tft.drawLine(x + i + 1, y + 1, x + i + 2, y - 1, color);
}
break;
}
}
// Sleep Management Functions
void displayExpression(int index) {
Expression& expr = expressions[index];
tft.fillScreen(TFT_BLACK);
// Adjust positions for 80x160 display
drawEye(25, 30, expr.leftEye, expr.primaryColor, expr.accentColor); // Left eye
drawEye(55, 30, expr.rightEye, expr.primaryColor, expr.accentColor); // Right eye
drawMouth(40, 70, expr.mouth, expr.primaryColor); // Mouth
// Battery indicator for charging and low battery states
if (strcmp(expr.name, "charging") == 0 || strcmp(expr.name, "low_battery") == 0) {
uint16_t indicatorColor = (strcmp(expr.name, "charging") == 0) ? TFT_GREEN : TFT_RED;
tft.drawRect(30, 10, 20, 5, indicatorColor);
tft.fillRect(30, 10, batteryLevel/5, 5, indicatorColor);
}
// Handle rainbow effect for special expressions
if (expr.primaryColor == TFT_RAINBOW) {
static uint8_t rainbowIndex = 0;
uint16_t rainbowColors[] = {TFT_RED, TFT_ORANGE, TFT_YELLOW, TFT_GREEN, TFT_BLUE, TFT_PURPLE};
// Use the cycled color for the next redraw
uint16_t currentRainbowColor = rainbowColors[rainbowIndex];
// Update expression and display elements with rainbow color
drawEye(25, 30, expr.leftEye, currentRainbowColor, expr.accentColor);
drawEye(55, 30, expr.rightEye, currentRainbowColor, expr.accentColor);
drawMouth(40, 70, expr.mouth, currentRainbowColor);
// Cycle to next color
rainbowIndex = (rainbowIndex + 1) % 6;
}
}
// Sleep Management Functions
void enterDeepSleep() {
setExpression(19); // shutting down expression
delay(2000); // Show expression briefly
// Save current state to RTC memory
Preferences preferences;
preferences.begin("robot-state", false);
preferences.putInt("energy", energyLevel);
preferences.putInt("mood", moodLevel);
preferences.end();
// Configure deep sleep
esp_sleep_enable_ext0_wakeup(GPIO_NUM_4, 1); // Wake on touch
esp_deep_sleep_start();
}
void wakeFromLightSleep() {
isInLightSleep = false;
setExpression(13); // waking expression
energyLevel = min(energyLevel + 20, 100);
lastInteractionTime = millis();
}
// Interaction Recording
void recordInteraction(int type) {
lastInteractions[interactionIndex].timestamp = millis();
lastInteractions[interactionIndex].type = type;
interactionIndex = (interactionIndex + 1) % 10;
}
// Sound Management
void playSound(const char* filename) {
if (!SPIFFS.exists(filename)) {
Serial.printf("File %s does not exist\n", filename);
return;
}
AudioFileSourceSPIFFS *audioFile = new AudioFileSourceSPIFFS(filename);
AudioGeneratorWAV *wav = new AudioGeneratorWAV();
wav->begin(audioFile, &audio);
while (wav->isRunning()) {
if (!wav->loop()) wav->stop();
}
delete wav;
delete audioFile;
}
void setup() {
Serial.begin(115200);
// Initialize pins
pinMode(TOUCH_PIN, INPUT);
pinMode(SHOCK_PIN, INPUT);
pinMode(VIBRATION_PIN, OUTPUT);
pinMode(BATTERY_PIN, INPUT);
// Initialize display
tft.init();
tft.setRotation(0);
tft.fillScreen(TFT_BLACK);
// Initialize audio
audio.SetPinout(SPEAKER_PIN, 26, 22);
audio.SetGain(0.5); // Set the volume to 50%
// Initialize SPIFFS
if (!SPIFFS.begin(true)) {
Serial.println("SPIFFS Mount Failed");
return;
}
// Restore state if waking from deep sleep
Preferences preferences;
preferences.begin("robot-state", true);
energyLevel = preferences.getInt("energy", 100);
moodLevel = preferences.getInt("mood", 50);
preferences.end();
// Set initial expression
setExpression(0);
// Initialize RTC
rtc.setTime(0, 0, 0, 1, 1, 2024); // Set initial time
// Show boot animation
for (int i = 0; i < 5; i++) {
tft.fillScreen(TFT_BLACK);
delay(100);
displayExpression(0);
delay(100);
}
lastInteractionTime = millis();
}
void loop() {
// Handle core functionalities
handleBattery();
handleSleepMode();
handleMoodChanges();
// Handle inputs
handleTouch();
handleShock();
// Update display if needed
static unsigned long lastUpdate = 0;
if (millis() - lastUpdate > 50) { // 20 FPS refresh rate
updateDisplay();
lastUpdate = millis();
}
// Handle expression timing
if (expressions[currentExpression].duration > 0) {
if (millis() - expressionStartTime > expressions[currentExpression].duration) {
setExpression(0); // Return to normal
}
}
// Random idle animations
if (millis() - lastInteractionTime > 10000) { // After 10 seconds of no interaction
if (random(100) < 5) { // 5% chance each loop
int randomExpr = random(61);
if (expressions[randomExpr].canInterrupt) {
setExpression(randomExpr);
}
}
}
delay(20); // Small delay to prevent too frequent updates
}