├── .gitignore
├── CHANGES.txt
├── LICENSE
├── README.md
├── data
└── src
│ ├── ESP8266Wemos-Vis.html
│ └── ESPWroom32-Vis.html
├── docs
├── gauge.png
└── scope.png
├── examples
├── ControlAssist-AdcMonitor
│ ├── ControlAssist-AdcMonitor.ino
│ └── adcMonitorPMem.h
├── ControlAssist-BrowserLog
│ ├── ControlAssist-BrowserLog.ino
│ └── remoteLogViewer.h
├── ControlAssist-ConnectionCtrl
│ └── ControlAssist-ConnectionCtrl.ino
├── ControlAssist-ESPVisualizer
│ ├── ControlAssist-ESPVisualizer.ino
│ ├── gpioPMemESP32.h
│ └── gpioPMemESP8266.h
├── ControlAssist-FadeLed
│ └── ControlAssist-FadeLed.ino
├── ControlAssist-Gauge
│ ├── ControlAssist-Gauge.ino
│ └── gaugePMem.h
├── ControlAssist-Joystick
│ ├── ControlAssist-Joystick.ino
│ └── joystickPMem.h
├── ControlAssist-Minimal
│ └── ControlAssist-Minimal.ino
├── ControlAssist-Scope
│ ├── ControlAssist-Scope.ino
│ └── scopePMem.h
├── ControlAssist-Simple
│ └── ControlAssist-Simple.ino
└── ControlAssist-ToggleLed
│ └── ControlAssist-ToggleLed.ino
├── keywords.txt
├── library,json
├── library.properties
└── src
├── ControlAssist.cpp
├── ControlAssist.h
├── controlAssistPMem.h
└── espLogger.h
/.gitignore:
--------------------------------------------------------------------------------
1 | /test/
2 | /debug.cfg
3 | /debug_custom.json
4 | /esp32.svd
5 | .pio/
6 | .vscode/
7 | platformio.ini
--------------------------------------------------------------------------------
/CHANGES.txt:
--------------------------------------------------------------------------------
1 | 1.1.6 (2025-01-27)
2 | * Set default AutoSendOnCon to true
3 |
4 | 1.1.5 (2024-12-24)
5 | * Use littleFS
6 | * Added log file in BrowserLog example
7 |
8 | 1.1.4 (2024-07-24)
9 | * Added sendSystemMsg to send connection close messages
10 |
11 | 1.1.3 (2024-05-27)
12 | * Removed webserver, added gthHtmlChunk for webserver handlers
13 |
14 | 1.1.2 (2024-05-17)
15 | * Removed sstrem, Fixed errors
16 |
17 | 1.1.1 (2024-01-19)
18 | * Fixed streamFile
19 |
20 | 1.1.0 (2024-01-09)
21 | * Added websockets status text on main page
22 |
23 | 1.0.9 (2023-11-28)
24 | * Added Joystick example.
25 |
26 | 1.0.8 (2023-11-22)
27 | * Load html code from spiffs or progmem.
28 |
29 | 1.0.7 (2023-11-11)
30 | * Fixed errors, ESPVisualizer example added.
31 |
32 | 1.0.6 (2023-11-06)
33 | * Update values on all connected clients.
34 |
35 | 1.0.5 (2023-11-04)
36 | * Auto send keys on ws connections. Added Adc monitor example.
37 |
38 | 1.0.4 (2023-11-01)
39 | * Added Browser log example.
40 |
41 | 1.0.2 (2023-10-30)
42 | * Fixed errors
43 |
44 | 1.0.1 (2023-10-28)
45 | * Fixed errors, Added gauge example
46 |
47 | 1.0.0 (2023-10-25)
48 | * Created ControlAssist library
49 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2023 gemi254
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # ControlAssist
2 | Multi-platform library for controlling html elements in a esp webpages at runtime using web sockets.
3 |
4 | ## Description
5 | A library allowing linking **html elements** to **sketch variables** on pages hosted on **esp32/esp8266** devices. It uses a **web socket server** on the esp **device** and a JavaScript **web socket client** implementation on the web **page** allowing bi-directional real-time communication between **device** and **page**.
6 |
7 | In a typical webpage, html **elements** like ``input box``, ``textbox``, ``range``, ``checkbox`` can be **binded** with ControlAssist internal variables using their unique **html ids** in order to associate their values. A **vectors** list will be generated to hold all associated element keys and their values.
8 |
9 | ## How it works
10 | Every time an element is changing its value in the **web page**, a web socket message will be send to server and ControlAssist will update its internal value. Also if you change a ControlAssist value inside your sketch, a message will be automatically send to client and the value of the associated html element will be updated.
11 |
12 | ControlAssist will automatically add JavaScript **onChange** handlers to the web page html code, so the binded elements will transmit their changes automatically to the server. It will also add JavaScript code to handle **incoming** web sockets messages so the values of the binded html elements can be updated.
13 |
14 | ESP device will transmit **changes** to all connected **web socket** clients. This will make possible to have one ESP device and multiple Web pages opened from different places inside the network, receiving and sending changes.
15 |
16 | ## Features
17 | * Automate **variables** and html **elements** in a typical ``ESP32/ESP8266`` project using web sockets communication.
18 | * Automatic reconnects on wifi disconnections.
19 | * Auto **synchronize** ESP32/ESP8266 internal **variables** with webpage elements.
20 | * Automatically generate required **webpage scripts** to handle connections and changes.
21 | * Support bi-directional hi-speed communications.
22 | * Allow **mult-client** applications. (One esp, multiple pages)
23 | * Support web sockets over **AP** connections.
24 |
25 |
26 |
27 |
28 |
29 |
30 | ## How to use
31 | Define yours internal page html code sections at Program mem.
32 |
33 | ```
34 | PROGMEM const char HTML_HEADERS[] = R"=====()=====";
35 | PROGMEM const char HTML_BODY[] = R"=====()=====";
36 | PROGMEM const char HTML_FOOTER[] = R"=====()=====";
37 | ```
38 | You can also upload page html code sections to spiffs as a html files
39 |
40 | ## ControlAssist init functions
41 | Define and initialize you class
42 | + include the **ControlAssist** class
43 | - `#include //ControlAssist class`
44 |
45 | + Define your static instance
46 | - `ControlAssist ctrl; //Default port 81 `
47 | - `ControlAssist ctrl(port); //Use port `
48 |
49 | + in your setup you must initialize control class by setting your webpage html code.
50 | - `ctrl.setHtmlHeaders(HTML_HEADERS);`
51 | - `ctrl.setHtmlBody(HTML_BODY);`
52 | - `ctrl.setHtmlFooter(HTML_SCRIPT);`
53 |
54 | + You can also use spiffs for the html code. Upload the files contained in the /data folder to your spiffs and define the file names.
55 | - `#define HTML_HEADERS_FILENAME "/src/ESPVisualizer/ESP8266Wemos-VisH.html"`
56 | - `#define HTML_BODY_FILENAME "/src/ESPVisualizer/ESP8266Wemos-VisB.html"`
57 | - `#define HTML_SCRIPT_FILENAME "/src/ESPVisualizer/ESP8266Wemos-VisF.html"`
58 |
59 | + Set the files to be loaded when the page is requested.
60 | - `ctrl.setHtmlHeadersFile(HTML_HEADERS_FILENAME);`
61 | - `ctrl.setHtmlBodyFile(HTML_BODY_FILENAME);`
62 | - `ctrl.setHtmlFooterFile(HTML_SCRIPT_FILENAME);`
63 |
64 | See example ControlAssist-ESPVisualizer
65 |
66 | + in your setup you must bind the html elements you want to control.
67 | - `ctrl.bind("html_id");` to link the html element
68 | - `ctrl.bind("html_id", start_value );` if you need to bind and init for sending on connection
69 | - `ctrl.bind("html_id", start_value, changeFunction);` if you need also to handle changes
70 |
71 | + in your setup specify if you want to auto send key initial values during web socket connection.
72 | - `ctrl.setAutoSendOnCon("html_id",true /* send: enable/disable */);`
73 | - `ctrl.put("html_id", value); // Set a default value to be send`
74 |
75 | + Configure web server to handle control assist page on a uri
76 | - ``` // Setup webserver
77 | server.on("/", []() {
78 | server.setContentLength(CONTENT_LENGTH_UNKNOWN);
79 | String res = "";
80 | res.reserve(CTRLASSIST_STREAM_CHUNKSIZE);
81 | while( ctrl.getHtmlChunk(res)){
82 | server.sendContent(res);
83 | }
84 | });
85 | ```
86 |
87 | + If you want to use a global callback function to handle key changes
88 | - `ctrl.setGlobalCallback(globalChangeFuncion);`
89 |
90 |
91 | + Start websockets server and listen for web socket client connections
92 | - `ctrl.begin();`
93 |
94 |
95 | ## ControlAssist control functions
96 | Controlling your elements inside you loop function
97 |
98 | + Change the values of html elements.
99 |
100 | - `ctrl.put("html_id", value, /* forceSend: send even no change */, /* forceAdd: add key if not exists */ );`
101 |
102 | + Read current value of html element
103 | - `html_val = ctrl["html_id"]`
104 |
105 | + Handle changes inside your code with a handler function
106 | - `void globalChangeFuncion(uint8_t ndx){ String key = ctrl[ndx].key; int val = ctrl[ndx].val.toInt() }`
107 |
108 | + Inside your main loop() call ControlAssist loop() to handle web sockets server clients
109 | - `ctrl.loop();`
110 |
111 | ##### Note that when a html control is "binded" (linked to some backend variable), the library checks whether the new value in ctrl.put differs from the previous send or initial value. If there’s no change, ControlAssist by default does not resend the same value to conserve bandwidth. You can use ``forceSend`` in ctrl.put to send even if there is no change. This optimization is particularly important in applications where frequent updates could lead to performance bottlenecks or excessive network usage (e.g., sliders, real-time monitoring systems, or IoT dashboards).
112 |
113 |
114 | ## JavaScript handlers inside your webpage
115 | A JavaScript event ``wsChange`` will be automatically send to each html element when the esp device changes it's value. You can add a JavaScript event listener on this event at your web page. So it will be possible to perform custom tasks when elements value is updated by web sockets.
116 |
117 | ```
118 | html_id.addEventListener("wsChange", (event) => {
119 | //Get the changed value
120 | value = event.target.value
121 | event.preventDefault();
122 | return false;
123 | });
124 | ```
125 | See example ``ControlAssist-Gauge.ino``
126 |
127 |
128 |
129 |
130 |
131 | ## Logging and debugging with log level
132 | In you application you use **LOG_E**, **LOG_W**, **LOG_I**, **LOG_D** macros instead of **Serial.prinf** to print your messages. **ControlAssist** displays these messages with **timestamps**
133 |
134 | You can define log level for each module
135 | ```
136 | #define LOGGER_LOG_LEVEL 4
137 | or
138 | build_flags = -DLOGGER_LOG_LEVEL=5
139 | ```
140 | ```
141 | #define _LOG_LEVEL_NONE (0)
142 | #define _LOG_LEVEL_ERROR (1)
143 | #define _LOG_LEVEL_WARN (2)
144 | #define _LOG_LEVEL_INFO (3)
145 | #define _LOG_LEVEL_DEBUG (4)
146 | #define _LOG_LEVEL_VERBOSE (5)
147 | ```
148 |
149 | ## Compile
150 | Download library files and place them on ./libraries directory under ArduinoProjects
151 | Then include the **ControlAssist.h** in your application and compile..
152 |
153 | + Compile for arduino-esp32 or arduino-esp8266.
154 | + In order to compile you must install **WebSocketsServer** library.
155 | + Use compiler flags build_flags = -DCA_USE_LITTLEFS to use LITTLEFS instead of SPIFFS
156 |
157 |
158 | ###### If you get compilation errors on arduino-esp32 you need to update your arduino-esp32 library in the IDE using Boards Manager
159 |
160 |
--------------------------------------------------------------------------------
/docs/gauge.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gemi254/ControlAssist-ESP32-ESP8266/f53a4ec26cb3fc1ba58f20c48dd843e2dbff5554/docs/gauge.png
--------------------------------------------------------------------------------
/docs/scope.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gemi254/ControlAssist-ESP32-ESP8266/f53a4ec26cb3fc1ba58f20c48dd843e2dbff5554/docs/scope.png
--------------------------------------------------------------------------------
/examples/ControlAssist-AdcMonitor/ControlAssist-AdcMonitor.ino:
--------------------------------------------------------------------------------
1 | #if defined(ESP32)
2 | #include
3 | #include
4 | #define WEB_SERVER WebServer
5 | #else
6 | #include
7 | #include
8 | #define WEB_SERVER ESP8266WebServer
9 | #endif
10 |
11 | #define LOGGER_LOG_LEVEL 5 // Define log level for this module
12 | #include // Control assist class
13 |
14 | #include "adcMonitorPmem.h" // Html page Program mem
15 |
16 | const char st_ssid[]=""; // Put connection SSID here. On empty an AP will be started
17 | const char st_pass[]=""; // Put your wifi passowrd.
18 | unsigned long pingMillis = millis(); // Ping millis
19 |
20 | unsigned long speed = 40;
21 | bool isPlaying = false;
22 | #ifdef ESP32
23 | // ADC channel 1
24 | // ADC channel 2 canot be used while WiFi is on, pins 27,26,25 15,14,13,12,3,2,0
25 | int adcPins[] ={ 39, 38, 37, 36, 35, 34, 33, 32};
26 | #else
27 | int adcPins[] ={ 0 };
28 | #endif
29 |
30 | ControlAssist ctrl; // Control assist class
31 | WEB_SERVER server(80); // Web server on port 80
32 |
33 | static int ctrlsNdx[ sizeof(adcPins) / sizeof(int) ]; // Array of control indexes
34 |
35 | // Init adc ports
36 | void initAdcadcPins(){
37 | for (byte i = 0; i < sizeof(adcPins) / sizeof(int); i++) {
38 | LOG_I("Init pin no: %i\n", adcPins[i]);
39 | pinMode(adcPins[i], INPUT);
40 | ctrl.bind( ("adc_" + String(adcPins[i])).c_str() );
41 | }
42 | // Get positions of the variables
43 | for (byte i = 0; i < sizeof(adcPins) / sizeof(int); i ++) {
44 | ctrlsNdx[i] = ctrl.getKeyNdx(("adc_" + String(adcPins[i])).c_str());
45 | }
46 | }
47 | // Read adc ports
48 | void readAdcadcPins(){
49 | for (byte i = 0; i < sizeof(adcPins) / sizeof(int); i++) {
50 | uint16_t v = analogRead( adcPins[i]);
51 | ctrl.set(ctrlsNdx[i], v);
52 | LOG_N("Read pin no: %i = %u\n", adcPins[i], v);
53 | }
54 | }
55 | // Change handler
56 | void speedChange(){
57 | LOG_V("speedChange %s\n", ctrl["speed"].c_str());
58 | speed = ctrl["speed"].toInt();
59 | }
60 | // Change handler
61 | void changeOnOff(){
62 | LOG_V("changeOnOff %li\n", ctrl["on-off"].toInt());
63 | isPlaying = ctrl["on-off"].toInt();
64 | }
65 |
66 | void setup() {
67 | Serial.begin(115200);
68 | Serial.print("\n\n\n\n");
69 | Serial.flush();
70 | LOG_I("Starting..\n");
71 |
72 | // Connect WIFI ?
73 | if(strlen(st_ssid)>0){
74 | LOG_E("Connect Wifi to %s.\n", st_ssid);
75 | WiFi.mode(WIFI_STA);
76 | WiFi.begin(st_ssid, st_pass);
77 | uint32_t startAttemptTime = millis();
78 | while (WiFi.status() != WL_CONNECTED && millis() - startAttemptTime < 20000) {
79 | Serial.print(".");
80 | delay(500);
81 | Serial.flush();
82 | }
83 | Serial.println();
84 | }
85 |
86 | // Check connection
87 | if(WiFi.status() == WL_CONNECTED ){
88 | LOG_I("Wifi AP SSID: %s connected, use 'http://%s' to connect\n", st_ssid, WiFi.localIP().toString().c_str());
89 | }else{
90 | LOG_E("Connect failed.\n");
91 | LOG_I("Starting AP.\n");
92 | String mac = WiFi.macAddress();
93 | mac.replace(":","");
94 | String hostName = "ControlAssist_" + mac.substring(6);
95 | WiFi.mode(WIFI_AP);
96 | WiFi.softAP(hostName.c_str(),"",1);
97 | LOG_I("Wifi AP SSID: %s started, use 'http://%s' to connect\n", WiFi.softAPSSID().c_str(), WiFi.softAPIP().toString().c_str());
98 | if (MDNS.begin(hostName.c_str())) LOG_V("AP MDNS responder Started\n");
99 | }
100 |
101 | // Generate body html and JavaScripts
102 | ctrl.setHtmlHeaders(CONTROLASSIST_HTML_HEADER);
103 | static String htmlBody(CONTROLASSIST_HTML_BODY);
104 | String barsScripts="";
105 | for (byte i = 0; i < sizeof(adcPins) / sizeof(int); i = i + 1) {
106 | String bar(CONTROLASSIST_HTML_BAR);
107 | bar.replace("{key}", String(adcPins[i]));
108 | htmlBody += bar;
109 |
110 | String barScript(CONTROLASSIST_HTML_BAR_SCRIPT);
111 | barScript.replace("{key}", String(adcPins[i]));
112 | barsScripts += barScript;
113 | }
114 |
115 | // Setup control assist
116 | ctrl.setHtmlBody(htmlBody.c_str());
117 | static String htmlFooter(CONTROLASSIST_HTML_FOOTER);
118 | htmlFooter.replace("/*{SUB_SCRIPT}*/",barsScripts);
119 | ctrl.setHtmlFooter(htmlFooter.c_str());
120 |
121 | // Bind controls
122 | ctrl.bind("on-off", isPlaying, changeOnOff);
123 | ctrl.bind("speed", speed, speedChange);
124 | // Handle web server root request and send html to client
125 | // Auto send all previous vars on ws connection
126 | ctrl.setAutoSendOnCon(true);
127 | // Init and bind all pins
128 | initAdcadcPins();
129 |
130 | // Start web sockets
131 | ctrl.begin();
132 | LOG_I("ControlAssist started.\n");
133 |
134 | // Add a web server handler on url "/"
135 | server.on("/", []() {
136 | server.setContentLength(CONTENT_LENGTH_UNKNOWN);
137 | String res = "";
138 | res.reserve(CTRLASSIST_STREAM_CHUNKSIZE);
139 | while( ctrl.getHtmlChunk(res)){
140 | server.sendContent(res);
141 | }
142 | });
143 | // Dump binded controls handler
144 | server.on("/d", []() {
145 | server.setContentLength(CONTENT_LENGTH_UNKNOWN);
146 | server.sendContent("Serial dump\n");
147 | String res = "";
148 | while( ctrl.dump(res) ){
149 | server.sendContent(res);
150 | }
151 | });
152 | // Start webserver
153 | server.begin();
154 | LOG_I("HTTP server started\n");
155 | }
156 |
157 | void loop() {
158 | if (millis() - pingMillis >= speed){
159 | if(isPlaying) readAdcadcPins();
160 | pingMillis = millis();
161 | }
162 |
163 | #if not defined(ESP32)
164 | if(MDNS.isRunning()) MDNS.update(); //Handle MDNS
165 | #endif
166 | // Handler webserver clients
167 | server.handleClient();
168 | // Handle websockets
169 | ctrl.loop();
170 | }
171 |
172 |
--------------------------------------------------------------------------------
/examples/ControlAssist-AdcMonitor/adcMonitorPMem.h:
--------------------------------------------------------------------------------
1 | PROGMEM const char CONTROLASSIST_HTML_HEADER[] = R"=====(
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | ControlAssist
10 |
77 | )=====";
78 |
79 | PROGMEM const char CONTROLASSIST_HTML_BODY[] = R"=====(
80 |
81 |
82 |