Advertisement
ADVERTISEMENT
Guest

broken code

Sep 29th, 2026
22
0
17 Hours
Not a member of GistPad yet? Sign Up, it unlocks many cool features!
None 204.34 KB | None | 0 0
  1. // ESP32 core 3.x defines these names in pins_arduino.h.
  2. // pin_config.h uses the same names for board constants.
  3. #ifdef SDMMC_CLK
  4. #undef SDMMC_CLK
  5. #endif
  6.  
  7. #ifdef SDMMC_CMD
  8. #undef SDMMC_CMD
  9. #endif
  10.  
  11. #include "pin_config.h"
  12.  
  13.  
  14. #include <Arduino.h>
  15. #include <Wire.h>
  16. #include <math.h>
  17. #include <memory>
  18.  
  19. #include "Arduino_DriveBus_Library.h"
  20. #include "Arduino_GFX_Library.h"
  21. #include "SensorQMI8658.hpp"
  22. #include "SensorPCF85063.hpp"
  23.  
  24. // ============================================================
  25. // POWER / BATTERY
  26. // ============================================================
  27.  
  28. #include "XPowersLib.h"
  29.  
  30. XPowersPMU power;
  31.  
  32. bool powerReady = false;
  33. bool batteryConnected = false;
  34. bool batteryCharging = false;
  35.  
  36. int batteryPercent = 0;
  37. int batteryVoltage = 0;
  38. int vbusVoltage = 0;
  39. int systemVoltage = 0;
  40.  
  41. unsigned long lastBatteryUpdate = 0;
  42.  
  43. const unsigned long BATTERY_UPDATE_INTERVAL = 10000;
  44.  
  45. // ============================================================
  46. // DIAGNOSTICS
  47. // ============================================================
  48.  
  49. unsigned long sensorSampleCount = 0;
  50. unsigned long lastHeartbeat = 0;
  51. const unsigned long HEARTBEAT_INTERVAL = 10000;
  52.  
  53. // ============================================================
  54. // AUDIO
  55. // ============================================================
  56.  
  57. #include "ESP_I2S.h"
  58. #include "esp_check.h"
  59. #include "es8311.h"
  60.  
  61. I2SClass i2s;
  62.  
  63. #define AUDIO_SAMPLE_RATE 16000
  64. #define AUDIO_VOLUME 85
  65. #define AUDIO_I2C_NUM 0
  66.  
  67. #define AUDIO_SCLK 41
  68. #define AUDIO_LRCK 45
  69. #define AUDIO_DSDIN 40
  70. #define AUDIO_ASDOUT 42
  71. #define AUDIO_MCLK 16
  72. #define AUDIO_PA_CTRL 46
  73.  
  74. // ============================================================
  75. // DISPLAY
  76. // ============================================================
  77.  
  78. Arduino_DataBus *bus = new Arduino_ESP32QSPI(
  79. LCD_CS, LCD_SCLK, LCD_SDIO0, LCD_SDIO1, LCD_SDIO2, LCD_SDIO3
  80. );
  81.  
  82. Arduino_GFX *gfx = new Arduino_CO5300(
  83. bus, LCD_RESET, 0, LCD_WIDTH, LCD_HEIGHT, 22, 0, 0, 0
  84. );
  85.  
  86. // ============================================================
  87. // LCD POWER RAIL (DSI_PWR_EN)
  88. // ============================================================
  89.  
  90. #ifndef LCD_EN
  91. #define LCD_EN 13
  92. #endif
  93.  
  94. const int LCD_EN_PIN = LCD_EN;
  95. const int LCD_CS_PIN = LCD_CS;
  96.  
  97. // ============================================================
  98. // SCREEN ON / OFF (HARDWARE POWER GATE)
  99. // ============================================================
  100.  
  101. bool screenIsOn = true;
  102. unsigned long screenOnTime = 0;
  103.  
  104. // Prevent accidental redraws while the panel is powered down.
  105. bool screenWakeRequested = false;
  106.  
  107. const unsigned long HOME_SCREEN_TIMEOUT = 15000;
  108. const unsigned long IM_SCREEN_TIMEOUT = 15000;
  109. const unsigned long GAME_SCREEN_TIMEOUT = 15000;
  110. const unsigned long CALC_SCREEN_TIMEOUT = 15000;
  111.  
  112.  
  113. // ============================================================
  114. // TOUCH
  115. // ============================================================
  116.  
  117. std::shared_ptr<Arduino_IIC_DriveBus> IIC_Bus =
  118. std::make_shared<Arduino_HWIIC>(IIC_SDA, IIC_SCL, &Wire);
  119.  
  120. void drawHomeScreen();
  121. void processHomeScreenTouch(int x, int y);
  122. void startCalc();
  123. void drawCalc();
  124. void processCalcTouch(int x, int y);
  125. void screenOn();
  126. void screenOff();
  127. void screenHardwareOn();
  128. void screenHardwareOff();
  129. void screenWake();
  130. void screenKeepOn();
  131. void screenAutoOffCheck();
  132. void drawBatteryIcon();
  133.  
  134. void Arduino_IIC_Touch_Interrupt(void);
  135.  
  136. std::unique_ptr<Arduino_IIC> FT3168(
  137. new Arduino_FT3x68(
  138. IIC_Bus,
  139. FT3168_DEVICE_ADDRESS,
  140. DRIVEBUS_DEFAULT_VALUE,
  141. TP_INT,
  142. Arduino_IIC_Touch_Interrupt
  143. )
  144. );
  145.  
  146. void Arduino_IIC_Touch_Interrupt(void)
  147. {
  148. FT3168->IIC_Interrupt_Flag = true;
  149. }
  150.  
  151. // ============================================================
  152. // QMI8658
  153. // ============================================================
  154.  
  155. SensorQMI8658 qmi;
  156. IMUdata acc;
  157.  
  158. // ============================================================
  159. // RTC
  160. // ============================================================
  161.  
  162. SensorPCF85063 rtc;
  163.  
  164. // ============================================================
  165. // PHYSICAL BUTTON
  166. // ============================================================
  167.  
  168. #define RESET_BUTTON_PIN 0
  169. #define BUTTON_PRESSED LOW
  170.  
  171. const unsigned long BUTTON_DEBOUNCE = 50;
  172. const unsigned long BUTTON_DOUBLE_PRESS_WINDOW = 700;
  173.  
  174. bool lastButtonReading = HIGH;
  175. bool buttonState = HIGH;
  176. unsigned long lastButtonChange = 0;
  177.  
  178. unsigned long firstButtonPressTime = 0;
  179. bool waitingForButtonSecondPress = false;
  180.  
  181.  
  182. // ============================================================
  183. // COLORS
  184. // ============================================================
  185.  
  186. #define COLOR_BG RGB565_BLACK
  187. #define COLOR_GREEN RGB565_GREEN
  188. #define COLOR_WHITE RGB565_WHITE
  189. #define COLOR_CYAN RGB565_CYAN
  190. #define COLOR_YELLOW RGB565_YELLOW
  191. #define COLOR_RED RGB565_RED
  192. #define COLOR_ORANGE RGB565_ORANGE
  193. #define COLOR_BLUE RGB565_BLUE
  194. #define COLOR_PURPLE RGB565_MAGENTA
  195.  
  196. // ============================================================
  197. // SEIZURE DETECTION
  198. // ============================================================
  199.  
  200. const unsigned long SENSOR_SAMPLE_INTERVAL = 1;
  201. const float MOTION_DELTA_THRESHOLD = 0.12f;
  202. const int MOTION_COUNT_THRESHOLD = 35;
  203. const unsigned long SEIZURE_WINDOW = 1000;
  204. const unsigned long SEIZURE_CONFIRM_TIME = 700;
  205. const unsigned long ALERT_DURATION = 10000;
  206. const unsigned long ALERT_COOLDOWN = 5000;
  207.  
  208. // ============================================================
  209. // SEIZURE STATE
  210. // ============================================================
  211.  
  212. enum MonitorState { MONITORING, POSSIBLE_SEIZURE, ALERT, COOLDOWN };
  213.  
  214. MonitorState monitorState = MONITORING;
  215. unsigned long possibleStart = 0;
  216. unsigned long alertStart = 0;
  217. unsigned long cooldownStart = 0;
  218.  
  219. // ============================================================
  220. // MOVEMENT ANALYSIS
  221. // ============================================================
  222.  
  223. float total_acc = 0.0f;
  224. float previous_acc = 1.0f;
  225. float motion_delta = 0.0f;
  226. unsigned long motionWindowStart = 0;
  227. int motionCount = 0;
  228.  
  229. // ============================================================
  230. // DISPLAY TIMING
  231. // ============================================================
  232.  
  233. unsigned long lastDisplay = 0;
  234. unsigned long lastClockDisplay = 0;
  235. unsigned long lastSerial = 0;
  236. unsigned long lastAlertScreen = 0;
  237. int lastDisplayedHour = -1;
  238. int lastDisplayedMinute = -1;
  239.  
  240. // ============================================================
  241. // TOUCH / GAME
  242. // ============================================================
  243.  
  244. enum GameState { GAME_NONE, GAME_QUESTION, GAME_REVEAL, GAME_RESULT };
  245.  
  246. GameState gameState = GAME_NONE;
  247. bool gameActive = false;
  248. unsigned long lastTouchTime = 0;
  249. unsigned long firstTapTime = 0;
  250. bool waitingForSecondTap = false;
  251.  
  252. const unsigned long DOUBLE_TAP_WINDOW = 600;
  253. const unsigned long TOUCH_DEBOUNCE = 100;
  254.  
  255. int touchX = 0;
  256. int touchY = 0;
  257. int score = 0;
  258. int rounds = 0;
  259. int selectedColor = 0;
  260.  
  261. // ============================================================
  262. // GAME COLORS
  263. // ============================================================
  264.  
  265. struct GameColor { const char *name; uint16_t color; };
  266.  
  267. GameColor gameColors[] =
  268. {
  269. { "RED", RGB565_RED },
  270. { "GREEN", RGB565_GREEN },
  271. { "BLUE", RGB565_BLUE },
  272. { "YELLOW", RGB565_YELLOW },
  273. { "PURPLE", RGB565_MAGENTA },
  274. { "CYAN", RGB565_CYAN }
  275. };
  276.  
  277. const int GAME_COLOR_COUNT = sizeof(gameColors) / sizeof(gameColors[0]);
  278.  
  279. // ============================================================
  280. // HOME SCREEN
  281. // ============================================================
  282.  
  283. #define COLOR_SOFT_PINK 0xfe19
  284. #define COLOR_PINK_DARK 0xD828
  285. #define COLOR_ICON_GREEN 0x16dd // this is actually a baby blue
  286. #define COLOR_ICON_CYAN 0xe864 // this is actually red
  287. #define COLOR_ICON_BLUE 0x0CAE
  288.  
  289. enum ScreenState { SCREEN_HOME, SCREEN_MONITORING, SCREEN_GAME, SCREEN_CALC };
  290.  
  291. ScreenState currentScreen = SCREEN_HOME;
  292.  
  293. const int ICON_SIZE = 100;
  294. const int ICON_GAP = 25;
  295. const int ICON_Y = 140;
  296. const int ICON_LABEL_Y = 250;
  297. int iconX[3] = { 25, 155, 285 };
  298.  
  299. // ============================================================
  300. // CALCULATOR
  301. // ============================================================
  302.  
  303. bool calcActive = false;
  304. float calcDisplay = 0.0f;
  305. float calcFirstOperand = 0.0f;
  306. char calcOperator = 0;
  307. bool calcNewInput = true;
  308. bool calcError = false;
  309. bool calcHasDecimal = false;
  310. float calcDecimalPlace = 0.1f;
  311.  
  312. const int CALC_BTN_SIZE = 72;
  313. const int CALC_BTN_GAP = 6;
  314. const int CALC_BTN_START_Y = 120;
  315. const int CALC_BTN_START_X = 52;
  316. const int CALC_COLS = 4;
  317. const int CALC_ROWS = 4;
  318. const int CALC_ROWS_TOTAL = 5;
  319.  
  320. const char *calcButtons[5][4] = {
  321. {"7", "8", "9", "/"},
  322. {"4", "5", "6", "*"},
  323. {"1", "2", "3", "-"},
  324. {"0", ".", "=", "+"},
  325. {"C", "C", "C", "C"}
  326. };
  327.  
  328. // ============================================================
  329. // AUDIO / ES8311
  330. // ============================================================
  331.  
  332. es8311_handle_t es_handle = NULL;
  333. bool audioReady = false;
  334. bool sirenActive = false;
  335. TaskHandle_t sirenTaskHandle = NULL;
  336.  
  337. // ============================================================
  338. // FORWARD DECLARATIONS
  339. // ============================================================
  340.  
  341. void updateMonitoringValues();
  342. void updateClockDisplay();
  343. void drawMonitoringScreen();
  344. void drawPossibleSeizureScreen();
  345. void drawAlertScreen();
  346. void drawCooldownScreen();
  347. void triggerAlert();
  348. void resetMonitor();
  349. void startGame();
  350. void newGameRound();
  351. void exitGame();
  352. void drawGameQuestion();
  353. void drawGameReveal();
  354. void processMonitoringTouch(int x, int y);
  355. void processGameTouch(int x, int y);
  356. void processTouch();
  357. bool readTouch(int &x, int &y);
  358. void checkResetButton();
  359. void updateSeizureDetection();
  360. void printSensorData();
  361. void printPowerDiagnostics();
  362. void i2cScan();
  363. void systemHeartbeat();
  364. bool initPower();
  365. void updateBatteryData();
  366. bool initAudio();
  367. void sirenTask(void *parameter);
  368. void startSiren();
  369. void stopSiren();
  370. void enablePowerADC();
  371. void disablePowerADC();
  372.  
  373. // ============================================================
  374. // SCREEN ON / OFF HELPERS
  375. // ============================================================
  376. //
  377. // These functions only control logical drawing.
  378. // The actual AMOLED power rail control is below.
  379. //
  380.  
  381. void screenOn()
  382. {
  383. if (!screenIsOn)
  384. screenHardwareOn();
  385. }
  386.  
  387.  
  388. void screenOff()
  389. {
  390. screenHardwareOff();
  391. }
  392.  
  393.  
  394. // ============================================================
  395. // HOME SCREEN ICONS
  396. // ============================================================
  397.  
  398. // Shadow + card base, shared by all three home icons
  399. void iconCard(int x, uint16_t cardColor)
  400. {
  401. gfx->fillRoundRect(x + 3, ICON_Y + 4, ICON_SIZE, ICON_SIZE, 14, COLOR_SOFT_PINK);
  402. gfx->fillRoundRect(x, ICON_Y, ICON_SIZE, ICON_SIZE, 14, cardColor);
  403. }
  404.  
  405. // Thick line (used for the ECG trace)
  406. void iconLine(int x0, int y0, int x1, int y1, int w, uint16_t color)
  407. {
  408. int steps = max(abs(x1 - x0), abs(y1 - y0));
  409.  
  410. if (steps <= 0)
  411. {
  412. gfx->fillRect(x0, y0, w, w, color);
  413. return;
  414. }
  415.  
  416. for (int i = 0; i <= steps; i++)
  417. {
  418. int x = x0 + ((x1 - x0) * i) / steps;
  419. int y = y0 + ((y1 - y0) * i) / steps;
  420. gfx->fillRect(x, y, w, w, color);
  421. }
  422. }
  423.  
  424. // --- Monitor icon: medical monitor + ECG pulse ---
  425. void drawMonitorIcon(int x)
  426. {
  427. iconCard(x, COLOR_ICON_GREEN);
  428.  
  429. // Screen
  430. gfx->fillRoundRect(x + 12, ICON_Y + 12, 76, 60, 8, 0x0838);
  431.  
  432. // Status LED
  433. gfx->fillCircle(x + 21, ICON_Y + 21, 3, COLOR_YELLOW);
  434.  
  435. // ECG trace
  436. iconLine(x + 18, ICON_Y + 44, x + 30, ICON_Y + 44, 3, 0xEFFF);
  437. iconLine(x + 30, ICON_Y + 44, x + 36, ICON_Y + 30, 3, 0xEFFF);
  438. iconLine(x + 36, ICON_Y + 30, x + 44, ICON_Y + 56, 3, 0xEFFF);
  439. iconLine(x + 44, ICON_Y + 56, x + 50, ICON_Y + 28, 3, 0xEFFF);
  440. iconLine(x + 50, ICON_Y + 28, x + 58, ICON_Y + 44, 3, 0xEFFF);
  441. iconLine(x + 58, ICON_Y + 44, x + 80, ICON_Y + 44, 3, 0xEFFF);
  442.  
  443. // Stand
  444. gfx->fillRect(x + 44, ICON_Y + 72, 12, 8, 0xEFFF);
  445. gfx->fillRect(x + 34, ICON_Y + 78, 32, 5, 0xEFFF);
  446. }
  447.  
  448. // --- Color icon: 8-segment color wheel ---
  449. void drawColorIcon(int x)
  450. {
  451. iconCard(x, COLOR_ICON_CYAN);
  452.  
  453. int cx = x + 50;
  454. int cy = ICON_Y + 50;
  455. int r = 34;
  456. int h = 24;
  457.  
  458. // Eight pie sectors
  459. gfx->fillTriangle(cx, cy, cx, cy - r, cx + h, cy - h, COLOR_RED);
  460. gfx->fillTriangle(cx, cy, cx + h, cy - h, cx + r, cy, COLOR_ORANGE);
  461. gfx->fillTriangle(cx, cy, cx + r, cy, cx + h, cy + h, COLOR_YELLOW);
  462. gfx->fillTriangle(cx, cy, cx + h, cy + h, cx, cy + r, COLOR_GREEN);
  463. gfx->fillTriangle(cx, cy, cx, cy + r, cx - h, cy + h, COLOR_CYAN);
  464. gfx->fillTriangle(cx, cy, cx - h, cy + h, cx - r, cy, COLOR_BLUE);
  465. gfx->fillTriangle(cx, cy, cx - r, cy, cx - h, cy - h, COLOR_PURPLE);
  466. gfx->fillTriangle(cx, cy, cx - h, cy - h, cx, cy - r, 0xFDBB);
  467.  
  468. // Center hub
  469. gfx->fillCircle(cx, cy, 12, 0xEFFF);
  470. gfx->fillCircle(cx, cy, 5, COLOR_ICON_CYAN);
  471. }
  472.  
  473. // --- Calc icon: mini calculator with screen and keys ---
  474. void drawCalcIcon(int x)
  475. {
  476. iconCard(x, COLOR_ICON_BLUE);
  477.  
  478. // LCD screen
  479. gfx->fillRoundRect(x + 14, ICON_Y + 14, 72, 26, 6, 0xEFFF);
  480. gfx->setTextColor(COLOR_ICON_BLUE);
  481. gfx->setTextSize(1);
  482. gfx->setCursor(x + 56, ICON_Y + 19);
  483. gfx->println("123");
  484.  
  485. // 3x3 key grid, orange = key
  486. for (int r = 0; r < 3; r++)
  487. {
  488. for (int c = 0; c < 3; c++)
  489. {
  490. int bx = x + 16 + c * 22;
  491. int by = ICON_Y + 48 + r * 16;
  492. uint16_t color;
  493.  
  494. color = (r == 2 && c == 2) ? COLOR_ORANGE : 0xEFFF;
  495.  
  496. gfx->fillRoundRect(bx, by, 16, 10, 3, color);
  497. }
  498. }
  499. }
  500.  
  501. // ============================================================
  502. // HOME SCREEN
  503. // ============================================================
  504.  
  505. void drawHomeScreen()
  506. {
  507. if(!screenIsOn)
  508. return;
  509.  
  510. gfx->fillScreen(COLOR_SOFT_PINK);
  511.  
  512. gfx->setTextColor(0x410F);
  513. gfx->setTextSize(2);
  514. gfx->setCursor(42, 30);
  515. gfx->println("Piper Hamilton 405-412-4500");
  516.  
  517. gfx->setTextColor(0x6510);
  518. gfx->setTextSize(1);
  519. gfx->setCursor(130, 60);
  520. gfx->println("Tap an app");
  521.  
  522. // Icons
  523. drawMonitorIcon(iconX[0]);
  524. drawColorIcon(iconX[1]);
  525. drawCalcIcon(iconX[2]);
  526.  
  527. // Labels
  528. gfx->setTextColor(0x410F);
  529. gfx->setTextSize(1);
  530.  
  531. gfx->setCursor(iconX[0] + 10, ICON_LABEL_Y);
  532. gfx->println("Monitor");
  533.  
  534. gfx->setCursor(iconX[1] + 5, ICON_LABEL_Y);
  535. gfx->println("Color");
  536.  
  537. gfx->setCursor(iconX[2] + 25, ICON_LABEL_Y);
  538. gfx->println("Calc");
  539.  
  540. // Battery
  541. gfx->setTextColor(0x410F);
  542. gfx->setTextSize(2);
  543. gfx->setCursor(160, 460);
  544. if (batteryConnected)
  545. {
  546. gfx->print(batteryPercent);
  547. gfx->print("%");
  548. }
  549. else
  550. {
  551. gfx->println("No Battery");
  552. }
  553.  
  554. // Time
  555. RTC_DateTime dt = rtc.getDateTime();
  556. char tstr[16];
  557. int h = dt.getHour();
  558. int m = dt.getMinute();
  559. int dh = (h == 0) ? 12 : (h > 12 ? h - 12 : h);
  560. snprintf(tstr, sizeof(tstr), "%d:%02d %s", dh, m, h < 12 ? "AM" : "PM");
  561. gfx->setTextColor(0x410F);
  562. gfx->setTextSize(2);
  563. gfx->setCursor(150, 420);
  564. gfx->println(tstr);
  565.  
  566. currentScreen = SCREEN_HOME;
  567. }
  568.  
  569. // ============================================================
  570. // HOME SCREEN TOUCH
  571. // ============================================================
  572.  
  573. void processHomeScreenTouch(int x, int y)
  574. {
  575. for (int i = 0; i < 3; i++)
  576. {
  577. bool hit =
  578. x >= iconX[i] && x <= iconX[i] + ICON_SIZE &&
  579. y >= ICON_Y && y <= ICON_Y + ICON_SIZE;
  580.  
  581. if (hit)
  582. {
  583. Serial.print("Home icon tapped: ");
  584. Serial.println(i);
  585.  
  586. switch (i)
  587. {
  588. case 0:
  589. currentScreen = SCREEN_MONITORING;
  590. drawMonitoringScreen();
  591. break;
  592. case 1:
  593. currentScreen = SCREEN_GAME;
  594. startGame();
  595. break;
  596. case 2:
  597. currentScreen = SCREEN_CALC;
  598. startCalc();
  599. break;
  600. }
  601. return;
  602. }
  603. }
  604. }
  605.  
  606. // ============================================================
  607. // CALCULATOR
  608. // ============================================================
  609.  
  610. void startCalc()
  611. {
  612. calcActive = true;
  613.  
  614. calcDisplay = 0.0f;
  615. calcFirstOperand = 0.0f;
  616. calcOperator = 0;
  617.  
  618. calcNewInput = true;
  619. calcHasDecimal = false;
  620.  
  621. calcError = false;
  622.  
  623. drawCalc();
  624.  
  625. Serial.println(">>> CALC STARTED <<<");
  626. }
  627.  
  628.  
  629. void drawCalc()
  630. {
  631. if(!screenIsOn)
  632. return;
  633.  
  634.  
  635. gfx->fillScreen(COLOR_SOFT_PINK);
  636.  
  637. gfx->setTextColor(0x410F);
  638. gfx->setTextSize(2);
  639. gfx->setCursor(160, 20);
  640. gfx->println("Calc");
  641.  
  642. gfx->fillRoundRect(20, 70, 370, 45, 8, 0x0A0A);
  643.  
  644. gfx->setTextColor(0xEFFF);
  645. gfx->setTextSize(3);
  646. gfx->setCursor(40, 80);
  647.  
  648. if (calcError)
  649. gfx->println("ERROR");
  650. else
  651. gfx->println(calcDisplay, 2);
  652.  
  653. for (int r = 0; r < CALC_ROWS_TOTAL; r++)
  654. {
  655. for (int c = 0; c < CALC_COLS; c++)
  656. {
  657. int bx = CALC_BTN_START_X + c * (CALC_BTN_SIZE + CALC_BTN_GAP);
  658. int by = CALC_BTN_START_Y + r * (CALC_BTN_SIZE + CALC_BTN_GAP);
  659.  
  660. uint16_t bgColor;
  661. if (c == 3) bgColor = 0x0CAE;
  662. else if (r == 4) bgColor = 0xD828;
  663. else bgColor = 0xFDBB;
  664.  
  665. gfx->fillRoundRect(bx, by, CALC_BTN_SIZE, CALC_BTN_SIZE, 10, bgColor);
  666.  
  667. gfx->setTextColor(0x410F);
  668. gfx->setTextSize(2);
  669. gfx->setCursor(bx + 25, by + 30);
  670. gfx->println(calcButtons[r][c]);
  671. }
  672. }
  673.  
  674. gfx->setTextColor(0x6510);
  675. gfx->setTextSize(1);
  676. gfx->setCursor(110, 480);
  677. gfx->println("GPIO0 = Home");
  678.  
  679. currentScreen = SCREEN_CALC;
  680. }
  681.  
  682. void processCalcTouch(int x, int y)
  683. {
  684. unsigned long now = millis();
  685.  
  686. // ------------------------------------------------------------
  687. // TOUCH DEBOUNCE
  688. // ------------------------------------------------------------
  689. if (now - lastTouchTime < TOUCH_DEBOUNCE)
  690. return;
  691.  
  692. lastTouchTime = now;
  693.  
  694.  
  695. // ------------------------------------------------------------
  696. // FIND THE BUTTON THAT WAS PRESSED
  697. // ------------------------------------------------------------
  698. for (int r = 0; r < CALC_ROWS_TOTAL; r++)
  699. {
  700. for (int c = 0; c < CALC_COLS; c++)
  701. {
  702. int bx = CALC_BTN_START_X +
  703. c * (CALC_BTN_SIZE + CALC_BTN_GAP);
  704.  
  705. int by = CALC_BTN_START_Y +
  706. r * (CALC_BTN_SIZE + CALC_BTN_GAP);
  707.  
  708. bool hit =
  709. x >= bx &&
  710. x <= bx + CALC_BTN_SIZE &&
  711. y >= by &&
  712. y <= by + CALC_BTN_SIZE;
  713.  
  714. if (!hit)
  715. continue;
  716.  
  717.  
  718. const char *btn = calcButtons[r][c];
  719. char key = btn[0];
  720.  
  721.  
  722. // ====================================================
  723. // ERROR STATE
  724. // ====================================================
  725. // Once we have an error, only C will do anything.
  726. if (calcError && key != 'C')
  727. return;
  728.  
  729.  
  730. // ====================================================
  731. // CLEAR
  732. // ====================================================
  733. if (key == 'C')
  734. {
  735. calcDisplay = 0.0f;
  736. calcFirstOperand = 0.0f;
  737. calcOperator = 0;
  738.  
  739. calcNewInput = true;
  740. calcHasDecimal = false;
  741. calcDecimalPlace = 0.1f;
  742.  
  743. calcError = false;
  744.  
  745. drawCalc();
  746. return;
  747. }
  748.  
  749.  
  750. // ====================================================
  751. // NUMBER BUTTON
  752. // ====================================================
  753. if (key >= '0' && key <= '9')
  754. {
  755. int digit = key - '0';
  756.  
  757. // ------------------------------------------------
  758. // We're starting a new number.
  759. //
  760. // Example:
  761. //
  762. // 7 * 8
  763. // ^
  764. // 8 starts a new number.
  765. // ------------------------------------------------
  766. if (calcNewInput)
  767. {
  768. calcDisplay = (float)digit;
  769.  
  770. calcNewInput = false;
  771.  
  772. calcHasDecimal = false;
  773. calcDecimalPlace = 0.1f;
  774. }
  775. else
  776. {
  777. // ------------------------------------------------
  778. // Normal whole-number entry.
  779. // Example: 1 -> 12 -> 123
  780. // ------------------------------------------------
  781. if (!calcHasDecimal)
  782. {
  783. calcDisplay =
  784. calcDisplay * 10.0f + (float)digit;
  785. }
  786.  
  787. // ------------------------------------------------
  788. // Decimal-number entry.
  789. //
  790. // Example:
  791. //
  792. // 7 . 5
  793. //
  794. // calcDisplay becomes 7.5
  795. // ------------------------------------------------
  796. else
  797. {
  798. calcDisplay +=
  799. (float)digit * calcDecimalPlace;
  800.  
  801. calcDecimalPlace *= 0.1f;
  802. }
  803. }
  804.  
  805. drawCalc();
  806. return;
  807. }
  808.  
  809.  
  810. // ====================================================
  811. // DECIMAL POINT
  812. // ====================================================
  813. if (key == '.')
  814. {
  815. // Don't allow two decimal points in one number.
  816. if (calcHasDecimal)
  817. return;
  818.  
  819.  
  820. // If "." is the first thing entered after an
  821. // operator, start with 0.
  822. //
  823. // Example:
  824. //
  825. // 7 * . 5
  826. //
  827. // becomes 7 * 0.5
  828. if (calcNewInput)
  829. {
  830. calcDisplay = 0.0f;
  831. calcNewInput = false;
  832. }
  833.  
  834.  
  835. calcHasDecimal = true;
  836. calcDecimalPlace = 0.1f;
  837.  
  838. drawCalc();
  839. return;
  840. }
  841.  
  842.  
  843. // ====================================================
  844. // ARITHMETIC OPERATOR
  845. // ====================================================
  846. if (key == '+' ||
  847. key == '-' ||
  848. key == '*' ||
  849. key == '/')
  850. {
  851. // ------------------------------------------------
  852. // If an operator was already pressed, but the user
  853. // hasn't entered the next number yet, simply
  854. // replace the operator.
  855. //
  856. // Example:
  857. //
  858. // 7 * +
  859. //
  860. // becomes:
  861. //
  862. // 7 +
  863. // ------------------------------------------------
  864. if (calcOperator != 0 && calcNewInput)
  865. {
  866. calcOperator = key;
  867.  
  868. drawCalc();
  869. return;
  870. }
  871.  
  872.  
  873. // ------------------------------------------------
  874. // FIRST OPERATOR
  875. //
  876. // Example:
  877. //
  878. // 7 *
  879. //
  880. // Save 7 as the first operand.
  881. //
  882. // IMPORTANT:
  883. // We DO NOT calculate 7 * 7 here.
  884. // ------------------------------------------------
  885. if (calcOperator == 0)
  886. {
  887. calcFirstOperand = calcDisplay;
  888. }
  889.  
  890.  
  891. // ------------------------------------------------
  892. // SECOND OPERATOR AFTER A NUMBER
  893. //
  894. // Example:
  895. //
  896. // 7 + 3 *
  897. //
  898. // First calculate 7 + 3 = 10.
  899. // Then remember that "*" is the next operation.
  900. // ------------------------------------------------
  901. else
  902. {
  903. switch (calcOperator)
  904. {
  905. case '+':
  906. calcFirstOperand =
  907. calcFirstOperand + calcDisplay;
  908. break;
  909.  
  910. case '-':
  911. calcFirstOperand =
  912. calcFirstOperand - calcDisplay;
  913. break;
  914.  
  915. case '*':
  916. calcFirstOperand =
  917. calcFirstOperand * calcDisplay;
  918. break;
  919.  
  920. case '/':
  921. if (calcDisplay == 0.0f)
  922. {
  923. calcError = true;
  924. drawCalc();
  925. return;
  926. }
  927.  
  928. calcFirstOperand =
  929. calcFirstOperand / calcDisplay;
  930. break;
  931. }
  932.  
  933. calcDisplay = calcFirstOperand;
  934. }
  935.  
  936.  
  937. // Save the newly pressed operator.
  938. calcOperator = key;
  939.  
  940. // The next digit begins a new number.
  941. calcNewInput = true;
  942.  
  943. calcHasDecimal = false;
  944. calcDecimalPlace = 0.1f;
  945.  
  946. drawCalc();
  947. return;
  948. }
  949.  
  950.  
  951. // ====================================================
  952. // EQUALS
  953. // ====================================================
  954. if (key == '=')
  955. {
  956. // Nothing to calculate if there is no operator.
  957. if (calcOperator == 0)
  958. {
  959. drawCalc();
  960. return;
  961. }
  962.  
  963.  
  964. // If the user presses "=" immediately after the
  965. // operator, don't calculate yet.
  966. //
  967. // Example:
  968. //
  969. // 7 *
  970. // =
  971. //
  972. // Just leave 7 on the display.
  973. if (calcNewInput)
  974. {
  975. drawCalc();
  976. return;
  977. }
  978.  
  979.  
  980. // ------------------------------------------------
  981. // Perform the pending calculation.
  982. // ------------------------------------------------
  983. switch (calcOperator)
  984. {
  985. case '+':
  986. calcDisplay =
  987. calcFirstOperand + calcDisplay;
  988. break;
  989.  
  990. case '-':
  991. calcDisplay =
  992. calcFirstOperand - calcDisplay;
  993. break;
  994.  
  995. case '*':
  996. calcDisplay =
  997. calcFirstOperand * calcDisplay;
  998. break;
  999.  
  1000. case '/':
  1001. if (calcDisplay == 0.0f)
  1002. {
  1003. calcError = true;
  1004. drawCalc();
  1005. return;
  1006. }
  1007.  
  1008. calcDisplay =
  1009. calcFirstOperand / calcDisplay;
  1010. break;
  1011. }
  1012.  
  1013.  
  1014. // The result becomes the new first operand.
  1015. calcFirstOperand = calcDisplay;
  1016.  
  1017. // Operation is finished.
  1018. calcOperator = 0;
  1019. calcNewInput = true;
  1020.  
  1021. calcHasDecimal = false;
  1022. calcDecimalPlace = 0.1f;
  1023.  
  1024. drawCalc();
  1025. return;
  1026. }
  1027.  
  1028.  
  1029. // If we get here, it wasn't a recognized calculator key.
  1030. return;
  1031. }
  1032. }
  1033. }
  1034.  
  1035. // ============================================================
  1036. // BATTERY HELPERS
  1037. // ============================================================
  1038.  
  1039. void enablePowerADC()
  1040. {
  1041. if (!powerReady) return;
  1042. power.enableTemperatureMeasure();
  1043. power.enableBattDetection();
  1044. power.enableVbusVoltageMeasure();
  1045. power.enableBattVoltageMeasure();
  1046. power.enableSystemVoltageMeasure();
  1047. }
  1048.  
  1049. void disablePowerADC()
  1050. {
  1051. if (!powerReady) return;
  1052. power.disableTemperatureMeasure();
  1053. power.disableBattDetection();
  1054. power.disableVbusVoltageMeasure();
  1055. power.disableBattVoltageMeasure();
  1056. power.disableSystemVoltageMeasure();
  1057. }
  1058.  
  1059. // ============================================================
  1060. // I2C SCANNER
  1061. // ============================================================
  1062.  
  1063. void i2cScan()
  1064. {
  1065. Serial.println();
  1066. Serial.println("========== I2C SCAN ==========");
  1067.  
  1068. uint8_t devicesFound = 0;
  1069.  
  1070. for (uint8_t address = 1; address < 127; address++)
  1071. {
  1072. Wire.beginTransmission(address);
  1073. uint8_t error = Wire.endTransmission();
  1074. if (error == 0)
  1075. {
  1076. Serial.print("I2C device found at 0x");
  1077. if (address < 16) Serial.print("0");
  1078. Serial.println(address, HEX);
  1079. devicesFound++;
  1080. }
  1081. }
  1082.  
  1083. if (devicesFound == 0)
  1084. Serial.println("No I2C devices found.");
  1085. else
  1086. {
  1087. Serial.print("I2C devices found: ");
  1088. Serial.println(devicesFound);
  1089. }
  1090.  
  1091. Serial.println("==============================");
  1092. Serial.println();
  1093. }
  1094.  
  1095. // ============================================================
  1096. // AXP2101 DIAGNOSTICS
  1097. // ============================================================
  1098.  
  1099. void printPowerDiagnostics()
  1100. {
  1101. if (!powerReady)
  1102. {
  1103. Serial.println("POWER DIAGNOSTICS: PMU NOT READY");
  1104. return;
  1105. }
  1106.  
  1107. uint16_t status = power.status();
  1108. uint8_t status1 = (status >> 8) & 0xFF;
  1109. uint8_t status2 = status & 0xFF;
  1110.  
  1111. Serial.println();
  1112. Serial.println("========== AXP2101 ==========");
  1113.  
  1114. Serial.print("Chip ID: 0x");
  1115. Serial.println(power.getChipID(), HEX);
  1116.  
  1117. Serial.print("STATUS1: 0x");
  1118. if (status1 < 0x10) Serial.print("0");
  1119. Serial.println(status1, HEX);
  1120.  
  1121. Serial.print("STATUS2: 0x");
  1122. if (status2 < 0x10) Serial.print("0");
  1123. Serial.println(status2, HEX);
  1124.  
  1125. Serial.print("Battery connected: ");
  1126. Serial.println(power.isBatteryConnect() ? "YES" : "NO");
  1127.  
  1128. Serial.print("Charging: ");
  1129. Serial.println(power.isCharging() ? "YES" : "NO");
  1130.  
  1131. Serial.print("Discharging: ");
  1132. Serial.println(power.isDischarge() ? "YES" : "NO");
  1133.  
  1134. Serial.print("Standby: ");
  1135. Serial.println(power.isStandby() ? "YES" : "NO");
  1136.  
  1137. Serial.print("VBUS input: ");
  1138. Serial.println(power.isVbusIn() ? "YES" : "NO");
  1139.  
  1140. Serial.print("VBUS good: ");
  1141. Serial.println(power.isVbusGood() ? "YES" : "NO");
  1142.  
  1143. Serial.print("Battery voltage: ");
  1144. Serial.print(power.getBattVoltage());
  1145. Serial.println(" mV");
  1146.  
  1147. Serial.print("VBUS voltage: ");
  1148. Serial.print(power.getVbusVoltage());
  1149. Serial.println(" mV");
  1150.  
  1151. Serial.print("System voltage: ");
  1152. Serial.print(power.getSystemVoltage());
  1153. Serial.println(" mV");
  1154.  
  1155. Serial.print("Battery percentage: ");
  1156. if (power.isBatteryConnect())
  1157. {
  1158. Serial.print(power.getBatteryPercent());
  1159. Serial.println("%");
  1160. }
  1161. else
  1162. Serial.println("N/A");
  1163.  
  1164. Serial.print("Charger status: ");
  1165. Serial.println(power.getChargerStatus());
  1166.  
  1167. Serial.print("Thermal regulation: ");
  1168. Serial.println(power.getThermalRegulationStatus() ? "YES" : "NO");
  1169.  
  1170. Serial.print("Current limit status: ");
  1171. Serial.println(power.getCurrentLimitStatus() ? "YES" : "NO");
  1172.  
  1173. Serial.println("=============================");
  1174. Serial.println();
  1175. }
  1176.  
  1177. // ============================================================
  1178. // SYSTEM HEARTBEAT
  1179. // ============================================================
  1180.  
  1181. void systemHeartbeat()
  1182. {
  1183. unsigned long now = millis();
  1184.  
  1185. if (now - lastHeartbeat < HEARTBEAT_INTERVAL) return;
  1186.  
  1187. lastHeartbeat = now;
  1188.  
  1189. Serial.println();
  1190. Serial.println("========== SYSTEM HEARTBEAT ==========");
  1191.  
  1192. Serial.print("Uptime: ");
  1193. Serial.print(now / 1000);
  1194. Serial.println(" seconds");
  1195.  
  1196. Serial.print("Sensor samples: ");
  1197. Serial.println(sensorSampleCount);
  1198.  
  1199. Serial.print("Free heap: ");
  1200. Serial.print(ESP.getFreeHeap());
  1201. Serial.println(" bytes");
  1202.  
  1203. Serial.print("Free PSRAM: ");
  1204. Serial.print(ESP.getFreePsram());
  1205. Serial.println(" bytes");
  1206.  
  1207. Serial.print("Battery: ");
  1208. if (batteryConnected)
  1209. {
  1210. Serial.print(batteryPercent);
  1211. Serial.print("% / ");
  1212. Serial.print(batteryVoltage);
  1213. Serial.println(" mV");
  1214. }
  1215. else
  1216. Serial.println("NOT CONNECTED");
  1217.  
  1218. Serial.print("VBUS: ");
  1219. Serial.print(vbusVoltage);
  1220. Serial.println(" mV");
  1221.  
  1222. Serial.print("System voltage: ");
  1223. Serial.print(systemVoltage);
  1224. Serial.println(" mV");
  1225.  
  1226. Serial.print("Screen: ");
  1227. Serial.println(screenIsOn ? "ON" : "OFF (panel gated)");
  1228.  
  1229. Serial.print("Monitor state: ");
  1230. switch (monitorState)
  1231. {
  1232. case MONITORING: Serial.println("MONITORING"); break;
  1233. case POSSIBLE_SEIZURE: Serial.println("POSSIBLE_SEIZURE"); break;
  1234. case ALERT: Serial.println("ALERT"); break;
  1235. case COOLDOWN: Serial.println("COOLDOWN"); break;
  1236. }
  1237.  
  1238. printPowerDiagnostics();
  1239.  
  1240. Serial.println("=======================================");
  1241. Serial.println();
  1242. }
  1243.  
  1244. // ============================================================
  1245. // INITIALIZE AXP2101
  1246. // ============================================================
  1247.  
  1248. bool initPower()
  1249. {
  1250. Serial.println("Initializing AXP2101 power management...");
  1251.  
  1252. Serial.print("PMU I2C SDA: ");
  1253. Serial.println(IIC_SDA);
  1254.  
  1255. Serial.print("PMU I2C SCL: ");
  1256. Serial.println(IIC_SCL);
  1257.  
  1258. Serial.print("PMU address: 0x");
  1259. Serial.println(AXP2101_SLAVE_ADDRESS, HEX);
  1260.  
  1261. if (!power.begin(Wire, AXP2101_SLAVE_ADDRESS, IIC_SDA, IIC_SCL))
  1262. {
  1263. Serial.println("AXP2101 initialization FAILED.");
  1264. return false;
  1265. }
  1266.  
  1267. Serial.println("AXP2101 responded.");
  1268.  
  1269. Serial.print("AXP2101 Chip ID: 0x");
  1270. Serial.println(power.getChipID(), HEX);
  1271.  
  1272. power.disableTSPinMeasure();
  1273.  
  1274. power.disableIRQ(XPOWERS_AXP2101_ALL_IRQ);
  1275. power.clearIrqStatus();
  1276.  
  1277. power.enableTemperatureMeasure();
  1278. power.enableBattDetection();
  1279. power.enableVbusVoltageMeasure();
  1280. power.enableBattVoltageMeasure();
  1281. power.enableSystemVoltageMeasure();
  1282.  
  1283. powerReady = true;
  1284.  
  1285. Serial.println("AXP2101 power management ready.");
  1286. printPowerDiagnostics();
  1287.  
  1288. return true;
  1289. }
  1290.  
  1291. // ============================================================
  1292. // UPDATE BATTERY DATA
  1293. // ============================================================
  1294.  
  1295. void updateBatteryData()
  1296. {
  1297. if (!powerReady) return;
  1298.  
  1299. unsigned long now = millis();
  1300.  
  1301. if (now - lastBatteryUpdate < BATTERY_UPDATE_INTERVAL) return;
  1302.  
  1303. lastBatteryUpdate = now;
  1304.  
  1305. batteryConnected = power.isBatteryConnect();
  1306. batteryCharging = power.isCharging();
  1307. batteryVoltage = power.getBattVoltage();
  1308. vbusVoltage = power.getVbusVoltage();
  1309. systemVoltage = power.getSystemVoltage();
  1310.  
  1311. if (batteryConnected)
  1312. {
  1313. batteryPercent = power.getBatteryPercent();
  1314. if (batteryPercent < 0) batteryPercent = 0;
  1315. if (batteryPercent > 100) batteryPercent = 100;
  1316. }
  1317. else
  1318. batteryPercent = 0;
  1319.  
  1320. Serial.print("BATTERY: ");
  1321. if (batteryConnected)
  1322. {
  1323. Serial.print(batteryPercent);
  1324. Serial.print("% ");
  1325. Serial.print(batteryVoltage);
  1326. Serial.print("mV");
  1327. }
  1328. else
  1329. Serial.print("NOT CONNECTED");
  1330.  
  1331. Serial.print(" CHARGING=");
  1332. Serial.print(batteryCharging ? "YES" : "NO");
  1333.  
  1334. Serial.print(" VBUS=");
  1335. Serial.print(vbusVoltage);
  1336. Serial.print("mV");
  1337.  
  1338. Serial.print(" VSYS=");
  1339. Serial.print(systemVoltage);
  1340. Serial.println("mV");
  1341. }
  1342.  
  1343. // ============================================================
  1344. // DRAW BATTERY ICON
  1345. // ============================================================
  1346.  
  1347. void drawBatteryIcon()
  1348. {
  1349. const int bx = 335;
  1350. const int by = 20;
  1351. const int bw = 48;
  1352. const int bh = 22;
  1353.  
  1354. gfx->fillRect(325, 10, 85, 40, COLOR_BG);
  1355. gfx->drawRect(bx, by, bw, bh, COLOR_WHITE);
  1356. gfx->fillRect(bx + bw, by + 6, 4, 10, COLOR_WHITE);
  1357.  
  1358. int fillWidth = ((bw - 4) * batteryPercent) / 100;
  1359.  
  1360. uint16_t fillColor = COLOR_GREEN;
  1361. if (batteryPercent <= 20) fillColor = COLOR_RED;
  1362. else if (batteryPercent <= 40) fillColor = COLOR_YELLOW;
  1363.  
  1364. if (fillWidth > 0)
  1365. gfx->fillRect(bx + 2, by + 2, fillWidth, bh - 4, fillColor);
  1366.  
  1367. if (batteryCharging)
  1368. {
  1369. gfx->setTextColor(COLOR_YELLOW);
  1370. gfx->setTextSize(2);
  1371. gfx->setCursor(290, 18);
  1372. gfx->print("+");
  1373. }
  1374.  
  1375. gfx->setTextColor(COLOR_WHITE);
  1376. gfx->setTextSize(1);
  1377. gfx->setCursor(335, 45);
  1378.  
  1379. if (batteryConnected)
  1380. {
  1381. gfx->print(batteryPercent);
  1382. gfx->print("%");
  1383. }
  1384. else
  1385. gfx->print("--%");
  1386. }
  1387.  
  1388. // ============================================================
  1389. // AUDIO INITIALIZATION
  1390. // ============================================================
  1391.  
  1392. bool initAudio()
  1393. {
  1394. Serial.println("Initializing ES8311 audio...");
  1395.  
  1396. i2s.setPins(AUDIO_SCLK, AUDIO_LRCK, AUDIO_DSDIN, AUDIO_ASDOUT, AUDIO_MCLK);
  1397.  
  1398. if (!i2s.begin(I2S_MODE_STD, AUDIO_SAMPLE_RATE, I2S_DATA_BIT_WIDTH_16BIT,
  1399. I2S_SLOT_MODE_STEREO, I2S_STD_SLOT_BOTH))
  1400. {
  1401. Serial.println("I2S initialization failed.");
  1402. return false;
  1403. }
  1404.  
  1405. Wire.begin(IIC_SDA, IIC_SCL);
  1406.  
  1407. pinMode(AUDIO_PA_CTRL, OUTPUT);
  1408. digitalWrite(AUDIO_PA_CTRL, HIGH);
  1409.  
  1410. es_handle = es8311_create(AUDIO_I2C_NUM, ES8311_ADDRRES_0);
  1411.  
  1412. if (!es_handle)
  1413. {
  1414. Serial.println("ES8311 create failed.");
  1415. return false;
  1416. }
  1417.  
  1418. const es8311_clock_config_t es_clk = {
  1419. .mclk_inverted = false,
  1420. .sclk_inverted = false,
  1421. .mclk_from_mclk_pin = true,
  1422. .mclk_frequency = AUDIO_SAMPLE_RATE * 256,
  1423. .sample_frequency = AUDIO_SAMPLE_RATE
  1424. };
  1425.  
  1426. if (es8311_init(es_handle, &es_clk, ES8311_RESOLUTION_16, ES8311_RESOLUTION_16) != ESP_OK)
  1427. {
  1428. Serial.println("ES8311 init failed.");
  1429. return false;
  1430. }
  1431.  
  1432. if (es8311_sample_frequency_config(es_handle, es_clk.mclk_frequency, es_clk.sample_frequency) != ESP_OK)
  1433. {
  1434. Serial.println("ES8311 sample rate configuration failed.");
  1435. return false;
  1436. }
  1437.  
  1438. if (es8311_voice_volume_set(es_handle, AUDIO_VOLUME, NULL) != ESP_OK)
  1439. {
  1440. Serial.println("ES8311 volume configuration failed.");
  1441. return false;
  1442. }
  1443.  
  1444. audioReady = true;
  1445. Serial.println("ES8311 audio ready.");
  1446. return true;
  1447. }
  1448.  
  1449. // ============================================================
  1450. // SIREN TASK
  1451. // ============================================================
  1452.  
  1453. void sirenTask(void *parameter)
  1454. {
  1455. const int samplesPerTone = 1600;
  1456.  
  1457. int16_t *buffer = (int16_t *)malloc(samplesPerTone * 2 * sizeof(int16_t));
  1458.  
  1459. if (!buffer)
  1460. {
  1461. Serial.println("Siren audio buffer allocation failed.");
  1462. sirenTaskHandle = NULL;
  1463. vTaskDelete(NULL);
  1464. return;
  1465. }
  1466.  
  1467. float phase = 0.0f;
  1468.  
  1469. while (true)
  1470. {
  1471. if (!sirenActive)
  1472. {
  1473. vTaskDelay(pdMS_TO_TICKS(50));
  1474. continue;
  1475. }
  1476.  
  1477. for (int i = 0; i < samplesPerTone; i++)
  1478. {
  1479. float frequency;
  1480.  
  1481. if (((millis() / 350) % 2) == 0)
  1482. frequency = 700.0f;
  1483. else
  1484. frequency = 1100.0f;
  1485.  
  1486. phase += (2.0f * PI * frequency) / AUDIO_SAMPLE_RATE;
  1487.  
  1488. if (phase >= 2.0f * PI)
  1489. phase -= 2.0f * PI;
  1490.  
  1491. int16_t sample = (int16_t)(sin(phase) * 11000.0f);
  1492. buffer[i * 2] = sample;
  1493. buffer[i * 2 + 1] = sample;
  1494. }
  1495.  
  1496. i2s.write((uint8_t *)buffer, samplesPerTone * 2 * sizeof(int16_t));
  1497. }
  1498. }
  1499.  
  1500. // ============================================================
  1501. // START / STOP SIREN
  1502. // ============================================================
  1503.  
  1504. void startSiren()
  1505. {
  1506. if (!audioReady) return;
  1507. sirenActive = true;
  1508. }
  1509.  
  1510. void stopSiren()
  1511. {
  1512. sirenActive = false;
  1513. }
  1514.  
  1515. // ============================================================
  1516. // MONITORING SCREEN
  1517. // ============================================================
  1518.  
  1519. void drawMonitoringScreen()
  1520. {
  1521. if(!screenIsOn)
  1522. return;
  1523.  
  1524. gfx->fillScreen(COLOR_BG);
  1525. gfx->setTextWrap(false);
  1526.  
  1527. gfx->setTextColor(COLOR_GREEN);
  1528. gfx->setTextSize(3);
  1529. gfx->setCursor(40, 25);
  1530. gfx->println("Monitoring");
  1531.  
  1532. drawBatteryIcon();
  1533.  
  1534. gfx->setTextColor(COLOR_YELLOW);
  1535. gfx->setTextSize(3);
  1536. gfx->setCursor(40, 90);
  1537. gfx->print("TOTAL");
  1538.  
  1539. gfx->setTextColor(COLOR_CYAN);
  1540. gfx->setTextSize(2);
  1541.  
  1542. gfx->setCursor(40, 165);
  1543. gfx->print("X:");
  1544. gfx->setCursor(40, 205);
  1545. gfx->print("Y:");
  1546. gfx->setCursor(40, 245);
  1547. gfx->print("Z:");
  1548.  
  1549. gfx->setTextColor(COLOR_GREEN);
  1550. gfx->setTextSize(2);
  1551. gfx->setCursor(40, 325);
  1552. gfx->println("MONITORING");
  1553.  
  1554. gfx->setTextColor(COLOR_WHITE);
  1555. gfx->setTextSize(3);
  1556. gfx->setCursor(105, 365);
  1557. gfx->println("--:-- --");
  1558.  
  1559. gfx->setTextSize(1);
  1560. gfx->setCursor(40, 420);
  1561. gfx->print("Motion threshold: ");
  1562. gfx->print(MOTION_DELTA_THRESHOLD, 2);
  1563. gfx->println(" G");
  1564.  
  1565. gfx->setCursor(40, 440);
  1566. gfx->print("Detection: ");
  1567. gfx->print(SEIZURE_CONFIRM_TIME);
  1568. gfx->println(" ms");
  1569.  
  1570. lastDisplayedHour = -1;
  1571. lastDisplayedMinute = -1;
  1572.  
  1573. updateMonitoringValues();
  1574. updateClockDisplay();
  1575.  
  1576. currentScreen = SCREEN_MONITORING;
  1577. }
  1578.  
  1579. // ============================================================
  1580. // MONITORING VALUES
  1581. // ============================================================
  1582.  
  1583. void updateMonitoringValues()
  1584. {
  1585. if(!screenIsOn)
  1586. return;
  1587.  
  1588.  
  1589. gfx->fillRect(130, 88, 230, 40, COLOR_BG);
  1590.  
  1591.  
  1592. gfx->setTextColor(COLOR_YELLOW);
  1593. gfx->setTextSize(3);
  1594. gfx->setCursor(130, 90);
  1595. gfx->print(total_acc, 2);
  1596. gfx->println(" G");
  1597.  
  1598. gfx->fillRect(80, 160, 220, 30, COLOR_BG);
  1599. gfx->setTextColor(COLOR_WHITE);
  1600. gfx->setTextSize(2);
  1601. gfx->setCursor(80, 165);
  1602. gfx->print(acc.x, 2);
  1603. gfx->println(" G");
  1604.  
  1605. gfx->fillRect(80, 200, 220, 30, COLOR_BG);
  1606. gfx->setCursor(80, 205);
  1607. gfx->print(acc.y, 2);
  1608. gfx->println(" G");
  1609.  
  1610. gfx->fillRect(80, 240, 220, 30, COLOR_BG);
  1611. gfx->setCursor(80, 245);
  1612. gfx->print(acc.z, 2);
  1613. gfx->println(" G");
  1614.  
  1615. drawBatteryIcon();
  1616. }
  1617.  
  1618. // ============================================================
  1619. // CLOCK
  1620. // ============================================================
  1621.  
  1622. void updateClockDisplay()
  1623. {
  1624. if (!screenIsOn) return;
  1625.  
  1626. RTC_DateTime datetime = rtc.getDateTime();
  1627.  
  1628. int hour = datetime.getHour();
  1629. int minute = datetime.getMinute();
  1630.  
  1631. if (hour == lastDisplayedHour && minute == lastDisplayedMinute) return;
  1632.  
  1633. lastDisplayedHour = hour;
  1634. lastDisplayedMinute = minute;
  1635.  
  1636. int displayHour = hour;
  1637. if (displayHour == 0) displayHour = 12;
  1638. else if (displayHour > 12) displayHour -= 12;
  1639.  
  1640. const char *ampm = (hour < 12) ? "AM" : "PM";
  1641.  
  1642. char timeString[16];
  1643. snprintf(timeString, sizeof(timeString), "%d:%02d %s", displayHour, minute, ampm);
  1644.  
  1645. gfx->fillRect(80, 355, 260, 50, COLOR_BG);
  1646. gfx->setTextColor(COLOR_WHITE);
  1647. gfx->setTextSize(3);
  1648. gfx->setCursor(105, 365);
  1649. gfx->println(timeString);
  1650. }
  1651.  
  1652. // ============================================================
  1653. // POSSIBLE SEIZURE SCREEN
  1654. // ============================================================
  1655.  
  1656. void drawPossibleSeizureScreen()
  1657. {
  1658. if(!screenIsOn)
  1659. return;
  1660.  
  1661. gfx->fillScreen(COLOR_ORANGE);
  1662.  
  1663. gfx->setTextColor(COLOR_BG);
  1664. gfx->setTextSize(3);
  1665. gfx->setCursor(35, 65);
  1666. gfx->println("CHECKING");
  1667. gfx->setCursor(35, 115);
  1668. gfx->println("MOVEMENT");
  1669.  
  1670. gfx->setTextSize(2);
  1671. gfx->setCursor(45, 210);
  1672. gfx->println("Possible seizure");
  1673. gfx->setCursor(45, 255);
  1674. gfx->println("pattern detected");
  1675. gfx->setCursor(45, 320);
  1676. gfx->print("Motion: ");
  1677. gfx->print(motionCount);
  1678. }
  1679.  
  1680. // ============================================================
  1681. // ALERT SCREEN
  1682. // ============================================================
  1683.  
  1684. void drawAlertScreen()
  1685. {
  1686. if(!screenIsOn)
  1687. return;
  1688.  
  1689. gfx->fillScreen(COLOR_RED);
  1690.  
  1691. gfx->setTextColor(COLOR_WHITE);
  1692. gfx->setTextSize(4);
  1693. gfx->setCursor(35, 70);
  1694. gfx->println("ALERT");
  1695. gfx->setCursor(35, 130);
  1696. gfx->println("Seizure Detected");
  1697.  
  1698. gfx->setTextSize(2);
  1699. gfx->setCursor(45, 225);
  1700. gfx->println("Movement pattern");
  1701. gfx->setCursor(45, 265);
  1702. gfx->println("requires attention");
  1703. gfx->setCursor(45, 335);
  1704. gfx->println("Press GPIO0");
  1705. gfx->setCursor(45, 370);
  1706. gfx->println("to acknowledge");
  1707. }
  1708.  
  1709. // ============================================================
  1710. // COOLDOWN SCREEN
  1711. // ============================================================
  1712.  
  1713. void drawCooldownScreen()
  1714. {
  1715. if(!screenIsOn)
  1716. return;
  1717.  
  1718.  
  1719. gfx->fillScreen(COLOR_BG);
  1720.  
  1721. gfx->setTextColor(COLOR_YELLOW);
  1722. gfx->setTextSize(3);
  1723. gfx->setCursor(55, 90);
  1724. gfx->println("COOLDOWN");
  1725.  
  1726. gfx->setTextColor(COLOR_WHITE);
  1727. gfx->setTextSize(2);
  1728. gfx->setCursor(45, 190);
  1729. gfx->println("Returning to");
  1730. gfx->setCursor(45, 230);
  1731. gfx->println("monitoring...");
  1732.  
  1733. drawBatteryIcon();
  1734. }
  1735.  
  1736. // ============================================================
  1737. // SCREEN ON / OFF — HARDWARE POWER GATE
  1738. // ============================================================
  1739.  
  1740. void screenHardwareOn()
  1741. {
  1742. if(screenIsOn)
  1743. {
  1744. screenOnTime = millis();
  1745. return;
  1746. }
  1747.  
  1748. Serial.println(">>> AMOLED WAKE <<<");
  1749.  
  1750. digitalWrite(LCD_EN_PIN, HIGH);
  1751.  
  1752. delay(200);
  1753.  
  1754. gfx->begin();
  1755.  
  1756. FT3168->begin();
  1757.  
  1758. FT3168->IIC_Write_Device_State(
  1759. FT3168->Arduino_IIC_Touch::Device::TOUCH_POWER_MODE,
  1760. FT3168->Arduino_IIC_Touch::Device_Mode::TOUCH_POWER_MONITOR);
  1761.  
  1762. screenIsOn = true;
  1763.  
  1764. screenOnTime = millis();
  1765.  
  1766. drawHomeScreen();
  1767. }
  1768.  
  1769.  
  1770.  
  1771.  
  1772. void screenHardwareOff()
  1773. {
  1774. if (!screenIsOn)
  1775. return;
  1776.  
  1777.  
  1778. Serial.println(">>> AMOLED SLEEP <<<");
  1779.  
  1780.  
  1781. //
  1782. // Stop panel activity first.
  1783. // Some Arduino_GFX versions do not implement this,
  1784. // so this is intentionally protected.
  1785. //
  1786. #ifdef ARDUINO_GFX_HAS_DISPLAY_OFF
  1787. gfx->displayOff();
  1788. #endif
  1789.  
  1790.  
  1791. delay(20);
  1792.  
  1793.  
  1794. //
  1795. // Disable panel communication and power.
  1796. //
  1797. digitalWrite(LCD_CS_PIN, HIGH);
  1798.  
  1799. delay(5);
  1800.  
  1801. digitalWrite(LCD_EN_PIN, LOW);
  1802.  
  1803.  
  1804. screenIsOn = false;
  1805. screenOnTime = millis();
  1806.  
  1807.  
  1808. Serial.println(">>> AMOLED POWER OFF <<<");
  1809. }
  1810.  
  1811.  
  1812.  
  1813. void screenWake()
  1814. {
  1815. screenWakeRequested = true;
  1816.  
  1817. screenHardwareOn();
  1818.  
  1819. screenWakeRequested = false;
  1820. }
  1821.  
  1822.  
  1823.  
  1824. void screenKeepOn()
  1825. {
  1826. if(screenIsOn)
  1827. screenOnTime = millis();
  1828. }
  1829.  
  1830.  
  1831. // ============================================================
  1832. // START ALERT
  1833. // ============================================================
  1834.  
  1835. void triggerAlert()
  1836. {
  1837. gameActive = false;
  1838. gameState = GAME_NONE;
  1839. waitingForSecondTap = false;
  1840. monitorState = ALERT;
  1841. alertStart = millis();
  1842.  
  1843. stopSiren();
  1844. screenWake();
  1845. drawAlertScreen();
  1846. startSiren();
  1847.  
  1848. screenKeepOn();
  1849.  
  1850. Serial.println();
  1851. Serial.println("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
  1852. Serial.println(" SEIZURE ALERT");
  1853. Serial.println("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
  1854. }
  1855.  
  1856. // ============================================================
  1857. // RESET MONITOR
  1858. // ============================================================
  1859.  
  1860. void resetMonitor()
  1861. {
  1862. stopSiren();
  1863. monitorState = MONITORING;
  1864. possibleStart = 0;
  1865. alertStart = 0;
  1866. cooldownStart = 0;
  1867. motionCount = 0;
  1868. motionWindowStart = millis();
  1869.  
  1870. if(screenIsOn)
  1871. {
  1872. drawMonitoringScreen();
  1873. screenKeepOn();
  1874. }
  1875.  
  1876.  
  1877. Serial.println(">>> MONITOR RESET <<<");
  1878. }
  1879.  
  1880. // ============================================================
  1881. // PHYSICAL BUTTON (GPIO0)
  1882. // ============================================================
  1883.  
  1884. void checkResetButton()
  1885. {
  1886. bool reading = digitalRead(RESET_BUTTON_PIN);
  1887. unsigned long now = millis();
  1888.  
  1889. // ------------------------------------------------------------
  1890. // DEBOUNCE
  1891. // ------------------------------------------------------------
  1892. // If the physical reading changed, start a new debounce timer.
  1893. if (reading != lastButtonReading)
  1894. {
  1895. lastButtonChange = now;
  1896. lastButtonReading = reading;
  1897. }
  1898.  
  1899. // Ignore the button until the reading has been stable.
  1900. if (now - lastButtonChange < BUTTON_DEBOUNCE)
  1901. return;
  1902.  
  1903. // ------------------------------------------------------------
  1904. // ONLY ACT ON A REAL STATE CHANGE
  1905. // ------------------------------------------------------------
  1906. if (reading == buttonState)
  1907. return;
  1908.  
  1909. buttonState = reading;
  1910.  
  1911. // ------------------------------------------------------------
  1912. // BUTTON PRESSED
  1913. // ------------------------------------------------------------
  1914. if (buttonState == BUTTON_PRESSED)
  1915. {
  1916. // If the screen is currently OFF, a single GPIO0 press
  1917. // simply wakes it. We do NOT start a double-press here.
  1918. if (!screenIsOn)
  1919. {
  1920. Serial.println("GPIO0: waking screen");
  1921. waitingForButtonSecondPress = false;
  1922. screenWake();
  1923. return;
  1924. }
  1925.  
  1926. // A second press within the allowed time = DOUBLE PRESS.
  1927. if (waitingForButtonSecondPress)
  1928. {
  1929. if (now - firstButtonPressTime <= BUTTON_DOUBLE_PRESS_WINDOW)
  1930. {
  1931. waitingForButtonSecondPress = false;
  1932.  
  1933. Serial.println(">>> GPIO0 DOUBLE PRESS <<<");
  1934.  
  1935. // Double press toggles the display.
  1936. if (screenIsOn)
  1937. {
  1938. Serial.println(">>> SCREEN OFF <<<");
  1939. screenHardwareOff();
  1940. }
  1941. else
  1942. {
  1943. Serial.println(">>> SCREEN WAKE <<<");
  1944. screenWake();
  1945. }
  1946.  
  1947. return;
  1948. }
  1949.  
  1950. // The previous press was too long ago.
  1951. // Treat this as a brand-new first press.
  1952. waitingForButtonSecondPress = false;
  1953. }
  1954.  
  1955. // --------------------------------------------------------
  1956. // FIRST PRESS
  1957. // --------------------------------------------------------
  1958. waitingForButtonSecondPress = true;
  1959. firstButtonPressTime = now;
  1960.  
  1961. Serial.println("GPIO0: first press");
  1962. }
  1963.  
  1964. // ------------------------------------------------------------
  1965. // SINGLE PRESS TIMEOUT
  1966. // ------------------------------------------------------------
  1967. // If nobody pressed GPIO0 a second time within the window,
  1968. // the first press becomes a normal single press.
  1969. if (waitingForButtonSecondPress &&
  1970. now - firstButtonPressTime > BUTTON_DOUBLE_PRESS_WINDOW)
  1971. {
  1972. waitingForButtonSecondPress = false;
  1973.  
  1974. Serial.println(">>> GPIO0 SINGLE PRESS <<<");
  1975.  
  1976. // If the screen somehow became OFF while waiting,
  1977. // just wake it.
  1978. if (!screenIsOn)
  1979. {
  1980. Serial.println("Screen asleep - waking only.");
  1981. screenWake();
  1982. return;
  1983. }
  1984.  
  1985. // --------------------------------------------------------
  1986. // HOME / RESET
  1987. // --------------------------------------------------------
  1988. Serial.println(">>> HOME / RESET <<<");
  1989.  
  1990. stopSiren();
  1991.  
  1992. monitorState = MONITORING;
  1993.  
  1994. possibleStart = 0;
  1995. alertStart = 0;
  1996. cooldownStart = 0;
  1997.  
  1998. motionCount = 0;
  1999. motionWindowStart = millis();
  2000.  
  2001. gameActive = false;
  2002. gameState = GAME_NONE;
  2003. waitingForSecondTap = false;
  2004.  
  2005. calcActive = false;
  2006.  
  2007. currentScreen = SCREEN_HOME;
  2008.  
  2009. drawHomeScreen();
  2010.  
  2011. screenKeepOn();
  2012. }
  2013. }
  2014.  
  2015.  
  2016.  
  2017. // ============================================================
  2018. // SEIZURE DETECTION
  2019. // ============================================================
  2020.  
  2021. void updateSeizureDetection()
  2022. {
  2023. unsigned long now = millis();
  2024.  
  2025. motion_delta = fabs(total_acc - previous_acc);
  2026. previous_acc = total_acc;
  2027.  
  2028. if (now - motionWindowStart >= SEIZURE_WINDOW)
  2029. {
  2030. motionWindowStart = now;
  2031. motionCount = 0;
  2032. }
  2033.  
  2034. if (motion_delta >= MOTION_DELTA_THRESHOLD)
  2035. motionCount++;
  2036.  
  2037. if (monitorState == MONITORING)
  2038. {
  2039. if (motionCount >= MOTION_COUNT_THRESHOLD)
  2040. {
  2041. monitorState = POSSIBLE_SEIZURE;
  2042. possibleStart = now;
  2043. screenWake();
  2044. drawPossibleSeizureScreen();
  2045. screenKeepOn();
  2046.  
  2047. Serial.println(">>> POSSIBLE SEIZURE PATTERN <<<");
  2048. return;
  2049. }
  2050. }
  2051. else if (monitorState == POSSIBLE_SEIZURE)
  2052. {
  2053. if (motionCount >= MOTION_COUNT_THRESHOLD)
  2054. {
  2055. if (now - possibleStart >= SEIZURE_CONFIRM_TIME)
  2056. {
  2057. triggerAlert();
  2058. return;
  2059. }
  2060. }
  2061. else
  2062. {
  2063. if (now - possibleStart > SEIZURE_CONFIRM_TIME)
  2064. {
  2065. monitorState = MONITORING;
  2066. if(screenIsOn)
  2067. {
  2068. drawMonitoringScreen();
  2069. screenKeepOn();
  2070. }
  2071.  
  2072. }
  2073. }
  2074. }
  2075. else if (monitorState == ALERT)
  2076. {
  2077. if (now - alertStart >= ALERT_DURATION)
  2078. {
  2079. stopSiren();
  2080. monitorState = COOLDOWN;
  2081. cooldownStart = now;
  2082. drawCooldownScreen();
  2083. screenKeepOn();
  2084. }
  2085. }
  2086. else if (monitorState == COOLDOWN)
  2087. {
  2088. if (now - cooldownStart >= ALERT_COOLDOWN)
  2089. {
  2090. monitorState = MONITORING;
  2091. if(screenIsOn)
  2092. {
  2093. drawMonitoringScreen();
  2094. screenKeepOn();
  2095. }
  2096.  
  2097. }
  2098. }
  2099. }
  2100.  
  2101. // ============================================================
  2102. // SERIAL SENSOR DATA
  2103. // ============================================================
  2104.  
  2105. void printSensorData()
  2106. {
  2107. unsigned long now = millis();
  2108.  
  2109. if (now - lastSerial < 250) return;
  2110.  
  2111. lastSerial = now;
  2112.  
  2113. Serial.print("X=");
  2114. Serial.print(acc.x, 3);
  2115. Serial.print(" G Y=");
  2116. Serial.print(acc.y, 3);
  2117. Serial.print(" G Z=");
  2118. Serial.print(acc.z, 3);
  2119. Serial.print(" G TOTAL=");
  2120. Serial.print(total_acc, 3);
  2121. Serial.print(" G DELTA=");
  2122. Serial.print(motion_delta, 3);
  2123. Serial.print(" MOTION=");
  2124. Serial.print(motionCount);
  2125. Serial.print(" STATE=");
  2126.  
  2127. switch (monitorState)
  2128. {
  2129. case MONITORING: Serial.println("MONITORING"); break;
  2130. case POSSIBLE_SEIZURE: Serial.println("POSSIBLE_SEIZURE"); break;
  2131. case ALERT: Serial.println("ALERT"); break;
  2132. case COOLDOWN: Serial.println("COOLDOWN"); break;
  2133. }
  2134. }
  2135.  
  2136. // ============================================================
  2137. // TOUCH READING
  2138. // ============================================================
  2139.  
  2140. bool readTouch(int &x, int &y)
  2141. {
  2142. x = FT3168->IIC_Read_Device_Value(
  2143. FT3168->Arduino_IIC_Touch::Value_Information::TOUCH_COORDINATE_X);
  2144.  
  2145. y = FT3168->IIC_Read_Device_Value(
  2146. FT3168->Arduino_IIC_Touch::Value_Information::TOUCH_COORDINATE_Y);
  2147.  
  2148. if (x < 0 || y < 0)
  2149. return false;
  2150.  
  2151. if (x >= LCD_WIDTH || y >= LCD_HEIGHT)
  2152. return false;
  2153.  
  2154. return true;
  2155. }
  2156.  
  2157.  
  2158. // ============================================================
  2159. // MONITORING TOUCH (DOUBLE-TAP TO START GAME)
  2160. // ============================================================
  2161.  
  2162. void processMonitoringTouch(int x, int y)
  2163. {
  2164. unsigned long now = millis();
  2165.  
  2166. if (now - lastTouchTime < TOUCH_DEBOUNCE) return;
  2167.  
  2168. lastTouchTime = now;
  2169.  
  2170. if (waitingForSecondTap)
  2171. {
  2172. if (now - firstTapTime <= DOUBLE_TAP_WINDOW)
  2173. {
  2174. waitingForSecondTap = false;
  2175.  
  2176. Serial.println(">>> DOUBLE TAP DETECTED <<<");
  2177. Serial.print("Second tap X=");
  2178. Serial.print(x);
  2179. Serial.print(" Y=");
  2180. Serial.println(y);
  2181.  
  2182. startGame();
  2183. return;
  2184. }
  2185.  
  2186. waitingForSecondTap = false;
  2187. Serial.println("Second tap timeout - starting new first tap.");
  2188. }
  2189.  
  2190. waitingForSecondTap = true;
  2191. firstTapTime = now;
  2192.  
  2193. Serial.println("First tap detected.");
  2194. Serial.println("Waiting for second tap...");
  2195. }
  2196.  
  2197. // ============================================================
  2198. // GAME SCREEN
  2199. // ============================================================
  2200.  
  2201. void drawGameQuestion()
  2202. {
  2203. if(!screenIsOn)
  2204. return;
  2205.  
  2206.  
  2207. gfx->fillScreen(COLOR_BG);
  2208.  
  2209. gfx->setTextColor(COLOR_CYAN);
  2210. gfx->setTextSize(3);
  2211. gfx->setCursor(55, 70);
  2212. gfx->println("GUESS THE");
  2213. gfx->setCursor(105, 120);
  2214. gfx->println("COLOR?");
  2215.  
  2216. gfx->setTextColor(COLOR_WHITE);
  2217. gfx->setTextSize(2);
  2218. gfx->setCursor(90, 230);
  2219. gfx->println("TAP TO REVEAL");
  2220.  
  2221. gfx->setTextSize(2);
  2222. gfx->setCursor(125, 430);
  2223. gfx->print("SCORE: ");
  2224. gfx->print(score);
  2225. }
  2226.  
  2227. // ============================================================
  2228. // GAME REVEAL SCREEN
  2229. // ============================================================
  2230.  
  2231. void drawGameReveal()
  2232. {
  2233. if(!screenIsOn)
  2234. return;
  2235.  
  2236. gfx->fillScreen(gameColors[selectedColor].color);
  2237.  
  2238. gfx->setTextColor(COLOR_WHITE);
  2239.  
  2240. if (selectedColor == 1 || selectedColor == 3 || selectedColor == 5)
  2241. gfx->setTextColor(COLOR_BG);
  2242.  
  2243. gfx->setTextSize(4);
  2244. gfx->setCursor(70, 120);
  2245. gfx->println(gameColors[selectedColor].name);
  2246.  
  2247. gfx->setTextSize(2);
  2248. gfx->setCursor(65, 230);
  2249. gfx->println("WERE YOU RIGHT?");
  2250.  
  2251. gfx->fillRoundRect(25, 300, 110, 65, 10, COLOR_GREEN);
  2252. gfx->setTextColor(COLOR_BG);
  2253. gfx->setTextSize(2);
  2254. gfx->setCursor(55, 323);
  2255. gfx->println("YES");
  2256.  
  2257. gfx->fillRoundRect(150, 300, 110, 65, 10, COLOR_RED);
  2258. gfx->setTextColor(COLOR_WHITE);
  2259. gfx->setCursor(185, 323);
  2260. gfx->println("NO");
  2261.  
  2262. gfx->fillRoundRect(275, 300, 110, 65, 10, COLOR_CYAN);
  2263. gfx->setTextColor(COLOR_BG);
  2264. gfx->setCursor(300, 323);
  2265. gfx->println("EXIT");
  2266.  
  2267. gfx->setTextColor(COLOR_WHITE);
  2268. gfx->setTextSize(2);
  2269. gfx->setCursor(130, 410);
  2270. gfx->print("SCORE: ");
  2271. gfx->print(score);
  2272. }
  2273.  
  2274. // ============================================================
  2275. // START / NEW ROUND / EXIT GAME
  2276. // ============================================================
  2277.  
  2278. void startGame()
  2279. {
  2280. gameActive = true;
  2281. gameState = GAME_QUESTION;
  2282. waitingForSecondTap = false;
  2283. score = 0;
  2284. rounds = 0;
  2285.  
  2286. Serial.println(">>> COLOR GAME STARTED <<<");
  2287.  
  2288. drawGameQuestion();
  2289. screenKeepOn();
  2290. }
  2291.  
  2292. void newGameRound()
  2293. {
  2294. selectedColor = random(0, GAME_COLOR_COUNT);
  2295. gameState = GAME_REVEAL;
  2296. drawGameReveal();
  2297. screenKeepOn();
  2298. }
  2299.  
  2300. void exitGame()
  2301. {
  2302. gameActive = false;
  2303. gameState = GAME_NONE;
  2304. waitingForSecondTap = false;
  2305.  
  2306. Serial.print("Color game finished. Score: ");
  2307. Serial.print(score);
  2308. Serial.print("/");
  2309. Serial.println(rounds);
  2310.  
  2311. if(screenIsOn)
  2312. {
  2313. drawMonitoringScreen();
  2314. screenKeepOn();
  2315. }
  2316.  
  2317. }
  2318.  
  2319. // ============================================================
  2320. // GAME TOUCH
  2321. // ============================================================
  2322.  
  2323. void processGameTouch(int x, int y)
  2324. {
  2325. unsigned long now = millis();
  2326.  
  2327. if (now - lastTouchTime < TOUCH_DEBOUNCE) return;
  2328.  
  2329. lastTouchTime = now;
  2330.  
  2331. if (gameState == GAME_QUESTION)
  2332. {
  2333. Serial.println("Game: question tapped");
  2334. newGameRound();
  2335. return;
  2336. }
  2337.  
  2338. if (gameState == GAME_REVEAL)
  2339. {
  2340. if (x >= 25 && x <= 135 && y >= 300 && y <= 365)
  2341. {
  2342. score++;
  2343. rounds++;
  2344. Serial.println("Game: YES");
  2345. drawGameQuestion();
  2346. gameState = GAME_QUESTION;
  2347. screenKeepOn();
  2348. return;
  2349. }
  2350.  
  2351. if (x >= 150 && x <= 260 && y >= 300 && y <= 365)
  2352. {
  2353. rounds++;
  2354. Serial.println("Game: NO");
  2355. drawGameQuestion();
  2356. gameState = GAME_QUESTION;
  2357. screenKeepOn();
  2358. return;
  2359. }
  2360.  
  2361. if (x >= 275 && x <= 385 && y >= 300 && y <= 365)
  2362. {
  2363. Serial.println("Game: EXIT");
  2364. exitGame();
  2365. return;
  2366. }
  2367. }
  2368. }
  2369.  
  2370. // ============================================================
  2371. // TOUCH SYSTEM
  2372. // ============================================================
  2373.  
  2374. void processTouch()
  2375. {
  2376. // ------------------------------------------------------------
  2377. // NO NEW TOUCH = NOTHING TO DO
  2378. // ------------------------------------------------------------
  2379. //
  2380. // The FT3168 interrupt sets this flag when a touch occurs.
  2381. // This prevents us from continuously reading old/stale
  2382. // touchscreen coordinates.
  2383. //
  2384. if (!FT3168->IIC_Interrupt_Flag)
  2385. return;
  2386.  
  2387. // Clear the event flag now that we are handling it.
  2388. FT3168->IIC_Interrupt_Flag = false;
  2389.  
  2390. int x, y;
  2391.  
  2392. // ------------------------------------------------------------
  2393. // READ THE TOUCH COORDINATES
  2394. // ------------------------------------------------------------
  2395. if (!readTouch(x, y))
  2396. return;
  2397.  
  2398. Serial.print("Touch X=");
  2399. Serial.print(x);
  2400. Serial.print(" Y=");
  2401. Serial.println(y);
  2402.  
  2403. // ------------------------------------------------------------
  2404. // SCREEN IS OFF
  2405. // ------------------------------------------------------------
  2406. //
  2407. // A real touchscreen interrupt while asleep wakes the watch.
  2408. // IMPORTANT: We do NOT send that same touch into an app.
  2409. //
  2410. if (!screenIsOn)
  2411. {
  2412. Serial.println(">>> TOUCH WAKE <<<");
  2413.  
  2414. screenWake();
  2415.  
  2416. return;
  2417. }
  2418.  
  2419. // ------------------------------------------------------------
  2420. // ALERT MODE
  2421. // ------------------------------------------------------------
  2422. //
  2423. // Don't let touchscreen taps interfere with an alert.
  2424. //
  2425. if (monitorState == ALERT)
  2426. return;
  2427.  
  2428. // ------------------------------------------------------------
  2429. // HOME SCREEN
  2430. // ------------------------------------------------------------
  2431. if (currentScreen == SCREEN_HOME)
  2432. {
  2433. processHomeScreenTouch(x, y);
  2434. return;
  2435. }
  2436.  
  2437. // ------------------------------------------------------------
  2438. // MONITORING SCREEN
  2439. // ------------------------------------------------------------
  2440. if (currentScreen == SCREEN_MONITORING)
  2441. {
  2442. if (monitorState == MONITORING)
  2443. {
  2444. processMonitoringTouch(x, y);
  2445. }
  2446.  
  2447. return;
  2448. }
  2449.  
  2450. // ------------------------------------------------------------
  2451. // COLOR GAME
  2452. // ------------------------------------------------------------
  2453. if (currentScreen == SCREEN_GAME)
  2454. {
  2455. processGameTouch(x, y);
  2456. return;
  2457. }
  2458.  
  2459. // ------------------------------------------------------------
  2460. // CALCULATOR
  2461. // ------------------------------------------------------------
  2462. if (currentScreen == SCREEN_CALC)
  2463. {
  2464. processCalcTouch(x, y);
  2465. return;
  2466. }
  2467. }
  2468.  
  2469. // ============================================================
  2470. // SETUP
  2471. // ============================================================
  2472.  
  2473. void setup()
  2474. {
  2475. Serial.begin(115200);
  2476. delay(3000);
  2477.  
  2478. Serial.println();
  2479. Serial.println("========================================");
  2480. Serial.println(" PIPER FALL / SEZURE WATCH");
  2481. Serial.println(" ESP32-S3 + CO5300 + QMI8658 + RTC");
  2482. Serial.println(" V2 + AXP2101 BATTERY");
  2483. Serial.println("========================================");
  2484.  
  2485. // Button
  2486. pinMode(RESET_BUTTON_PIN, INPUT_PULLUP);
  2487.  
  2488. // Waveshare hardware
  2489. #ifdef DEV_DEVICE_INIT
  2490. DEV_DEVICE_INIT();
  2491. Serial.println("Waveshare hardware initialized.");
  2492. #else
  2493. Serial.println("WARNING: DEV_DEVICE_INIT not defined!");
  2494. #endif
  2495.  
  2496. // LCD power rail
  2497. pinMode(LCD_EN_PIN, OUTPUT);
  2498. digitalWrite(LCD_EN_PIN, HIGH);
  2499. screenIsOn = true;
  2500. screenOnTime = millis();
  2501. screenWakeRequested = false;
  2502.  
  2503.  
  2504. Serial.println("LCD power rail (DSI_PWR_EN) on GPIO");
  2505. Serial.println(LCD_EN_PIN);
  2506.  
  2507. // Display
  2508. Serial.println("Initializing AMOLED...");
  2509.  
  2510. if (!gfx->begin())
  2511. {
  2512. Serial.println("AMOLED initialization failed!");
  2513. while (true) delay(1000);
  2514. }
  2515.  
  2516. gfx->fillScreen(COLOR_BG);
  2517. Serial.println("AMOLED initialized.");
  2518.  
  2519. // I2C
  2520. Wire.begin(IIC_SDA, IIC_SCL);
  2521. Serial.println("I2C bus initialized.");
  2522.  
  2523. // I2C scan
  2524. i2cScan();
  2525.  
  2526. // Power / AXP2101
  2527. if (!initPower())
  2528. {
  2529. Serial.println("WARNING: AXP2101 unavailable.");
  2530. Serial.println("Continuing without battery diagnostics.");
  2531. }
  2532.  
  2533. // Touch
  2534. Serial.println("Initializing FT3168 touch...");
  2535.  
  2536. unsigned long ft3168Start = millis();
  2537.  
  2538. while (true)
  2539. {
  2540. if (FT3168->begin())
  2541. {
  2542. break;
  2543. }
  2544.  
  2545. Serial.println("FT3168 init failed, retrying...");
  2546. delay(1000);
  2547.  
  2548. if (millis() - ft3168Start > 15000)
  2549. {
  2550. Serial.println("FT3168 timeout - continuing without touch.");
  2551. break;
  2552. }
  2553. }
  2554.  
  2555. FT3168->IIC_Write_Device_State(
  2556. FT3168->Arduino_IIC_Touch::Device::TOUCH_POWER_MODE,
  2557. FT3168->Arduino_IIC_Touch::Device_Mode::TOUCH_POWER_MONITOR);
  2558.  
  2559. Serial.println("FT3168 touch ready.");
  2560. Serial.print("TP_INT pin: ");
  2561. Serial.println(TP_INT);
  2562.  
  2563. pinMode(TP_INT, INPUT_PULLUP);
  2564.  
  2565. Serial.print("TP_INT state: ");
  2566. Serial.println(digitalRead(TP_INT));
  2567.  
  2568.  
  2569. // RTC
  2570. Serial.println("Initializing PCF85063 RTC...");
  2571.  
  2572. if (!rtc.begin(Wire, IIC_SDA, IIC_SCL))
  2573. {
  2574. Serial.println("RTC initialization failed!");
  2575.  
  2576. gfx->fillScreen(COLOR_BG);
  2577. gfx->setTextColor(COLOR_RED);
  2578. gfx->setTextSize(3);
  2579. gfx->setCursor(35, 100);
  2580. gfx->println("RTC ERROR");
  2581.  
  2582. while (true) delay(1000);
  2583. }
  2584.  
  2585. Serial.println("PCF85063 detected.");
  2586.  
  2587. // Set RTC from compile time
  2588. uint8_t buildMonth = 1;
  2589. uint8_t buildDay;
  2590. uint16_t buildYear;
  2591. uint8_t buildHour;
  2592. uint8_t buildMinute;
  2593. uint8_t buildSecond;
  2594.  
  2595. char monthString[4];
  2596. int day, year;
  2597.  
  2598. sscanf(__DATE__, "%3s %d %d", monthString, &day, &year);
  2599.  
  2600. if (strcmp(monthString, "Jan") == 0) buildMonth = 1;
  2601. else if (strcmp(monthString, "Feb") == 0) buildMonth = 2;
  2602. else if (strcmp(monthString, "Mar") == 0) buildMonth = 3;
  2603. else if (strcmp(monthString, "Apr") == 0) buildMonth = 4;
  2604. else if (strcmp(monthString, "May") == 0) buildMonth = 5;
  2605. else if (strcmp(monthString, "Jun") == 0) buildMonth = 6;
  2606. else if (strcmp(monthString, "Jul") == 0) buildMonth = 7;
  2607. else if (strcmp(monthString, "Aug") == 0) buildMonth = 8;
  2608. else if (strcmp(monthString, "Sep") == 0) buildMonth = 9;
  2609. else if (strcmp(monthString, "Oct") == 0) buildMonth = 10;
  2610. else if (strcmp(monthString, "Nov") == 0) buildMonth = 11;
  2611. else buildMonth = 12;
  2612.  
  2613. buildYear = year;
  2614. buildDay = day;
  2615.  
  2616. sscanf(__TIME__, "%hhu:%hhu:%hhu", &buildHour, &buildMinute, &buildSecond);
  2617.  
  2618. buildMinute += 3;
  2619. if (buildMinute >= 60)
  2620. {
  2621. buildMinute -= 60;
  2622. buildHour++;
  2623. if (buildHour >= 24) { buildHour = 0; buildDay++; }
  2624. }
  2625.  
  2626. rtc.setDateTime(buildYear, buildMonth, buildDay, buildHour, buildMinute, buildSecond);
  2627.  
  2628. RTC_DateTime rtcNow = rtc.getDateTime();
  2629. Serial.print("RTC: ");
  2630. Serial.print(rtcNow.getHour());
  2631. Serial.print(":");
  2632. Serial.print(rtcNow.getMinute());
  2633. Serial.println();
  2634.  
  2635. // QMI8658
  2636. Serial.println("Initializing QMI8658...");
  2637.  
  2638. if (!qmi.begin(Wire, QMI8658_L_SLAVE_ADDRESS, IIC_SDA, IIC_SCL))
  2639. {
  2640. Serial.println("QMI8658 initialization failed.");
  2641.  
  2642. gfx->fillScreen(COLOR_BG);
  2643. gfx->setTextColor(COLOR_RED);
  2644. gfx->setTextSize(3);
  2645. gfx->setCursor(40, 100);
  2646. gfx->println("QMI8658");
  2647. gfx->setCursor(40, 150);
  2648. gfx->println("ERROR");
  2649.  
  2650. while (true) delay(1000);
  2651. }
  2652.  
  2653. Serial.println("QMI8658 detected.");
  2654.  
  2655. if (!qmi.configAccelerometer(
  2656. SensorQMI8658::ACC_RANGE_4G,
  2657. SensorQMI8658::ACC_ODR_1000Hz,
  2658. SensorQMI8658::LPF_MODE_0))
  2659. {
  2660. Serial.println("Accelerometer configuration failed.");
  2661. }
  2662. else
  2663. {
  2664. Serial.println("Accelerometer configured.");
  2665. }
  2666.  
  2667. if (!qmi.enableAccelerometer())
  2668. {
  2669. Serial.println("Accelerometer enable failed.");
  2670. }
  2671. else
  2672. {
  2673. Serial.println("Accelerometer enabled.");
  2674. }
  2675.  
  2676. // Audio
  2677. initAudio();
  2678.  
  2679. // Siren task
  2680. if (audioReady)
  2681. {
  2682. xTaskCreatePinnedToCore(
  2683. sirenTask, "SirenTask", 4096, NULL, 1, &sirenTaskHandle, 0);
  2684. }
  2685.  
  2686. // Random
  2687. randomSeed(micros());
  2688.  
  2689. // Initial values
  2690. motionWindowStart = millis();
  2691. previous_acc = 1.0f;
  2692. sensorSampleCount = 0;
  2693.  
  2694. // Initial battery
  2695. if (powerReady)
  2696. {
  2697. lastBatteryUpdate = millis() - BATTERY_UPDATE_INTERVAL;
  2698. updateBatteryData();
  2699. }
  2700.  
  2701. // Initial screen
  2702. drawHomeScreen();
  2703.  
  2704. // Heartbeat
  2705. lastHeartbeat = millis();
  2706.  
  2707. Serial.println();
  2708. Serial.println("========================================");
  2709. Serial.println(" SYSTEM READY");
  2710. Serial.println("========================================");
  2711. Serial.println("GPIO0 = Single press Home / Reset");
  2712. Serial.println("GPIO0 = Double press Screen ON/OFF");
  2713. Serial.print("GPIO0 double-press window = ");
  2714. Serial.print(BUTTON_DOUBLE_PRESS_WINDOW);
  2715. Serial.println(" ms");
  2716.  
  2717.  
  2718. Serial.println("Tap icons to launch apps");
  2719. Serial.println("Touch controller = FT3168");
  2720. Serial.println("Power management = AXP2101");
  2721.  
  2722. Serial.print("Motion delta threshold = ");
  2723. Serial.print(MOTION_DELTA_THRESHOLD, 3);
  2724. Serial.println(" G");
  2725.  
  2726. Serial.print("Double-tap window = ");
  2727. Serial.print(DOUBLE_TAP_WINDOW);
  2728. Serial.println(" ms");
  2729.  
  2730. Serial.print("Touch debounce = ");
  2731. Serial.print(TOUCH_DEBOUNCE);
  2732. Serial.println(" ms");
  2733.  
  2734. Serial.print("Screen auto-off = ");
  2735. Serial.print(HOME_SCREEN_TIMEOUT);
  2736. Serial.println(" ms");
  2737.  
  2738. Serial.println("10-second system heartbeat enabled.");
  2739. Serial.println();
  2740. }
  2741.  
  2742. // ============================================================
  2743. // SCREEN AUTO OFF
  2744. // ============================================================
  2745.  
  2746. void screenAutoOffCheck()
  2747. {
  2748. if(!screenIsOn)
  2749. return;
  2750.  
  2751.  
  2752. unsigned long timeout;
  2753.  
  2754.  
  2755. switch(currentScreen)
  2756. {
  2757. case SCREEN_HOME:
  2758. timeout = HOME_SCREEN_TIMEOUT;
  2759. break;
  2760.  
  2761.  
  2762. case SCREEN_MONITORING:
  2763. timeout = IM_SCREEN_TIMEOUT;
  2764. break;
  2765.  
  2766.  
  2767. case SCREEN_GAME:
  2768. timeout = GAME_SCREEN_TIMEOUT;
  2769. break;
  2770.  
  2771.  
  2772. case SCREEN_CALC:
  2773. timeout = CALC_SCREEN_TIMEOUT;
  2774. break;
  2775.  
  2776.  
  2777. default:
  2778. timeout = HOME_SCREEN_TIMEOUT;
  2779. break;
  2780. }
  2781.  
  2782.  
  2783. if(millis() - screenOnTime >= timeout)
  2784. {
  2785. Serial.println(">>> SCREEN TIMEOUT <<<");
  2786.  
  2787. screenHardwareOff();
  2788. }
  2789. }
  2790.  
  2791.  
  2792. // ============================================================
  2793. // LOOP
  2794. // ============================================================
  2795.  
  2796. void loop()
  2797. {
  2798. // Physical button
  2799. checkResetButton();
  2800.  
  2801. // Touch
  2802. processTouch();
  2803.  
  2804. // Clock
  2805. unsigned long now = millis();
  2806.  
  2807. if (now - lastClockDisplay >= 1000)
  2808. {
  2809. lastClockDisplay = now;
  2810.  
  2811. // Only redraw the clock when the panel is actually on.
  2812. if (screenIsOn && monitorState == MONITORING && currentScreen == SCREEN_MONITORING)
  2813. updateClockDisplay();
  2814. }
  2815.  
  2816. // Screen auto-off
  2817. screenAutoOffCheck();
  2818.  
  2819. // Battery
  2820. updateBatteryData();
  2821.  
  2822. // Accelerometer
  2823. if (qmi.getDataReady())
  2824. {
  2825. if (qmi.getAccelerometer(acc.x, acc.y, acc.z))
  2826. {
  2827. sensorSampleCount++;
  2828.  
  2829. total_acc = sqrt((acc.x * acc.x) + (acc.y * acc.y) + (acc.z * acc.z));
  2830.  
  2831. printSensorData();
  2832. updateSeizureDetection();
  2833.  
  2834. now = millis();
  2835.  
  2836. // Only redraw monitoring when the panel is actually on.
  2837. if (screenIsOn && monitorState == MONITORING && currentScreen == SCREEN_MONITORING)
  2838. {
  2839. if (now - lastDisplay >= 100)
  2840. {
  2841. lastDisplay = now;
  2842. updateMonitoringValues();
  2843. }
  2844. }
  2845. }
  2846. }
  2847.  
  2848. // Heartbeat
  2849. systemHeartbeat();
  2850.  
  2851. // Yield
  2852. yield();
  2853. }
RAW Paste Data Copied
Advertisement
Advertisement