diff --git a/pio_examples/completeExample/platformio.ini b/pio_examples/completeExample/platformio.ini new file mode 100644 index 0000000..62410c5 --- /dev/null +++ b/pio_examples/completeExample/platformio.ini @@ -0,0 +1,43 @@ +; PlatformIO Project Configuration File +; +; Build options: build flags, source filter +; Upload options: custom upload port, speed and extra flags +; Library options: dependencies, extra library storages +; Advanced options: extra scripting +; +; Please visit documentation for the other options and examples +; https://docs.platformio.org/page/projectconf.html + +[platformio] +src_dir = ./src +data_dir = ../../data + +[env] +framework = arduino +board_build.filesystem = littlefs +lib_extra_dirs = ../../ +lib_deps = +; bblanchon/ArduinoJson @ ^6.18.5 + bblanchon/ArduinoJson @ ^7.0.4 + https://github.com/bmedici/ESPAsyncWebServer ; Use a fork of the library that has a bugfix for the compile.... https://github.com/esphome/ESPAsyncWebServer/pull/17 + +lib_ignore = + ESP Async WebServer ; force the use of the esphome version + AsyncTCP ; force the use of the esphome version + LittleFS_esp32 ; force the use of the ESP32 built into the core version + +[env:esp8266] +platform = espressif8266 +board = nodemcuv2 +monitor_speed = 115200 + +[env:esp32] +platform = espressif32 +board = esp32dev +monitor_filters = esp32_exception_decoder +board_build.flash_mode = dout + +lib_deps = + ${env.lib_deps} + me-no-dev/AsyncTCP +monitor_speed = 115200 diff --git a/pio_examples/completeExample/src/completeExample.cpp b/pio_examples/completeExample/src/completeExample.cpp new file mode 100644 index 0000000..d6e5b16 --- /dev/null +++ b/pio_examples/completeExample/src/completeExample.cpp @@ -0,0 +1,544 @@ +/** + * @file completeExample.cpp + * @author Ian Gray @iangray1000 + * + * This is an example GUI to show off all of the features of ESPUI. + * This can be built using the Arduino IDE, or PlatformIO. + * + * --------------------------------------------------------------------------------------- + * If you just want to see examples of the ESPUI code, jump down to the setUpUI() function + * --------------------------------------------------------------------------------------- + * + * When this program boots, it will load an SSID and password from the EEPROM. + * The SSID is a null-terminated C string stored at EEPROM addresses 0-31 + * The password is a null-terminated C string stored at EEPROM addresses 32-95. + * If these credentials do not work for some reason, the ESP will create an Access + * Point wifi with the SSID HOSTNAME (defined below). You can then connect and use + * the controls on the "Wifi Credentials" tab to store credentials into the EEPROM. + * + */ + +#include +#include +#include + +#if defined(ESP32) +#include +#include +#else +// esp8266 +#include +#include +#endif + +//Settings +#define SLOW_BOOT 0 +#define HOSTNAME "ESPUITest" +#define FORCE_USE_HOTSPOT 0 + + +//Function Prototypes +void connectWifi(); +void setUpUI(); +void enterWifiDetailsCallback(Control *sender, int type); +void textCallback(Control *sender, int type); +void generalCallback(Control *sender, int type); +void scrambleCallback(Control *sender, int type); +void styleCallback(Control *sender, int type); +void updateCallback(Control *sender, int type); +void getTimeCallback(Control *sender, int type); +void graphAddCallback(Control *sender, int type); +void graphClearCallback(Control *sender, int type); +void randomString(char *buf, int len); +void extendedCallback(Control* sender, int type, void* param); + +//UI handles +uint16_t wifi_ssid_text, wifi_pass_text; +uint16_t mainLabel, mainSwitcher, mainSlider, mainText, mainNumber, mainScrambleButton, mainTime; +uint16_t styleButton, styleLabel, styleSwitcher, styleSlider, styleButton2, styleLabel2, styleSlider2; +uint16_t graph; +volatile bool updates = false; + + + +// This is the main function which builds our GUI +void setUpUI() { + + //Turn off verbose debugging + ESPUI.setVerbosity(Verbosity::Quiet); + + //Make sliders continually report their position as they are being dragged. + ESPUI.sliderContinuous = true; + + //This GUI is going to be a tabbed GUI, so we are adding most controls using ESPUI.addControl + //which allows us to set a parent control. If we didn't need tabs we could use the simpler add + //functions like: + // ESPUI.button() + // ESPUI.label() + + + /* + * Tab: Basic Controls + * This tab contains all the basic ESPUI controls, and shows how to read and update them at runtime. + *-----------------------------------------------------------------------------------------------------------*/ + auto maintab = ESPUI.addControl(Tab, "", "Basic controls"); + + ESPUI.addControl(Separator, "General controls", "", None, maintab); + ESPUI.addControl(Button, "Button", "Button 1", Alizarin, maintab, extendedCallback, (void*)19); + mainLabel = ESPUI.addControl(Label, "Label", "Label text", Emerald, maintab, generalCallback); + mainSwitcher = ESPUI.addControl(Switcher, "Switcher", "", Sunflower, maintab, generalCallback); + + //Sliders default to being 0 to 100, but if you want different limits you can add a Min and Max control + mainSlider = ESPUI.addControl(Slider, "Slider", "200", Turquoise, maintab, generalCallback); + ESPUI.addControl(Min, "", "10", None, mainSlider); + ESPUI.addControl(Max, "", "400", None, mainSlider); + + //These are the values for the selector's options. (Note that they *must* be declared static + //so that the storage is allocated in global memory and not just on the stack of this function.) + static String optionValues[] {"Value 1", "Value 2", "Value 3", "Value 4", "Value 5"}; + auto mainselector = ESPUI.addControl(Select, "Selector", "Selector", Wetasphalt, maintab, generalCallback); + for(auto const& v : optionValues) { + ESPUI.addControl(Option, v.c_str(), v, None, mainselector); + } + + mainText = ESPUI.addControl(Text, "Text Input", "Initial value", Alizarin, maintab, generalCallback); + + //Number inputs also accept Min and Max components, but you should still validate the values. + mainNumber = ESPUI.addControl(Number, "Number Input", "42", Emerald, maintab, generalCallback); + ESPUI.addControl(Min, "", "10", None, mainNumber); + ESPUI.addControl(Max, "", "50", None, mainNumber); + + ESPUI.addControl(Separator, "Updates", "", None, maintab); + + //This button will update all the updatable controls on this tab to random values + mainScrambleButton = ESPUI.addControl(Button, "Scramble Values", "Scramble Values", Carrot, maintab, scrambleCallback); + ESPUI.addControl(Switcher, "Constant updates", "0", Carrot, maintab, updateCallback); + mainTime = ESPUI.addControl(Time, "", "", None, 0, generalCallback); + ESPUI.addControl(Button, "Get Time", "Get Time", Carrot, maintab, getTimeCallback); + + ESPUI.addControl(Separator, "Control Pads", "", None, maintab); + ESPUI.addControl(Pad, "Normal", "", Peterriver, maintab, generalCallback); + ESPUI.addControl(PadWithCenter, "With center", "", Peterriver, maintab, generalCallback); + + + /* + * Tab: Colours + * This tab shows all the basic colours + *-----------------------------------------------------------------------------------------------------------*/ + auto colourtab = ESPUI.addControl(Tab, "", "Colours"); + ESPUI.addControl(Button, "Alizarin", "Alizarin", Alizarin, colourtab, generalCallback); + ESPUI.addControl(Button, "Turquoise", "Turquoise", Turquoise, colourtab, generalCallback); + ESPUI.addControl(Button, "Emerald", "Emerald", Emerald, colourtab, generalCallback); + ESPUI.addControl(Button, "Peterriver", "Peterriver", Peterriver, colourtab, generalCallback); + ESPUI.addControl(Button, "Wetasphalt", "Wetasphalt", Wetasphalt, colourtab, generalCallback); + ESPUI.addControl(Button, "Sunflower", "Sunflower", Sunflower, colourtab, generalCallback); + ESPUI.addControl(Button, "Carrot", "Carrot", Carrot, colourtab, generalCallback); + ESPUI.addControl(Button, "Dark", "Dark", Dark, colourtab, generalCallback); + + + /* + * Tab: Styled controls + * This tab shows off how inline CSS styles can be applied to elements and panels in order + * to customise the look of the UI. + *-----------------------------------------------------------------------------------------------------------*/ + auto styletab = ESPUI.addControl(Tab, "", "Styled controls"); + styleButton = ESPUI.addControl(Button, "Styled Button", "Button", Alizarin, styletab, generalCallback); + styleLabel = ESPUI.addControl(Label, "Styled Label", "This is a label", Alizarin, styletab, generalCallback); + styleSwitcher = ESPUI.addControl(Switcher, "Styled Switcher", "1", Alizarin, styletab, generalCallback); + styleSlider = ESPUI.addControl(Slider, "Styled Slider", "0", Alizarin, styletab, generalCallback); + + //This button will randomise the colours of the above controls to show updating of inline styles + ESPUI.addControl(Button, "Randomise Colours", "Randomise Colours", Sunflower, styletab, styleCallback); + + ESPUI.addControl(Separator, "Other styling examples", "", None, styletab); + styleButton2 = ESPUI.addControl(Button, "Styled Button", "Button", Alizarin, styletab, generalCallback); + ESPUI.setPanelStyle(styleButton2, "background: linear-gradient(90deg, rgba(131,58,180,1) 0%, rgba(253,29,29,1) 50%, rgba(252,176,69,1) 100%); border-bottom: #555;"); + ESPUI.setElementStyle(styleButton2, "border-radius: 2em; border: 3px solid black; width: 30%; background-color: #8df;"); + + styleSlider2 = ESPUI.addControl(Slider, "Styled Slider", "0", Dark, styletab, generalCallback); + ESPUI.setElementStyle(styleSlider2, "background: linear-gradient(to right, red, orange, yellow, green, blue);"); + + styleLabel2 = ESPUI.addControl(Label, "Styled Label", "This is a label", Dark, styletab, generalCallback); + ESPUI.setElementStyle(styleLabel2, "text-shadow: 3px 3px #74b1ff, 6px 6px #c64ad7; font-size: 60px; font-variant-caps: small-caps; background-color: unset; color: #c4f0bb; -webkit-text-stroke: 1px black;"); + + + /* + * Tab: Grouped controls + * This tab shows how multiple control can be grouped into the same panel through the use of the + * parentControl value. This also shows how to add labels to grouped controls, and how to use vertical controls. + *-----------------------------------------------------------------------------------------------------------*/ + auto grouptab = ESPUI.addControl(Tab, "", "Grouped controls"); + + //The parent of this button is a tab, so it will create a new panel with one control. + auto groupbutton = ESPUI.addControl(Button, "Button Group", "Button A", Dark, grouptab, generalCallback); + //However the parent of this button is another control, so therefore no new panel is + //created and the button is added to the existing panel. + ESPUI.addControl(Button, "", "Button B", Alizarin, groupbutton, generalCallback); + ESPUI.addControl(Button, "", "Button C", Alizarin, groupbutton, generalCallback); + + + //Sliders can be grouped as well + //To label each slider in the group, we are going add additional labels and give them custom CSS styles + //We need this CSS style rule, which will remove the label's background and ensure that it takes up the entire width of the panel + String clearLabelStyle = "background-color: unset; width: 100%;"; + //First we add the main slider to create a panel + auto groupsliders = ESPUI.addControl(Slider, "Slider Group", "10", Dark, grouptab, generalCallback); + //Then we add a label and set its style to the clearLabelStyle. Here we've just given it the name "A" + ESPUI.setElementStyle(ESPUI.addControl(Label, "", "A", None, groupsliders), clearLabelStyle); + //We can now continue to add additional sliders and labels + ESPUI.addControl(Slider, "", "20", None, groupsliders, generalCallback); + ESPUI.setElementStyle(ESPUI.addControl(Label, "", "B", None, groupsliders), clearLabelStyle); + ESPUI.addControl(Slider, "", "30", None, groupsliders, generalCallback); + ESPUI.setElementStyle(ESPUI.addControl(Label, "", "C", None, groupsliders), clearLabelStyle); + + //We can also usefully group switchers. + auto groupswitcher = ESPUI.addControl(Switcher, "Switcher Group", "0", Dark, grouptab, generalCallback); + ESPUI.addControl(Switcher, "", "1", Sunflower, groupswitcher, generalCallback); + ESPUI.addControl(Switcher, "", "0", Sunflower, groupswitcher, generalCallback); + ESPUI.addControl(Switcher, "", "1", Sunflower, groupswitcher, generalCallback); + //To label these switchers we need to first go onto a "new line" below the line of switchers + //To do this we add an empty label set to be clear and full width (with our clearLabelStyle) + ESPUI.setElementStyle(ESPUI.addControl(Label, "", "", None, groupswitcher), clearLabelStyle); + //We will now need another label style. This one sets its width to the same as a switcher (and turns off the background) + String switcherLabelStyle = "width: 60px; margin-left: .3rem; margin-right: .3rem; background-color: unset;"; + //We can now just add the styled labels. + ESPUI.setElementStyle(ESPUI.addControl(Label, "", "A", None, groupswitcher), switcherLabelStyle); + ESPUI.setElementStyle(ESPUI.addControl(Label, "", "B", None, groupswitcher), switcherLabelStyle); + ESPUI.setElementStyle(ESPUI.addControl(Label, "", "C", None, groupswitcher), switcherLabelStyle); + ESPUI.setElementStyle(ESPUI.addControl(Label, "", "D", None, groupswitcher), switcherLabelStyle); + + //You can mix and match different control types, but the results might sometimes + //need additional styling to lay out nicely. + auto grouplabel = ESPUI.addControl(Label, "Mixed Group", "Main label", Dark, grouptab); + auto grouplabel2 = ESPUI.addControl(Label, "", "Secondary label", Emerald, grouplabel); + ESPUI.addControl(Button, "", "Button D", Alizarin, grouplabel, generalCallback); + ESPUI.addControl(Switcher, "", "1", Sunflower, grouplabel, generalCallback); + ESPUI.setElementStyle(grouplabel2, "font-size: x-large; font-family: serif;"); + + //Some controls can even support vertical orientation, currently Switchers and Sliders + ESPUI.addControl(Separator, "Vertical controls", "", None, grouptab); + auto vertgroupswitcher = ESPUI.addControl(Switcher, "Vertical Switcher Group", "0", Dark, grouptab, generalCallback); + ESPUI.setVertical(vertgroupswitcher); + //On the following lines we wrap the value returned from addControl and send it straight to setVertical + ESPUI.setVertical(ESPUI.addControl(Switcher, "", "0", None, vertgroupswitcher, generalCallback)); + ESPUI.setVertical(ESPUI.addControl(Switcher, "", "0", None, vertgroupswitcher, generalCallback)); + ESPUI.setVertical(ESPUI.addControl(Switcher, "", "0", None, vertgroupswitcher, generalCallback)); + //The mechanism for labelling vertical switchers is the same as we used above for horizontal ones + ESPUI.setElementStyle(ESPUI.addControl(Label, "", "", None, vertgroupswitcher), clearLabelStyle); + ESPUI.setElementStyle(ESPUI.addControl(Label, "", "A", None, vertgroupswitcher), switcherLabelStyle); + ESPUI.setElementStyle(ESPUI.addControl(Label, "", "B", None, vertgroupswitcher), switcherLabelStyle); + ESPUI.setElementStyle(ESPUI.addControl(Label, "", "C", None, vertgroupswitcher), switcherLabelStyle); + ESPUI.setElementStyle(ESPUI.addControl(Label, "", "D", None, vertgroupswitcher), switcherLabelStyle); + + auto vertgroupslider = ESPUI.addControl(Slider, "Vertical Slider Group", "15", Dark, grouptab, generalCallback); + ESPUI.setVertical(vertgroupslider); + ESPUI.setVertical(ESPUI.addControl(Slider, "", "25", None, vertgroupslider, generalCallback)); + ESPUI.setVertical(ESPUI.addControl(Slider, "", "35", None, vertgroupslider, generalCallback)); + ESPUI.setVertical(ESPUI.addControl(Slider, "", "45", None, vertgroupslider, generalCallback)); + //The mechanism for labelling vertical sliders is the same as we used above for switchers + ESPUI.setElementStyle(ESPUI.addControl(Label, "", "", None, vertgroupslider), clearLabelStyle); + ESPUI.setElementStyle(ESPUI.addControl(Label, "", "A", None, vertgroupslider), switcherLabelStyle); + ESPUI.setElementStyle(ESPUI.addControl(Label, "", "B", None, vertgroupslider), switcherLabelStyle); + ESPUI.setElementStyle(ESPUI.addControl(Label, "", "C", None, vertgroupslider), switcherLabelStyle); + ESPUI.setElementStyle(ESPUI.addControl(Label, "", "D", None, vertgroupslider), switcherLabelStyle); + + //Note that combining vertical and horizontal sliders is going to result in very messy layout! + + /* + * Tab: Example UI + * An example UI for the documentation + *-----------------------------------------------------------------------------------------------------------*/ + auto exampletab = ESPUI.addControl(Tab, "Example", "Example"); + ESPUI.addControl(Separator, "Control and Status", "", None, exampletab); + ESPUI.addControl(Switcher, "Power", "1", Alizarin, exampletab, generalCallback); + ESPUI.addControl(Label, "Status", "System status: OK", Wetasphalt, exampletab, generalCallback); + + ESPUI.addControl(Separator, "Settings", "", None, exampletab); + ESPUI.addControl(PadWithCenter, "Attitude Control", "", Dark, exampletab, generalCallback); + auto examplegroup1 = ESPUI.addControl(Button, "Activate Features", "Feature A", Carrot, exampletab, generalCallback); + ESPUI.addControl(Button, "Activate Features", "Feature B", Carrot, examplegroup1, generalCallback); + ESPUI.addControl(Button, "Activate Features", "Feature C", Carrot, examplegroup1, generalCallback); + ESPUI.addControl(Slider, "Value control", "45", Peterriver, exampletab, generalCallback); + + /* + * Tab: WiFi Credentials + * You use this tab to enter the SSID and password of a wifi network to autoconnect to. + *-----------------------------------------------------------------------------------------------------------*/ + auto wifitab = ESPUI.addControl(Tab, "", "WiFi Credentials"); + wifi_ssid_text = ESPUI.addControl(Text, "SSID", "", Alizarin, wifitab, textCallback); + //Note that adding a "Max" control to a text control sets the max length + ESPUI.addControl(Max, "", "32", None, wifi_ssid_text); + wifi_pass_text = ESPUI.addControl(Text, "Password", "", Alizarin, wifitab, textCallback); + ESPUI.addControl(Max, "", "64", None, wifi_pass_text); + ESPUI.addControl(Button, "Save", "Save", Peterriver, wifitab, enterWifiDetailsCallback); + + + //Finally, start up the UI. + //This should only be called once we are connected to WiFi. + ESPUI.begin(HOSTNAME); +} + +//This callback generates and applies inline styles to a bunch of controls to change their colour. +//The styles created are of the form: +// "border-bottom: #999 3px solid; background-color: #aabbcc;" +// "background-color: #aabbcc;" +void styleCallback(Control *sender, int type) { + //Declare space for style strings. These have to be static so that they are always available + //to the websocket layer. If we'd not made them static they'd be allocated on the heap and + //will be unavailable when we leave this function. + static char stylecol1[60], stylecol2[30]; + if(type == B_UP) { + //Generate two random HTML hex colour codes, and print them into CSS style rules + sprintf(stylecol1, "border-bottom: #999 3px solid; background-color: #%06X;", (unsigned int) random(0x0, 0xFFFFFF)); + sprintf(stylecol2, "background-color: #%06X;", (unsigned int) random(0x0, 0xFFFFFF)); + + //Apply those styles to various elements to show how controls react to styling + ESPUI.setPanelStyle(styleButton, stylecol1); + ESPUI.setElementStyle(styleButton, stylecol2); + ESPUI.setPanelStyle(styleLabel, stylecol1); + ESPUI.setElementStyle(styleLabel, stylecol2); + ESPUI.setPanelStyle(styleSwitcher, stylecol1); + ESPUI.setElementStyle(styleSwitcher, stylecol2); + ESPUI.setPanelStyle(styleSlider, stylecol1); + ESPUI.setElementStyle(styleSlider, stylecol2); + } +} + + +//This callback updates the "values" of a bunch of controls +void scrambleCallback(Control *sender, int type) { + static char rndString1[10]; + static char rndString2[20]; + static bool scText = false; + + if(type == B_UP) { //Button callbacks generate events for both UP and DOWN. + //Generate some random text + randomString(rndString1, 10); + randomString(rndString2, 20); + + //Set the various controls to random value to show how controls can be updated at runtime + ESPUI.updateLabel(mainLabel, String(rndString1)); + ESPUI.updateSwitcher(mainSwitcher, ESPUI.getControl(mainSwitcher)->value.toInt() ? false : true); + ESPUI.updateSlider(mainSlider, random(10, 400)); + ESPUI.updateText(mainText, String(rndString2)); + ESPUI.updateNumber(mainNumber, random(100000)); + ESPUI.updateButton(mainScrambleButton, scText ? "Scrambled!" : "Scrambled."); + scText = !scText; + } +} + +void updateCallback(Control *sender, int type) { + updates = (sender->value.toInt() > 0); +} + +void getTimeCallback(Control *sender, int type) { + if(type == B_UP) { + ESPUI.updateTime(mainTime); + } +} + +void graphAddCallback(Control *sender, int type) { + if(type == B_UP) { + ESPUI.addGraphPoint(graph, random(1, 50)); + } +} + +void graphClearCallback(Control *sender, int type) { + if(type == B_UP) { + ESPUI.clearGraph(graph); + } +} + + +//Most elements in this test UI are assigned this generic callback which prints some +//basic information. Event types are defined in ESPUI.h +void generalCallback(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); +} + +// Most elements in this test UI are assigned this generic callback which prints some +// basic information. Event types are defined in ESPUI.h +// The extended param can be used to hold a pointer to additional information +// or for C++ it can be used to return a this pointer for quick access +// using a lambda function +void extendedCallback(Control* sender, int type, void* param) +{ + 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); + Serial.print("param = "); + Serial.println((long)param); +} + +void setup() { + randomSeed(0); + Serial.begin(115200); + while(!Serial); + if(SLOW_BOOT) delay(5000); //Delay booting to give time to connect a serial monitor + connectWifi(); + #if defined(ESP32) + WiFi.setSleep(false); //For the ESP32: turn off sleeping to increase UI responsivness (at the cost of power use) + #endif + setUpUI(); +} + +void loop() { + static long unsigned lastTime = 0; + + //Send periodic updates if switcher is turned on + if(updates && millis() > lastTime + 500) { + static uint16_t sliderVal = 10; + + //Flick this switcher on and off + ESPUI.updateSwitcher(mainSwitcher, ESPUI.getControl(mainSwitcher)->value.toInt() ? false : true); + sliderVal += 10; + if(sliderVal > 400) sliderVal = 10; + + //Sliders, numbers, and labels can all be updated at will + ESPUI.updateSlider(mainSlider, sliderVal); + ESPUI.updateNumber(mainNumber, random(100000)); + ESPUI.updateLabel(mainLabel, String(sliderVal)); + lastTime = millis(); + } + + //Simple debug UART interface + if(Serial.available()) { + switch(Serial.read()) { + case 'w': //Print IP details + Serial.println(WiFi.localIP()); + break; + case 'W': //Reconnect wifi + connectWifi(); + break; + case 'C': //Force a crash (for testing exception decoder) + #if !defined(ESP32) + ((void (*)())0xf00fdead)(); + #endif + break; + default: + Serial.print('#'); + break; + } + } + + #if !defined(ESP32) + //We don't need to call this explicitly on ESP32 but we do on 8266 + MDNS.update(); + #endif + +} + + + + +//Utilities +// +//If you are here just to see examples of how to use ESPUI, you can ignore the following functions +//------------------------------------------------------------------------------------------------ +void readStringFromEEPROM(String& buf, int baseaddress, int size) { + buf.reserve(size); + for (int i = baseaddress; i < baseaddress+size; i++) { + char c = EEPROM.read(i); + buf += c; + if(!c) break; + } +} + +void connectWifi() { + int connect_timeout; + +#if defined(ESP32) + WiFi.setHostname(HOSTNAME); +#else + WiFi.hostname(HOSTNAME); +#endif + Serial.println("Begin wifi..."); + + //Load credentials from EEPROM + if(!(FORCE_USE_HOTSPOT)) { + yield(); + EEPROM.begin(100); + String stored_ssid, stored_pass; + readStringFromEEPROM(stored_ssid, 0, 32); + readStringFromEEPROM(stored_pass, 32, 96); + EEPROM.end(); + + //Try to connect with stored credentials, fire up an access point if they don't work. + #if defined(ESP32) + WiFi.begin(stored_ssid.c_str(), stored_pass.c_str()); + #else + WiFi.begin(stored_ssid, stored_pass); + #endif + connect_timeout = 28; //7 seconds + while (WiFi.status() != WL_CONNECTED && connect_timeout > 0) { + delay(250); + Serial.print("."); + connect_timeout--; + } + } + + if (WiFi.status() == WL_CONNECTED) { + Serial.println(WiFi.localIP()); + Serial.println("Wifi started"); + + if (!MDNS.begin(HOSTNAME)) { + Serial.println("Error setting up MDNS responder!"); + } + } else { + Serial.println("\nCreating access point..."); + WiFi.mode(WIFI_AP); + WiFi.softAPConfig(IPAddress(192, 168, 1, 1), IPAddress(192, 168, 1, 1), IPAddress(255, 255, 255, 0)); + WiFi.softAP(HOSTNAME); + + connect_timeout = 20; + do { + delay(250); + Serial.print(","); + connect_timeout--; + } while(connect_timeout); + } +} + +void enterWifiDetailsCallback(Control *sender, int type) { + if(type == B_UP) { + Serial.println("Saving credentials to EPROM..."); + Serial.println(ESPUI.getControl(wifi_ssid_text)->value); + Serial.println(ESPUI.getControl(wifi_pass_text)->value); + unsigned int i; + EEPROM.begin(100); + for(i = 0; i < ESPUI.getControl(wifi_ssid_text)->value.length(); i++) { + EEPROM.write(i, ESPUI.getControl(wifi_ssid_text)->value.charAt(i)); + if(i==30) break; //Even though we provided a max length, user input should never be trusted + } + EEPROM.write(i, '\0'); + + for(i = 0; i < ESPUI.getControl(wifi_pass_text)->value.length(); i++) { + EEPROM.write(i + 32, ESPUI.getControl(wifi_pass_text)->value.charAt(i)); + if(i==94) break; //Even though we provided a max length, user input should never be trusted + } + EEPROM.write(i + 32, '\0'); + EEPROM.end(); + } +} + +void textCallback(Control *sender, int type) { + //This callback is needed to handle the changed values, even though it doesn't do anything itself. +} + +void randomString(char *buf, int len) { + for(auto i = 0; i < len-1; i++) + buf[i] = random(0, 26) + 'A'; + buf[len-1] = '\0'; +} diff --git a/pio_examples/completeLambda/platformio.ini b/pio_examples/completeLambda/platformio.ini new file mode 100644 index 0000000..62410c5 --- /dev/null +++ b/pio_examples/completeLambda/platformio.ini @@ -0,0 +1,43 @@ +; PlatformIO Project Configuration File +; +; Build options: build flags, source filter +; Upload options: custom upload port, speed and extra flags +; Library options: dependencies, extra library storages +; Advanced options: extra scripting +; +; Please visit documentation for the other options and examples +; https://docs.platformio.org/page/projectconf.html + +[platformio] +src_dir = ./src +data_dir = ../../data + +[env] +framework = arduino +board_build.filesystem = littlefs +lib_extra_dirs = ../../ +lib_deps = +; bblanchon/ArduinoJson @ ^6.18.5 + bblanchon/ArduinoJson @ ^7.0.4 + https://github.com/bmedici/ESPAsyncWebServer ; Use a fork of the library that has a bugfix for the compile.... https://github.com/esphome/ESPAsyncWebServer/pull/17 + +lib_ignore = + ESP Async WebServer ; force the use of the esphome version + AsyncTCP ; force the use of the esphome version + LittleFS_esp32 ; force the use of the ESP32 built into the core version + +[env:esp8266] +platform = espressif8266 +board = nodemcuv2 +monitor_speed = 115200 + +[env:esp32] +platform = espressif32 +board = esp32dev +monitor_filters = esp32_exception_decoder +board_build.flash_mode = dout + +lib_deps = + ${env.lib_deps} + me-no-dev/AsyncTCP +monitor_speed = 115200 diff --git a/pio_examples/completeLambda/src/completeLambda.cpp b/pio_examples/completeLambda/src/completeLambda.cpp new file mode 100644 index 0000000..7928350 --- /dev/null +++ b/pio_examples/completeLambda/src/completeLambda.cpp @@ -0,0 +1,524 @@ +/** + * @file completeLambda.cpp + * @author Ian Gray @iangray1000 + * + * This is an example GUI to show off all of the features of ESPUI. + * This can be built using the Arduino IDE, or PlatformIO. + * + * --------------------------------------------------------------------------------------- + * If you just want to see examples of the ESPUI code, jump down to the setUpUI() function + * --------------------------------------------------------------------------------------- + * + * When this program boots, it will load an SSID and password from the EEPROM. + * The SSID is a null-terminated C string stored at EEPROM addresses 0-31 + * The password is a null-terminated C string stored at EEPROM addresses 32-95. + * If these credentials do not work for some reason, the ESP will create an Access + * Point wifi with the SSID HOSTNAME (defined below). You can then connect and use + * the controls on the "Wifi Credentials" tab to store credentials into the EEPROM. + * + * Version with lambdas. Comparing to version with only callbacks: + * diff -u ../completeExample/completeExample.cpp completeLambda.cpp|less + * + */ + +#include +#include +#include + +#if defined(ESP32) +#include +#include +#else +// esp8266 +#include +#include +#endif + +//Settings +#define SLOW_BOOT 0 +#define HOSTNAME "ESPUITest" +#define FORCE_USE_HOTSPOT 0 + + +//Function Prototypes +void connectWifi(); +void setUpUI(); +void textCallback(Control *sender, int type); +void generalCallback(Control *sender, int type); +void randomString(char *buf, int len); +void paramCallback(Control* sender, int type, int param); + +//UI handles +uint16_t wifi_ssid_text, wifi_pass_text; +uint16_t mainLabel, mainSwitcher, mainSlider, mainText, mainNumber, mainScrambleButton, mainTime; +uint16_t styleButton, styleLabel, styleSwitcher, styleSlider, styleButton2, styleLabel2, styleSlider2; +uint16_t graph; +volatile bool updates = false; + + + +// This is the main function which builds our GUI +void setUpUI() { + + //Turn off verbose debugging + ESPUI.setVerbosity(Verbosity::Quiet); + + //Make sliders continually report their position as they are being dragged. + ESPUI.sliderContinuous = true; + + //This GUI is going to be a tabbed GUI, so we are adding most controls using ESPUI.addControl + //which allows us to set a parent control. If we didn't need tabs we could use the simpler add + //functions like: + // ESPUI.button() + // ESPUI.label() + + + /* + * Tab: Basic Controls + * This tab contains all the basic ESPUI controls, and shows how to read and update them at runtime. + *-----------------------------------------------------------------------------------------------------------*/ + auto maintab = ESPUI.addControl(Tab, "", "Basic controls"); + + ESPUI.addControl(Separator, "General controls", "", None, maintab); + ESPUI.addControl(Button, "Button", "Button 1", Alizarin, maintab, [](Control *sender, int type){ paramCallback(sender, type, 19); }); + mainLabel = ESPUI.addControl(Label, "Label", "Label text", Emerald, maintab, generalCallback); + mainSwitcher = ESPUI.addControl(Switcher, "Switcher", "", Sunflower, maintab, generalCallback); + + //Sliders default to being 0 to 100, but if you want different limits you can add a Min and Max control + mainSlider = ESPUI.addControl(Slider, "Slider", "200", Turquoise, maintab, generalCallback); + ESPUI.addControl(Min, "", "10", None, mainSlider); + ESPUI.addControl(Max, "", "400", None, mainSlider); + + //These are the values for the selector's options. (Note that they *must* be declared static + //so that the storage is allocated in global memory and not just on the stack of this function.) + static String optionValues[] {"Value 1", "Value 2", "Value 3", "Value 4", "Value 5"}; + auto mainselector = ESPUI.addControl(Select, "Selector", "Selector", Wetasphalt, maintab, generalCallback); + for(auto const& v : optionValues) { + ESPUI.addControl(Option, v.c_str(), v, None, mainselector); + } + + mainText = ESPUI.addControl(Text, "Text Input", "Initial value", Alizarin, maintab, generalCallback); + + //Number inputs also accept Min and Max components, but you should still validate the values. + mainNumber = ESPUI.addControl(Number, "Number Input", "42", Emerald, maintab, generalCallback); + ESPUI.addControl(Min, "", "10", None, mainNumber); + ESPUI.addControl(Max, "", "50", None, mainNumber); + + ESPUI.addControl(Separator, "Updates", "", None, maintab); + + //This button will update all the updatable controls on this tab to random values + mainScrambleButton = ESPUI.addControl(Button, "Scramble Values", "Scramble Values", Carrot, maintab, + //This callback updates the "values" of a bunch of controls + [](Control *sender, int type) { + static char rndString1[10]; + static char rndString2[20]; + static bool scText = false; + + if(type == B_UP) { //Button callbacks generate events for both UP and DOWN. + //Generate some random text + randomString(rndString1, 10); + randomString(rndString2, 20); + + //Set the various controls to random value to show how controls can be updated at runtime + ESPUI.updateLabel(mainLabel, String(rndString1)); + ESPUI.updateSwitcher(mainSwitcher, ESPUI.getControl(mainSwitcher)->value.toInt() ? false : true); + ESPUI.updateSlider(mainSlider, random(10, 400)); + ESPUI.updateText(mainText, String(rndString2)); + ESPUI.updateNumber(mainNumber, random(100000)); + ESPUI.updateButton(mainScrambleButton, scText ? "Scrambled!" : "Scrambled."); + scText = !scText; + } + }); + + ESPUI.addControl(Switcher, "Constant updates", "0", Carrot, maintab, + [](Control *sender, int type) { + updates = (sender->value.toInt() > 0); + }); + + mainTime = ESPUI.addControl(Time, "", "", None, 0, generalCallback); + + ESPUI.addControl(Button, "Get Time", "Get Time", Carrot, maintab, + [](Control *sender, int type) { + if(type == B_UP) { + ESPUI.updateTime(mainTime); + } + }); + + ESPUI.addControl(Separator, "Control Pads", "", None, maintab); + ESPUI.addControl(Pad, "Normal", "", Peterriver, maintab, generalCallback); + ESPUI.addControl(PadWithCenter, "With center", "", Peterriver, maintab, generalCallback); + + + /* + * Tab: Colours + * This tab shows all the basic colours + *-----------------------------------------------------------------------------------------------------------*/ + auto colourtab = ESPUI.addControl(Tab, "", "Colours"); + ESPUI.addControl(Button, "Alizarin", "Alizarin", Alizarin, colourtab, generalCallback); + ESPUI.addControl(Button, "Turquoise", "Turquoise", Turquoise, colourtab, generalCallback); + ESPUI.addControl(Button, "Emerald", "Emerald", Emerald, colourtab, generalCallback); + ESPUI.addControl(Button, "Peterriver", "Peterriver", Peterriver, colourtab, generalCallback); + ESPUI.addControl(Button, "Wetasphalt", "Wetasphalt", Wetasphalt, colourtab, generalCallback); + ESPUI.addControl(Button, "Sunflower", "Sunflower", Sunflower, colourtab, generalCallback); + ESPUI.addControl(Button, "Carrot", "Carrot", Carrot, colourtab, generalCallback); + ESPUI.addControl(Button, "Dark", "Dark", Dark, colourtab, generalCallback); + + + /* + * Tab: Styled controls + * This tab shows off how inline CSS styles can be applied to elements and panels in order + * to customise the look of the UI. + *-----------------------------------------------------------------------------------------------------------*/ + auto styletab = ESPUI.addControl(Tab, "", "Styled controls"); + styleButton = ESPUI.addControl(Button, "Styled Button", "Button", Alizarin, styletab, generalCallback); + styleLabel = ESPUI.addControl(Label, "Styled Label", "This is a label", Alizarin, styletab, generalCallback); + styleSwitcher = ESPUI.addControl(Switcher, "Styled Switcher", "1", Alizarin, styletab, generalCallback); + styleSlider = ESPUI.addControl(Slider, "Styled Slider", "0", Alizarin, styletab, generalCallback); + + //This button will randomise the colours of the above controls to show updating of inline styles + ESPUI.addControl(Button, "Randomise Colours", "Randomise Colours", Sunflower, styletab, + //This callback generates and applies inline styles to a bunch of controls to change their colour. + //The styles created are of the form: + // "border-bottom: #999 3px solid; background-color: #aabbcc;" + // "background-color: #aabbcc;" + [](Control *sender, int type) { + //Declare space for style strings. These have to be static so that they are always available + //to the websocket layer. If we'd not made them static they'd be allocated on the heap and + //will be unavailable when we leave this function. + static char stylecol1[60], stylecol2[30]; + if(type == B_UP) { + //Generate two random HTML hex colour codes, and print them into CSS style rules + sprintf(stylecol1, "border-bottom: #999 3px solid; background-color: #%06X;", (unsigned int) random(0x0, 0xFFFFFF)); + sprintf(stylecol2, "background-color: #%06X;", (unsigned int) random(0x0, 0xFFFFFF)); + + //Apply those styles to various elements to show how controls react to styling + ESPUI.setPanelStyle(styleButton, stylecol1); + ESPUI.setElementStyle(styleButton, stylecol2); + ESPUI.setPanelStyle(styleLabel, stylecol1); + ESPUI.setElementStyle(styleLabel, stylecol2); + ESPUI.setPanelStyle(styleSwitcher, stylecol1); + ESPUI.setElementStyle(styleSwitcher, stylecol2); + ESPUI.setPanelStyle(styleSlider, stylecol1); + ESPUI.setElementStyle(styleSlider, stylecol2); + } + }); + + ESPUI.addControl(Separator, "Other styling examples", "", None, styletab); + styleButton2 = ESPUI.addControl(Button, "Styled Button", "Button", Alizarin, styletab, generalCallback); + ESPUI.setPanelStyle(styleButton2, "background: linear-gradient(90deg, rgba(131,58,180,1) 0%, rgba(253,29,29,1) 50%, rgba(252,176,69,1) 100%); border-bottom: #555;"); + ESPUI.setElementStyle(styleButton2, "border-radius: 2em; border: 3px solid black; width: 30%; background-color: #8df;"); + + styleSlider2 = ESPUI.addControl(Slider, "Styled Slider", "0", Dark, styletab, generalCallback); + ESPUI.setElementStyle(styleSlider2, "background: linear-gradient(to right, red, orange, yellow, green, blue);"); + + styleLabel2 = ESPUI.addControl(Label, "Styled Label", "This is a label", Dark, styletab, generalCallback); + ESPUI.setElementStyle(styleLabel2, "text-shadow: 3px 3px #74b1ff, 6px 6px #c64ad7; font-size: 60px; font-variant-caps: small-caps; background-color: unset; color: #c4f0bb; -webkit-text-stroke: 1px black;"); + + + /* + * Tab: Grouped controls + * This tab shows how multiple control can be grouped into the same panel through the use of the + * parentControl value. This also shows how to add labels to grouped controls, and how to use vertical controls. + *-----------------------------------------------------------------------------------------------------------*/ + auto grouptab = ESPUI.addControl(Tab, "", "Grouped controls"); + + //The parent of this button is a tab, so it will create a new panel with one control. + auto groupbutton = ESPUI.addControl(Button, "Button Group", "Button A", Dark, grouptab, generalCallback); + //However the parent of this button is another control, so therefore no new panel is + //created and the button is added to the existing panel. + ESPUI.addControl(Button, "", "Button B", Alizarin, groupbutton, generalCallback); + ESPUI.addControl(Button, "", "Button C", Alizarin, groupbutton, generalCallback); + + + //Sliders can be grouped as well + //To label each slider in the group, we are going add additional labels and give them custom CSS styles + //We need this CSS style rule, which will remove the label's background and ensure that it takes up the entire width of the panel + String clearLabelStyle = "background-color: unset; width: 100%;"; + //First we add the main slider to create a panel + auto groupsliders = ESPUI.addControl(Slider, "Slider Group", "10", Dark, grouptab, generalCallback); + //Then we add a label and set its style to the clearLabelStyle. Here we've just given it the name "A" + ESPUI.setElementStyle(ESPUI.addControl(Label, "", "A", None, groupsliders), clearLabelStyle); + //We can now continue to add additional sliders and labels + ESPUI.addControl(Slider, "", "20", None, groupsliders, generalCallback); + ESPUI.setElementStyle(ESPUI.addControl(Label, "", "B", None, groupsliders), clearLabelStyle); + ESPUI.addControl(Slider, "", "30", None, groupsliders, generalCallback); + ESPUI.setElementStyle(ESPUI.addControl(Label, "", "C", None, groupsliders), clearLabelStyle); + + //We can also usefully group switchers. + auto groupswitcher = ESPUI.addControl(Switcher, "Switcher Group", "0", Dark, grouptab, generalCallback); + ESPUI.addControl(Switcher, "", "1", Sunflower, groupswitcher, generalCallback); + ESPUI.addControl(Switcher, "", "0", Sunflower, groupswitcher, generalCallback); + ESPUI.addControl(Switcher, "", "1", Sunflower, groupswitcher, generalCallback); + //To label these switchers we need to first go onto a "new line" below the line of switchers + //To do this we add an empty label set to be clear and full width (with our clearLabelStyle) + ESPUI.setElementStyle(ESPUI.addControl(Label, "", "", None, groupswitcher), clearLabelStyle); + //We will now need another label style. This one sets its width to the same as a switcher (and turns off the background) + String switcherLabelStyle = "width: 60px; margin-left: .3rem; margin-right: .3rem; background-color: unset;"; + //We can now just add the styled labels. + ESPUI.setElementStyle(ESPUI.addControl(Label, "", "A", None, groupswitcher), switcherLabelStyle); + ESPUI.setElementStyle(ESPUI.addControl(Label, "", "B", None, groupswitcher), switcherLabelStyle); + ESPUI.setElementStyle(ESPUI.addControl(Label, "", "C", None, groupswitcher), switcherLabelStyle); + ESPUI.setElementStyle(ESPUI.addControl(Label, "", "D", None, groupswitcher), switcherLabelStyle); + + //You can mix and match different control types, but the results might sometimes + //need additional styling to lay out nicely. + auto grouplabel = ESPUI.addControl(Label, "Mixed Group", "Main label", Dark, grouptab); + auto grouplabel2 = ESPUI.addControl(Label, "", "Secondary label", Emerald, grouplabel); + ESPUI.addControl(Button, "", "Button D", Alizarin, grouplabel, generalCallback); + ESPUI.addControl(Switcher, "", "1", Sunflower, grouplabel, generalCallback); + ESPUI.setElementStyle(grouplabel2, "font-size: x-large; font-family: serif;"); + + //Some controls can even support vertical orientation, currently Switchers and Sliders + ESPUI.addControl(Separator, "Vertical controls", "", None, grouptab); + auto vertgroupswitcher = ESPUI.addControl(Switcher, "Vertical Switcher Group", "0", Dark, grouptab, generalCallback); + ESPUI.setVertical(vertgroupswitcher); + //On the following lines we wrap the value returned from addControl and send it straight to setVertical + ESPUI.setVertical(ESPUI.addControl(Switcher, "", "0", None, vertgroupswitcher, generalCallback)); + ESPUI.setVertical(ESPUI.addControl(Switcher, "", "0", None, vertgroupswitcher, generalCallback)); + ESPUI.setVertical(ESPUI.addControl(Switcher, "", "0", None, vertgroupswitcher, generalCallback)); + //The mechanism for labelling vertical switchers is the same as we used above for horizontal ones + ESPUI.setElementStyle(ESPUI.addControl(Label, "", "", None, vertgroupswitcher), clearLabelStyle); + ESPUI.setElementStyle(ESPUI.addControl(Label, "", "A", None, vertgroupswitcher), switcherLabelStyle); + ESPUI.setElementStyle(ESPUI.addControl(Label, "", "B", None, vertgroupswitcher), switcherLabelStyle); + ESPUI.setElementStyle(ESPUI.addControl(Label, "", "C", None, vertgroupswitcher), switcherLabelStyle); + ESPUI.setElementStyle(ESPUI.addControl(Label, "", "D", None, vertgroupswitcher), switcherLabelStyle); + + auto vertgroupslider = ESPUI.addControl(Slider, "Vertical Slider Group", "15", Dark, grouptab, generalCallback); + ESPUI.setVertical(vertgroupslider); + ESPUI.setVertical(ESPUI.addControl(Slider, "", "25", None, vertgroupslider, generalCallback)); + ESPUI.setVertical(ESPUI.addControl(Slider, "", "35", None, vertgroupslider, generalCallback)); + ESPUI.setVertical(ESPUI.addControl(Slider, "", "45", None, vertgroupslider, generalCallback)); + //The mechanism for labelling vertical sliders is the same as we used above for switchers + ESPUI.setElementStyle(ESPUI.addControl(Label, "", "", None, vertgroupslider), clearLabelStyle); + ESPUI.setElementStyle(ESPUI.addControl(Label, "", "A", None, vertgroupslider), switcherLabelStyle); + ESPUI.setElementStyle(ESPUI.addControl(Label, "", "B", None, vertgroupslider), switcherLabelStyle); + ESPUI.setElementStyle(ESPUI.addControl(Label, "", "C", None, vertgroupslider), switcherLabelStyle); + ESPUI.setElementStyle(ESPUI.addControl(Label, "", "D", None, vertgroupslider), switcherLabelStyle); + + //Note that combining vertical and horizontal sliders is going to result in very messy layout! + + /* + * Tab: Example UI + * An example UI for the documentation + *-----------------------------------------------------------------------------------------------------------*/ + auto exampletab = ESPUI.addControl(Tab, "Example", "Example"); + ESPUI.addControl(Separator, "Control and Status", "", None, exampletab); + ESPUI.addControl(Switcher, "Power", "1", Alizarin, exampletab, generalCallback); + ESPUI.addControl(Label, "Status", "System status: OK", Wetasphalt, exampletab, generalCallback); + + ESPUI.addControl(Separator, "Settings", "", None, exampletab); + ESPUI.addControl(PadWithCenter, "Attitude Control", "", Dark, exampletab, generalCallback); + auto examplegroup1 = ESPUI.addControl(Button, "Activate Features", "Feature A", Carrot, exampletab, generalCallback); + ESPUI.addControl(Button, "Activate Features", "Feature B", Carrot, examplegroup1, generalCallback); + ESPUI.addControl(Button, "Activate Features", "Feature C", Carrot, examplegroup1, generalCallback); + ESPUI.addControl(Slider, "Value control", "45", Peterriver, exampletab, generalCallback); + + /* + * Tab: WiFi Credentials + * You use this tab to enter the SSID and password of a wifi network to autoconnect to. + *-----------------------------------------------------------------------------------------------------------*/ + auto wifitab = ESPUI.addControl(Tab, "", "WiFi Credentials"); + wifi_ssid_text = ESPUI.addControl(Text, "SSID", "", Alizarin, wifitab, textCallback); + //Note that adding a "Max" control to a text control sets the max length + ESPUI.addControl(Max, "", "32", None, wifi_ssid_text); + wifi_pass_text = ESPUI.addControl(Text, "Password", "", Alizarin, wifitab, textCallback); + ESPUI.addControl(Max, "", "64", None, wifi_pass_text); + ESPUI.addControl(Button, "Save", "Save", Peterriver, wifitab, + [](Control *sender, int type) { + if(type == B_UP) { + Serial.println("Saving credentials to EPROM..."); + Serial.println(ESPUI.getControl(wifi_ssid_text)->value); + Serial.println(ESPUI.getControl(wifi_pass_text)->value); + unsigned int i; + EEPROM.begin(100); + for(i = 0; i < ESPUI.getControl(wifi_ssid_text)->value.length(); i++) { + EEPROM.write(i, ESPUI.getControl(wifi_ssid_text)->value.charAt(i)); + if(i==30) break; //Even though we provided a max length, user input should never be trusted + } + EEPROM.write(i, '\0'); + + for(i = 0; i < ESPUI.getControl(wifi_pass_text)->value.length(); i++) { + EEPROM.write(i + 32, ESPUI.getControl(wifi_pass_text)->value.charAt(i)); + if(i==94) break; //Even though we provided a max length, user input should never be trusted + } + EEPROM.write(i + 32, '\0'); + EEPROM.end(); + } + }); + + + //Finally, start up the UI. + //This should only be called once we are connected to WiFi. + ESPUI.begin(HOSTNAME); +} + + +//Most elements in this test UI are assigned this generic callback which prints some +//basic information. Event types are defined in ESPUI.h +void generalCallback(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); +} + +// Most elements in this test UI are assigned this generic callback which prints some +// basic information. Event types are defined in ESPUI.h +// The extended param can be used to pass additional information +void paramCallback(Control* sender, int type, int param) +{ + 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); + Serial.print("param = "); + Serial.println(param); +} + +void setup() { + randomSeed(0); + Serial.begin(115200); + while(!Serial); + if(SLOW_BOOT) delay(5000); //Delay booting to give time to connect a serial monitor + connectWifi(); + #if defined(ESP32) + WiFi.setSleep(false); //For the ESP32: turn off sleeping to increase UI responsivness (at the cost of power use) + #endif + setUpUI(); +} + +void loop() { + static long unsigned lastTime = 0; + + //Send periodic updates if switcher is turned on + if(updates && millis() > lastTime + 500) { + static uint16_t sliderVal = 10; + + //Flick this switcher on and off + ESPUI.updateSwitcher(mainSwitcher, ESPUI.getControl(mainSwitcher)->value.toInt() ? false : true); + sliderVal += 10; + if(sliderVal > 400) sliderVal = 10; + + //Sliders, numbers, and labels can all be updated at will + ESPUI.updateSlider(mainSlider, sliderVal); + ESPUI.updateNumber(mainNumber, random(100000)); + ESPUI.updateLabel(mainLabel, String(sliderVal)); + lastTime = millis(); + } + + //Simple debug UART interface + if(Serial.available()) { + switch(Serial.read()) { + case 'w': //Print IP details + Serial.println(WiFi.localIP()); + break; + case 'W': //Reconnect wifi + connectWifi(); + break; + case 'C': //Force a crash (for testing exception decoder) + #if !defined(ESP32) + ((void (*)())0xf00fdead)(); + #endif + break; + default: + Serial.print('#'); + break; + } + } + + #if !defined(ESP32) + //We don't need to call this explicitly on ESP32 but we do on 8266 + MDNS.update(); + #endif + +} + + + + +//Utilities +// +//If you are here just to see examples of how to use ESPUI, you can ignore the following functions +//------------------------------------------------------------------------------------------------ +void readStringFromEEPROM(String& buf, int baseaddress, int size) { + buf.reserve(size); + for (int i = baseaddress; i < baseaddress+size; i++) { + char c = EEPROM.read(i); + buf += c; + if(!c) break; + } +} + +void connectWifi() { + int connect_timeout; + +#if defined(ESP32) + WiFi.setHostname(HOSTNAME); +#else + WiFi.hostname(HOSTNAME); +#endif + Serial.println("Begin wifi..."); + + //Load credentials from EEPROM + if(!(FORCE_USE_HOTSPOT)) { + yield(); + EEPROM.begin(100); + String stored_ssid, stored_pass; + readStringFromEEPROM(stored_ssid, 0, 32); + readStringFromEEPROM(stored_pass, 32, 96); + EEPROM.end(); + + //Try to connect with stored credentials, fire up an access point if they don't work. + #if defined(ESP32) + WiFi.begin(stored_ssid.c_str(), stored_pass.c_str()); + #else + WiFi.begin(stored_ssid, stored_pass); + #endif + connect_timeout = 28; //7 seconds + while (WiFi.status() != WL_CONNECTED && connect_timeout > 0) { + delay(250); + Serial.print("."); + connect_timeout--; + } + } + + if (WiFi.status() == WL_CONNECTED) { + Serial.println(WiFi.localIP()); + Serial.println("Wifi started"); + + if (!MDNS.begin(HOSTNAME)) { + Serial.println("Error setting up MDNS responder!"); + } + } else { + Serial.println("\nCreating access point..."); + WiFi.mode(WIFI_AP); + WiFi.softAPConfig(IPAddress(192, 168, 1, 1), IPAddress(192, 168, 1, 1), IPAddress(255, 255, 255, 0)); + WiFi.softAP(HOSTNAME); + + connect_timeout = 20; + do { + delay(250); + Serial.print(","); + connect_timeout--; + } while(connect_timeout); + } +} + + +void textCallback(Control *sender, int type) { + //This callback is needed to handle the changed values, even though it doesn't do anything itself. +} + +void randomString(char *buf, int len) { + for(auto i = 0; i < len-1; i++) + buf[i] = random(0, 26) + 'A'; + buf[len-1] = '\0'; +} diff --git a/pio_examples/gui-generic-api/platformio.ini b/pio_examples/gui-generic-api/platformio.ini new file mode 100644 index 0000000..62410c5 --- /dev/null +++ b/pio_examples/gui-generic-api/platformio.ini @@ -0,0 +1,43 @@ +; PlatformIO Project Configuration File +; +; Build options: build flags, source filter +; Upload options: custom upload port, speed and extra flags +; Library options: dependencies, extra library storages +; Advanced options: extra scripting +; +; Please visit documentation for the other options and examples +; https://docs.platformio.org/page/projectconf.html + +[platformio] +src_dir = ./src +data_dir = ../../data + +[env] +framework = arduino +board_build.filesystem = littlefs +lib_extra_dirs = ../../ +lib_deps = +; bblanchon/ArduinoJson @ ^6.18.5 + bblanchon/ArduinoJson @ ^7.0.4 + https://github.com/bmedici/ESPAsyncWebServer ; Use a fork of the library that has a bugfix for the compile.... https://github.com/esphome/ESPAsyncWebServer/pull/17 + +lib_ignore = + ESP Async WebServer ; force the use of the esphome version + AsyncTCP ; force the use of the esphome version + LittleFS_esp32 ; force the use of the ESP32 built into the core version + +[env:esp8266] +platform = espressif8266 +board = nodemcuv2 +monitor_speed = 115200 + +[env:esp32] +platform = espressif32 +board = esp32dev +monitor_filters = esp32_exception_decoder +board_build.flash_mode = dout + +lib_deps = + ${env.lib_deps} + me-no-dev/AsyncTCP +monitor_speed = 115200 diff --git a/pio_examples/gui-generic-api/src/gui-generic-api.cpp b/pio_examples/gui-generic-api/src/gui-generic-api.cpp new file mode 100644 index 0000000..aefbdbf --- /dev/null +++ b/pio_examples/gui-generic-api/src/gui-generic-api.cpp @@ -0,0 +1,303 @@ +#include +#include + +const byte DNS_PORT = 53; +IPAddress apIP(192, 168, 4, 1); +DNSServer dnsServer; + +#if defined(ESP32) +#include +#else +// esp8266 +#include +#endif + +const char* ssid = "ESPUI"; +const char* password = "espui"; +const char* hostname = "espui"; + +uint16_t status; +uint16_t button1; +uint16_t millisLabelId; +uint16_t switchOne; + +void numberCall(Control* sender, int type) +{ + Serial.println(sender->value); +} + +void textCall(Control* sender, int type) +{ + Serial.print("Text: ID: "); + Serial.print(sender->id); + Serial.print(", Value: "); + Serial.println(sender->value); +} + +void slider(Control* sender, int type) +{ + Serial.print("Slider: ID: "); + Serial.print(sender->id); + Serial.print(", Value: "); + Serial.println(sender->value); +} + +void buttonCallback(Control* sender, int type) +{ + switch (type) + { + case B_DOWN: + Serial.println("Button DOWN"); + break; + + case B_UP: + Serial.println("Button UP"); + break; + } +} + +void buttonExample(Control* sender, int type, void* param) +{ + Serial.print("param: "); + Serial.println((long)param); + switch (type) + { + case B_DOWN: + Serial.println("Status: Start"); + ESPUI.updateControlValue(status, "Start"); + + ESPUI.getControl(button1)->color = ControlColor::Carrot; + ESPUI.updateControl(button1); + break; + + case B_UP: + Serial.println("Status: Stop"); + ESPUI.updateControlValue(status, "Stop"); + + ESPUI.getControl(button1)->color = ControlColor::Peterriver; + ESPUI.updateControl(button1); + break; + } +} + +void padExample(Control* sender, int value) +{ + switch (value) + { + case P_LEFT_DOWN: + Serial.print("left down"); + break; + + case P_LEFT_UP: + Serial.print("left up"); + break; + + case P_RIGHT_DOWN: + Serial.print("right down"); + break; + + case P_RIGHT_UP: + Serial.print("right up"); + break; + + case P_FOR_DOWN: + Serial.print("for down"); + break; + + case P_FOR_UP: + Serial.print("for up"); + break; + + case P_BACK_DOWN: + Serial.print("back down"); + break; + + case P_BACK_UP: + Serial.print("back up"); + break; + + case P_CENTER_DOWN: + Serial.print("center down"); + break; + + case P_CENTER_UP: + Serial.print("center up"); + break; + } + + Serial.print(" "); + Serial.println(sender->id); +} + +void switchExample(Control* sender, int value) +{ + switch (value) + { + case S_ACTIVE: + Serial.print("Active:"); + break; + + case S_INACTIVE: + Serial.print("Inactive"); + break; + } + + Serial.print(" "); + Serial.println(sender->id); +} + +void selectExample(Control* sender, int value) +{ + Serial.print("Select: ID: "); + Serial.print(sender->id); + Serial.print(", Value: "); + Serial.println(sender->value); +} + +void otherSwitchExample(Control* sender, int value) +{ + switch (value) + { + case S_ACTIVE: + Serial.print("Active:"); + break; + + case S_INACTIVE: + Serial.print("Inactive"); + break; + } + + Serial.print(" "); + Serial.println(sender->id); +} + +void setup(void) +{ + ESPUI.setVerbosity(Verbosity::VerboseJSON); + Serial.begin(115200); + +#if defined(ESP32) + WiFi.setHostname(hostname); +#else + WiFi.hostname(hostname); +#endif + + // try to connect to existing network + WiFi.begin(ssid, password); + Serial.print("\n\nTry to connect to existing network"); + + { + uint8_t timeout = 10; + + // Wait for connection, 5s timeout + do + { + delay(500); + Serial.print("."); + timeout--; + } while (timeout && WiFi.status() != WL_CONNECTED); + + // not connected -> create hotspot + if (WiFi.status() != WL_CONNECTED) + { + Serial.print("\n\nCreating 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 + char ap_ssid[25]; + snprintf(ap_ssid, 26, "ESPUI-%08X", chipid); + WiFi.softAP(ap_ssid); + + timeout = 5; + + do + { + delay(500); + Serial.print("."); + timeout--; + } while (timeout); + } + } + + dnsServer.start(DNS_PORT, "*", apIP); + + Serial.println("\n\nWiFi parameters:"); + Serial.print("Mode: "); + Serial.println(WiFi.getMode() == WIFI_AP ? "Station" : "Client"); + Serial.print("IP address: "); + Serial.println(WiFi.getMode() == WIFI_AP ? WiFi.softAPIP() : WiFi.localIP()); + + status = ESPUI.addControl(ControlType::Label, "Status:", "Stop", ControlColor::Turquoise); + + uint16_t select1 = ESPUI.addControl( + ControlType::Select, "Select:", "", ControlColor::Alizarin, Control::noParent, &selectExample); + + ESPUI.addControl(ControlType::Option, "Option1", "Opt1", ControlColor::Alizarin, select1); + ESPUI.addControl(ControlType::Option, "Option2", "Opt2", ControlColor::Alizarin, select1); + ESPUI.addControl(ControlType::Option, "Option3", "Opt3", ControlColor::Alizarin, select1); + + ESPUI.addControl( + ControlType::Text, "Text Test:", "a Text Field", ControlColor::Alizarin, Control::noParent, &textCall); + + millisLabelId = ESPUI.addControl(ControlType::Label, "Millis:", "0", ControlColor::Emerald, Control::noParent); + button1 = ESPUI.addControl( + ControlType::Button, "Push Button", "Press", ControlColor::Peterriver, Control::noParent, &buttonCallback); + ESPUI.addControl( + ControlType::Button, "Other Button", "Press", ControlColor::Wetasphalt, Control::noParent, &buttonExample, (void*)19); + ESPUI.addControl( + ControlType::PadWithCenter, "Pad with center", "", ControlColor::Sunflower, Control::noParent, &padExample); + ESPUI.addControl(ControlType::Pad, "Pad without center", "", ControlColor::Carrot, Control::noParent, &padExample); + switchOne = ESPUI.addControl( + ControlType::Switcher, "Switch one", "", ControlColor::Alizarin, Control::noParent, &switchExample); + ESPUI.addControl( + ControlType::Switcher, "Switch two", "", ControlColor::None, Control::noParent, &otherSwitchExample); + ESPUI.addControl(ControlType::Slider, "Slider one", "30", ControlColor::Alizarin, Control::noParent, &slider); + ESPUI.addControl(ControlType::Slider, "Slider two", "100", ControlColor::Alizarin, Control::noParent, &slider); + ESPUI.addControl(ControlType::Number, "Number:", "50", ControlColor::Alizarin, Control::noParent, &numberCall); + + /* + * .begin loads and serves all files from PROGMEM directly. + * If you want to serve the files from LITTLEFS use ESPUI.beginLITTLEFS + * (.prepareFileSystem has to be run in an empty sketch before) + */ + + // Enable this option if you want sliders to be continuous (update during move) and not discrete (update on stop) + // ESPUI.sliderContinuous = true; + + /* + * Optionally you can use HTTP BasicAuth. Keep in mind that this is NOT a + * SECURE way of limiting access. + * Anyone who is able to sniff traffic will be able to intercept your password + * since it is transmitted in cleartext. Just add a string as username and + * password, for example begin("ESPUI Control", "username", "password") + */ + + ESPUI.begin("ESPUI Control"); +} + +void loop(void) +{ + dnsServer.processNextRequest(); + + static long oldTime = 0; + static bool testSwitchState = false; + + if (millis() - oldTime > 5000) + { + ESPUI.updateControlValue(millisLabelId, String(millis())); + testSwitchState = !testSwitchState; + ESPUI.updateControlValue(switchOne, testSwitchState ? "1" : "0"); + + oldTime = millis(); + } +} diff --git a/pio_examples/prepareFilesystem/platformio.ini b/pio_examples/prepareFilesystem/platformio.ini new file mode 100644 index 0000000..62410c5 --- /dev/null +++ b/pio_examples/prepareFilesystem/platformio.ini @@ -0,0 +1,43 @@ +; PlatformIO Project Configuration File +; +; Build options: build flags, source filter +; Upload options: custom upload port, speed and extra flags +; Library options: dependencies, extra library storages +; Advanced options: extra scripting +; +; Please visit documentation for the other options and examples +; https://docs.platformio.org/page/projectconf.html + +[platformio] +src_dir = ./src +data_dir = ../../data + +[env] +framework = arduino +board_build.filesystem = littlefs +lib_extra_dirs = ../../ +lib_deps = +; bblanchon/ArduinoJson @ ^6.18.5 + bblanchon/ArduinoJson @ ^7.0.4 + https://github.com/bmedici/ESPAsyncWebServer ; Use a fork of the library that has a bugfix for the compile.... https://github.com/esphome/ESPAsyncWebServer/pull/17 + +lib_ignore = + ESP Async WebServer ; force the use of the esphome version + AsyncTCP ; force the use of the esphome version + LittleFS_esp32 ; force the use of the ESP32 built into the core version + +[env:esp8266] +platform = espressif8266 +board = nodemcuv2 +monitor_speed = 115200 + +[env:esp32] +platform = espressif32 +board = esp32dev +monitor_filters = esp32_exception_decoder +board_build.flash_mode = dout + +lib_deps = + ${env.lib_deps} + me-no-dev/AsyncTCP +monitor_speed = 115200 diff --git a/pio_examples/prepareFilesystem/src/prepareFilesystem.cpp b/pio_examples/prepareFilesystem/src/prepareFilesystem.cpp new file mode 100644 index 0000000..f682d57 --- /dev/null +++ b/pio_examples/prepareFilesystem/src/prepareFilesystem.cpp @@ -0,0 +1,17 @@ +#include +#include + +void setup(void) +{ + Serial.begin(115200); + ESPUI.setVerbosity(Verbosity::Verbose); //Enable verbose output so you see the files in LittleFS + delay(500); //Delay to allow Serial Monitor to start after a reset + Serial.println(F("\nPreparing filesystem with ESPUI resources")); + ESPUI.prepareFileSystem(); //Copy across current version of ESPUI resources + Serial.println(F("Done, files...")); + ESPUI.list(); //List all files on LittleFS, for info +} + +void loop() +{ +} diff --git a/pio_examples/tabbedGui/platformio.ini b/pio_examples/tabbedGui/platformio.ini new file mode 100644 index 0000000..62410c5 --- /dev/null +++ b/pio_examples/tabbedGui/platformio.ini @@ -0,0 +1,43 @@ +; PlatformIO Project Configuration File +; +; Build options: build flags, source filter +; Upload options: custom upload port, speed and extra flags +; Library options: dependencies, extra library storages +; Advanced options: extra scripting +; +; Please visit documentation for the other options and examples +; https://docs.platformio.org/page/projectconf.html + +[platformio] +src_dir = ./src +data_dir = ../../data + +[env] +framework = arduino +board_build.filesystem = littlefs +lib_extra_dirs = ../../ +lib_deps = +; bblanchon/ArduinoJson @ ^6.18.5 + bblanchon/ArduinoJson @ ^7.0.4 + https://github.com/bmedici/ESPAsyncWebServer ; Use a fork of the library that has a bugfix for the compile.... https://github.com/esphome/ESPAsyncWebServer/pull/17 + +lib_ignore = + ESP Async WebServer ; force the use of the esphome version + AsyncTCP ; force the use of the esphome version + LittleFS_esp32 ; force the use of the ESP32 built into the core version + +[env:esp8266] +platform = espressif8266 +board = nodemcuv2 +monitor_speed = 115200 + +[env:esp32] +platform = espressif32 +board = esp32dev +monitor_filters = esp32_exception_decoder +board_build.flash_mode = dout + +lib_deps = + ${env.lib_deps} + me-no-dev/AsyncTCP +monitor_speed = 115200 diff --git a/pio_examples/tabbedGui/src/tabbedGui.cpp b/pio_examples/tabbedGui/src/tabbedGui.cpp new file mode 100644 index 0000000..5cf9aaf --- /dev/null +++ b/pio_examples/tabbedGui/src/tabbedGui.cpp @@ -0,0 +1,300 @@ +#include +#include + +const byte DNS_PORT = 53; +IPAddress apIP(192, 168, 4, 1); +DNSServer dnsServer; + +#if defined(ESP32) +#include +#else +// esp8266 +#include +#endif + +const char* ssid = "ESPUI"; +const char* password = "espui"; +const char* hostname = "espui"; + +uint16_t button1; +uint16_t switchOne; +uint16_t status; + +void numberCall(Control* sender, int type) +{ + Serial.println(sender->value); +} + +void textCall(Control* sender, int type) +{ + Serial.print("Text: ID: "); + Serial.print(sender->id); + Serial.print(", Value: "); + Serial.println(sender->value); +} + +void slider(Control* sender, int type) +{ + Serial.print("Slider: ID: "); + Serial.print(sender->id); + Serial.print(", Value: "); + Serial.println(sender->value); +} + +void buttonCallback(Control* sender, int type) +{ + switch (type) + { + case B_DOWN: + Serial.println("Button DOWN"); + break; + + case B_UP: + Serial.println("Button UP"); + break; + } +} + +void buttonExample(Control* sender, int type, void* param) +{ + Serial.print("param: "); + Serial.println((long)param); + switch (type) + { + case B_DOWN: + Serial.println("Status: Start"); + ESPUI.updateControlValue(status, "Start"); + + ESPUI.getControl(button1)->color = ControlColor::Carrot; + ESPUI.updateControl(button1); + break; + + case B_UP: + Serial.println("Status: Stop"); + ESPUI.updateControlValue(status, "Stop"); + + ESPUI.getControl(button1)->color = ControlColor::Peterriver; + ESPUI.updateControl(button1); + break; + } +} + +void padExample(Control* sender, int value) +{ + switch (value) + { + case P_LEFT_DOWN: + Serial.print("left down"); + break; + + case P_LEFT_UP: + Serial.print("left up"); + break; + + case P_RIGHT_DOWN: + Serial.print("right down"); + break; + + case P_RIGHT_UP: + Serial.print("right up"); + break; + + case P_FOR_DOWN: + Serial.print("for down"); + break; + + case P_FOR_UP: + Serial.print("for up"); + break; + + case P_BACK_DOWN: + Serial.print("back down"); + break; + + case P_BACK_UP: + Serial.print("back up"); + break; + + case P_CENTER_DOWN: + Serial.print("center down"); + break; + + case P_CENTER_UP: + Serial.print("center up"); + break; + } + + Serial.print(" "); + Serial.println(sender->id); +} + +void switchExample(Control* sender, int value) +{ + switch (value) + { + case S_ACTIVE: + Serial.print("Active:"); + break; + + case S_INACTIVE: + Serial.print("Inactive"); + break; + } + + Serial.print(" "); + Serial.println(sender->id); +} + +void selectExample(Control* sender, int value) +{ + Serial.print("Select: ID: "); + Serial.print(sender->id); + Serial.print(", Value: "); + Serial.println(sender->value); +} + +void otherSwitchExample(Control* sender, int value) +{ + switch (value) + { + case S_ACTIVE: + Serial.print("Active:"); + break; + + case S_INACTIVE: + Serial.print("Inactive"); + break; + } + + Serial.print(" "); + Serial.println(sender->id); +} + +void setup(void) +{ + Serial.begin(115200); + +#if defined(ESP32) + WiFi.setHostname(hostname); +#else + WiFi.hostname(hostname); +#endif + + // try to connect to existing network + WiFi.begin(ssid, password); + Serial.print("\n\nTry to connect to existing network"); + + { + uint8_t timeout = 10; + + // Wait for connection, 5s timeout + do + { + delay(500); + Serial.print("."); + timeout--; + } while (timeout && WiFi.status() != WL_CONNECTED); + + // not connected -> create hotspot + if (WiFi.status() != WL_CONNECTED) + { + Serial.print("\n\nCreating 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 + char ap_ssid[25]; + snprintf(ap_ssid, 26, "ESPUI-%08X", chipid); + WiFi.softAP(ap_ssid); + + timeout = 5; + + do + { + delay(500); + Serial.print("."); + timeout--; + } while (timeout); + } + } + + dnsServer.start(DNS_PORT, "*", apIP); + + Serial.println("\n\nWiFi parameters:"); + Serial.print("Mode: "); + Serial.println(WiFi.getMode() == WIFI_AP ? "Station" : "Client"); + Serial.print("IP address: "); + Serial.println(WiFi.getMode() == WIFI_AP ? WiFi.softAPIP() : WiFi.localIP()); + + uint16_t tab1 = ESPUI.addControl(ControlType::Tab, "Settings 1", "Settings 1"); + uint16_t tab2 = ESPUI.addControl(ControlType::Tab, "Settings 2", "Settings 2"); + uint16_t tab3 = ESPUI.addControl(ControlType::Tab, "Settings 3", "Settings 3"); + + // shown above all tabs + status = ESPUI.addControl(ControlType::Label, "Status:", "Stop", ControlColor::Turquoise); + + uint16_t select1 + = ESPUI.addControl(ControlType::Select, "Select:", "", ControlColor::Alizarin, tab1, &selectExample); + ESPUI.addControl(ControlType::Option, "Option1", "Opt1", ControlColor::Alizarin, select1); + ESPUI.addControl(ControlType::Option, "Option2", "Opt2", ControlColor::Alizarin, select1); + ESPUI.addControl(ControlType::Option, "Option3", "Opt3", ControlColor::Alizarin, select1); + + ESPUI.addControl(ControlType::Text, "Text Test:", "a Text Field", ControlColor::Alizarin, tab1, &textCall); + + // tabbed controls + ESPUI.addControl(ControlType::Label, "Millis:", "0", ControlColor::Emerald, tab1); + button1 = ESPUI.addControl( + ControlType::Button, "Push Button", "Press", ControlColor::Peterriver, tab1, &buttonCallback); + ESPUI.addControl(ControlType::Button, "Other Button", "Press", ControlColor::Wetasphalt, tab1, &buttonExample, (void*)19); + ESPUI.addControl(ControlType::PadWithCenter, "Pad with center", "", ControlColor::Sunflower, tab2, &padExample); + ESPUI.addControl(ControlType::Pad, "Pad without center", "", ControlColor::Carrot, tab3, &padExample); + switchOne = ESPUI.addControl(ControlType::Switcher, "Switch one", "", ControlColor::Alizarin, tab3, &switchExample); + ESPUI.addControl(ControlType::Switcher, "Switch two", "", ControlColor::None, tab3, &otherSwitchExample); + ESPUI.addControl(ControlType::Slider, "Slider one", "30", ControlColor::Alizarin, tab1, &slider); + ESPUI.addControl(ControlType::Slider, "Slider two", "100", ControlColor::Alizarin, tab3, &slider); + ESPUI.addControl(ControlType::Number, "Number:", "50", ControlColor::Alizarin, tab3, &numberCall); + + /* + * .begin loads and serves all files from PROGMEM directly. + * If you want to serve the files from LITTLEFS use ESPUI.beginLITTLEFS + * (.prepareFileSystem has to be run in an empty sketch before) + */ + + // Enable this option if you want sliders to be continuous (update during move) and not discrete (update on stop) + // ESPUI.sliderContinuous = true; + + /* + * Optionally you can use HTTP BasicAuth. Keep in mind that this is NOT a + * SECURE way of limiting access. + * Anyone who is able to sniff traffic will be able to intercept your password + * since it is transmitted in cleartext. Just add a string as username and + * password, for example begin("ESPUI Control", "username", "password") + */ + + ESPUI.begin("ESPUI Control"); +} + +void loop(void) +{ + dnsServer.processNextRequest(); + + static long oldTime = 0; + static bool switchi = false; + + if (millis() - oldTime > 5000) + { + switchi = !switchi; + ESPUI.updateControlValue(switchOne, switchi ? "1" : "0"); + + oldTime = millis(); + } +}