pleasedontcode

# Emotion Display rev_02

Mar 9th, 2026
24
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Arduino 29.67 KB | None | 0 0
  1. /********* Pleasedontcode.com **********
  2.  
  3.     Pleasedontcode thanks you for automatic code generation! Enjoy your code!
  4.  
  5.     - Terms and Conditions:
  6.     You have a non-exclusive, revocable, worldwide, royalty-free license
  7.     for personal and commercial use. Attribution is optional; modifications
  8.     are allowed, but you're responsible for code maintenance. We're not
  9.     liable for any loss or damage. For full terms,
  10.     please visit pleasedontcode.com/termsandconditions.
  11.  
  12.     - Project: # Emotion Display
  13.     - Version: 002
  14.     - Source Code NOT compiled for: ESP32S3 Dev Module
  15.     - Source Code created on: 2026-03-09 21:11:32
  16.  
  17. ********* Pleasedontcode.com **********/
  18.  
  19. /****** SYSTEM REQUIREMENTS *****/
  20. /****** SYSTEM REQUIREMENT 1 *****/
  21.     /* Procedural face animation system: render eyes with */
  22.     /* physics-based blinking, pupil tracking with */
  23.     /* inertia damping, iris glow effects, eyelids, */
  24.     /* eyebrows, mouth expressions, cheeks, blush, nose */
  25.     /* on 128x64 OLED at 45FPS */
  26. /****** SYSTEM REQUIREMENT 2 *****/
  27.     /* Emotion system with 8 states: neutral, happy, sad, */
  28.     /* excited, sleepy, curious, shy, playful. Each */
  29.     /* emotion modifies facial features, animation speed, */
  30.     /* and micro-movement patterns for lifelike */
  31.     /* expression */
  32. /****** SYSTEM REQUIREMENT 3 *****/
  33.     /* Touch button input cycles through emotions */
  34.     /* sequentially. When idle (no touch for 5 seconds), */
  35.     /* enter autonomous baby-like behavior mode with */
  36.     /* random gestures, exploration, micro-movements, and */
  37.     /* periodic state changes */
  38. /****** SYSTEM REQUIREMENT 4 *****/
  39.     /* Modular architecture with separate animation */
  40.     /* modules, physics engine, emotion state machine. */
  41.     /* Code fully commented, optimized for ESP32-S3 */
  42.     /* performance, minimal memory footprint */
  43. /****** END SYSTEM REQUIREMENTS *****/
  44.  
  45.  
  46. /* START CODE */
  47.  
  48. /****** DEFINITION OF LIBRARIES *****/
  49. #include <Adafruit_SSD1306.h>  // https://github.com/adafruit/Adafruit_SSD1306
  50. #include <Adafruit_GFX.h>      // https://github.com/adafruit/Adafruit-GFX-Library
  51. #include <EasyButton.h>        // https://github.com/evert-arias/EasyButton
  52.  
  53. /****** SYSTEM REQUIREMENTS *****/
  54. // SR1: Procedural face animation system: render eyes with physics-based blinking,
  55. //      pupil tracking with inertia damping, iris glow effects, eyelids, eyebrows,
  56. //      mouth expressions, cheeks, blush, nose on 128x64 OLED at 45FPS
  57. // SR2: Emotion system with 8 states: neutral, happy, sad, excited, sleepy, curious,
  58. //      shy, playful. Each emotion modifies facial features, animation speed, and
  59. //      micro-movement patterns for lifelike expression
  60. // SR3: Touch button input cycles through emotions sequentially. When idle (no touch
  61. //      for 5 seconds), enter autonomous baby-like behavior mode with random gestures,
  62. //      exploration, micro-movements, and periodic state changes
  63. // SR4: Modular architecture with separate animation modules, physics engine, emotion
  64. //      state machine. Code fully commented, optimized for ESP32-S3 performance,
  65. //      minimal memory footprint
  66.  
  67. /****** FUNCTION PROTOTYPES *****/
  68. void setup(void);
  69. void loop(void);
  70.  
  71. /***** DEFINITION OF DIGITAL INPUT PINS *****/
  72. const uint8_t faceDisplay_PushButton_PIN_D1 = 1;
  73.  
  74. /***** DEFINITION OF DIGITAL OUTPUT PINS *****/
  75. const uint8_t emotionButton_LED_PIN_D2 = 2;
  76.  
  77. /****** OLED DISPLAY CONFIGURATION *****/
  78. #define SCREEN_WIDTH 128
  79. #define SCREEN_HEIGHT 64
  80. #define OLED_RESET -1
  81. #define SCREEN_ADDRESS 0x3C
  82.  
  83. // Create display instance using I2C
  84. Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
  85.  
  86. /****** EASY BUTTON CONFIGURATION *****/
  87. EasyButton button(faceDisplay_PushButton_PIN_D1);
  88.  
  89. /****** EMOTION ENUMERATION *****/
  90. enum Emotion {
  91.   NEUTRAL = 0,
  92.   HAPPY = 1,
  93.   SAD = 2,
  94.   EXCITED = 3,
  95.   SLEEPY = 4,
  96.   CURIOUS = 5,
  97.   SHY = 6,
  98.   PLAYFUL = 7
  99. };
  100.  
  101. /****** PHYSICS ENGINE STRUCTURE *****/
  102. struct PhysicsState {
  103.   // Eye pupil tracking physics
  104.   float pupilX;           // Current pupil X position (-1.0 to 1.0)
  105.   float pupilY;           // Current pupil Y position (-1.0 to 1.0)
  106.   float pupilVelX;        // Pupil velocity X (inertia)
  107.   float pupilVelY;        // Pupil velocity Y (inertia)
  108.   float targetPupilX;     // Target pupil X position
  109.   float targetPupilY;     // Target pupil Y position
  110.  
  111.   // Blinking physics
  112.   float blinkValue;       // 0.0 (open) to 1.0 (closed)
  113.   float blinkVel;         // Blinking velocity for smooth animation
  114.   bool isBlinking;        // Currently blinking
  115.   unsigned long blinkStartTime;
  116.  
  117.   // Eyelid animation
  118.   float eyelidOffset;     // Additional eyelid movement beyond blink
  119.  
  120.   // Eyebrow animation
  121.   float eyebrowAngle;     // Eyebrow rotation in degrees
  122.   float eyebrowTargetAngle;
  123.  
  124.   // Iris glow
  125.   float irisGlow;         // 0.0 to 1.0 glow intensity
  126.  
  127.   // Mouth expression
  128.   float mouthHeight;      // Mouth curvature
  129.   float mouthAngle;       // Mouth angle/tilt
  130.  
  131.   // Cheek blush
  132.   float cheekBlush;       // Cheek color intensity
  133.  
  134.   // Nose animation
  135.   float noseWiggle;       // Nose movement micro-animation
  136. };
  137.  
  138. /****** ANIMATION STATE STRUCTURE *****/
  139. struct AnimationState {
  140.   Emotion currentEmotion;
  141.   Emotion targetEmotion;
  142.  
  143.   // Autonomous behavior
  144.   bool autonomousMode;
  145.   unsigned long lastInputTime;
  146.   unsigned long idleThreshold;  // 5000ms = 5 seconds
  147.  
  148.   // Animation timing
  149.   unsigned long frameTime;      // Current frame timestamp
  150.   unsigned long lastFrameTime;
  151.   float deltaTime;              // Time since last frame in seconds
  152.   uint32_t frameCount;          // Total frames rendered
  153.  
  154.   // Animation phase for cyclic behaviors
  155.   float animationPhase;         // 0.0 to 1.0, wraps around
  156.   float phaseVelocity;          // Controls animation speed based on emotion
  157.  
  158.   // Micro-movement - FIXED: moved from PhysicsState to AnimationState
  159.   float microMoveX;             // Subtle position jitter
  160.   float microMoveY;             // Subtle position jitter
  161.  
  162.   // Gaze direction
  163.   float gazeX;                  // Where the eyes are looking
  164.   float gazeY;
  165. };
  166.  
  167. /****** EMOTION CONFIGURATION STRUCTURE *****/
  168. struct EmotionConfig {
  169.   float animationSpeed;         // How fast animations play
  170.   float blinkFrequency;         // How often to blink (blinks per second)
  171.   float pupilTrackingIntensity; // How responsive pupil is to gaze
  172.   float microMoveAmount;        // Amount of micro-movement jitter
  173.   int8_t eyebrowAngle;          // Base eyebrow angle in degrees
  174.   float mouthHeight;            // Base mouth curve height
  175.   float cheekColor;             // Cheek blush intensity (0.0-1.0)
  176.   float irisGlowIntensity;      // Iris glow brightness
  177. };
  178.  
  179. /****** GLOBAL STATE VARIABLES *****/
  180. PhysicsState physics;
  181. AnimationState animation;
  182. EmotionConfig emotionConfigs[8];
  183.  
  184. /****** INITIALIZE EMOTION CONFIGURATIONS *****/
  185. void initializeEmotionConfigs() {
  186.   // NEUTRAL: calm, baseline state
  187.   emotionConfigs[NEUTRAL] = {
  188.     1.0f,      // animationSpeed
  189.     1.5f,      // blinkFrequency (blinks per second)
  190.     0.7f,      // pupilTrackingIntensity
  191.     0.5f,      // microMoveAmount
  192.     0,         // eyebrowAngle
  193.     0.3f,      // mouthHeight
  194.     0.1f,      // cheekColor
  195.     0.3f       // irisGlowIntensity
  196.   };
  197.  
  198.   // HAPPY: cheerful, animated
  199.   emotionConfigs[HAPPY] = {
  200.     1.5f,      // animationSpeed (faster)
  201.     2.0f,      // blinkFrequency
  202.     0.8f,      // pupilTrackingIntensity
  203.     1.0f,      // microMoveAmount (more movement)
  204.     15,        // eyebrowAngle (raised)
  205.     0.7f,      // mouthHeight (big smile)
  206.     0.6f,      // cheekColor (strong blush)
  207.     0.5f       // irisGlowIntensity
  208.   };
  209.  
  210.   // SAD: depressed, slower movements
  211.   emotionConfigs[SAD] = {
  212.     0.5f,      // animationSpeed (slower)
  213.     0.8f,      // blinkFrequency
  214.     0.5f,      // pupilTrackingIntensity
  215.     0.2f,      // microMoveAmount
  216.     -15,       // eyebrowAngle (lowered)
  217.     -0.5f,     // mouthHeight (frown)
  218.     0.2f,      // cheekColor
  219.     0.1f       // irisGlowIntensity
  220.   };
  221.  
  222.   // EXCITED: very animated, rapid blinks
  223.   emotionConfigs[EXCITED] = {
  224.     2.0f,      // animationSpeed (very fast)
  225.     3.0f,      // blinkFrequency
  226.     0.9f,      // pupilTrackingIntensity
  227.     1.5f,      // microMoveAmount
  228.     20,        // eyebrowAngle
  229.     0.8f,      // mouthHeight
  230.     0.7f,      // cheekColor
  231.     0.7f       // irisGlowIntensity
  232.   };
  233.  
  234.   // SLEEPY: slow, droopy eyes
  235.   emotionConfigs[SLEEPY] = {
  236.     0.3f,      // animationSpeed
  237.     0.3f,      // blinkFrequency (frequent half-blinks)
  238.     0.3f,      // pupilTrackingIntensity
  239.     0.1f,      // microMoveAmount
  240.     -20,       // eyebrowAngle (relaxed)
  241.     0.2f,      // mouthHeight
  242.     0.05f,     // cheekColor
  243.     0.05f      // irisGlowIntensity
  244.   };
  245.  
  246.   // CURIOUS: searching movements, alert
  247.   emotionConfigs[CURIOUS] = {
  248.     1.3f,      // animationSpeed
  249.     1.2f,      // blinkFrequency
  250.     1.0f,      // pupilTrackingIntensity (maximum)
  251.     0.8f,      // microMoveAmount
  252.     10,        // eyebrowAngle
  253.     0.4f,      // mouthHeight
  254.     0.2f,      // cheekColor
  255.     0.6f       // irisGlowIntensity
  256.   };
  257.  
  258.   // SHY: minimal movements, down-looking
  259.   emotionConfigs[SHY] = {
  260.     0.6f,      // animationSpeed
  261.     1.0f,      // blinkFrequency
  262.     0.4f,      // pupilTrackingIntensity
  263.     0.3f,      // microMoveAmount
  264.     -10,       // eyebrowAngle
  265.     0.1f,      // mouthHeight
  266.     0.4f,      // cheekColor (blushing)
  267.     0.2f       // irisGlowIntensity
  268.   };
  269.  
  270.   // PLAYFUL: bouncy, dynamic movements
  271.   emotionConfigs[PLAYFUL] = {
  272.     1.8f,      // animationSpeed
  273.     2.5f,      // blinkFrequency
  274.     0.85f,     // pupilTrackingIntensity
  275.     1.3f,      // microMoveAmount
  276.     12,        // eyebrowAngle
  277.     0.6f,      // mouthHeight
  278.     0.5f,      // cheekColor
  279.     0.6f       // irisGlowIntensity
  280.   };
  281. }
  282.  
  283. /****** PHYSICS ENGINE: UPDATE PUPIL TRACKING *****/
  284. void updatePupilTracking(float deltaTime) {
  285.   EmotionConfig& config = emotionConfigs[animation.currentEmotion];
  286.  
  287.   // Calculate target pupil position based on gaze
  288.   physics.targetPupilX = animation.gazeX * config.pupilTrackingIntensity;
  289.   physics.targetPupilY = animation.gazeY * config.pupilTrackingIntensity;
  290.  
  291.   // Inertia damping: smooth acceleration towards target
  292.   float inertiaFactor = 5.0f * config.pupilTrackingIntensity;
  293.   float maxVelocity = 2.0f;
  294.  
  295.   // Update velocity with acceleration towards target
  296.   physics.pupilVelX += (physics.targetPupilX - physics.pupilX) * inertiaFactor * deltaTime;
  297.   physics.pupilVelY += (physics.targetPupilY - physics.pupilY) * inertiaFactor * deltaTime;
  298.  
  299.   // Clamp velocity
  300.   physics.pupilVelX = constrain(physics.pupilVelX, -maxVelocity, maxVelocity);
  301.   physics.pupilVelY = constrain(physics.pupilVelY, -maxVelocity, maxVelocity);
  302.  
  303.   // Apply damping (friction)
  304.   float damping = 0.92f;
  305.   physics.pupilVelX *= damping;
  306.   physics.pupilVelY *= damping;
  307.  
  308.   // Update position
  309.   physics.pupilX += physics.pupilVelX * deltaTime;
  310.   physics.pupilY += physics.pupilVelY * deltaTime;
  311.  
  312.   // Clamp pupil position within eye bounds (-1.0 to 1.0)
  313.   physics.pupilX = constrain(physics.pupilX, -1.0f, 1.0f);
  314.   physics.pupilY = constrain(physics.pupilY, -1.0f, 1.0f);
  315. }
  316.  
  317. /****** PHYSICS ENGINE: UPDATE BLINKING *****/
  318. void updateBlinking(float deltaTime) {
  319.   EmotionConfig& config = emotionConfigs[animation.currentEmotion];
  320.  
  321.   // Calculate blink duration based on emotion
  322.   float blinkDuration = 0.15f;  // 150ms standard blink
  323.   float blinkInterval = 1.0f / config.blinkFrequency;
  324.  
  325.   if (!physics.isBlinking) {
  326.     // Check if it's time to blink
  327.     float timeSinceBlink = (millis() - physics.blinkStartTime) / 1000.0f;
  328.    
  329.     // Add some randomness to blink timing
  330.     float randomBlink = 0.8f + (random(40) / 100.0f);  // 0.8 to 1.2 variation
  331.    
  332.     if (timeSinceBlink > blinkInterval * randomBlink) {
  333.       physics.isBlinking = true;
  334.       physics.blinkStartTime = millis();
  335.       physics.blinkVel = 4.0f / blinkDuration;  // Velocity to close eyes in blinkDuration
  336.     }
  337.   } else {
  338.     // Animate blinking
  339.     float timeSinceBlink = (millis() - physics.blinkStartTime) / 1000.0f;
  340.    
  341.     if (timeSinceBlink < 0.075f) {
  342.       // Closing phase (first 75ms)
  343.       physics.blinkValue += physics.blinkVel * deltaTime;
  344.       physics.blinkValue = min(physics.blinkValue, 1.0f);
  345.     } else if (timeSinceBlink < 0.15f) {
  346.       // Opening phase (next 75ms)
  347.       physics.blinkValue -= physics.blinkVel * deltaTime;
  348.       physics.blinkValue = max(physics.blinkValue, 0.0f);
  349.     } else {
  350.       // Blink complete
  351.       physics.isBlinking = false;
  352.       physics.blinkValue = 0.0f;
  353.       physics.blinkStartTime = millis();
  354.     }
  355.   }
  356. }
  357.  
  358. /****** PHYSICS ENGINE: UPDATE EYEBROW ANIMATION *****/
  359. void updateEyebrows(float deltaTime) {
  360.   EmotionConfig& config = emotionConfigs[animation.currentEmotion];
  361.  
  362.   // Base eyebrow angle from emotion config
  363.   physics.eyebrowTargetAngle = config.eyebrowAngle;
  364.  
  365.   // Add micro-animations based on emotion phase
  366.   float emotionInfluence = sin(animation.animationPhase * M_PI * 2.0f);
  367.  
  368.   switch(animation.currentEmotion) {
  369.     case HAPPY:
  370.     case EXCITED:
  371.       physics.eyebrowTargetAngle += emotionInfluence * 5.0f;
  372.       break;
  373.     case CURIOUS:
  374.       physics.eyebrowTargetAngle += abs(emotionInfluence) * 3.0f;
  375.       break;
  376.     case SHY:
  377.       physics.eyebrowTargetAngle -= abs(emotionInfluence) * 2.0f;
  378.       break;
  379.     default:
  380.       break;
  381.   }
  382.  
  383.   // Smoothly interpolate to target angle
  384.   float lerpFactor = 8.0f * deltaTime;
  385.   physics.eyebrowAngle += (physics.eyebrowTargetAngle - physics.eyebrowAngle) * lerpFactor;
  386. }
  387.  
  388. /****** PHYSICS ENGINE: UPDATE MOUTH EXPRESSION *****/
  389. void updateMouthExpression(float deltaTime) {
  390.   EmotionConfig& config = emotionConfigs[animation.currentEmotion];
  391.  
  392.   // Base mouth height from emotion
  393.   float targetMouthHeight = config.mouthHeight;
  394.  
  395.   // Add animation variation
  396.   float animVariation = sin(animation.animationPhase * M_PI * 2.0f);
  397.   targetMouthHeight += animVariation * 0.1f * config.animationSpeed;
  398.  
  399.   // Add mouth angle (smile tilt)
  400.   physics.mouthAngle = config.eyebrowAngle * 0.3f;
  401.  
  402.   // Smooth interpolation
  403.   float lerpFactor = 6.0f * deltaTime;
  404.   physics.mouthHeight += (targetMouthHeight - physics.mouthHeight) * lerpFactor;
  405. }
  406.  
  407. /****** PHYSICS ENGINE: UPDATE CHEEK BLUSH *****/
  408. void updateCheeks(float deltaTime) {
  409.   EmotionConfig& config = emotionConfigs[animation.currentEmotion];
  410.  
  411.   // Base cheek blush from emotion
  412.   float targetCheekBlush = config.cheekColor;
  413.  
  414.   // Pulse the blush slightly based on animation phase
  415.   float pulseInfluence = (sin(animation.animationPhase * M_PI * 2.0f) + 1.0f) * 0.5f;
  416.   targetCheekBlush += pulseInfluence * 0.15f * config.animationSpeed;
  417.  
  418.   // Smooth interpolation
  419.   float lerpFactor = 3.0f * deltaTime;
  420.   physics.cheekBlush += (targetCheekBlush - physics.cheekBlush) * lerpFactor;
  421.   physics.cheekBlush = constrain(physics.cheekBlush, 0.0f, 1.0f);
  422. }
  423.  
  424. /****** PHYSICS ENGINE: UPDATE IRIS GLOW *****/
  425. void updateIrisGlow(float deltaTime) {
  426.   EmotionConfig& config = emotionConfigs[animation.currentEmotion];
  427.  
  428.   // Base glow intensity from emotion
  429.   float targetGlow = config.irisGlowIntensity;
  430.  
  431.   // Pulse glow subtly
  432.   float glowPulse = (sin(animation.animationPhase * M_PI * 4.0f) + 1.0f) * 0.5f;
  433.   targetGlow += glowPulse * 0.1f;
  434.  
  435.   // Smooth interpolation
  436.   float lerpFactor = 4.0f * deltaTime;
  437.   physics.irisGlow += (targetGlow - physics.irisGlow) * lerpFactor;
  438.   physics.irisGlow = constrain(physics.irisGlow, 0.0f, 1.0f);
  439. }
  440.  
  441. /****** PHYSICS ENGINE: UPDATE NOSE WIGGLE *****/
  442. void updateNose(float deltaTime) {
  443.   // Nose wiggles subtly with high-frequency animation
  444.   physics.noseWiggle = sin(animation.animationPhase * M_PI * 8.0f) * 0.5f;
  445. }
  446.  
  447. /****** PHYSICS ENGINE: UPDATE MICRO-MOVEMENTS *****/
  448. void updateMicroMovements(float deltaTime) {
  449.   EmotionConfig& config = emotionConfigs[animation.currentEmotion];
  450.  
  451.   // Generate micro-movement using noise-like pattern
  452.   float time = millis() / 1000.0f;
  453.  
  454.   // Use sine waves at different frequencies to create natural micro-jitter
  455.   // FIXED: Now correctly assigning to animation.microMoveX and animation.microMoveY
  456.   animation.microMoveX = sin(time * 2.3f) * 0.5f + sin(time * 3.7f) * 0.3f;
  457.   animation.microMoveY = cos(time * 1.9f) * 0.5f + cos(time * 4.1f) * 0.3f;
  458.  
  459.   // Scale by emotion's micro-move amount
  460.   animation.microMoveX *= config.microMoveAmount;
  461.   animation.microMoveY *= config.microMoveAmount;
  462. }
  463.  
  464. /****** ANIMATION ENGINE: UPDATE GAZE *****/
  465. void updateGaze(float deltaTime) {
  466.   // Generate gaze pattern based on emotion
  467.   float time = millis() / 1000.0f;
  468.  
  469.   switch(animation.currentEmotion) {
  470.     case NEUTRAL:
  471.       // Gentle scanning
  472.       animation.gazeX = sin(time * 0.5f) * 0.7f;
  473.       animation.gazeY = cos(time * 0.7f) * 0.5f;
  474.       break;
  475.      
  476.     case HAPPY:
  477.       // Looking upward and around
  478.       animation.gazeX = sin(time * 0.8f) * 0.8f;
  479.       animation.gazeY = 0.5f + cos(time * 0.6f) * 0.3f;
  480.       break;
  481.      
  482.     case SAD:
  483.       // Looking downward
  484.       animation.gazeX = sin(time * 0.3f) * 0.4f;
  485.       animation.gazeY = -0.6f + sin(time * 0.5f) * 0.2f;
  486.       break;
  487.      
  488.     case EXCITED:
  489.       // Rapid, wide scanning
  490.       animation.gazeX = sin(time * 1.5f);
  491.       animation.gazeY = cos(time * 1.8f);
  492.       break;
  493.      
  494.     case SLEEPY:
  495.       // Slowly drooping gaze
  496.       animation.gazeX = sin(time * 0.2f) * 0.3f;
  497.       animation.gazeY = -0.5f - abs(sin(time * 0.3f)) * 0.3f;
  498.       break;
  499.      
  500.     case CURIOUS:
  501.       // Focused searching
  502.       animation.gazeX = sin(time * 1.2f) * 0.9f;
  503.       animation.gazeY = cos(time * 1.0f) * 0.8f;
  504.       break;
  505.      
  506.     case SHY:
  507.       // Shy glancing down
  508.       animation.gazeX = sin(time * 0.4f) * 0.3f;
  509.       animation.gazeY = -0.7f + sin(time * 0.6f) * 0.2f;
  510.       break;
  511.      
  512.     case PLAYFUL:
  513.       // Bouncy, playful eye movement
  514.       animation.gazeX = sin(time * 1.3f) * 0.85f;
  515.       animation.gazeY = abs(sin(time * 1.6f)) * 0.7f;
  516.       break;
  517.   }
  518.  
  519.   // Clamp gaze to valid range
  520.   animation.gazeX = constrain(animation.gazeX, -1.0f, 1.0f);
  521.   animation.gazeY = constrain(animation.gazeY, -1.0f, 1.0f);
  522. }
  523.  
  524. /****** ANIMATION ENGINE: UPDATE AUTONOMOUS BEHAVIOR *****/
  525. void updateAutonomousBehavior(float deltaTime) {
  526.   // Generate random gestures and state changes in autonomous mode
  527.   static float autonomousPhase = 0.0f;
  528.  
  529.   // Slowly accumulate phase
  530.   autonomousPhase += deltaTime * 0.2f;
  531.   if (autonomousPhase > 1.0f) {
  532.     autonomousPhase -= 1.0f;
  533.    
  534.     // Randomly change emotion during autonomous mode
  535.     if (random(100) < 30) {  // 30% chance each cycle
  536.       animation.targetEmotion = (Emotion)(random(8));
  537.     }
  538.   }
  539.  
  540.   // Add more pronounced micro-movements in autonomous mode
  541.   float autonomousJitter = sin(millis() / 500.0f) * 0.3f;
  542.   animation.microMoveX += autonomousJitter;
  543.   animation.microMoveY += cos(millis() / 700.0f) * 0.3f;
  544. }
  545.  
  546. /****** ANIMATION ENGINE: UPDATE ANIMATION PHASE *****/
  547. void updateAnimationPhase(float deltaTime) {
  548.   EmotionConfig& config = emotionConfigs[animation.currentEmotion];
  549.  
  550.   // Update animation phase (cycles 0.0 to 1.0)
  551.   animation.animationPhase += deltaTime * config.animationSpeed;
  552.   if (animation.animationPhase > 1.0f) {
  553.     animation.animationPhase -= 1.0f;
  554.   }
  555. }
  556.  
  557. /****** ANIMATION ENGINE: UPDATE EMOTION TRANSITION *****/
  558. void updateEmotionTransition(float deltaTime) {
  559.   // Smooth transition between emotions
  560.   if (animation.currentEmotion != animation.targetEmotion) {
  561.     // Immediate emotion change (can be made smoother if needed)
  562.     animation.currentEmotion = animation.targetEmotion;
  563.   }
  564. }
  565.  
  566. /****** ANIMATION ENGINE: MAIN UPDATE LOOP *****/
  567. void updateAnimation() {
  568.   // Calculate delta time
  569.   unsigned long currentTime = millis();
  570.   animation.frameTime = currentTime;
  571.  
  572.   if (animation.lastFrameTime == 0) {
  573.     animation.lastFrameTime = currentTime;
  574.   }
  575.  
  576.   animation.deltaTime = (currentTime - animation.lastFrameTime) / 1000.0f;
  577.   animation.lastFrameTime = currentTime;
  578.  
  579.   // Cap delta time to prevent physics instability
  580.   if (animation.deltaTime > 0.033f) {
  581.     animation.deltaTime = 0.033f;
  582.   }
  583.  
  584.   // Update physics systems
  585.   updatePupilTracking(animation.deltaTime);
  586.   updateBlinking(animation.deltaTime);
  587.   updateEyebrows(animation.deltaTime);
  588.   updateMouthExpression(animation.deltaTime);
  589.   updateCheeks(animation.deltaTime);
  590.   updateIrisGlow(animation.deltaTime);
  591.   updateNose(animation.deltaTime);
  592.   updateMicroMovements(animation.deltaTime);
  593.   updateGaze(animation.deltaTime);
  594.  
  595.   // Update animation control
  596.   updateAnimationPhase(animation.deltaTime);
  597.   updateEmotionTransition(animation.deltaTime);
  598.  
  599.   // Update autonomous behavior if active
  600.   if (animation.autonomousMode) {
  601.     updateAutonomousBehavior(animation.deltaTime);
  602.   }
  603.  
  604.   // Increment frame count
  605.   animation.frameCount++;
  606. }
  607.  
  608. /****** RENDERING: DRAW LEFT EYE *****/
  609. void drawLeftEye(int16_t centerX, int16_t centerY, int16_t radius) {
  610.   // Eye white (sclera)
  611.   display.fillCircle(centerX, centerY, radius, SSD1306_WHITE);
  612.  
  613.   // Apply eyelid closure (blink effect)
  614.   if (physics.blinkValue > 0.1f) {
  615.     int16_t eyelidHeight = (int16_t)(radius * 2.0f * physics.blinkValue);
  616.     // Top eyelid
  617.     display.fillRect(centerX - radius, centerY - radius, radius * 2, eyelidHeight, SSD1306_BLACK);
  618.   }
  619.  
  620.   // Calculate pupil position with physics
  621.   int16_t pupilX = centerX + (int16_t)(physics.pupilX * (radius - 5));
  622.   int16_t pupilY = centerY + (int16_t)(physics.pupilY * (radius - 5));
  623.  
  624.   // Iris (darker circle)
  625.   display.fillCircle(pupilX, pupilY, radius / 2, SSD1306_BLACK);
  626.  
  627.   // Pupil (very dark center)
  628.   display.fillCircle(pupilX, pupilY, radius / 3, SSD1306_BLACK);
  629.  
  630.   // Iris glow effect (small highlights)
  631.   if (physics.irisGlow > 0.0f) {
  632.     int8_t glowIntensity = (int8_t)(physics.irisGlow * 255);
  633.     if (glowIntensity > 50) {
  634.       display.drawCircle(pupilX - 2, pupilY - 2, 2, SSD1306_WHITE);
  635.     }
  636.   }
  637. }
  638.  
  639. /****** RENDERING: DRAW RIGHT EYE *****/
  640. void drawRightEye(int16_t centerX, int16_t centerY, int16_t radius) {
  641.   // Eye white (sclera)
  642.   display.fillCircle(centerX, centerY, radius, SSD1306_WHITE);
  643.  
  644.   // Apply eyelid closure (blink effect)
  645.   if (physics.blinkValue > 0.1f) {
  646.     int16_t eyelidHeight = (int16_t)(radius * 2.0f * physics.blinkValue);
  647.     // Top eyelid
  648.     display.fillRect(centerX - radius, centerY - radius, radius * 2, eyelidHeight, SSD1306_BLACK);
  649.   }
  650.  
  651.   // Calculate pupil position with physics
  652.   int16_t pupilX = centerX + (int16_t)(physics.pupilX * (radius - 5));
  653.   int16_t pupilY = centerY + (int16_t)(physics.pupilY * (radius - 5));
  654.  
  655.   // Iris (darker circle)
  656.   display.fillCircle(pupilX, pupilY, radius / 2, SSD1306_BLACK);
  657.  
  658.   // Pupil (very dark center)
  659.   display.fillCircle(pupilX, pupilY, radius / 3, SSD1306_BLACK);
  660.  
  661.   // Iris glow effect (small highlights)
  662.   if (physics.irisGlow > 0.0f) {
  663.     int8_t glowIntensity = (int8_t)(physics.irisGlow * 255);
  664.     if (glowIntensity > 50) {
  665.       display.drawCircle(pupilX - 2, pupilY - 2, 2, SSD1306_WHITE);
  666.     }
  667.   }
  668. }
  669.  
  670. /****** RENDERING: DRAW EYEBROWS *****/
  671. void drawEyebrows(int16_t leftEyeX, int16_t rightEyeX, int16_t eyeY, int16_t eyeRadius) {
  672.   int16_t eyebrowY = eyeY - eyeRadius - 8;
  673.   int16_t eyebrowWidth = eyeRadius + 5;
  674.   int16_t eyebrowHeight = 3;
  675.  
  676.   // Convert angle to slope for drawing
  677.   int8_t angleOffset = (int8_t)(physics.eyebrowAngle * 0.3f);
  678.  
  679.   // Left eyebrow (with angle)
  680.   display.drawLine(leftEyeX - eyebrowWidth, eyebrowY + angleOffset,
  681.                    leftEyeX + eyebrowWidth, eyebrowY - angleOffset, SSD1306_WHITE);
  682.   display.drawLine(leftEyeX - eyebrowWidth, eyebrowY + angleOffset + 1,
  683.                    leftEyeX + eyebrowWidth, eyebrowY - angleOffset + 1, SSD1306_WHITE);
  684.  
  685.   // Right eyebrow (with angle)
  686.   display.drawLine(rightEyeX - eyebrowWidth, eyebrowY - angleOffset,
  687.                    rightEyeX + eyebrowWidth, eyebrowY + angleOffset, SSD1306_WHITE);
  688.   display.drawLine(rightEyeX - eyebrowWidth, eyebrowY - angleOffset + 1,
  689.                    rightEyeX + eyebrowWidth, eyebrowY + angleOffset + 1, SSD1306_WHITE);
  690. }
  691.  
  692. /****** RENDERING: DRAW MOUTH *****/
  693. void drawMouth(int16_t centerX, int16_t centerY) {
  694.   int16_t mouthWidth = 20;
  695.   int16_t mouthY = centerY + 10;
  696.  
  697.   // Mouth curve based on expression
  698.   int16_t curveHeight = (int16_t)(physics.mouthHeight * 8.0f);
  699.  
  700.   if (curveHeight > 0) {
  701.     // Happy/smiling mouth (arc)
  702.     for (int16_t x = -mouthWidth; x <= mouthWidth; x += 2) {
  703.       int16_t y = (int16_t)(curveHeight - (x * x) / (float)(mouthWidth * mouthWidth) * curveHeight);
  704.       display.drawPixel(centerX + x, mouthY + y, SSD1306_WHITE);
  705.     }
  706.   } else if (curveHeight < 0) {
  707.     // Sad mouth (inverted arc)
  708.     for (int16_t x = -mouthWidth; x <= mouthWidth; x += 2) {
  709.       int16_t y = (int16_t)(curveHeight + (x * x) / (float)(mouthWidth * mouthWidth) * abs(curveHeight));
  710.       display.drawPixel(centerX + x, mouthY + y, SSD1306_WHITE);
  711.     }
  712.   } else {
  713.     // Neutral mouth (straight line)
  714.     display.drawLine(centerX - mouthWidth, mouthY, centerX + mouthWidth, mouthY, SSD1306_WHITE);
  715.   }
  716. }
  717.  
  718. /****** RENDERING: DRAW NOSE *****/
  719. void drawNose(int16_t centerX, int16_t centerY) {
  720.   int16_t noseX = centerX + (int16_t)(physics.noseWiggle * 2.0f);
  721.   int16_t noseY = centerY - 2;
  722.  
  723.   // Simple nose (small triangle)
  724.   display.drawLine(noseX, noseY, noseX - 2, noseY + 3, SSD1306_WHITE);
  725.   display.drawLine(noseX, noseY, noseX + 2, noseY + 3, SSD1306_WHITE);
  726.   display.drawLine(noseX - 2, noseY + 3, noseX + 2, noseY + 3, SSD1306_WHITE);
  727. }
  728.  
  729. /****** RENDERING: DRAW CHEEKS *****/
  730. void drawCheeks(int16_t leftEyeX, int16_t rightEyeX, int16_t eyeY) {
  731.   int16_t cheekRadius = 5;
  732.   int16_t cheekY = eyeY + 10;
  733.  
  734.   // Cheek blush intensity determines if we draw
  735.   if (physics.cheekBlush > 0.1f) {
  736.     // Left cheek
  737.     for (int16_t i = 0; i < cheekRadius; i++) {
  738.       display.drawCircle(leftEyeX - 20, cheekY, i, SSD1306_WHITE);
  739.     }
  740.    
  741.     // Right cheek
  742.     for (int16_t i = 0; i < cheekRadius; i++) {
  743.       display.drawCircle(rightEyeX + 20, cheekY, i, SSD1306_WHITE);
  744.     }
  745.   }
  746. }
  747.  
  748. /****** RENDERING: MAIN DRAW FUNCTION *****/
  749. void drawFace() {
  750.   // Clear display
  751.   display.clearDisplay();
  752.  
  753.   // Calculate face position with micro-movements
  754.   // FIXED: Now correctly using animation.microMoveX and animation.microMoveY
  755.   int16_t faceCenterX = SCREEN_WIDTH / 2 + (int16_t)(animation.microMoveX * 2.0f);
  756.   int16_t faceCenterY = SCREEN_HEIGHT / 2 + (int16_t)(animation.microMoveY * 2.0f);
  757.  
  758.   int16_t eyeRadius = 8;
  759.   int16_t eyeSeparation = 30;
  760.   int16_t eyeY = faceCenterY - 10;
  761.  
  762.   // Draw eyes
  763.   drawLeftEye(faceCenterX - eyeSeparation / 2, eyeY, eyeRadius);
  764.   drawRightEye(faceCenterX + eyeSeparation / 2, eyeY, eyeRadius);
  765.  
  766.   // Draw eyebrows
  767.   drawEyebrows(faceCenterX - eyeSeparation / 2, faceCenterX + eyeSeparation / 2, eyeY, eyeRadius);
  768.  
  769.   // Draw nose
  770.   drawNose(faceCenterX, faceCenterY);
  771.  
  772.   // Draw mouth
  773.   drawMouth(faceCenterX, faceCenterY);
  774.  
  775.   // Draw cheeks
  776.   drawCheeks(faceCenterX - eyeSeparation / 2, faceCenterX + eyeSeparation / 2, eyeY);
  777.  
  778.   // Display status text (optional, comment out if needed for more room)
  779.   display.setTextSize(1);
  780.   display.setTextColor(SSD1306_WHITE);
  781.   display.setCursor(0, 0);
  782.   display.println(animation.frameCount);
  783.  
  784.   // Push to display
  785.   display.display();
  786. }
  787.  
  788. /****** BUTTON EVENT HANDLER *****/
  789. void handleButtonPress() {
  790.   // Cycle to next emotion
  791.   animation.targetEmotion = (Emotion)((animation.currentEmotion + 1) % 8);
  792.   animation.lastInputTime = millis();
  793.   animation.autonomousMode = false;
  794.  
  795.   // Blink indicator LED
  796.   digitalWrite(emotionButton_LED_PIN_D2, HIGH);
  797.   delay(100);
  798.   digitalWrite(emotionButton_LED_PIN_D2, LOW);
  799. }
  800.  
  801. /****** SETUP FUNCTION *****/
  802. void setup(void) {
  803.   // Initialize serial for debugging
  804.   Serial.begin(115200);
  805.   delay(100);
  806.  
  807.   // Configure GPIO pins
  808.   pinMode(faceDisplay_PushButton_PIN_D1, INPUT_PULLUP);
  809.   pinMode(emotionButton_LED_PIN_D2, OUTPUT);
  810.   digitalWrite(emotionButton_LED_PIN_D2, LOW);
  811.  
  812.   // Initialize OLED display
  813.   if (!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) {
  814.     Serial.println(F("SSD1306 allocation failed"));
  815.     for (;;);  // Halt if display fails
  816.   }
  817.  
  818.   // Clear and show startup message
  819.   display.clearDisplay();
  820.   display.setTextSize(1);
  821.   display.setTextColor(SSD1306_WHITE);
  822.   display.setCursor(32, 28);
  823.   display.println(F("Initializing..."));
  824.   display.display();
  825.   delay(1000);
  826.  
  827.   // Initialize emotion configurations
  828.   initializeEmotionConfigs();
  829.  
  830.   // Initialize animation state
  831.   animation.currentEmotion = NEUTRAL;
  832.   animation.targetEmotion = NEUTRAL;
  833.   animation.autonomousMode = false;
  834.   animation.lastInputTime = millis();
  835.   animation.idleThreshold = 5000;  // 5 seconds
  836.   animation.frameTime = 0;
  837.   animation.lastFrameTime = 0;
  838.   animation.frameCount = 0;
  839.   animation.animationPhase = 0.0f;
  840.   animation.phaseVelocity = 0.0f;
  841.   // Initialize micro-movement variables in AnimationState
  842.   animation.microMoveX = 0.0f;
  843.   animation.microMoveY = 0.0f;
  844.   animation.gazeX = 0.0f;
  845.   animation.gazeY = 0.0f;
  846.  
  847.   // Initialize physics state
  848.   physics.pupilX = 0.0f;
  849.   physics.pupilY = 0.0f;
  850.   physics.pupilVelX = 0.0f;
  851.   physics.pupilVelY = 0.0f;
  852.   physics.targetPupilX = 0.0f;
  853.   physics.targetPupilY = 0.0f;
  854.   physics.blinkValue = 0.0f;
  855.   physics.blinkVel = 0.0f;
  856.   physics.isBlinking = false;
  857.   physics.blinkStartTime = millis();
  858.   physics.eyelidOffset = 0.0f;
  859.   physics.eyebrowAngle = 0.0f;
  860.   physics.eyebrowTargetAngle = 0.0f;
  861.   physics.irisGlow = 0.3f;
  862.   physics.mouthHeight = 0.3f;
  863.   physics.mouthAngle = 0.0f;
  864.   physics.cheekBlush = 0.0f;
  865.   physics.noseWiggle = 0.0f;
  866.  
  867.   // Setup EasyButton
  868.   button.begin();
  869.   button.onPressed(handleButtonPress);
  870.  
  871.   Serial.println(F("Face animation system initialized!"));
  872.   Serial.println(F("Press button to cycle emotions."));
  873.   Serial.println(F("System idle for 5 seconds to enter autonomous mode."));
  874. }
  875.  
  876. /****** MAIN LOOP *****/
  877. void loop(void) {
  878.   // Read button input
  879.   button.read();
  880.  
  881.   // Check for idle timeout (autonomous mode)
  882.   unsigned long timeSinceLastInput = millis() - animation.lastInputTime;
  883.   if (timeSinceLastInput > animation.idleThreshold && !animation.autonomousMode) {
  884.     animation.autonomousMode = true;
  885.     Serial.println(F("Entering autonomous mode..."));
  886.   }
  887.  
  888.   // Update animation physics and state
  889.   updateAnimation();
  890.  
  891.   // Render face to OLED
  892.   drawFace();
  893.  
  894.   // Target 45 FPS (approximately 22ms per frame)
  895.   // delayMicroseconds(22222);  // Optional frame rate limiting
  896. }
  897.  
  898. /* END CODE */
  899.  
Advertisement
Add Comment
Please, Sign In to add comment