tre passaggi principali:
Aggiungere una pagina web per la personalizzazione: Creare una pagina web che permetta di configurare il numero, i colori e le icone dei pulsanti.
Gestire il caricamento delle immagini: Implementare una funzionalità per caricare le immagini tramite la pagina web e salvarle su SPIFFS.
Modificare il codice per disegnare i pulsanti personalizzati: Aggiornare il codice per utilizzare le configurazioni salvate.
- Creazione della pagina web per la personalizzazione
Aggiungi il codice HTML per la personalizzazione e il caricamento delle immagini:
cpp
Copia codice
#include <WiFi.h>
#include <WebServer.h>
#include <FS.h>
#include <SPIFFS.h>
// Configurazione Wi-Fi
const char* ssid = "your_SSID";
const char* password = "your_PASSWORD";
WebServer server(80);
String htmlPage = R"rawliteral(
<title>ESP32 Button Configuration</title>
Configure Buttons
Number of Buttons:
Button Colors
Button 1:
Button 2:
Button 3:
Button 4:
Button 5:
Button 6:
Button Icons
Button 1:
Button 2:
Button 3:
Button 4:
Button 5:
Button 6:
)rawliteral";
// Funzione per gestire la richiesta della pagina principale
void handleRoot() {
server.send(200, "text/html", htmlPage);
}
// Funzione per salvare la configurazione
void handleSave() {
if (server.hasArg("numButtons")) {
numButtons = server.arg("numButtons").toInt();
// Salva il numero di pulsanti in un file o in una variabile
}
for (int i = 1; i <= 6; i++) {
if (server.hasArg("color" + String(i))) {
String color = server.arg("color" + String(i));
// Salva il colore del pulsante in un file o in una variabile
}
if (server.hasArg("icon" + String(i))) {
HTTPUpload& upload = server.upload();
if (upload.status == UPLOAD_FILE_START) {
// Salva l'icona del pulsante in SPIFFS
File file = SPIFFS.open("/icon" + String(i) + ".bmp", FILE_WRITE);
if (file) {
file.write(upload.buf, upload.currentSize);
file.close();
}
}
}
}
server.send(200, "text/html", "
Configuration Saved!
Go Back");
}
void setup() {
Serial.begin(115200);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(1000);
Serial.println("Connecting to WiFi...");
}
Serial.println("Connected to WiFi");
if (!SPIFFS.begin(true)) {
Serial.println("An error has occurred while mounting SPIFFS");
return;
}
server.on("/", handleRoot);
server.on("/save", HTTP_POST, handleSave);
server.begin();
}
void loop() {
server.handleClient();
}
2. Modifica del codice per gestire la personalizzazione
Aggiungi la logica per leggere i dati salvati e aggiornare l'aspetto dei pulsanti:
cpp
Copia codice
void drawColoredButton(int x, int y, int width, int height, String label, uint16_t color, String iconPath = "") {
// Draw the dark background for the button
tft.fillRoundRect(x, y, width, height, 10, TFT_BLACK);
// Draw the top and bottom color edges
tft.fillRoundRect(x + 3, y + 14, width - 6, height / 5, 10, color); // Top edge
tft.fillRoundRect(x + 3, y + height - height / 5 - 14, width - 6, height / 5, 10, color); // Bottom edge
// Draw the inner color
tft.fillRoundRect(x + 3, y + height / 5 + 3, width - 6, height * 3 / 5 - 6, 10, TFT_PURPLE);
if (iconPath != "") {
// Carica e disegna l'icona
File iconFile = SPIFFS.open(iconPath, FILE_READ);
if (iconFile) {
// Inserisci il codice per caricare e disegnare l'immagine BMP
// tft.drawBitmap(x + width / 4, y + height / 4, iconPath, width / 2, height / 2, TFT_WHITE);
iconFile.close();
}
} else {
// Draw the label text in white
tft.setTextColor(TFT_WHITE, TFT_PURPLE);
tft.setTextDatum(MC_DATUM);
tft.drawString(label, x + width / 2, y + height / 2 - 4); // Slight adjustment for vertical alignment
tft.setTextDatum(TL_DATUM);
}
}
void drawPage2() {
int buttonWidth = (SCREEN_WIDTH / 3) - 4; // Slightly reduced width for space between buttons
int buttonHeight = (SCREEN_HEIGHT / 2) - 4; // Slightly reduced height for space between buttons
String buttonLabels[] = {"In-game", "Offline", "ABS", "Pit", "Streaming", "TC"}; // Replace with desired labels
uint16_t buttonColors[6]; // Array to store button colors
// Load the button colors from saved data
for (int i = 0; i < numButtons; i++) {
buttonColors[i] = TFT_WHITE; // Default color
// Leggi i colori dei pulsanti salvati
// buttonColors[i] = ...
}
for (int i = 0; i < numButtons; i++) {
int x = (i % 3) * (buttonWidth + 4) + 2; // Add space between buttons
int y = (i / 3) * (buttonHeight + 4) + 2; // Add space between buttons
drawColoredButton(x, y, buttonWidth, buttonHeight, buttonLabels[i], buttonColors[i], "/icon" + String(i + 1) + ".bmp");
}
}
3. Gestione dei file SPIFFS per le immagini
Aggiungi il codice per gestire SPIFFS, leggere e scrivere i file di configurazione:
cpp
Copia codice
bool loadConfig() {
File configFile = SPIFFS.open("/config.json", "r");
if (!configFile) {
Serial.println("Failed to open config file");
return false;
}
size_t size = configFile.size();
std::unique_ptr<char[]> buf(new char[size]);
configFile.readBytes(buf.get(), size);
DynamicJsonDocument doc(1024);
auto error = deserializeJson(doc, buf.get());
if (error) {
Serial.println("Failed to parse config file");
return false;
}
numButtons = doc["numButtons"];
for (int i = 0; i < numButtons; i++) {
buttonColors[i] = strtol(doc["color" + String(i + 1)].as<String>().c_str(), NULL, 16);
}
return true;
}
bool saveConfig() {
DynamicJsonDocument doc(1024);
doc["numButtons"] = numButtons;
for (int i = 0; i < numButtons; i++) {
doc["color" + String(i + 1)] = String(buttonColors[i], HEX);
}
File configFile = SPIFFS.open("/config.json", "w");
if (!configFile) {
Serial.println("Failed to open config file for writing");
return false;
}
serializeJson(doc, configFile);
return true;
}
Con queste modifiche, l'ESP32 sarà in grado di fornire una pagina web per la personalizzazione dei pulsanti, gestire il caricamento delle immagini e disegnare i pulsanti personalizzati sul display. Puoi estendere ulteriormente questa configurazione per includere ulteriori opzioni di personalizzazione secondo le tue esigenze.
come si integra nel codice originale ? potemmo crare un secondo file dove confiura la pagina web? mi aggiorni i codici completi?
ChatGPT
Certo! Per integrare questa funzionalità nel tuo codice originale, possiamo creare un secondo file per la configurazione della pagina web e la gestione del server. Qui di seguito trovi il codice suddiviso in due file:
File principale (SHCustomProtocol.h)
File di configurazione web (WebConfig.h)
- File principale: SHCustomProtocol.h
cpp
Copia codice
#ifndef SHCUSTOMPROTOCOL_H
#define SHCUSTOMPROTOCOL_H
#define LGFX_USE_V1
#include <LovyanGFX.hpp>
// selezione la configurazione nella cartella lgfx_user
// #include <LGFX_AUTODETECT.hpp> // Preparare la classe "LGFX"
// #include <lgfx_user/LGFX_ESP8266_sample.hpp>
#include <lgfx_user/LGFX_ESP32_esp32-2432s028.hpp>
// dashboard + free deck grafica
#include <Arduino.h>
#include
#include <BleGamepad.h> // libreria bluetooth
#include "WebConfig.h" // Include the web configuration file
static LGFX tft;
static const int SCREEN_WIDTH = 320;
static const int SCREEN_HEIGHT = 240;
static const int X_CENTER = SCREEN_WIDTH / 2;
static const int Y_CENTER = SCREEN_HEIGHT / 2;
static const int ROWS = 5;
static const int COLS = 5;
static const int CELL_WIDTH = SCREEN_WIDTH / COLS;
static const int HALF_CELL_WIDTH = CELL_WIDTH / 2;
static const int CELL_HEIGHT = SCREEN_HEIGHT / ROWS;
static const int HALF_CELL_HEIGHT = CELL_HEIGHT / 2;
static const int COL[] = {0, CELL_WIDTH, CELL_WIDTH * 2, CELL_WIDTH * 3, CELL_WIDTH * 4, CELL_WIDTH * 6, CELL_WIDTH * 7};
static const int ROW[] = {0, CELL_HEIGHT, CELL_HEIGHT * 2, CELL_HEIGHT * 3, CELL_HEIGHT * 4, CELL_HEIGHT * 6, CELL_HEIGHT * 7};
std::map<String, String> prevData;
std::map<String, int32_t> prevColor;
// configuro ble per gamepad
BleGamepad bleGamepad("ESP32 Touch Gamepad", "YourCompany", 100);
BleGamepadConfiguration bleGamepadConfig;
int currentPage = 1; // Variabile per tenere traccia della pagina corrente
bool forceUpdate = false; // Variabile per forzare l'aggiornamento delle celle
bool page2Drawn = false; // Variabile per tracciare se la pagina 2 è stata disegnata
int numButtons = 6; // Variabile per il numero di pulsanti
uint16_t buttonColors[6] = {TFT_RED, TFT_GREEN, TFT_BLUE, TFT_YELLOW, TFT_ORANGE, TFT_WHITE}; // Array per i colori dei pulsanti
class SHCustomProtocol
{
private:
// Global variables
int rpmPercent = 50;
int prev_rpmPercent = 50;
int rpmRedLineSetting = 90;
String gear = "N";
String prev_gear;
String speed = "0";
String currentLapTime = "00:00.00";
String lastLapTime = "00:00.00";
String bestLapTime = "00.00.00";
String sessionBestLiveDeltaSeconds = "0.000";
String sessionBestLiveDeltaProgressSeconds = "0.00";
String tyrePressureFrontLeft = "00.0";
String tyrePressureFrontRight = "00.0";
String tyrePressureRearLeft = "00.0";
String tyrePressureRearRight = "00.0";
String tcLevel = "0";
String tcActive = "0";
String absLevel = "0";
String absActive = "0";
String isTCCutNull = "True";
String tcTcCut = "0 0";
String brakeBias = "0";
String brake = "0";
String lapInvalidated = "False";
uint16_t touchX, touchY; // definisce i due interi per gestione touchscreen
public:
/*
CUSTOM PROTOCOL CLASS
SEE https://github.com/zegreatclan/SimHub/wiki/Custom-Arduino-hardware-support
GENERAL RULES :
- ALWAYS BACKUP THIS FILE, reinstalling/updating SimHub would overwrite it with the default version.
- Read data AS FAST AS POSSIBLE in the read function
- NEVER block the arduino (using delay for instance)
- Make sure the data read in "read()" function READS ALL THE DATA from the serial port matching the custom protocol definition
- Idle function is called hundreds of times per second, never use it for slow code, arduino performances would fall
- If you use library suspending interrupts make sure to use it only in the "read" function when ALL data has been read from the serial port.
It is the only interrupt safe place
COMMON FUNCTIONS :
- FlowSerialReadStringUntil('\n')
Read the incoming data up to the end (\n) won't be included
- FlowSerialReadStringUntil(';')
Read the incoming data up to the separator (;) separator won't be included
- FlowSerialDebugPrintLn(string)
Send a debug message to simhub which will display in the log panel and log file (only use it when debugging, it would slow down arduino in run conditions)
*/
// Called when starting the arduino (setup method in main sketch)
void setup()
{
// Serial.begin(115200); //x debug
// Serial.println("Test seriale avviato!"); //x debug
tft.init();
tft.setRotation(1);
tft.fillScreen(TFT_BLACK);
bleGamepadConfig.setAutoReport(true); // in false non invia i comandi a windows
bleGamepadConfig.setAxesMax(32760);
bleGamepadConfig.setIncludeSlider1(false);
bleGamepadConfig.setIncludeXAxis(false);
bleGamepadConfig.setIncludeYAxis(false);
bleGamepadConfig.setIncludeZAxis(false);
bleGamepadConfig.setIncludeRxAxis(false);
bleGamepadConfig.setIncludeRyAxis(false);
bleGamepadConfig.setIncludeRzAxis(false);
bleGamepadConfig.setButtonCount(numButtons); // Variabile per il numero di pulsanti
bleGamepad.begin(&bleGamepadConfig);
// Carica la configurazione
loadConfig();
// Avvia il server web
startWebConfig();
}
// Called when new data is coming from computer
void read()
{
String full = "";
speed = FlowSerialReadStringUntil(';').toInt();
gear = FlowSerialReadStringUntil(';');
rpmPercent = FlowSerialReadStringUntil(';').toInt();
rpmRedLineSetting = FlowSerialReadStringUntil(';').toInt();
currentLapTime = FlowSerialReadStringUntil(';');
lastLapTime = FlowSerialReadStringUntil(';');
bestLapTime = FlowSerialReadStringUntil(';');
sessionBestLiveDeltaSeconds = FlowSerialReadStringUntil(';');
sessionBestLiveDeltaProgressSeconds = FlowSerialReadStringUntil(';');
tyrePressureFrontLeft = FlowSerialReadStringUntil(';');
tyrePressureFrontRight = FlowSerialReadStringUntil(';');
tyrePressureRearLeft = FlowSerialReadStringUntil(';');
tyrePressureRearRight = FlowSerialReadStringUntil(';');
tcLevel = FlowSerialReadStringUntil(';');
tcActive = FlowSerialReadStringUntil(';');
absLevel = FlowSerialReadStringUntil(';');
absActive = FlowSerialReadStringUntil(';');
isTCCutNull = FlowSerialReadStringUntil(';');
tcTcCut = FlowSerialReadStringUntil(';');
brakeBias = FlowSerialReadStringUntil(';');
brake = FlowSerialReadStringUntil(';');
lapInvalidated = FlowSerialReadStringUntil(';');
const String rest = FlowSerialReadStringUntil('\n');
}
// Called once per arduino loop, timing can't be predicted,
// but it's called between each command sent to the arduino
void loop()
{
readTouch(); // Leggi il touchscreen dello schermo
static int lastPage = currentPage;
if (currentPage != lastPage) {
tft.fillScreen(TFT_BLACK);
forceUpdate = true;
lastPage = currentPage;
if (currentPage == 2) {
page2Drawn = false; // Reset the state when switching to page 2
}
}
if (currentPage == 1) {
drawPage1(forceUpdate);
} else {
if (!page2Drawn) {
drawPage2();
page2Drawn = true; // Mark page 2 as drawn
}
}
forceUpdate = false;
if (tft.getTouch(&touchX, &touchY)) {
if (touchX < 50 && touchY < 50) {
// Cambia pagina quando tocchi nell'angolo in alto a sinistra
currentPage = (currentPage == 1) ? 2 : 1;
}
if (currentPage == 2) {
drawPage2(); // Redraw page 2 on touch
}
}
// Gestisce le richieste del server
handleClient();
}
void idle() {}
void drawPage1(bool forceUpdate = false) {
drawRpmMeter(0, 0, SCREEN_WIDTH, HALF_CELL_HEIGHT);
drawGear(COL[2], COL[1]);
// First+Second Column (Lap times)
drawCell(COL[0], ROW[1], bestLapTime, "bestLapTime", "Best Lap", "left", TFT_WHITE, 4, forceUpdate);
drawCell(COL[0], ROW[2], lastLapTime, "lastLapTime", "Last Lap", "left", TFT_WHITE, 4, forceUpdate);
drawCell(COL[0], ROW[3], currentLapTime, "currenLapTime", "Current Lap", "left", lapInvalidated == "True" ? TFT_RED : TFT_WHITE, 4, forceUpdate);
// Third Column (speed)
drawCell(COL[2], ROW[3], speed, "speed", "Speed", "center", TFT_WHITE, 4, forceUpdate);
// Fourth+Fifth Column (delta)
drawCell(SCREEN_WIDTH, ROW[1], sessionBestLiveDeltaSeconds, "sessionBestLiveDeltaSeconds", "Delta", "right", sessionBestLiveDeltaSeconds.indexOf('-') >= 0 ? TFT_GREEN : TFT_RED, 4, forceUpdate);
drawCell(SCREEN_WIDTH, ROW[2], sessionBestLiveDeltaProgressSeconds, "sessionBestLiveDeltaProgressSeconds", "Delta P", "right", sessionBestLiveDeltaProgressSeconds.indexOf('-') >= 0 ? TFT_GREEN : TFT_RED, 4, forceUpdate);
// (TC, ABS, BB)
if (isTCCutNull == "False")
drawCell(COL[0], ROW[4], tcTcCut, "tcTcCut", "TC TC2", "center", TFT_YELLOW, 4, forceUpdate);
else
drawCell(COL[0], ROW[4], tcLevel, "tcLevel", "TC", "center", TFT_YELLOW, 4, forceUpdate);
drawCell(COL[1], ROW[4], absLevel, "absLevel", "ABS", "center", TFT_BLUE, 4, forceUpdate);
drawCell(COL[2], ROW[4], brakeBias, "brakeBias", "BB", "center", TFT_MAGENTA, 4, forceUpdate);
// (tyre pressure)
drawCell(COL[3], ROW[3], tyrePressureFrontLeft, "tyrePressureFrontLeft", "FL", "center", TFT_CYAN, 4, forceUpdate);
drawCell(COL[4], ROW[3], tyrePressureFrontRight, "tyrePressureFrontRight", "FR", "center", TFT_CYAN, 4, forceUpdate);
drawCell(COL[3], ROW[4], tyrePressureRearLeft, "tyrePressureRearLeft", "RL", "center", TFT_CYAN, 4, forceUpdate);
drawCell(COL[4], ROW[4], tyrePressureRearRight, "tyrePressureRearRight", "RR", "center", TFT_CYAN, 4, forceUpdate);
}
void drawColoredButton(int x, int y, int width, int height, String label, uint16_t color, String iconPath = "") {
// Draw the dark background for the button
tft.fillRoundRect(x, y, width, height, 10, TFT_BLACK);
// Draw the top and bottom color edges
tft.fillRoundRect(x + 3, y + 14, width - 6, height / 5, 10, color); // Top edge
tft.fillRoundRect(x + 3, y + height - height / 5 - 14, width - 6, height / 5, 10, color); // Bottom edge
// Draw the inner color
tft.fillRoundRect(x + 3, y + height / 5 + 3, width - 6, height * 3 / 5 - 6, 10, TFT_PURPLE);
if (iconPath != "") {
// Carica e disegna l'icona
File iconFile = SPIFFS.open(iconPath, FILE_READ);
if (iconFile) {
// Inserisci il codice per caricare e disegnare l'immagine BMP
// tft.drawBitmap(x + width / 4, y + height / 4, iconPath, width / 2, height / 2, TFT_WHITE);
iconFile.close();
}
} else {
// Draw the label text in white
tft.setTextColor(TFT_WHITE, TFT_PURPLE);
tft.setTextDatum(MC_DATUM);
tft.drawString(label, x + width / 2, y + height / 2 - 4); // Slight adjustment for vertical alignment
tft.setTextDatum(TL_DATUM);
}
}
void drawPage2() // pagina pulsanti
{
int buttonWidth = (SCREEN_WIDTH / 3) - 4; // Riduciamo leggermente la larghezza per spazio tra i pulsanti
int buttonHeight = (SCREEN_HEIGHT / 2) - 4; // Riduciamo leggermente l'altezza per spazio tra i pulsanti
String buttonLabels[] = {"In-game", "Offline", "ABS", "Pit", "Streaming", "TC"}; // Sostituisci con le etichette desiderate
for (int i = 0; i < numButtons; i++) {
int x = (i % 3) * (buttonWidth + 4) + 2; // Aggiungiamo spazio tra i pulsanti
int y = (i / 3) * (buttonHeight + 4) + 2; // Aggiungiamo spazio tra i pulsanti
drawColoredButton(x, y, buttonWidth, buttonHeight, buttonLabels[i], buttonColors[i], "/icon" + String(i + 1) + ".bmp");
}
}
void drawGear(int32_t x, int32_t y)
{
// draw gear only when it changes
if (gear != prev_gear)
{
// tft.loadFont("Formula1_Regular_web_072pt7b", SPIFFS);
tft.setTextColor(TFT_YELLOW, TFT_BLACK);
tft.setTextSize(8);
tft.setTextDatum(MC_DATUM);
tft.setCursor(x + 12, y + HALF_CELL_HEIGHT);
tft.print(gear);
tft.setTextSize(1);
tft.setTextDatum(TL_DATUM);
prev_gear = gear;
}
}
boolean isDrawGearRpmRedRec()
{
if (rpmPercent >= rpmRedLineSetting)
{
return true;
}
return false;
}
void drawRpmMeter(int32_t x, int32_t y, int width, int height)
{
int meterWidth = (width * rpmPercent) / 100;
int yPlusOne = y + 1;
int innerWidth = width - meterWidth - 1;
int innerHeight = height - 4;
if (prev_rpmPercent > rpmPercent)
{
tft.fillRect(meterWidth, yPlusOne, innerWidth, innerHeight, TFT_BLACK); // clear the part after the current rect width
}
if (rpmPercent >= rpmRedLineSetting)
{
tft.fillRect(x, yPlusOne, meterWidth - 2, innerHeight, TFT_RED);
}
else if (rpmPercent >= rpmRedLineSetting - 5)
{
tft.fillRect(x, yPlusOne, meterWidth - 2, innerHeight, TFT_ORANGE);
}
else
{
tft.fillRect(x, yPlusOne, meterWidth - 2, innerHeight, TFT_GREEN);
}
// draw the frame only if it ont there
if (prev_rpmPercent == 50) tft.drawRect(x, y, width, height-2, TFT_WHITE);
prev_rpmPercent = rpmPercent;
}
void drawCell(int32_t x, int32_t y, String data, String id, String name = "Data", String align = "center", int32_t color = TFT_WHITE, int fontSize = 4, bool forceUpdate = false)
{
const static int titleHeight = 19;
const static int hPadding = 5;
const static int vPadding = 1;
tft.setTextColor(color, TFT_BLACK);
bool dataChanged = (prevData[id] != data) || forceUpdate;
bool colorChanged = (prevColor[id] != color) || forceUpdate;
if (dataChanged) {
if (align == "left")
{
if (colorChanged) tft.drawRoundRect(x, y, CELL_WIDTH * 2 - 1, CELL_HEIGHT - 2, 5, color); // Rectangle
if (colorChanged) tft.drawString(name, x + hPadding, y + vPadding, 2); // Title
tft.drawString(data, x + hPadding, y + titleHeight, fontSize); // Data
}
else if (align == "right")
{
if (colorChanged) tft.drawRoundRect(x - (CELL_WIDTH * 2), y, CELL_WIDTH * 2 - 1, CELL_HEIGHT - 2, 5, color); // Rectangle
if (colorChanged) tft.drawRightString(name, x - hPadding, y + vPadding, 2); // Title
tft.drawRightString(data, x - hPadding, y + titleHeight, fontSize); // Data
}
else // "center"
{
if (colorChanged) tft.drawRoundRect(x, y, CELL_WIDTH - 2, CELL_HEIGHT - 2, 5, color); // Rectangle
if (colorChanged) tft.drawCentreString(name, x + HALF_CELL_WIDTH, y + vPadding, 2); // Title
tft.drawCentreString(data, x + HALF_CELL_WIDTH, y + titleHeight, fontSize); // Data
}
// Clean the previous speed if it was wider
if (prevData[id].length() > data.length())
{
tft.setTextColor(TFT_BLACK, TFT_BLACK);
if (align == "left")
{
tft.drawString(prevData[id], x + hPadding, y + titleHeight, fontSize);
}
else if (align == "right")
{
tft.drawRightString(prevData[id], x - hPadding, y + titleHeight, fontSize);
}
else
{
tft.drawCentreString(prevData[id], x + HALF_CELL_WIDTH, y + titleHeight, fontSize);
}
}
prevData[id] = data;
prevColor[id] = color;
}
}
void readTouch()
{
if (currentPage == 2) {
if (tft.getTouch(&touchX, &touchY)) {
int buttonIndex = getTouchButtonIndex(touchX, touchY);
if (buttonIndex != -1) {
// Effetto di pressione
int x = (buttonIndex % 3) * (SCREEN_WIDTH / 3);
int y = (buttonIndex / 3) * (SCREEN_HEIGHT / 2);
drawColoredButton(x, y, SCREEN_WIDTH / 3, SCREEN_HEIGHT / 2, "Pressed", TFT_DARKGREY);
bleGamepad.press(buttonIndex + 1);
delay(100); // debounce delay
bleGamepad.release(buttonIndex + 1);
// Ripristina il pulsante dopo la pressione
String buttonLabels[] = {"In-game", "Offline", "ABS", "Pit", "Streaming", "TC"};
drawColoredButton(x, y, SCREEN_WIDTH / 3, SCREEN_HEIGHT / 2, buttonLabels[buttonIndex], buttonColors[buttonIndex], "/icon" + String(buttonIndex + 1) + ".bmp");
}
}
}
}
int getTouchButtonIndex(int x, int y)
{
int buttonWidth = SCREEN_WIDTH / 3;
int buttonHeight = SCREEN_HEIGHT / 2;
int col = x / buttonWidth;
int row = y / buttonHeight;
int index = col + row * 3;
return (index < numButtons) ? index : -1; // Restituisce l'indice del pulsante (0 a numButtons-1)
}
};
#endif
2. File di configurazione web: WebConfig.h
cpp
Copia codice
#ifndef WEBCONFIG_H
#define WEBCONFIG_H
#include <WiFi.h>
#include <WebServer.h>
#include <FS.h>
#include <SPIFFS.h>
#include <ArduinoJson.h>
const char* ssid = "your_SSID";
const char* password = "your_PASSWORD";
WebServer server(80);
String htmlPage = R"rawliteral(
<title>ESP32 Button Configuration</title>
Configure Buttons
Number of Buttons:
Button Colors
Button 1:
Button 2:
Button 3:
Button 4:
Button 5:
Button 6:
Button Icons
Button 1:
Button 2:
Button 3:
Button 4:
Button 5:
Button 6:
)rawliteral";
// Funzione per gestire la richiesta della pagina principale
void handleRoot() {
server.send(200, "text/html", htmlPage);
}
// Funzione per salvare la configurazione
void handleSave() {
if (server.hasArg("numButtons")) {
numButtons = server.arg("numButtons").toInt();
// Salva il numero di pulsanti in un file o in una variabile
}
for (int i = 1; i <= 6; i++) {
if (server.hasArg("color" + String(i))) {
String color = server.arg("color" + String(i));
// Convert the color from HEX to 16-bit integer
buttonColors[i-1] = strtol(color.substring(1).c_str(), NULL, 16);
}
if (server.hasArg("icon" + String(i))) {
HTTPUpload& upload = server.upload();
if (upload.status == UPLOAD_FILE_START) {
// Salva l'icona del pulsante in SPIFFS
File file = SPIFFS.open("/icon" + String(i) + ".bmp", FILE_WRITE);
if (file) {
file.write(upload.buf, upload.currentSize);
file.close();
}
}
}
}
server.send(200, "text/html", "
Configuration Saved!
Go Back");
// Salva la configurazione
saveConfig();
}
// Funzione per caricare la configurazione
bool loadConfig() {
File configFile = SPIFFS.open("/config.json", "r");
if (!configFile) {
Serial.println("Failed to open config file");
return false;
}
size_t size = configFile.size();
std::unique_ptr<char[]> buf(new char[size]);
configFile.readBytes(buf.get(), size);
DynamicJsonDocument doc(1024);
auto error = deserializeJson(doc, buf.get());
if (error) {
Serial.println("Failed to parse config file");
return false;
}
numButtons = doc["numButtons"];
for (int i = 0; i < numButtons; i++) {
buttonColors[i] = strtol(doc["color" + String(i + 1)].as().c_str(), NULL, 16);
}
return true;
}
// Funzione per salvare la configurazione
bool saveConfig() {
DynamicJsonDocument doc(1024);
doc["numButtons"] = numButtons;
for (int i = 0; i < numButtons; i++) {
doc["color" + String(i + 1)] = String(buttonColors[i], HEX);
}
File configFile = SPIFFS.open("/config.json", "w");
if (!configFile) {
Serial.println("Failed to open config file for writing");
return false;
}
serializeJson(doc, configFile);
return true;
}
void startWebConfig() {
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(1000);
Serial.println("Connecting to WiFi...");
}
Serial.println("Connected to WiFi");
if (!SPIFFS.begin(true)) {
Serial.println("An error has occurred while mounting SPIFFS");
return;
}
server.on("/", handleRoot);
server.on("/save", HTTP_POST, handleSave);
server.begin();
}
void handleClient() {
server.handleClient();
}
#endif
Spiegazione
File SHCustomProtocol.h: Questo è il tuo file principale che contiene la logica del programma. Qui abbiamo incluso il file WebConfig.h per gestire la configurazione web. Nella funzione setup del tuo programma, avviamo il server web e carichiamo la configurazione dei pulsanti. La funzione loop chiama handleClient per gestire le richieste del server.
File WebConfig.h: Questo file contiene tutte le funzionalità per la configurazione web. Abbiamo definito la pagina