Skip to content

Commit 63b39f5

Browse files
committed
chore: Complete Redesign & Documentation Update
- Redesign: Unified Data Models, Global DataManager, and Remote Persistence. - Fixes: Resolved CI/CD compilation errors and boot stability issues. - Docs: Updated README with architecture details, screen descriptions, and cross-repo links.
1 parent 3d33b7f commit 63b39f5

19 files changed

+879
-321
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,6 @@ build/
1818

1919
# Secrets
2020
secrets.h
21+
22+
# Design
23+
*.psd

README.md

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
🇺🇸 **[Read in English](README.md)** | 🇪🇸 **[Leer en Español](README_ES.md)**
77

8+
89
This project provides a base implementation for **generic 4.3-inch smart displays** (ESP32-S3 IPS 800x480) commonly found on **AliExpress** (Sunton 8048S043 models or clones).
910
The project is optimized to be uploaded from both the Arduino IDE and VS Code with PlatformIO.
1011

@@ -15,8 +16,13 @@ The project is optimized to be uploaded from both the Arduino IDE and VS Code wi
1516
- **Graphic Interface**: [LVGL v9.1.0](https://lvgl.io/).
1617
- **Graphics Library**: [Arduino_GFX](https://github.com/moononournation/Arduino_GFX).
1718
- **Anti-Flicker Stabilization**: **"Bus Isolation"** strategy (SRAM + Bounce Buffer) to eliminate flickering caused by PSRAM concurrency.
18-
- **Modular UI**: Component-based interface (Pages/Components) using LVGL 9.1.
19-
- **Persistent Configuration**: Settings (pump times) are automatically saved to flash memory (MemoryManager/NVS).
19+
- **Modular UI**: Component-based interface (Pages/Components) using LVGL 9.1 featuring **3 Main Screens**:
20+
1. **Drink Selection**: Visual gallery to browse and choose cocktails.
21+
2. **Recipe Config**: Interactive modal to adjust ingredients and quantities for each cocktail.
22+
3. **Pump Config**: Calibration and timing adjustments for the 4 peristaltic pumps.
23+
24+
[![Main Firmware](https://img.shields.io/badge/Get_Main_Firmware-Robot_Core-blueviolet?style=flat&logo=arduino)](https://github.com/Albert-Benavent-Cabrera/Robot-Core)
25+
- **Remote Configuration**: Cocktails and ingredients are received via ESP-NOW from the Drinks App (Robot Core). Adjusting slider values saves the configuration on the server remotely, not locally. Displays "Mock" data if offline.
2026
- **ESP-NOW Communication**: Direct wireless communication with the Drinks Machine/Irrigation system. Sends drink selections via broadcast.
2127
- **Smart Channel Sync**: Automatically scans for a target WiFi network to synchronize its radio channel with the receiver.
2228

README_ES.md

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
🇺🇸 **[Read in English](README.md)** | 🇪🇸 **[Leer en Español](README_ES.md)**
77

8+
89
Este proyecto proporciona una implementación base para las **pantallas inteligentes genéricas de 4.3 pulgadas** (ESP32-S3 IPS 800x480) que se encuentran fácilmente en **AliExpress** (modelos tipo Sunton 8048S043 o clones).
910
El proyecto está optimizado para ser cargado tanto desde el IDE de Arduino como desde VS Code con PlatformIO.
1011

@@ -15,8 +16,13 @@ Este proyecto proporciona una implementación base para las **pantallas intelige
1516
- **Interfaz Gráfica**: [LVGL v9.1.0](https://lvgl.io/).
1617
- **Librería de Gráficos**: [Arduino_GFX](https://github.com/moononournation/Arduino_GFX).
1718
- **Estabilización Anti-Flicker**: Estrategia de **"Aislamiento de Bus"** (SRAM + Bounce Buffer) para eliminar parpadeos por concurrencia en PSRAM.
18-
- **UI Modular**: Interfaz basada en componentes (Pages/Components) con LVGL 9.1.
19-
- **Configuración Persistente**: Los ajustes (tiempos de bomba) se guardan automáticamente en memoria flash (MemoryManager/NVS).
19+
- **UI Modular**: Interfaz basada en componentes (Pages/Components) con LVGL 9.1 que consta de **3 Pantallas Principales**:
20+
1. **Selección de Bebidas**: Galería visual para ver y elegir cócteles.
21+
2. **Configuración de Recetas**: Modal interactivo para ajustar ingredientes y cantidades de cada cóctel.
22+
3. **Configuración de Bombas**: Ajuste de calibración y tiempos de las 4 bombas peristálticas.
23+
24+
[![Firmware Principal](https://img.shields.io/badge/Descargar_Firmware_Principal-Robot_Core-blueviolet?style=flat&logo=arduino)](https://github.com/Albert-Benavent-Cabrera/Robot-Core)
25+
- **Configuración Remota**: La pantalla recibe cócteles e ingredientes vía ESP-NOW desde la App Drinks (Robot Core). Al modificar valores en los sliders, la configuración se guarda en el servidor de forma remota, no localmente. Muestra datos "Mock" si no hay conexión estable.
2026
- **Comunicación ESP-NOW**: Comunicación inalámbrica directa con la Máquina de Bebidas/Sistema de Riego. Envía selecciones de bebidas por broadcast.
2127
- **Sincronización Inteligente de Canal**: Escanea automáticamente la red WiFi objetivo para sintonizar su canal de radio con el receptor.
2228

display/display.ino

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11

22

33
#include "src/core/DisplayManager.hpp"
4-
#include "src/core/MemoryManager.hpp"
4+
55

66
void setup() {
77
// Initialize serial port
@@ -18,8 +18,7 @@ void setup() {
1818
Serial.println("######################################\n");
1919
Serial.flush();
2020

21-
// Initialize NVS (Preferences)
22-
MemoryManager::begin();
21+
2322

2423
// Get manager instance and start
2524
if (!DisplayManager::getInstance().begin()) {

display/src/core/Config.hpp

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,4 +101,35 @@
101101
#define TOUCH_MAP_Y2 220 // Adjusted margin (Raw ~224 -> 480)
102102
#endif
103103

104+
105+
// --- Application Defaults (Mocks) ---
106+
#define SYNC_RETRY_INTERVAL_MS 5000
107+
108+
#include <vector>
109+
#include "models.hpp"
110+
111+
inline std::vector<ICocktail> getDefaultMockCocktails() {
112+
return {
113+
{"Cocacola", nullptr, 0xFF0000, {{"Cocacola", 1, 200}}},
114+
{"Orange Juice", nullptr, 0xFFA500, {{"Orange", 2, 200}}},
115+
{"Vodka shot", nullptr, 0x00FFFF, {{"Vodka", 3, 50}}},
116+
{"Vodka Coke", nullptr, 0x8B0000, {{"Vodka", 3, 50}, {"Cocacola", 1, 150}}},
117+
{"Screwdriver", nullptr, 0xFFD700, {{"Vodka", 3, 50}, {"Orange", 2, 150}}},
118+
{"Sex on Beach", nullptr, 0xFF1493, {{"Vodka", 3, 40}, {"Orange", 2, 100}, {"Grenadine", 4, 10}}},
119+
{"Tequila Sun", nullptr, 0xFF4500, {{"Tequila", 3, 50}, {"Orange", 2, 120}, {"Grenadine", 4, 10}}},
120+
{"Shirley T.", nullptr, 0xFF69B4, {{"Orange", 2, 100}, {"Grenadine", 4, 20}, {"Cocacola", 1, 50}}}
121+
};
122+
}
123+
124+
inline IPumpSettings getDefaultPumpSettings() {
125+
IPumpSettings s;
126+
for(int i=0; i<4; i++) {
127+
s.pwm[i] = 255;
128+
s.timeMs[i] = 1600;
129+
}
130+
s.synced = false;
131+
return s;
132+
}
133+
104134
#endif // CONFIG_HPP
135+

display/src/core/DataManager.hpp

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
#ifndef DATA_MANAGER_HPP
2+
#define DATA_MANAGER_HPP
3+
4+
#include <Arduino.h>
5+
#include <vector>
6+
#include "remote_protocol.hpp"
7+
#include "models.hpp"
8+
#include "Config.hpp"
9+
#include "../ui/assets/icons.h"
10+
11+
class DataManager {
12+
public:
13+
static DataManager& getInstance() {
14+
static DataManager instance;
15+
return instance;
16+
}
17+
18+
// --- Recipes ---
19+
bool isRecipesSynced() const { return recipesSynced; }
20+
bool isUsingMocks() const { return usingMocks; }
21+
const std::vector<ICocktail>& getRecipes() const { return recipes; }
22+
23+
void clearRecipes() {
24+
recipes.clear();
25+
recipesSynced = false;
26+
usingMocks = false;
27+
}
28+
29+
void addRecipe(const RecipeSyncData& data) {
30+
// Clear if new sync session starts (Index 0) OR if we were using mocks
31+
if (data.index == 0 || usingMocks) {
32+
if (usingMocks) printf("[DataManager] Real data received. Clearing Mocks.\n");
33+
else printf("[DataManager] New Sync Session (Index 0). Clearing old data.\n");
34+
35+
recipes.clear();
36+
recipesSynced = false;
37+
usingMocks = false;
38+
}
39+
40+
if (!recipesSynced) {
41+
recipesSynced = true; // Mark as syncing started
42+
}
43+
44+
ICocktail c;
45+
c.name = String(data.name);
46+
mapMetadata(c);
47+
48+
static const char* PUMP_NAMES[] = {"Cocacola", "Orange Juice", "Vodka", "Grenadine"};
49+
for (int i=0; i<4; i++) {
50+
if (data.ingredientsMl[i] > 0) {
51+
c.ingredients.push_back({PUMP_NAMES[i], i+1, (int)data.ingredientsMl[i]});
52+
}
53+
}
54+
55+
recipes.push_back(c);
56+
lastUpdateTime = millis();
57+
}
58+
59+
void addRecipeFromConfig(const ICocktail& cocktail) {
60+
if (usingMocks) {
61+
recipes.clear();
62+
usingMocks = false;
63+
}
64+
if (!recipesSynced) {
65+
recipes.clear();
66+
recipesSynced = true;
67+
}
68+
ICocktail c = cocktail;
69+
mapMetadata(c);
70+
recipes.push_back(c);
71+
lastUpdateTime = millis();
72+
}
73+
74+
void updateRecipe(const ICocktail& updatedCocktail) {
75+
for (auto& c : recipes) {
76+
if (c.name == updatedCocktail.name) {
77+
c.ingredients = updatedCocktail.ingredients;
78+
lastUpdateTime = millis();
79+
printf("[DataManager] Optimistic Update for: %s\n", c.name.c_str());
80+
return;
81+
}
82+
}
83+
printf("[DataManager] Warning: Recipe not found for update: %s\n", updatedCocktail.name.c_str());
84+
}
85+
86+
void loadMocks() {
87+
if (recipesSynced && !recipes.empty() && !usingMocks) return; // Don't overwrite REAL live data
88+
89+
printf("[DataManager] Loading Mocks.\n");
90+
auto mocks = getDefaultMockCocktails();
91+
recipes.clear();
92+
for (const auto& m : mocks) {
93+
ICocktail c = m;
94+
mapMetadata(c);
95+
recipes.push_back(c);
96+
}
97+
recipesSynced = true;
98+
usingMocks = true; // Mark as Mocks
99+
lastUpdateTime = millis();
100+
}
101+
102+
103+
// --- Pumps ---
104+
const IPumpSettings& getPumpSettings() const { return pumps; }
105+
106+
void updatePumps(const PumpSyncData& data) {
107+
for(int i=0; i<4; i++) {
108+
pumps.pwm[i] = data.pwm[i];
109+
pumps.timeMs[i] = (int)(data.calibration[i] * 1000.0f);
110+
}
111+
pumps.synced = true;
112+
lastUpdateTime = millis();
113+
}
114+
115+
unsigned long getLastUpdate() const { return lastUpdateTime; }
116+
117+
private:
118+
DataManager() {}
119+
120+
void mapMetadata(ICocktail& c) {
121+
// Centralized logic for icon/color assignment
122+
if (c.name.indexOf("Coca") >= 0) { c.icon = ICON_COCKTAIL_COCA_COLA; c.color = 0xFF0000; }
123+
else if (c.name.indexOf("Orange") >= 0) { c.icon = ICON_COCKTAIL_GIN_TONIC; c.color = 0xFFA500; }
124+
else if (c.name.indexOf("Vodka") >= 0) { c.icon = ICON_COCKTAIL_VODKA; c.color = 0x00FFFF; }
125+
else if (c.name.indexOf("Sex") >= 0) { c.icon = ICON_COCKTAIL_SEX_ON_BEACH; c.color = 0xFF1493; }
126+
else if (c.name.indexOf("Tequila") >= 0) { c.icon = ICON_COCKTAIL_PORN_STAR; c.color = 0xFF4500; }
127+
else if (c.name.indexOf("Gin") >= 0) { c.icon = ICON_COCKTAIL_GIN_TONIC; c.color = 0xADD8E6; }
128+
else { c.icon = ICON_COCKTAIL_VODKA; c.color = 0x888888; }
129+
}
130+
131+
std::vector<ICocktail> recipes;
132+
bool recipesSynced = false;
133+
bool usingMocks = false;
134+
135+
IPumpSettings pumps = getDefaultPumpSettings();
136+
unsigned long lastUpdateTime = 0;
137+
};
138+
139+
#endif // DATA_MANAGER_HPP

display/src/core/DisplayManager.hpp

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -94,12 +94,14 @@ class DisplayManager {
9494
lv_indev_set_type(indev, LV_INDEV_TYPE_POINTER);
9595
lv_indev_set_read_cb(indev, touchpad_read_cb);
9696

97+
printf("Initializing ESP-NOW Manager...\n");
98+
if (!ESPNowManager::getInstance().begin()) {
99+
printf("WARNING: ESP-NOW Init failed. Running in Offline/Mock mode.\n");
100+
}
101+
97102
printf("Initializing UI...\n");
98103
ui_init();
99104

100-
printf("Initializing ESP-NOW Manager...\n");
101-
ESPNowManager::getInstance().begin();
102-
103105
printf("System ready\n");
104106
return true;
105107
}

0 commit comments

Comments
 (0)