/* Balance connectée WiFi — ESP32 + HX711 ---------------------------------------- - L'ESP32 crée un point d'accès WiFi (mode AP) - Un portail captif redirige automatiquement le smartphone vers la page de pesée - La page se met à jour en temps réel via Server-Sent Events (SSE) - La calibration se fait depuis la page web, le facteur est sauvegardé en NVS Bibliothèques requises : - HX711 (bogde) - ESPAsyncWebServer (me-no-dev) - AsyncTCP (me-no-dev) - Preferences (intégrée ESP32) Câblage HX711 : DOUT → GPIO 4 SCK → GPIO 5 VCC → 3V3 (ou 5V si module compatible) GND → GND */ #include #include #include #include #include "HX711.h" // --- Pins HX711 --- static const int PIN_DOUT = 4; static const int PIN_SCK = 5; // --- WiFi AP --- static const char* AP_SSID = "Balance-ESP32"; static const char* AP_PASS = ""; // réseau ouvert // --- Objets globaux --- HX711 scale; DNSServer dnsServer; AsyncWebServer server(80); AsyncEventSource events("/events"); Preferences prefs; float calibrationFactor = 1.0f; // chargé depuis NVS au démarrage // --- Flags inter-tâches (handler HTTP → loop) --- // AsyncWebServer tourne dans une tâche séparée ; les opérations HX711 // doivent rester dans loop() pour éviter les conflits de timing. volatile bool pendingTare = false; volatile bool pendingCalib = false; volatile float pendingCalibMass = 0.0f; // --- Page HTML embarquée --- static const char HTML[] PROGMEM = R"rawliteral( Balance

Balance connectée

grammes
Connexion…

Calibration

① Vider la balance, puis mettre à zéro :

② Poser une masse connue et entrer sa valeur :

)rawliteral"; // --- Portail captif --- static void handleCaptiveRedirect(AsyncWebServerRequest* req) { req->redirect("http://192.168.4.1/"); } void setup() { Serial.begin(115200); delay(200); // --- Chargement facteur de calibration depuis NVS --- prefs.begin("balance", true); // lecture seule calibrationFactor = prefs.getFloat("calib", 1.0f); prefs.end(); Serial.printf("Facteur de calibration : %.2f%s\n", calibrationFactor, calibrationFactor == 1.0f ? " (défaut — calibration requise)" : " (chargé)"); // --- HX711 --- scale.begin(PIN_DOUT, PIN_SCK); if (!scale.is_ready()) { Serial.println("HX711 non détecté — vérifier câblage."); } scale.set_scale(); scale.tare(20); scale.set_scale(calibrationFactor); Serial.println("HX711 prêt."); // --- WiFi AP --- WiFi.mode(WIFI_AP); WiFi.softAP(AP_SSID, AP_PASS); Serial.printf("AP démarré — SSID : %s — IP : %s\n", AP_SSID, WiFi.softAPIP().toString().c_str()); // --- DNS captif --- dnsServer.start(53, "*", WiFi.softAPIP()); // --- Routes HTTP --- server.on("/", HTTP_GET, [](AsyncWebServerRequest* req) { req->send_P(200, "text/html", HTML); }); // Tare server.on("/tare", HTTP_POST, [](AsyncWebServerRequest* req) { pendingTare = true; req->send(200, "text/plain", "OK"); }); // Calibration server.on("/calibrate", HTTP_POST, [](AsyncWebServerRequest* req) { if (req->hasParam("mass", true)) { float m = req->getParam("mass", true)->value().toFloat(); if (m > 0.0f) { pendingCalibMass = m; pendingCalib = true; req->send(200, "text/plain", "OK"); } else { req->send(400, "text/plain", "Masse invalide"); } } else { req->send(400, "text/plain", "Parametre mass manquant"); } }); // Portail captif Android / iOS server.on("/generate_204", HTTP_GET, handleCaptiveRedirect); server.on("/connecttest.txt", HTTP_GET, handleCaptiveRedirect); server.on("/redirect", HTTP_GET, handleCaptiveRedirect); server.on("/hotspot-detect.html", HTTP_GET, handleCaptiveRedirect); server.on("/library/test/success.html", HTTP_GET, handleCaptiveRedirect); server.onNotFound(handleCaptiveRedirect); // --- SSE --- events.onConnect([](AsyncEventSourceClient* client) { client->send("0.0", "weight", millis(), 500); }); server.addHandler(&events); server.begin(); Serial.println("Serveur web démarré."); } void loop() { dnsServer.processNextRequest(); // --- Tare --- if (pendingTare) { pendingTare = false; scale.set_scale(); // remet le facteur à 1 avant la tare scale.tare(20); scale.set_scale(calibrationFactor); events.send("tare_ok", "info", millis()); Serial.println("Tare effectuée."); } // --- Calibration --- if (pendingCalib) { pendingCalib = false; scale.set_scale(1.0f); // facteur neutre pour lire la valeur brute float raw = scale.get_value(20); float factor = raw / pendingCalibMass; calibrationFactor = factor; scale.set_scale(calibrationFactor); prefs.begin("balance", false); // écriture prefs.putFloat("calib", calibrationFactor); prefs.end(); Serial.printf("Calibration : brut=%.0f masse=%.1fg facteur=%.2f\n", raw, pendingCalibMass, calibrationFactor); String msg = "calib_ok:" + String(calibrationFactor, 2); events.send(msg.c_str(), "info", millis()); } // --- Lecture et diffusion du poids --- if (scale.is_ready()) { float grams = scale.get_units(5); events.send(String(grams, 1).c_str(), "weight", millis()); Serial.printf("Poids : %.1f g\n", grams); } delay(500); }