Anleitung: ESPUI webbasierte Oberfläche für ESP32 und ESP8266

Meine ersten Erfahrungen mit ESP32 und ESPUI. Mangels ESP8266 in meiner Bastelkiste teste ich nur mit ESP32.

Zu installierende Bibliotheken:

Datei ESP_ESPUI\ESP_ESPUI.ino mit dem Inhalt des Bibliotheksbeispiels completeExample.cpp angelegt, kompiliert und zum ESP32 übertragen.

Der Sketch verwendet 837893 Bytes (63%) des Programmspeicherplatzes. Das Maximum sind 1310720 Bytes.
Globale Variablen verwenden 40088 Bytes (12%) des dynamischen Speichers, 287592 Bytes für lokale Variablen verbleiben. Das Maximum sind 327680 Bytes.

Serieller Monitor:

Begin wifi...
............................
Creating access point...
,,,,,,,,,,,,,,,,,,,,

Die Verbindung zum Router war mangels SSID und Passwort nicht erfolgreich, das ist beim ersten Mal richtig. Der ESP32 wird alternativ als Accsess Point gestartet.

In Win10 "Netzwerkstatus/ Verfügbare Netzwerke" mit "ESPUITest" verbinden:
grafik

Zu den "Eigenschaften" wechseln:
grafik

Dort zu "Privat" wechseln, sonst meldest sich bei mir die Feuerwand:
grafik

Weiter unten sehe ich:
grafik

Also öffne ich einen Browser und gebe "http://192.168.1.1" ein.

Nun sehe ich im Browser (nur der obere Teil ist dargestellt):

Da ich den ESP32 als offenen Access Point nicht nutzen möchte, wechsle ich im Browser zum Tab "WiFi Credentials", wo ich SSID und Passwort meiner Fritz!Box (Router) eingebe:

Nach Klick auf "Save" sehe ich im seriellen Monitor "Saving credentials to EPROM..." zusammen mit eingegebenen SSID und Passwort.

SSID und Passwort stehen also im EEPROM und brauchen später nicht erneut eingegeben zu werden. Solange sie gültig sind, erfolgt eine Anmeldung am Router mit einer möglicherweise wechselnden IP.

Am ESP32 drücke ich den Reset-Knopf und sehe im seriellen Monitor

Begin wifi...
.192.168.178.22
Wifi started

Im Browser tippe ich die angegebene lokale IP "192.168.178.22" oder "http://espuitest.local/" ein und sehe:
grafik

Ich möchte gerne das Dateisystem LittleFS verwenden, um Dateien dort zu speichern. Daher öffne ich nun das Bibliotheksbeispiel prepareFilesystem.ino, kompiliere und lade es auf den ESP32. Im seriellen Monitor sehe ich:

Preparing filesystem with ESPUI resources
About to prepare filesystem...
./components/esp_littlefs/src/littlefs/lfs.c:1229:error: Corrupted dir pair at {0x0, 0x1}
E (532) esp_littlefs: mount failed,  (-84)
E (536) esp_littlefs: Failed to initialize LittleFS
./components/esp_littlefs/src/littlefs/lfs.c:1229:error: Corrupted dir pair at {0x0, 0x1}
E (548) esp_littlefs: mount failed,  (-84)
E (552) esp_littlefs: Failed to initialize LittleFS
Listing directory: /
LittleFS Mount ESP32 Done
File: /index.htm does not exist, not deleting
File: /css/style.css does not exist, not deleting
File: /css/normalize.css does not exist, not deleting
File: /js/zepto.min.js does not exist, not deleting
File: /js/controls.js does not exist, not deleting
File: /js/slider.js does not exist, not deleting
File: /js/graph.js does not exist, not deleting
File: /js/tabbedcontent.js does not exist, not deleting
Cleanup done
Writing file: /index.htm
File written
Writing file: /css/style.css
File written
Writing file: /css/normalize.css
File written
Writing file: /js/zepto.min.js
File written
Writing file: /js/controls.js
File written
Writing file: /js/slider.js
File written
Writing file: /js/graph.js
File written
Writing file: /js/tabbedcontent.js
File written
Done Initializing filesystem :-)
Listing directory: /
  DIR : css
Listing directory: /css
  FILE: normalize.css  SIZE: 1945
  FILE: style.css  SIZE: 15650
  FILE: index.htm  SIZE: 1492
  DIR : js
Listing directory: /js
  FILE: controls.js  SIZE: 16895
  FILE: graph.js  SIZE: 4768
  FILE: slider.js  SIZE: 2897
  FILE: tabbedcontent.js  SIZE: 4464
  FILE: zepto.min.js  SIZE: 26320
Done, files...
Listing directory: /
  DIR : css
Listing directory: /css
  FILE: normalize.css  SIZE: 1945
  FILE: style.css  SIZE: 15650
  FILE: index.htm  SIZE: 1492
  DIR : js
Listing directory: /js
  FILE: controls.js  SIZE: 16895
  FILE: graph.js  SIZE: 4768
  FILE: slider.js  SIZE: 2897
  FILE: tabbedcontent.js  SIZE: 4464
  FILE: zepto.min.js  SIZE: 26320
Total KB: 1472
Used KB: 116

Da LittleFS funktioniert, wird die Bibliothek LittleFS_esp32 tatsächlich nicht benötigt (IDE 1.8.19 und Core 2.0.5).

Um die Dateien in LittleFS wirklich zu benutzen, ändere ich ESPUI.begin in ESPUI.beginLITTLEFS:

  //Finally, start up the UI. 
  //This should only be called once we are connected to WiFi.
  //ESPUI.begin(HOSTNAME);
  ESPUI.beginLITTLEFS(HOSTNAME);  // If you want to serve the files from LittleFS

Diesen Text habe ich von RemoteXY Erfahrung hierher verschoben. Im ursprünglichen Thema sollen Temperaturistwert angezeigt und Temperatursollwert verändert werden. Dazu kürze ich das Bibliotheksbeispiel gui.ino bis auf die zwei notwendigen Grafikelemente und ergänze OTA.

#include "zugangsdaten.h"
#include <ArduinoOTA.h>  // Programm hochladen Over The Air, siehe IDE Werkzeuge/Port
#include <ESPUI.h>

#if defined(ESP32)
#include <WiFi.h>
#else
#include <ESP8266WiFi.h>
#endif

const char* ssid = STA_SSID;            // << kann bis zu 32 Zeichen haben
const char* password = STA_PASSWORD;    // << mindestens 8 Zeichen jedoch nicht länger als 64 Zeichen

int millisLabelId;
uint8_t sollTemperatur = 20;

void numberCall(Control* sender, int type)
{
  /*
    Serial.print("CB: id(");
    Serial.print(sender->id);
    Serial.print(") Type(");
    Serial.print(type);
    Serial.print(") '");
    Serial.print(sender->label);
    Serial.print("' = ");
    Serial.println(sender->value);
  */
  String temp = sender->value;
  sollTemperatur = temp.toInt();
  Serial.printf("Neue Solltemperatur %u°C\n", sollTemperatur);
}

void setup(void)
{
  //ESPUI.setVerbosity(Verbosity::VerboseJSON);
  ESPUI.setVerbosity(Verbosity::Quiet);
  Serial.begin(115200);
  WiFi.begin(ssid, password);  // try to connect to existing network
  Serial.print("\nTry to connect to existing network");
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("");
  Serial.println("WiFi connected");
  Serial.print("IP address: ");
  Serial.println(WiFi.localIP());

  millisLabelId = ESPUI.label("Isttemperatur:", ControlColor::Wetasphalt, "0");
  ESPUI.number("Solltemperatur", &numberCall, ControlColor::Wetasphalt, 20, 15, 35);  // Start, Min, Max

  ESPUI.beginLITTLEFS("ESPUI Control");
  ArduinoOTA.begin();  // OTA starten
}

void loop(void)
{
  ArduinoOTA.handle();    // OTA abhandeln

  static long oldTime = 0;
  uint8_t istTemperatur = random(15, 35);
  if (millis() - oldTime > 5000)
  {
    oldTime = millis();
    ESPUI.print(millisLabelId, String(istTemperatur));
    Serial.printf("Solltemperatur %u°C\n", sollTemperatur);
  }
}

Anzeige im seriellen Monitor einschließlich Änderung der Solltemperatur:

.
WiFi connected
IP address: 192.168.178.22
Solltemperatur 20°C
Solltemperatur 20°C
Neue Solltemperatur 21°C
Neue Solltemperatur 22°C
Solltemperatur 22°C

Anzeige im Browser:

Nicht perfekt, aber ein Anfang :smiley:


Eine Nacht drüber geschlafen für ein Fazit:

  1. Am Morgen angefangen und am Abend ein Ergebnis, das ist gut. Ich habe keine Stoppuhr mitlaufen lassen, zusätzlich andere Dinge getan, schätze die Zeit auf ein paar Stunden.
  2. Die Bibliotheksdokumentation beschreibt die Möglichkeiten der GUI, ist bei den ersten Schritten aber etwas sparsam mit Hinweisen. Ein Text "Melde Dich im Browser mit der IP 192.168.1.1 an" wäre beispielsweise sehr hilfreich.
  3. Ohne Kenntnisse in HTML, CSS, JSON und JavaScript ein funktionales Ergebnis zu erzielen, finde ich eine Empfehlung wert!
4 Likes

Danke für deine Beschreibung, für ESPUI hatte ich mich auch interessiert. :+1:

Mir ist allerdings der Overhead zu groß - OTA dürfte man so gut wie vergessen können damit. Ich warte eigentlich immer noch auf einen Generator, der mir nach Vorgabe der Bedienelemente und Variablen HTML, Javascript, CSS und den passenden C++-Code produziert, um das auch mit dem nicht-asynchronen Webserver und LittleFS laufen lassen zu können, der wesentlich codesparsamer ist.

Wieso?

Der Sketch verwendet 852421 Bytes (65%) des Programmspeicherplatzes. Das Maximum sind 1310720 Bytes.
Globale Variablen verwenden 42048 Bytes (12%) des dynamischen Speichers, 285632 Bytes für lokale Variablen verbleiben. Das Maximum sind 327680 Bytes.
Sending invitation to 192.168.178.22 
Uploading: [============================================================] 100% Done...

Mal provokativ formuliert, ist mir der Overhead für C++ viel zu groß gegenüber Assembler :crazy_face:

Für Komfort muß man zahlen, ist irgendwie immer so.

Solange das Programm nicht träge und instabil wird, ist es pragmatisch betrachtet doch vollkommen egal, wieviel Daten im µC gespeichert sind.

An dieser Stelle Dank an @StefanL38 für die Anregung!

Aber suche bitte weiter und melde Dich mit Deinem Fund :slightly_smiling_face:

Mach' ich! :laughing:

Wg. OTA: beim ESP32 geht das, beim ESP8266 wird es schon enger und beim ESP8285 ist bei knapp 500kB Schicht.

Hast Du doch. Der sitzt bei Dir vorm Monitor :wink:
Das wäre doch langweilig, alles generieren zu lassen.
Ich habe einen "Grundwebserver" mit LittleFS, OTA und dem Filemanager von Fips.
Der Rest (HTML,JS,CSS,C++) bleibt Handarbeit und das finde ich gut so.

Gruß Tommy

@agmue positiv animieren möchte ich dich noch ein "Einfaches" Beispiel mit zwei Eingabefeldern zu machen. z.B. ein Textfeld für eine Parameter und ein Slider für PWM Ausgabe.

negativ: ich hab ehrlichrweise nur kurz drüber gesehen, außer dass der HTML syntax halt versteckt ist, sehe ich dann wieder viele Callbacks und Funktionsaufrufe mit vielen Parametern.

Bisher sehe ich das so: Bis ich mich da in die Lib reinfuchse ... hab ich die drei HTML auch so weit gelernt.

Siehst Du Dich in der Zielgruppe dieser Bibliothek? Ich eher nicht. Selbst bei Fips sehe ich Dich nicht, Du machst doch lieber Dein eigenes Ding! Oder?

Gerne doch, ich habe mich für einen Slider für PWM und die Ausgabe von Datum und Zeit entschieden. Ist das in Deinem Sinn?

Beim ESP32 geht PWM etwas anders als beim UNO, ist dafür aber flexibler in den Parametern.

Die Zeit hole ich von meiner Fritz!Box, weil ich keinen NTP-Server ärgern möchte.

Das Programm, ausschließlich mit dem ESP32 getstet, läßt sich mittels OTA übertragen.

#include <ArduinoOTA.h>  // Programm hochladen Over The Air, siehe IDE Werkzeuge/Port
#include <ESPUI.h>

#if defined(ESP32)
#include <WiFi.h>
#else
#include <ESP8266WiFi.h>
#endif

#define DEBUGGING                     // Einkommentieren für die Serielle Ausgabe

#ifdef DEBUGGING
#define DEBUG_B(...) Serial.begin(__VA_ARGS__)
#define DEBUG_P(...) Serial.println(__VA_ARGS__)
#define DEBUG_F(...) Serial.printf(__VA_ARGS__)
#else
#define DEBUG_B(...)
#define DEBUG_P(...)
#define DEBUG_F(...)
#endif

const char* ssid = STA_SSID;            // << kann bis zu 32 Zeichen haben
const char* password = STA_PASSWORD;    // << mindestens 8 Zeichen jedoch nicht länger als 64 Zeichen

const char * NTP_SERVER = "fritz.box";
//const char * NTP_SERVER = "de.pool.ntp.org";
const char * TZ_INFO = "WEST-1DWEST-2,M3.5.0/02:00:00,M10.5.0/03:00:00"; // Western European Time
struct tm lokaleZeit;

const int pwmPin = 12;  // the number of the PWM pin, 12 corresponds to GPIO12
const int pwmChannel = 0;
const int resolution = 8;
int freq = 2500;
int dutyCycle = 0;

int zeitLabelId;

void slider(Control* sender, int type)
{
  DEBUG_F("Slider: ID: %d, Value: %s\n", sender->id, sender->value.c_str());
  // Like all Control Values in ESPUI slider values are Strings. To use them as int simply do this:
  dutyCycle = sender->value.toInt();
  ledcWrite(pwmChannel, dutyCycle);  // changing the LED brightness with PWM
  DEBUG_F("SliderValue ");
  DEBUG_P(dutyCycle);
}


void setup(void)
{
  //ESPUI.setVerbosity(Verbosity::VerboseJSON);
  ESPUI.setVerbosity(Verbosity::Quiet);
  DEBUG_B(115200);

  // try to connect to existing network
  WiFi.begin(ssid, password);
  DEBUG_F("\nMit existierendem Netzwerk verbinden ");
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    DEBUG_F(".");
  }
  DEBUG_P("");
  DEBUG_P("WiFi connected");
  DEBUG_F("IP address: ");
  DEBUG_P(WiFi.localIP());

  zeitLabelId = ESPUI.label("Zeit", ControlColor::Emerald, "0");
  ESPUI.slider("PWM", &slider, ControlColor::Alizarin, 0, 0, 255);  // Start, Min, Max

  //ESPUI.begin("ESPUI Control");
  ESPUI.beginLITTLEFS("ESPUI Control");

  setenv("TZ", TZ_INFO, 1);
  tzset();
  DEBUG_F("Hole NTP Zeit von ");
  DEBUG_P(NTP_SERVER);
  configTzTime(TZ_INFO, NTP_SERVER); // ESP32 Systemzeit mit NTP Synchronisieren
  getLocalTime(&lokaleZeit, 10000);      // Versuche 10 s zu Synchronisieren
  DEBUG_P(&lokaleZeit, "Datum: %d.%m.%y  Zeit: %X"); // Zeit Datum Print Ausgabe formatieren

  ledcSetup(pwmChannel, freq, resolution);  // configure PWM functionalitites
  ledcAttachPin(pwmPin, pwmChannel);        // attach the channel to the GPIO to be controlled
  ledcWrite(pwmChannel, dutyCycle);  // changing the LED brightness with PWM

  ArduinoOTA.begin();  // OTA starten
}

void loop(void)
{
  ArduinoOTA.handle();    // OTA abhandeln

  static long oldTime = 0;

  if (millis() - oldTime > 1000)
  {
    getLocalTime(&lokaleZeit, 50);      // Versuche 50 ms zu Synchronisieren
    char buf[50] = {"\0"};
    strftime (buf, sizeof(buf), "Datum: %d.%m.%Y  Zeit: %T", &lokaleZeit);
    ESPUI.print( zeitLabelId, buf );
    oldTime = millis();
  }
}

Serieller Monitor:

Mit existierendem Netzwerk verbinden .
WiFi connected
IP address: 192.168.178.22
Hole NTP Zeit von fritz.box
Datum: 31.10.22  Zeit: 15:52:36
Slider: ID: 2, Value: 101
SliderValue 101

Browser:

Die LED an Pin 12 leuchtet entsprechend der PWM-Einstellung heller oder dunkler.

2 Likes

Auf die schnelle: Danke. Ich wollte mir mal ansehen wie das Interface genutzt wird. Werde ich sicher ausprobieren.

Ich habe beim letzten Sketch versucht, HTML Tags für checkboxen, Radiobuttons, etc ein wenig zu generalisieren um nicht alles wiederholt hinschreiben zu müssen. Aber schön (im Sinne von schöner Code) ist das auch nicht geworden...

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.