Bonsoir,
J'ai un code monolithique (ESP32) qui commence à devenir trop grop et que je veux structurer en plusieurs fichiers. Je commence à comprendre la logique (enfin je pense) et je suis arrivé à extraire une partie du code...
J'ai fait une version simplifiée du code pour la poster ici, sinon c'est trop long
Le fichier .ino
#include <WiFi.h>
#include <AsyncTCP.h>
#include <ESPAsyncWebServer.h>
#include <AsyncElegantOTA.h>
#include <LittleFS.h>
#include "servweb.h"
#define LED_BUILTIN 15
volatile bool ledState;
volatile bool rafraichir;
volatile int valeur;
// Réseau perso
String ssid = "xxxxxxxx";
String pass = "xxxxxxxxxxxxxxxx";
String hostname = "ESP32-WS";
String AP_ssid = "AP_EST32";
String AP_pass = "";
byte AP_canal = 7;
int periode = 1000;
// ------------------------------ PROCESSOR --------------------------------------
String processor(const String& var){
if(var == "VALEUR"){
return (String)valeur;
}
return String();
}
// ------------------------------ INTERRUPTIONS ----------------------------------
hw_timer_t * timer = NULL;
void IRAM_ATTR timer_isr() {
// code du timer ici
ledState = !ledState;
if (ledState) {
digitalWrite(LED_BUILTIN, HIGH);
} else {
digitalWrite(LED_BUILTIN, LOW);
}
valeur = random(6) + 1; // tirage du dé
rafraichir = true; // pour dire à la loop() qu'il y a de nouvelles données
}
// ------------------------------ SETUP ------------------------------------------
void setup(){
Serial.begin(115200);
delay(2000); // laisse moi le temps de démarrer la console série
pinMode(LED_BUILTIN, OUTPUT);
Serial.println("démarrage");
randomSeed(analogRead(13)); // 13 = broche en l'air, sel aléatoire
if(!LittleFS.begin(1)){
Serial.println("erreur de montage LittleFS");
return;
}
// wifi avec fall-back en AP si erreur
WiFi.setAutoConnect(false);
WiFi.config(INADDR_NONE, INADDR_NONE, INADDR_NONE, INADDR_NONE);
WiFi.setHostname(hostname.c_str()); // pour définir le hostname vu par le réseau wifi
Serial.printf("Recherche du réseau %s\r\n", ssid); // réseau prédéfini (non vide)
WiFi.mode(WIFI_STA);
WiFi.disconnect();
delay(200);
int nbwifi = WiFi.scanNetworks();
for (int n = 0; n < nbwifi; ++n) {
if (WiFi.SSID(n) == ssid) { // trouvé le bon réseau dans la liste
WiFi.mode(WIFI_STA); //Mode Station seulement
// WiFi.config(ip, gateway, subnet); // pour IP fixe
WiFi.begin(ssid, pass);
ulong timeout = millis();
Serial.print("Connexion au wifi en cours.");
while ((WiFi.status() != WL_CONNECTED) and (millis()-timeout < 10000)) {
digitalWrite(LED_BUILTIN, HIGH);
delay(100);
digitalWrite(LED_BUILTIN, LOW);
delay(100);
Serial.print(".");
}
}
delay(100);
}
if (WiFi.status() == WL_CONNECTED) {
// la connexion a réussi
Serial.println(" Connecté au wifi maison !");
Serial.print("Serveur accessible à l'adresse : ");
Serial.println(WiFi.localIP());
WiFi.setAutoReconnect(false); // pas sur que ce soit nécessaire, mais vu dans plusieurs exemples
} else {
// echec de connexion
Serial.println(" Échec de connexion au wifi maison - Fallback en AP");
WiFi.mode(WIFI_AP); // drop station mode if LAN/WiFi is down
WiFi.softAP(AP_ssid, AP_pass);
Serial.printf("AP : %s | Adresse IP : %s\r\n", AP_ssid, WiFi.softAPIP().toString().c_str());
}
initWebSocket();
// Chemin vers les fichiers...
// pour la racine, servir index.html en GET
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
request->send(LittleFS, "/index.html", String(), false, processor);
});
// répondre aussi pour index.html appelé explicitement (c'est ballot sinon)
server.on("/index.html", HTTP_GET, [](AsyncWebServerRequest *request){
request->send(LittleFS, "/index.html", String(), false, processor);
});
server.on("/apropos.html", HTTP_GET, [](AsyncWebServerRequest *request){
request->send(LittleFS, "/apropos.html", String(), false, processor);
});
AsyncElegantOTA.begin(&server); // OTA c'est cool
server.begin();
// interruptions
// timer
uint8_t timer_id = 0;
uint16_t prescaler = 80; // CPU à 80MHz ça simplifie les calculs
int threshold = periode * 1000; // nbr de µs
timer = timerBegin(timer_id, prescaler, true);
timerAttachInterrupt(timer, &timer_isr, true);
timerAlarmWrite(timer, threshold, true);
timerAlarmEnable(timer);
}
// ------------------------------ FIN DU SETUP -----------------------------------
// ------------------------------ LOOP -------------------------------------------
void loop(){
ws.cleanupClients();
if (rafraichir) {
rafraichir = false;
ws.textAll(String(valeur));
}
}
Je voudrais sortir tout ce qui se rapporte au serveur web, j'ai un fichier servweb.h
:
#include <Arduino.h>
#include <ESPAsyncWebServer.h>
// ------------------------------ DÉFINITIONS ------------------------------------
extern volatile bool ledState;
extern volatile int valeur;
extern AsyncWebServer server;
extern AsyncWebSocket ws;
// ------------------------------ ROUTINES DE WEBSOCKET --------------------------
void notifyClients();
void handleWebSocketMessage(void *arg, uint8_t *data, size_t len);
void onEvent(AsyncWebSocket *server, AsyncWebSocketClient *client, AwsEventType type,
void *arg, uint8_t *data, size_t len);
void initWebSocket();
et le fichier servweb.cpp correspondant :
#include <Arduino.h>
#include <ESPAsyncWebServer.h>
// ------------------------------ DÉFINITIONS ------------------------------------
extern volatile bool ledState;
extern volatile int valeur;
// ------------------------------ ROUTINES DE WEBSOCKET --------------------------
// copiées collées de https://randomnerdtutorials.com/esp32-websocket-server-arduino/
// Le serveur port 80 et la chaussette
AsyncWebServer server(80);
AsyncWebSocket ws("/ws");
void notifyClients() {
ws.textAll(String(valeur));
}
void handleWebSocketMessage(void *arg, uint8_t *data, size_t len) {
// pas utilisé puisque pas de bouton dans ma page
AwsFrameInfo *info = (AwsFrameInfo*)arg;
if (info->final && info->index == 0 && info->len == len && info->opcode == WS_TEXT) {
data[len] = 0;
if (strcmp((char*)data, "toggle") == 0) {
ledState = !ledState;
notifyClients();
}
}
}
// pour recevoir les eventements du client
void onEvent(AsyncWebSocket *server, AsyncWebSocketClient *client, AwsEventType type,
void *arg, uint8_t *data, size_t len) {
switch (type) {
case WS_EVT_CONNECT:
Serial.printf("WebSocket client #%u connected from %s\n", client->id(), client->remoteIP().toString().c_str());
// globalClient = client;
break;
case WS_EVT_DISCONNECT:
Serial.printf("WebSocket client #%u disconnected\n", client->id());
// globalClient = NULL;
break;
case WS_EVT_DATA:
handleWebSocketMessage(arg, data, len);
Serial.printf("WS_EVT_DATA\n", client->id());
// à priori rien non plus ici puisque rien sur la page (pour le moment)
break;
case WS_EVT_PONG:
case WS_EVT_ERROR:
break;
}
}
void initWebSocket() {
ws.onEvent(onEvent);
server.addHandler(&ws);
}
Tout ça c'est OK, ça compile et ça fonctionne...
Mais j'aimerai aussi sortir du code principal la partie String processor(const String& var){}
parce que dans le "vrai" code, elle fait quand même 50 lignes et aussi toute la partie qui est dans le setup()
avec les fonctions server.on()
(200 lignes dans le vrai code)
J'ai essayé de faire un couper-coller de la partie String processor(const String& var){}
dans le fichier .cpp et j'ai ajouté String processor();
dans le fichier .h mais la ça compile plus.
Les messages d'erreurs :
/home/christian/Arduino/problemeWS/problemeWS.ino: In lambda function:
problemeWS:111:70: error: no matching function for call to 'AsyncWebServerRequest::send(fs::LittleFSFS&, const char [12], String, bool, String (&)())'
request->send(LittleFS, "/index.html", String(), false, processor);
^
In file included from /home/christian/Arduino/problemeWS/problemeWS.ino:3:
/home/christian/Arduino/libraries/ESP_Async_WebServer/src/ESPAsyncWebServer.h:236:10: note: candidate: 'void AsyncWebServerRequest::send(AsyncWebServerResponse*)'
void send(AsyncWebServerResponse *response);
^~~~
/home/christian/Arduino/libraries/ESP_Async_WebServer/src/ESPAsyncWebServer.h:236:10: note: candidate expects 1 argument, 5 provided
/home/christian/Arduino/libraries/ESP_Async_WebServer/src/ESPAsyncWebServer.h:237:10: note: candidate: 'void AsyncWebServerRequest::send(int, const String&, const String&)'
void send(int code, const String& contentType=String(), const String& content=String());
^~~~
/home/christian/Arduino/libraries/ESP_Async_WebServer/src/ESPAsyncWebServer.h:237:10: note: candidate expects 3 arguments, 5 provided
/home/christian/Arduino/libraries/ESP_Async_WebServer/src/ESPAsyncWebServer.h:238:10: note: candidate: 'void AsyncWebServerRequest::send(AsyncWebServerRequest::FS&, const String&, const String&, bool, AwsTemplateProcessor)'
void send(FS &fs, const String& path, const String& contentType=String(), bool download=false, AwsTemplateProcessor callback=nullptr);
^~~~
(je coupe pour pas faire trop long)
Et là je ne comprend pas les messages d'erreur ni ce qu'il faut faire...
Deuxième souci, je ne vois pas du tout comment sortir également toute la partie qui est dans le setup()
!