2026-04-08 18:37:08 +02:00

254 lines
7.9 KiB
C++

#include <U8g2lib.h>
#include <SPI.h>
#include <math.h>
#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();
}
}