/**
* @file ESP32_DasaiMochi.ino
* @brief Main sketch for Dasai Mochi - Interactive Expression Display
*
* Hardware:
* - ESP32 Development Board
* - SSD1306 OLED Display (128x64, I2C)
* - TTP223 Touch Sensor
* - MPU6050 Accelerometer/Gyroscope
*
* Behavior:
* - Standby: Random expressions (Normal, Happy, Sad, Sleepy, Loving, Surprised)
* - Touch: Changes to surprised expression while touched
* - Shake: Becomes dizzy, then angry if shaking continues
*
* @author Tukucode
* @version 1
* @date 2026
*/
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <Adafruit_MPU6050.h>
#include <Adafruit_Sensor.h>
// Project headers
#include "config.h"
// Global display instance
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
// Include modules after display is declared
#include "expressions.h"
#include "sensors.h"
#include "state_machine.h"
#include "ai_service.h"
#include "sounds.h"
// Debug flag
#define DEBUG_MODE true
/**
* @brief Setup function - runs once at startup
*/
void setup() {
// Initialize serial for debugging
Serial.begin(115200);
delay(100);
Serial.println();
Serial.println("================================");
Serial.println(" Tuku Mochi v1 - Starting Up! ");
Serial.println("================================");
// Initialize I2C
Wire.begin(SDA_PIN, SCL_PIN);
// Initialize OLED display
if (!display.begin(SSD1306_SWITCHCAPVCC, OLED_ADDRESS)) {
Serial.println("ERROR: SSD1306 allocation failed!");
// Show error on serial and halt
while (true) {
Serial.println("Display init failed. Check wiring.");
delay(1000);
}
}
Serial.println("OLED display initialized");
// Clear display and show startup
display.clearDisplay();
display.setTextSize(2);
display.setTextColor(SSD1306_WHITE);
display.setCursor(5, 15);
display.println("Tuku Mochi");
display.setTextSize(1);
display.setCursor(30, 45);
display.println("by Tukucode");
display.display();
delay(2000);
// Initialize MPU6050
if (!initMPU6050()) {
Serial.println("WARNING: MPU6050 not found. Shake detection disabled.");
display.clearDisplay();
display.setCursor(10, 25);
display.println("MPU6050 not found");
display.setCursor(10, 40);
display.println("Shake disabled");
display.display();
delay(1500);
}
// Initialize touch sensor
initTouchSensor();
// Initialize LDR sensor
initLDRSensor();
// Initialize buzzer
pinMode(BUZZER_PIN, OUTPUT);
noTone(BUZZER_PIN);
// Initialize WiFi for AI
initWiFi();
// Create background AI task
xTaskCreate(
aiBackgroundTask, /* Task function. */
"AITask", /* name of task. */
8192, /* Stack size of task */
NULL, /* parameter of the task */
1, /* priority of the task */
&aiTaskHandle /* Task handle to keep track of created task */
);
// Initialize random seed
randomSeed(analogRead(0));
// Load persistent settings (Brightness, Invert)
loadPersistentSettings();
// Start in standby state with initial expression
transitionToState(STATE_STANDBY);
Serial.println("================================");
Serial.println(" Initialization Complete! ");
Serial.println("================================");
Serial.println();
}
/**
* @brief Main loop - runs continuously
*/
void loop() {
// Update the state machine (handles all logic)
updateStateMachine();
// Handle AI Serial Communication
handleSerialAI();
// Debug output (every 2 seconds)
#if DEBUG_MODE
static unsigned long lastDebugTime = 0;
if (millis() - lastDebugTime > 2000) {
Serial.print("State: ");
Serial.print(getStateName());
Serial.print(" | Expression: ");
Serial.print(getExpressionName());
Serial.print(" | Touch: ");
Serial.print(isTouchActive() ? "YES" : "NO");
Serial.print(" | LDR: ");
Serial.println(isLDRActive() ? "YES" : "NO");
Serial.print(" | Shake: ");
Serial.println(isShaking() ? "YES" : "NO");
lastDebugTime = millis();
}
#endif
// Small delay to prevent CPU hogging
delay(10);
}