Hi everybody,
some days ago I started learning the ESPUI-library which can be downloaded here
No html-knowledge required.
ESPUI allows to create webinterfaces with a lot of different elements like buttons, switches, sliders etc.
All this is done through pure C++-function calls. You need to no nothing about html
The documentation in the
explains the most important basics.
The examples show different options but are IMHO somehow too much at once
for explaining how it works in detail
Here is a code that combines the preferences-library with ESPUI.
compared to the examples that are comming with the library I changed a lot of variable-names to be more self-explaining.
here is a demo-code that shows how to use tabs
and how to place UI-elements on the tabs.
UI-elements on tabs must be created and accessed different than on non-tabbed wesites
I added my own standard-functions for debugging and connecting to WiFi
This demo-code uses the library MobaTools for controlling a RC-Servo
There are two tabs "Operation" and "Settings"
Tab Operation shows three buttons and a status-text
Tab Settings shows three number-input-fields with Min/Max-values
a button for saving the values into flash and a "set default values" button
I hope this help clarifying some details how to use the ESPUI-library.
If you post your codes just do so
#define dbg(myFixedText, variableName) \
Serial.print( F(#myFixedText " " #variableName"=") ); \
Serial.println(variableName);
#define dbgi(myFixedText, variableName,timeInterval) \
do { \
static unsigned long intervalStartTime; \
if ( millis() - intervalStartTime >= timeInterval ){ \
intervalStartTime = millis(); \
Serial.print( F(#myFixedText " " #variableName"=") ); \
Serial.println(variableName); \
} \
} while (false);
#include <Preferences.h> // add sourcecode file
Preferences myPrefInstance; // create an instance of the object
#include <DNSServer.h>
#include <ESPUI.h>
const byte DNS_PORT = 53;
IPAddress apIP(192, 168, 4, 1);
DNSServer dnsServer;
#if defined(ESP32)
#include <WiFi.h>
#else
#include <ESP8266WiFi.h>
#endif
#include <MobaTools.h>
const byte servoPin = 14; // GPIO 14 is labeled D5
const byte Speed = 230;
MoToServo myServo;
#define ServoMin "700"
#define ServoMax "2300"
unsigned long MyTestTimer = 0; // Timer-variables MUST be of type unsigned long
const byte OnBoard_LED = 2;
unsigned long SwitchStateTimer = 0;
const char *home_ssid = "FRITZ!Box 7490";
const char *home_password = "";
char ap_ssid[25];
const char* AP_password = "espui";
const char* AP_hostname = "espui";
uint16_t myTab1_ID;
uint16_t myTab2_ID;
uint16_t statusLabel_ID;
uint16_t button1_ID;
uint16_t button2_ID;
uint16_t button3_ID;
uint16_t ServoPos1_ID;
uint16_t ServoPos2_ID;
uint16_t ServoPos3_ID;
uint16_t buttonSave_ID;
uint16_t buttonSetDefault_ID;
#define myNameSpace "ServoPos"
const boolean ReadOnly = true;
const boolean ReadWrite = !ReadOnly; // not-operator inverts the value
int servoPos1microS;
int servoPos2microS;
int servoPos3microS;
void PrintFileNameDateTime() {
Serial.println( F("Code running comes from file ") );
Serial.println( F(__FILE__) );
Serial.print( F(" compiled ") );
Serial.print( F(__DATE__) );
Serial.print( F(" ") );
Serial.println( F(__TIME__) );
}
// easy to use helper-function for non-blocking timing
boolean TimePeriodIsOver (unsigned long &startOfPeriod, unsigned long TimePeriod) {
unsigned long currentMillis = millis();
if ( currentMillis - startOfPeriod >= TimePeriod ) {
// more time than TimePeriod has elapsed since last time if-condition was true
startOfPeriod = currentMillis; // a new period starts right here so set new starttime
return true;
}
else return false; // actual TimePeriod is NOT yet over
}
void BlinkHeartBeatLED(int IO_Pin, int BlinkPeriod) {
static unsigned long MyBlinkTimer;
pinMode(IO_Pin, OUTPUT);
if ( TimePeriodIsOver(MyBlinkTimer, BlinkPeriod) ) {
digitalWrite(IO_Pin, !digitalRead(IO_Pin) );
}
}
void number1Callback(Control* sender, int eventType) {
Serial.print(" ");
Serial.print(sender->label);
Serial.println(sender->value);
servoPos1microS = atoi(sender->value.c_str());
dbg("Pos1", servoPos1microS);
}
void number2Callback(Control* sender, int eventType) {
Serial.print(" ");
Serial.print(sender->label);
Serial.println(sender->value);
servoPos2microS = atoi(sender->value.c_str());
dbg("Pos2", servoPos2microS);
}
void number3Callback(Control* sender, int eventType) {
Serial.print(" ");
Serial.print(sender->label);
Serial.println(sender->value);
servoPos3microS = atoi(sender->value.c_str());
dbg("Pos3", servoPos3microS);
}
void buttonSetDefaultCallback(Control* sender, int eventType) {
Serial.print("ID: ");
Serial.print(sender->id);
Serial.print(" ");
Serial.println(sender->label);
if (eventType == B_UP) {
servoPos1microS = 1100;
servoPos2microS = 1500;
servoPos3microS = 1900;
ESPUI.updateControlValue(ServoPos1_ID, String(servoPos1microS) );
ESPUI.updateControlValue(ServoPos2_ID, String(servoPos2microS) );
ESPUI.updateControlValue(ServoPos3_ID, String(servoPos3microS) );
dbg("Default", servoPos1microS);
dbg("Default", servoPos2microS);
dbg("Default", servoPos3microS);
ESPUI.updateControlValue(statusLabel_ID, "Set positions to default");
}
}
void buttonSaveCallback(Control* sender, int eventType) {
Serial.print("ID: ");
Serial.print(sender->id);
Serial.print(" ");
Serial.println(sender->label);
if (eventType == B_UP) {
SafePreferences();
ESPUI.updateControlValue(statusLabel_ID, "positions saved to flash");
}
}
void button123Callback(Control* sender, int eventType) {
uint16_t UI_ID = sender->id;
switch (eventType) {
case B_DOWN:
//Serial.println("Button DOWN");
break;
case B_UP:
Serial.print("Button UP ");
Serial.print("label ");
Serial.print(sender->label);
dbg(" Btn123", UI_ID);
if (UI_ID == button1_ID) {
myServo.write(servoPos1microS);
ESPUI.updateControlValue(statusLabel_ID, "Position 1 clicked");
}
if (UI_ID == button2_ID) {
myServo.write(servoPos2microS);
ESPUI.updateControlValue(statusLabel_ID, "Position 2 clicked");
}
if (UI_ID == button3_ID) {
myServo.write(servoPos3microS);
ESPUI.updateControlValue(statusLabel_ID, "Position 3 clicked");
}
break;
}
}
void ConnectToWiFi() {
int myCount = 0;
#if defined(ESP32)
WiFi.setHostname(AP_hostname); // xxy
#else
WiFi.hostname(AP_hostname);
#endif
// try to connect to existing network
WiFi.begin(home_ssid, home_password);
Serial.print("\n\nTry to connect to existing network");
Serial.print(" named #");
Serial.print(home_ssid);
Serial.println("#");
// Wait for connection
while (WiFi.status() != WL_CONNECTED && myCount < 31) {
yield(); // very important to execute yield to make it work
BlinkHeartBeatLED(OnBoard_LED, 50); // blink LED fast during attempt to connect
if ( TimePeriodIsOver(MyTestTimer, 500) ) { // once every 500 miliseconds
Serial.print("."); // print a dot
myCount++;
if (myCount > 30) { // after 30 dots = 15 seconds restart
Serial.println();
Serial.print("not connected ");
}
}
}
if (WiFi.status() == WL_CONNECTED ) {
Serial.println("");
Serial.print("Connected to #");
Serial.print(home_ssid);
Serial.print("# IP address: ");
Serial.println(WiFi.localIP());
}
}
void createOwnAP() {
// not connected -> create hotspot
if (WiFi.status() != WL_CONNECTED) {
Serial.print("\n\n no connection to SSID #");
Serial.print(home_ssid);
Serial.println("#\n Creating hotspot");
WiFi.mode(WIFI_AP);
delay(100);
WiFi.softAPConfig(apIP, apIP, IPAddress(255, 255, 255, 0));
#if defined(ESP32)
uint32_t chipid = 0;
for (int i = 0; i < 17; i = i + 8) {
chipid |= ((ESP.getEfuseMac() >> (40 - i)) & 0xff) << i;
}
#else
uint32_t chipid = ESP.getChipId();
#endif
snprintf(ap_ssid, 26, "ESPUI-%08X", chipid);
WiFi.softAP(ap_ssid);
Serial.print("SSID #");
Serial.print(ap_ssid);
Serial.println("#");
//dnsServer.start(DNS_PORT, "*", apIP);
}
}
void printWiFiModeAndIP() {
if (WiFi.getMode() == WIFI_AP) {
Serial.print("ESP is its OWN accesspoint with SSID ");
Serial.println(ap_ssid);
Serial.print("IP address: ");
Serial.println(WiFi.softAPIP() );
}
else {
Serial.print("ESP connected to existing WLAN named ");
Serial.println(home_ssid);
Serial.print("IPAdress: ");
Serial.println(WiFi.localIP() );
}
}
void setup() {
delay(1000);
Serial.begin(115200);
delay(1000);
PrintFileNameDateTime();
#if defined(ESP32)
WiFi.setHostname(AP_hostname);
#else
WiFi.hostname(AP_hostname);
#endif
ConnectToWiFi();
if (WiFi.status() != WL_CONNECTED) {
createOwnAP();
}
dnsServer.start(DNS_PORT, "*", apIP);
printWiFiModeAndIP();
defineUI();
ESPUI.begin("I am the website created by the ESPUI-Demo");
LoadPreferences();
setupServo();
}
void loop() {
BlinkHeartBeatLED(OnBoard_LED, 500);
dnsServer.processNextRequest();
if ( TimePeriodIsOver(MyTestTimer, 1000) ) {
}
if ( TimePeriodIsOver(SwitchStateTimer, 5000) ) {
}
}
void defineUI() {
// defining the website-tabs
myTab1_ID = ESPUI.addControl(ControlType::Tab, "Operation", "Operation");
myTab2_ID = ESPUI.addControl(ControlType::Tab, "Settings", "Settings");
// shown above all tabs
statusLabel_ID = ESPUI.addControl(ControlType::Label, "last action:", "Last Action: none", ControlColor::Turquoise);
// elements on myTab1_ID
button1_ID = ESPUI.addControl(ControlType::Button, "Position 1", "Pos 1", ControlColor::Peterriver, myTab1_ID, &button123Callback);
button2_ID = ESPUI.addControl(ControlType::Button, "Position 2", "Pos 2", ControlColor::Wetasphalt, myTab1_ID, &button123Callback);
button3_ID = ESPUI.addControl(ControlType::Button, "Position 3", "Pos 3", ControlColor::Wetasphalt, myTab1_ID, &button123Callback);
// elements on myTab2_ID
ServoPos1_ID = ESPUI.addControl(ControlType::Number, "Servo position 1 (µseconds):", "1100", ControlColor::Alizarin, myTab2_ID, &number1Callback);
ESPUI.addControl(ControlType::Min, "Min-value", ServoMin, ControlColor::None, ServoPos1_ID);
ESPUI.addControl(ControlType::Max, "Max-value", ServoMax, ControlColor::None, ServoPos1_ID);
ServoPos2_ID = ESPUI.addControl(ControlType::Number, "Servo position 2 (µseconds):", "1500", ControlColor::Alizarin, myTab2_ID, &number2Callback);
ESPUI.addControl(ControlType::Min, "Min-value", ServoMin, ControlColor::None, ServoPos2_ID);
ESPUI.addControl(ControlType::Max, "Max-value", ServoMax, ControlColor::None, ServoPos2_ID);
ServoPos3_ID = ESPUI.addControl(ControlType::Number, "Servo position 3 (µseconds):", "1900", ControlColor::Alizarin, myTab2_ID, &number3Callback);
ESPUI.addControl(ControlType::Min, "Min-value", ServoMin, ControlColor::None, ServoPos3_ID);
ESPUI.addControl(ControlType::Max, "Max-value", ServoMax, ControlColor::None, ServoPos3_ID);
buttonSave_ID = ESPUI.addControl(ControlType::Button, "Save to Flash ", "Save ", ControlColor::Peterriver, myTab2_ID, &buttonSaveCallback);
buttonSetDefault_ID = ESPUI.addControl(ControlType::Button, "Set Default ", "Default ", ControlColor::Peterriver, myTab2_ID, &buttonSetDefaultCallback);
}
void LoadPreferences() {
Serial.println("LoadPreferences");
myPrefInstance.begin(myNameSpace, ReadOnly);
servoPos1microS = myPrefInstance.getInt ("ServoPos1" , 1101);
servoPos2microS = myPrefInstance.getInt ("ServoPos2" , 1501);
servoPos3microS = myPrefInstance.getInt ("ServoPos3" , 1901);
ESPUI.updateControlValue(ServoPos1_ID, String(servoPos1microS) );
ESPUI.updateControlValue(ServoPos2_ID, String(servoPos2microS) );
ESPUI.updateControlValue(ServoPos3_ID, String(servoPos3microS) );
dbg("LP", servoPos1microS);
dbg("LP", servoPos2microS);
dbg("LP", servoPos3microS);
myPrefInstance.end();
}
void SafePreferences() {
Serial.println("SafePreferences");
myPrefInstance.begin(myNameSpace, ReadWrite);
//myPrefInstance.clear();
myPrefInstance.putInt ("ServoPos1" , servoPos1microS);
myPrefInstance.putInt ("ServoPos2" , servoPos2microS);
myPrefInstance.putInt ("ServoPos3" , servoPos3microS);
myPrefInstance.end();
dbg("SP", servoPos1microS);
dbg("SP", servoPos2microS);
dbg("SP", servoPos3microS);
LoadPreferences();
}
void setupServo() {
myServo.attach(servoPin);
myServo.setMinimumPulse(700);
myServo.setMaximumPulse(2300);
myServo.setSpeed(Speed);
myServo.write(servoPos2microS);
}