#include #include #include #define CS 5 #define DC 16 #define RESET 17 U8G2_SSD1309_128X64_NONAME0_F_4W_HW_SPI u8g2(U8G2_R2, CS, DC, RESET); // ---- Géométrie commune ---- const int EYE_R = 29; const int BAR_THICK = 5; const int BAR_PITCH = 6; const int BAR_ROUND = 2; const int N_BARS = 10; const int EYE_L_CX = 32; const int EYE_R_CX = 96; const int EYE_CY = 32; const int PUPIL_R = 11; enum Emotion { NEUTRAL = 0, HAPPY, SAD, ANGRY, SURPRISED, SLEEPY, WINK, LOVE, DIZZY, DEAD, N_EMOTIONS }; // ---- Utilitaire : trace une barre horizontale arrondie dont la largeur // suit le profil d'un cercle de rayon eyeR, à l'offset vertical dy ---- void drawBar(int cx, int cy, int dy, int eyeR) { int dy2 = dy * dy; int r2 = eyeR * eyeR; if (dy2 >= r2) return; int halfW = (int)sqrtf((float)(r2 - dy2)); int topY = cy + dy - BAR_THICK / 2; int w = 2 * halfW + 1; if (w > 2 * BAR_ROUND && BAR_THICK > 2 * BAR_ROUND) { u8g2.drawRBox(cx - halfW, topY, w, BAR_THICK, BAR_ROUND); } else { u8g2.drawBox(cx - halfW, topY, w, BAR_THICK); } } // ========================================================================= // 1) NEUTRAL : état "par défaut", 10 barres, 4e supprimée, pupille haute, // 3e barre redessinée par-dessus la pupille. // ========================================================================= void drawNeutral(int cx, int cy) { const int SKIP_BAR = 3; const int OVER_BAR = 2; const int PUPIL_DY = -3; for (int i = 0; i < N_BARS; i++) { if (i == SKIP_BAR) continue; float off = (i - (N_BARS - 1) / 2.0f) * BAR_PITCH; drawBar(cx, cy, (int)roundf(off), EYE_R); } u8g2.setDrawColor(0); u8g2.drawDisc(cx, cy + PUPIL_DY, PUPIL_R); u8g2.setDrawColor(1); float off = (OVER_BAR - (N_BARS - 1) / 2.0f) * BAR_PITCH; drawBar(cx, cy, (int)roundf(off), EYE_R); } // ========================================================================= // 2) HAPPY : arc courbé vers le haut (^_^) — croissant fait avec 2 discs // ========================================================================= void drawHappy(int cx, int cy) { u8g2.drawDisc(cx, cy + 8, EYE_R - 2); u8g2.setDrawColor(0); u8g2.drawDisc(cx, cy + 16, EYE_R - 2); u8g2.setDrawColor(1); } // ========================================================================= // 3) SAD : arc inversé (⌣) + petite larme sur l'œil gauche // ========================================================================= void drawSad(int cx, int cy) { u8g2.drawDisc(cx, cy - 8, EYE_R - 2); u8g2.setDrawColor(0); u8g2.drawDisc(cx, cy - 16, EYE_R - 2); u8g2.setDrawColor(1); // larme sous l'œil (côté extérieur) bool isLeft = (cx < 64); int tx = isLeft ? (cx - 14) : (cx + 14); u8g2.drawDisc(tx, cy + 20, 3); u8g2.drawTriangle(tx - 3, cy + 20, tx + 3, cy + 20, tx, cy + 14); } // ========================================================================= // 4) ANGRY : œil écrasé + sourcil diagonal froncé // ========================================================================= void drawAngry(int cx, int cy) { int cyShift = cy + 6; int smallR = EYE_R - 6; // 4 barres centrales for (int i = 3; i <= 6; i++) { float off = (i - (N_BARS - 1) / 2.0f) * BAR_PITCH; drawBar(cx, cyShift, (int)roundf(off), smallR); } // petite pupille u8g2.setDrawColor(0); u8g2.drawDisc(cx, cyShift, 5); u8g2.setDrawColor(1); // sourcil diagonal épais bool isLeft = (cx < 64); int x1, y1, x2, y2; if (isLeft) { x1 = cx - smallR - 2; y1 = cy - smallR - 4; x2 = cx + smallR + 2; y2 = cy - smallR + 6; } else { x1 = cx - smallR - 2; y1 = cy - smallR + 6; x2 = cx + smallR + 2; y2 = cy - smallR - 4; } for (int k = -2; k <= 2; k++) { u8g2.drawLine(x1, y1 + k, x2, y2 + k); } } // ========================================================================= // 5) SURPRISED : œil plus grand, toutes les barres présentes, petite pupille // ========================================================================= void drawSurprised(int cx, int cy) { for (int i = 0; i < N_BARS; i++) { float off = (i - (N_BARS - 1) / 2.0f) * BAR_PITCH; drawBar(cx, cy, (int)roundf(off), EYE_R); } u8g2.setDrawColor(0); u8g2.drawDisc(cx, cy, 5); u8g2.setDrawColor(1); } // ========================================================================= // 6) SLEEPY : seulement les barres du bas + paupière épaisse en haut // ========================================================================= void drawSleepy(int cx, int cy) { for (int i = 5; i < N_BARS; i++) { float off = (i - (N_BARS - 1) / 2.0f) * BAR_PITCH; drawBar(cx, cy, (int)roundf(off), EYE_R); } u8g2.drawRBox(cx - EYE_R + 2, cy - 2, 2 * EYE_R - 3, 4, 2); } // ========================================================================= // 7) WINK : œil gauche normal, œil droit fermé (barre horizontale) // ========================================================================= void drawWinkClosed(int cx, int cy) { u8g2.drawRBox(cx - EYE_R + 4, cy - 3, 2 * EYE_R - 7, 6, 3); } // ========================================================================= // 8) LOVE : cœur plein (2 discs + triangle) // ========================================================================= void drawHeart(int cx, int cy) { const int r = 11; int topY = cy - 6; u8g2.drawDisc(cx - 7, topY, r); u8g2.drawDisc(cx + 7, topY, r); u8g2.drawTriangle(cx - 17, topY + 2, cx + 17, topY + 2, cx, cy + 20); } // ========================================================================= // 9) DIZZY : cercles concentriques + petite pupille décalée // ========================================================================= void drawDizzy(int cx, int cy) { u8g2.drawCircle(cx, cy, EYE_R - 2); u8g2.drawCircle(cx, cy, EYE_R - 8); u8g2.drawCircle(cx, cy, EYE_R - 14); u8g2.drawCircle(cx, cy, EYE_R - 20); u8g2.drawDisc(cx + 4, cy - 2, 3); } // ========================================================================= // 10) DEAD : deux lignes épaisses en X // ========================================================================= void drawDead(int cx, int cy) { int a = EYE_R - 4; for (int k = -2; k <= 2; k++) { u8g2.drawLine(cx - a + k, cy - a, cx + a + k, cy + a); u8g2.drawLine(cx - a + k, cy + a, cx + a + k, cy - a); } } // ========================================================================= // Cycle des émotions // ========================================================================= void drawEmotion(Emotion e) { u8g2.clearBuffer(); switch (e) { case NEUTRAL: drawNeutral(EYE_L_CX, EYE_CY); drawNeutral(EYE_R_CX, EYE_CY); break; case HAPPY: drawHappy(EYE_L_CX, EYE_CY); drawHappy(EYE_R_CX, EYE_CY); break; case SAD: drawSad(EYE_L_CX, EYE_CY); drawSad(EYE_R_CX, EYE_CY); break; case ANGRY: drawAngry(EYE_L_CX, EYE_CY); drawAngry(EYE_R_CX, EYE_CY); break; case SURPRISED: drawSurprised(EYE_L_CX, EYE_CY); drawSurprised(EYE_R_CX, EYE_CY); break; case SLEEPY: drawSleepy(EYE_L_CX, EYE_CY); drawSleepy(EYE_R_CX, EYE_CY); break; case WINK: drawNeutral(EYE_L_CX, EYE_CY); drawWinkClosed(EYE_R_CX, EYE_CY); break; case LOVE: drawHeart(EYE_L_CX, EYE_CY); drawHeart(EYE_R_CX, EYE_CY); break; case DIZZY: drawDizzy(EYE_L_CX, EYE_CY); drawDizzy(EYE_R_CX, EYE_CY); break; case DEAD: drawDead(EYE_L_CX, EYE_CY); drawDead(EYE_R_CX, EYE_CY); break; default: break; } u8g2.sendBuffer(); } const unsigned long EMOTION_INTERVAL = 3000; unsigned long lastSwitch = 0; int current = 0; void setup() { u8g2.begin(); drawEmotion((Emotion)current); lastSwitch = millis(); } void loop() { if (millis() - lastSwitch >= EMOTION_INTERVAL) { current = (current + 1) % N_EMOTIONS; drawEmotion((Emotion)current); lastSwitch = millis(); } }