To use the node in your project you should have the nazarijtipusak080/esp32-web-interface-1 library installed. Use the “File → Add Library” menu item in XOD IDE if you don’t have it yet. See Using libraries for more info.
C++ implementation
#pragma XOD require "https://github.com/me-no-dev/ESPAsyncWebServer"
#pragma XOD require "https://github.com/me-no-dev/AsyncTCP"
#pragma XOD error_raise enable
#include <WiFi.h>
#include <AsyncTCP.h>
#include <ESPAsyncWebServer.h>
AsyncWebServer server(80);
// --- глобальні прапорці кнопок ---
bool btn1 = false;
bool btn2 = false;
bool btn3 = false;
bool btn4 = false;
// --- текст змінної для відображення на сторінці ---
namespace input {
String title = "";
String style = "";
String operation = "";
}
const char index_html[] PROGMEM = R"rawliteral(
<!DOCTYPE HTML><html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
%TITLE%
<style>
button { width:120px; height:50px; font-size:18px; margin:10px; }
%STYLE%
</style>
</head>
<body>
<h2>ESP32 Button Interface</h2>
<button onclick="press(1)">Button 1</button>
<button onclick="press(2)">Button 2</button>
<button onclick="press(3)">Button 3</button>
<button onclick="press(4)">Button 4</button>
<hr>
<b>Last pressed:</b> <span id="operation">%OPERATION%</span>
<script>
function press(num){
var xhttp = new XMLHttpRequest();
xhttp.open("GET", "/press?b=" + num, true);
xhttp.send();
}
setInterval(function(){
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
document.getElementById("operation").innerHTML = this.responseText;
}
};
xhttp.open("GET", "/operation", true);
xhttp.send();
}, 1000);
</script>
</body>
</html>
)rawliteral";
String processor(const String& var) {
if (var == "TITLE") return input::title;
if (var == "STYLE") return input::style;
if (var == "OPERATION") return input::operation;
return String();
}
node {
#define WIFI_TIMEOUT_MS 20000
void evaluate(Context ctx) {
// --- старт сервера при CONN ---
if (isInputDirty<input_CONN>(ctx)) {
// TITLE
auto tt = getValue<input_TITLE>(ctx);
char _tt[length(tt) + 1] = {0};
dump(tt, _tt);
// build title string
String ttl = "";
for (size_t i = 0; i < sizeof(_tt) && _tt[i] != 0; ++i) ttl += _tt[i];
input::title = "<title>" + ttl + "</title>";
// STYLE
auto st = getValue<input_STYLE>(ctx);
char _st[length(st) + 1] = {0};
dump(st, _st);
String sty = "";
for (size_t i = 0; i < sizeof(_st) && _st[i] != 0; ++i) sty += _st[i];
input::style = sty;
// OP initial
auto op = getValue<input_OP>(ctx);
char _op[length(op) + 1] = {0};
dump(op, _op);
String opp = "";
for (size_t i = 0; i < sizeof(_op) && _op[i] != 0; ++i) opp += _op[i];
input::operation = opp;
// routes
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
request->send_P(200, "text/html", index_html, processor);
});
server.on("/operation", HTTP_GET, [](AsyncWebServerRequest *request){
request->send_P(200, "text/plain", input::operation.c_str());
});
server.on("/press", HTTP_GET, [](AsyncWebServerRequest *request){
if (request->hasParam("b")) {
int b = request->getParam("b")->value().toInt();
if (b == 1) { btn1 = true; input::operation = "Button 1"; }
if (b == 2) { btn2 = true; input::operation = "Button 2"; }
if (b == 3) { btn3 = true; input::operation = "Button 3"; }
if (b == 4) { btn4 = true; input::operation = "Button 4"; }
request->send(200, "text/plain", input::operation.c_str());
} else {
request->send(400, "text/plain", "Missing b parameter");
}
});
server.begin();
// Видаємо IP (якщо Wi-Fi підключений) і DONE
if (WiFi.status() == WL_CONNECTED) {
emitValue<output_IP>(ctx, WiFi.localIP());
} else {
// все одно емiтимо локальний IP (0.0.0.0) якщо немає підключення
emitValue<output_IP>(ctx, WiFi.localIP());
}
}
// --- обробка RUN: оновлюємо OP і STYLE зі входів (ручне оновлення) ---
if (isInputDirty<input_RUN>(ctx)) {
// OP
auto op = getValue<input_OP>(ctx);
char _op[length(op) + 1] = {0};
dump(op, _op);
String opp = "";
for (size_t i = 0; i < sizeof(_op) && _op[i] != 0; ++i) opp += _op[i];
input::operation = opp;
// STYLE (оновлюємо стиль при RUN)
auto st = getValue<input_STYLE>(ctx);
char _st[length(st) + 1] = {0};
dump(st, _st);
String sty = "";
for (size_t i = 0; i < sizeof(_st) && _st[i] != 0; ++i) sty += _st[i];
input::style = sty;
}
// --- генерування виходів (імпульси при натисканні) ---
if (btn1) {
// видати короткий імпульс — тут ми робимо toggle від поточного стану виходу
emitValue<output_B1>(ctx, !(getValue<output_B1>(ctx)));
btn1 = false;
}
if (btn2) {
emitValue<output_B2>(ctx, !(getValue<output_B2>(ctx)));
btn2 = false;
}
if (btn3) {
emitValue<output_B3>(ctx, !(getValue<output_B3>(ctx)));
btn3 = false;
}
if (btn4) {
emitValue<output_B4>(ctx, !(getValue<output_B4>(ctx)));
btn4 = false;
}
emitValue<output_DONE>(ctx, 1);
}
}