2024-01-06 16:54:23 +00:00
|
|
|
/*
|
|
|
|
xdrv_121_gpioviewer.ino - GPIOViewer for Tasmota
|
|
|
|
|
2024-01-08 21:16:33 +00:00
|
|
|
SPDX-FileCopyrightText: 2024 Theo Arends, Stephan Hadinger and Charles Giguere
|
2024-01-06 16:54:23 +00:00
|
|
|
|
|
|
|
SPDX-License-Identifier: GPL-3.0-only
|
|
|
|
*/
|
|
|
|
|
|
|
|
#ifdef USE_GPIO_VIEWER
|
|
|
|
/*********************************************************************************************\
|
2024-01-08 21:16:33 +00:00
|
|
|
* GPIOViewer support based on work by Charles Giguere
|
2024-01-06 16:54:23 +00:00
|
|
|
*
|
2024-01-08 21:16:33 +00:00
|
|
|
* Resources:
|
|
|
|
* GPIO Viewer: https://github.com/thelastoutpostworkshop/gpio_viewer
|
|
|
|
* Server Sent Event: https://github.com/esp8266/Arduino/issues/7008
|
2024-01-06 16:54:23 +00:00
|
|
|
\*********************************************************************************************/
|
|
|
|
|
|
|
|
#define XDRV_121 121
|
|
|
|
|
2024-01-10 14:51:19 +00:00
|
|
|
#ifndef GV_PORT
|
2024-01-07 14:10:19 +00:00
|
|
|
#define GV_PORT 5557
|
2024-01-10 14:51:19 +00:00
|
|
|
#endif
|
|
|
|
#ifndef GV_SAMPLING_INTERVAL
|
|
|
|
#define GV_SAMPLING_INTERVAL 100 // [GvSampling] milliseconds - Use Tasmota Scheduler (100) or Ticker (20..99,101..1000)
|
|
|
|
#endif
|
|
|
|
|
2024-01-08 21:16:33 +00:00
|
|
|
#define GV_KEEP_ALIVE 1000 // milliseconds - If no activity after this do a heap size event anyway
|
2024-01-06 16:54:23 +00:00
|
|
|
|
2024-01-12 11:17:31 +00:00
|
|
|
//#define GV_BASE_URL "https://thelastoutpostworkshop.github.io/microcontroller_devkit/gpio_viewer/assets/"
|
|
|
|
#ifdef ESP8266
|
|
|
|
#ifndef GV_BASE_URL
|
|
|
|
#undef GV_BASE_URL // Fix compiler warning
|
|
|
|
#define GV_BASE_URL "https://ota.tasmota.com/tasmota/gpio_viewer/assets/"
|
|
|
|
#endif
|
|
|
|
#endif // ESP8266
|
|
|
|
#ifdef ESP32
|
|
|
|
#ifndef GV_BASE_URL
|
|
|
|
#undef GV_BASE_URL // Fix compiler warning
|
|
|
|
#define GV_BASE_URL "https://ota.tasmota.com/tasmota32/gpio_viewer/assets/"
|
|
|
|
#endif
|
|
|
|
#endif // ESP32
|
2024-01-06 16:54:23 +00:00
|
|
|
|
2024-01-09 13:58:33 +00:00
|
|
|
const char *GVRelease = "1.0.7";
|
2024-01-07 17:10:30 +00:00
|
|
|
|
2024-01-06 16:54:23 +00:00
|
|
|
const char HTTP_GV_PAGE[] PROGMEM =
|
|
|
|
"<!DOCTYPE HTML>"
|
|
|
|
"<html>"
|
|
|
|
"<head>"
|
2024-01-08 21:16:33 +00:00
|
|
|
"<title>%s - GPIO Viewer</title>"
|
2024-01-06 16:54:23 +00:00
|
|
|
"<base href='" GV_BASE_URL "'>"
|
|
|
|
"<link id='defaultStyleSheet' rel='stylesheet' href=''>"
|
|
|
|
"<link id='boardStyleSheet' rel='stylesheet' href=''>"
|
|
|
|
"<link rel='icon' href='favicon.ico' type='image/x-icon'>"
|
|
|
|
"<script src='script/webSocket.js'></script>"
|
|
|
|
"<script src='script/boardSwitcher.js'></script>"
|
|
|
|
"<script>"
|
|
|
|
"var serverPort=" STR(GV_PORT) ";"
|
|
|
|
"var ip='%s';" // WiFi.localIP().toString().c_str()
|
|
|
|
"var source=new EventSource('http://%s:" STR(GV_PORT) "/events');" // WiFi.localIP().toString().c_str()
|
2024-01-10 14:51:19 +00:00
|
|
|
"var sampling_interval='%d';" // Gv.sampling
|
2024-01-08 21:16:33 +00:00
|
|
|
#ifdef ESP32
|
|
|
|
"var psramSize='%d KB';" // ESP.getPsramSize() / 1024
|
|
|
|
#endif // ESP32
|
2024-01-07 15:06:31 +00:00
|
|
|
"var freeSketchSpace='%d KB';" // ESP_getFreeSketchSpace() / 1024
|
2024-01-06 16:54:23 +00:00
|
|
|
"</script>"
|
|
|
|
"</head>"
|
|
|
|
"<body>"
|
|
|
|
"<div class='grid-container'>"
|
2024-01-07 14:10:19 +00:00
|
|
|
"<div id='messageBox' class='message-box hidden'></div>"
|
2024-01-06 16:54:23 +00:00
|
|
|
"<header class='header'></header>"
|
|
|
|
// Image
|
|
|
|
"<div class='image-container'>"
|
|
|
|
"<div id='imageWrapper' class='image-wrapper'>"
|
|
|
|
"<img id='boardImage' src='' alt='Board Image'>"
|
|
|
|
"<div id='indicators'></div>"
|
|
|
|
"</div>"
|
|
|
|
"</div>"
|
|
|
|
"</div>"
|
|
|
|
"</body>"
|
|
|
|
"</html>";
|
|
|
|
|
2024-01-06 20:57:09 +00:00
|
|
|
const char HTTP_GV_EVENT[] PROGMEM =
|
|
|
|
"HTTP/1.1 200 OK\n"
|
|
|
|
"Content-Type: text/event-stream;\n"
|
|
|
|
"Connection: keep-alive\n"
|
|
|
|
"Cache-Control: no-cache\n"
|
|
|
|
"Access-Control-Allow-Origin: *\n\n";
|
|
|
|
|
2024-01-06 16:54:23 +00:00
|
|
|
enum GVPinTypes {
|
2024-01-08 21:16:33 +00:00
|
|
|
GV_DigitalPin = 0,
|
|
|
|
GV_PWMPin = 1,
|
|
|
|
GV_AnalogPin = 2
|
2024-01-06 16:54:23 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
struct {
|
|
|
|
WiFiClient WebClient;
|
|
|
|
ESP8266WebServer *WebServer;
|
2024-01-10 14:51:19 +00:00
|
|
|
Ticker ticker;
|
2024-01-09 13:58:33 +00:00
|
|
|
int lastPinStates[MAX_GPIO_PIN];
|
2024-01-08 21:16:33 +00:00
|
|
|
uint32_t lastSentWithNoActivity;
|
2024-01-07 14:10:19 +00:00
|
|
|
uint32_t freeHeap;
|
|
|
|
uint32_t freePSRAM;
|
2024-01-10 14:51:19 +00:00
|
|
|
uint32_t sampling;
|
2024-01-06 16:54:23 +00:00
|
|
|
bool active;
|
2024-01-09 13:58:33 +00:00
|
|
|
bool sse_ready;
|
2024-01-06 16:54:23 +00:00
|
|
|
} GV;
|
|
|
|
|
2024-01-12 15:54:24 +00:00
|
|
|
void GVStop(void) {
|
|
|
|
GV.sse_ready = false;
|
|
|
|
GV.ticker.detach();
|
|
|
|
GV.active = false;
|
|
|
|
|
|
|
|
GV.WebServer->stop();
|
|
|
|
GV.WebServer = nullptr;
|
|
|
|
}
|
|
|
|
|
2024-01-09 16:08:13 +00:00
|
|
|
void GVBegin(void) {
|
2024-01-10 14:51:19 +00:00
|
|
|
if (0 == GV.sampling) {
|
|
|
|
GV.sampling = (GV_SAMPLING_INTERVAL < 20) ? 20 : GV_SAMPLING_INTERVAL;
|
|
|
|
}
|
|
|
|
|
2024-01-09 16:08:13 +00:00
|
|
|
GV.WebServer = new ESP8266WebServer(GV_PORT);
|
|
|
|
// Set CORS headers for global responses
|
|
|
|
GV.WebServer->sendHeader("Access-Control-Allow-Origin", "*");
|
|
|
|
GV.WebServer->sendHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
|
|
|
|
GV.WebServer->sendHeader("Access-Control-Allow-Headers", "Content-Type");
|
|
|
|
GV.WebServer->on("/", GVHandleRoot);
|
|
|
|
GV.WebServer->on("/release", GVHandleRelease);
|
|
|
|
GV.WebServer->on("/free_psram", GVHandleFreePSRam);
|
2024-01-12 15:54:24 +00:00
|
|
|
GV.WebServer->on("/events", GVHandleEvents);
|
2024-01-09 16:08:13 +00:00
|
|
|
GV.WebServer->begin();
|
|
|
|
|
|
|
|
GV.active = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void GVHandleRoot(void) {
|
2024-01-12 15:54:24 +00:00
|
|
|
GVCloseEvent();
|
|
|
|
|
2024-01-09 16:08:13 +00:00
|
|
|
char* content = ext_snprintf_malloc_P(HTTP_GV_PAGE,
|
|
|
|
SettingsTextEscaped(SET_DEVICENAME).c_str(),
|
2024-01-10 14:51:19 +00:00
|
|
|
WiFi.localIP().toString().c_str(),
|
|
|
|
WiFi.localIP().toString().c_str(),
|
|
|
|
GV.sampling,
|
2024-01-09 16:08:13 +00:00
|
|
|
#ifdef ESP32
|
|
|
|
ESP.getPsramSize() / 1024,
|
|
|
|
#endif // ESP32
|
|
|
|
ESP_getFreeSketchSpace() / 1024);
|
|
|
|
if (content == nullptr) { return; } // Avoid crash
|
|
|
|
GV.WebServer->send_P(200, "text/html", content);
|
|
|
|
free(content);
|
|
|
|
}
|
|
|
|
|
|
|
|
void GVHandleRelease(void) {
|
|
|
|
String jsonResponse = "{\"release\":\"" + String(GVRelease) + "\"}";
|
|
|
|
GV.WebServer->send(200, "application/json", jsonResponse);
|
|
|
|
}
|
|
|
|
|
|
|
|
void GVHandleFreePSRam(void) {
|
|
|
|
String jsonResponse = "{\"freePSRAM\":\"";
|
|
|
|
#ifdef ESP32
|
|
|
|
if (UsePSRAM()) {
|
|
|
|
jsonResponse += String(ESP.getFreePsram() / 1024) + " KB\"}";
|
|
|
|
} else
|
|
|
|
#endif
|
|
|
|
jsonResponse += "No PSRAM\"}";
|
|
|
|
GV.WebServer->send(200, "application/json", jsonResponse);
|
|
|
|
}
|
|
|
|
|
2024-01-12 15:54:24 +00:00
|
|
|
void GVHandleEvents(void) {
|
|
|
|
GV.WebClient = GV.WebServer->client();
|
|
|
|
GV.WebClient.setNoDelay(true);
|
|
|
|
// GV.WebClient.setSync(true);
|
|
|
|
|
|
|
|
GV.WebServer->setContentLength(CONTENT_LENGTH_UNKNOWN); // The payload can go on forever
|
|
|
|
GV.WebServer->sendContent_P(HTTP_GV_EVENT);
|
|
|
|
|
|
|
|
GV.sse_ready = true; // Ready for async updates
|
|
|
|
if (GV.sampling != 100) {
|
|
|
|
GV.ticker.attach_ms(GV.sampling, GVMonitorTask); // Use Tasmota Scheduler (100) or Ticker (20..99,101..1000)
|
|
|
|
}
|
|
|
|
AddLog(LOG_LEVEL_DEBUG, PSTR("IOV: Connected"));
|
|
|
|
}
|
|
|
|
|
|
|
|
void GVEventDisconnected(void) {
|
|
|
|
if (GV.sse_ready) {
|
|
|
|
AddLog(LOG_LEVEL_DEBUG, PSTR("IOV: Disconnected"));
|
|
|
|
}
|
|
|
|
GV.sse_ready = false; // This just stops the event to be restarted by opening root page again
|
|
|
|
GV.ticker.detach();
|
|
|
|
}
|
|
|
|
|
|
|
|
void GVCloseEvent(void) {
|
|
|
|
GVEventSend("{}", "close", millis()); // Closes window
|
|
|
|
GVEventDisconnected();
|
|
|
|
}
|
|
|
|
|
2024-01-06 20:57:09 +00:00
|
|
|
//void GVEventSend(const char *message, const char *event=NULL, uint32_t id=0, uint32_t reconnect=0);
|
|
|
|
void GVEventSend(const char *message, const char *event, uint32_t id) {
|
2024-01-06 16:54:23 +00:00
|
|
|
if (GV.WebClient.connected()) {
|
|
|
|
// generateEventMessage() in AsyncEventSource.cpp
|
|
|
|
// GV.WebClient.printf_P(PSTR("retry: 0\r\nid: %u\r\nevent: %s\r\ndata: %s\r\n\r\n"), id, event, message);
|
|
|
|
GV.WebClient.printf_P(PSTR("id: %u\r\nevent: %s\r\ndata: %s\r\n\r\n"), id, event, message);
|
2024-01-09 16:08:13 +00:00
|
|
|
} else {
|
2024-01-12 15:54:24 +00:00
|
|
|
GVEventDisconnected();
|
2024-01-06 16:54:23 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void GVMonitorTask(void) {
|
2024-01-09 13:58:33 +00:00
|
|
|
// Monitor GPIO Values
|
|
|
|
uint32_t originalValue;
|
2024-01-06 16:54:23 +00:00
|
|
|
uint32_t pintype;
|
|
|
|
bool hasChanges = false;
|
2024-01-09 13:58:33 +00:00
|
|
|
|
2024-01-08 21:16:33 +00:00
|
|
|
String jsonMessage = "{";
|
2024-01-07 14:10:19 +00:00
|
|
|
for (uint32_t pin = 0; pin < MAX_GPIO_PIN; pin++) {
|
2024-01-09 13:58:33 +00:00
|
|
|
int currentState = 0;
|
|
|
|
/*
|
2024-01-12 11:17:31 +00:00
|
|
|
// Skip unconfigured GPIO
|
2024-01-09 13:58:33 +00:00
|
|
|
uint32_t pin_type = GetPin(pin) / 32;
|
|
|
|
if (GPIO_NONE == pin_type) {
|
|
|
|
pintype = GV_DigitalPin;
|
|
|
|
originalValue = 0;
|
|
|
|
// currentState = 0;
|
|
|
|
}
|
|
|
|
*/
|
|
|
|
#ifdef ESP32
|
2024-01-12 11:17:31 +00:00
|
|
|
// Read PWM GPIO
|
2024-01-09 13:58:33 +00:00
|
|
|
int pwm_resolution = ledcReadDutyResolution(pin);
|
|
|
|
if (pwm_resolution > 0) {
|
|
|
|
pintype = GV_PWMPin;
|
|
|
|
originalValue = ledcRead2(pin);
|
|
|
|
currentState = changeUIntScale(originalValue, 0, pwm_resolution, 0, 255); // bring back to 0..255
|
|
|
|
}
|
|
|
|
#endif // ESP32
|
|
|
|
|
|
|
|
#ifdef ESP8266
|
2024-01-12 11:17:31 +00:00
|
|
|
// Read PWM GPIO
|
2024-01-09 13:58:33 +00:00
|
|
|
int pwm_value = AnalogRead(pin);
|
|
|
|
if (pwm_value > -1) {
|
|
|
|
pintype = GV_PWMPin;
|
|
|
|
originalValue = pwm_value;
|
|
|
|
currentState = changeUIntScale(originalValue, 0, Settings->pwm_range, 0, 255); // bring back to 0..255
|
|
|
|
}
|
|
|
|
#endif // ESP8266
|
|
|
|
|
|
|
|
else if (AdcPin(pin)) {
|
2024-01-12 11:17:31 +00:00
|
|
|
// Read Analog (ADC) GPIO
|
2024-01-09 13:58:33 +00:00
|
|
|
pintype = GV_AnalogPin;
|
2024-01-12 11:17:31 +00:00
|
|
|
/*
|
|
|
|
#ifdef ESP32
|
2024-01-09 13:58:33 +00:00
|
|
|
originalValue = AdcRead(pin, 2);
|
2024-01-12 11:17:31 +00:00
|
|
|
#endif // ESP32
|
|
|
|
#ifdef ESP8266
|
|
|
|
// Fix exception 9 if using ticker - GV.sampling != 100 (CallChain: (phy)pm_wakeup_init, (adc)test_tout, ets_timer_arm_new, delay, AdcRead, String6concat, MonitorTask)
|
|
|
|
originalValue = (GV.sampling != 100) ? analogRead(pin) : AdcRead(pin, 1);
|
|
|
|
#endif // ESP8266
|
|
|
|
*/
|
|
|
|
originalValue = AdcRead1(pin);
|
|
|
|
currentState = changeUIntScale(originalValue, 0, AdcRange(), 0, 255); // bring back to 0..255
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
// Read digital GPIO
|
2024-01-09 13:58:33 +00:00
|
|
|
pintype = GV_DigitalPin;
|
|
|
|
int value = digitalRead(pin);
|
|
|
|
originalValue = value;
|
|
|
|
if (value == 1) {
|
|
|
|
currentState = 256;
|
2024-01-10 14:51:19 +00:00
|
|
|
// } else {
|
|
|
|
// currentState = 0;
|
2024-01-09 13:58:33 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (originalValue != GV.lastPinStates[pin]) {
|
2024-01-08 21:16:33 +00:00
|
|
|
if (hasChanges) { jsonMessage += ","; }
|
2024-01-09 13:58:33 +00:00
|
|
|
jsonMessage += "\"" + String(pin) + "\":{\"s\":" + currentState + ",\"v\":" + originalValue + ",\"t\":" + pintype + "}";
|
|
|
|
GV.lastPinStates[pin] = originalValue;
|
2024-01-07 14:10:19 +00:00
|
|
|
hasChanges = true;
|
2024-01-06 16:54:23 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
jsonMessage += "}";
|
|
|
|
if (hasChanges) {
|
2024-01-06 20:57:09 +00:00
|
|
|
GVEventSend(jsonMessage.c_str(), "gpio-state", millis());
|
2024-01-06 16:54:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
uint32_t heap = ESP_getFreeHeap();
|
|
|
|
if (heap != GV.freeHeap) {
|
|
|
|
GV.freeHeap = heap;
|
2024-01-07 15:06:31 +00:00
|
|
|
char temp[20];
|
|
|
|
snprintf_P(temp, sizeof(temp), PSTR("%d KB"), heap / 1024);
|
|
|
|
GVEventSend(temp, "free_heap", millis());
|
2024-01-08 21:16:33 +00:00
|
|
|
hasChanges = true;
|
2024-01-06 16:54:23 +00:00
|
|
|
}
|
2024-01-07 14:10:19 +00:00
|
|
|
|
|
|
|
#ifdef ESP32
|
|
|
|
if (UsePSRAM()) {
|
|
|
|
uint32_t psram = ESP.getFreePsram();
|
|
|
|
if (psram != GV.freePSRAM) {
|
|
|
|
GV.freePSRAM = psram;
|
2024-01-07 15:06:31 +00:00
|
|
|
char temp[20];
|
|
|
|
snprintf_P(temp, sizeof(temp), PSTR("%d KB"), psram / 1024);
|
|
|
|
GVEventSend(temp, "free_psram", millis());
|
2024-01-08 21:16:33 +00:00
|
|
|
hasChanges = true;
|
2024-01-07 14:10:19 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif // ESP32
|
2024-01-08 21:16:33 +00:00
|
|
|
|
|
|
|
if (!hasChanges) {
|
|
|
|
uint32_t last_sent = millis() - GV.lastSentWithNoActivity;
|
|
|
|
if (last_sent > GV_KEEP_ALIVE) {
|
|
|
|
// No activity, resending for pulse
|
|
|
|
char temp[20];
|
|
|
|
snprintf_P(temp, sizeof(temp), PSTR("%d KB"), heap / 1024);
|
|
|
|
GVEventSend(temp, "free_heap", millis());
|
|
|
|
GV.lastSentWithNoActivity = millis();
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
GV.lastSentWithNoActivity = millis();
|
|
|
|
}
|
2024-01-06 16:54:23 +00:00
|
|
|
}
|
|
|
|
|
2024-01-09 16:08:13 +00:00
|
|
|
/*********************************************************************************************\
|
|
|
|
* Commands
|
|
|
|
\*********************************************************************************************/
|
2024-01-06 20:57:09 +00:00
|
|
|
|
2024-01-09 16:08:13 +00:00
|
|
|
const char kGVCommands[] PROGMEM = "GV|" // Prefix
|
2024-01-10 14:51:19 +00:00
|
|
|
"Viewer|Sampling";
|
2024-01-06 16:54:23 +00:00
|
|
|
|
2024-01-09 16:08:13 +00:00
|
|
|
void (* const GVCommand[])(void) PROGMEM = {
|
2024-01-10 14:51:19 +00:00
|
|
|
&CmndGvViewer, &CmndGvSampling };
|
|
|
|
|
|
|
|
void CmndGvViewer(void) {
|
|
|
|
/* GvViewer - Show current viewer state
|
|
|
|
GvViewer 0 - Turn viewer off
|
|
|
|
GvViewer 1 - Turn viewer On
|
|
|
|
GvViewer 2 - Toggle viewer state
|
|
|
|
*/
|
2024-01-09 16:08:13 +00:00
|
|
|
if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 2)) {
|
|
|
|
uint32_t state = XdrvMailbox.payload;
|
|
|
|
if (2 == state) { // Toggle
|
|
|
|
state = GV.active ^1;
|
|
|
|
}
|
|
|
|
if (state) { // On
|
|
|
|
GVBegin();
|
|
|
|
} else { // Off
|
2024-01-12 15:54:24 +00:00
|
|
|
GVCloseEvent(); // Stop current updates
|
2024-01-09 16:08:13 +00:00
|
|
|
GVStop();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (GV.active) {
|
|
|
|
Response_P(PSTR("{\"%s\":\"Active on http://%s:" STR(GV_PORT) "/\"}"), XdrvMailbox.command, WiFi.localIP().toString().c_str());
|
|
|
|
} else {
|
|
|
|
ResponseCmndChar_P(PSTR("Stopped"));
|
|
|
|
}
|
2024-01-06 16:54:23 +00:00
|
|
|
}
|
|
|
|
|
2024-01-10 14:51:19 +00:00
|
|
|
void CmndGvSampling(void) {
|
|
|
|
/* GvSampling - Show current sampling interval
|
|
|
|
GvSampling 20 .. 1000 - Set sampling interval
|
|
|
|
*/
|
|
|
|
if ((XdrvMailbox.payload >= 20) && (XdrvMailbox.payload <= 1000)) {
|
2024-01-12 15:54:24 +00:00
|
|
|
GVCloseEvent(); // Stop current updates
|
2024-01-10 14:51:19 +00:00
|
|
|
GV.sampling = XdrvMailbox.payload; // 20 - 1000 milliseconds
|
|
|
|
}
|
|
|
|
ResponseCmndNumber(GV.sampling);
|
|
|
|
}
|
|
|
|
|
2024-01-09 13:58:33 +00:00
|
|
|
/*********************************************************************************************\
|
|
|
|
* GUI
|
|
|
|
\*********************************************************************************************/
|
|
|
|
#ifdef USE_WEBSERVER
|
|
|
|
#define WEB_HANDLE_GV "gv1"
|
|
|
|
|
|
|
|
const char HTTP_BTN_MENU_GV[] PROGMEM =
|
|
|
|
"<p><form action='" WEB_HANDLE_GV "' method='get' target='_blank'><button>" D_GPIO_VIEWER "</button></form></p>";
|
|
|
|
|
|
|
|
void GVSetupAndStart(void) {
|
|
|
|
if (!HttpCheckPriviledgedAccess()) { return; }
|
|
|
|
|
|
|
|
AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_HTTP D_GPIO_VIEWER));
|
|
|
|
|
2024-01-12 15:54:24 +00:00
|
|
|
if (!GV.active) { // WebServer not started
|
2024-01-09 13:58:33 +00:00
|
|
|
GVBegin();
|
|
|
|
}
|
|
|
|
|
|
|
|
char redirect[100];
|
|
|
|
snprintf_P(redirect, sizeof(redirect), PSTR("http://%s:" STR(GV_PORT) "/"), WiFi.localIP().toString().c_str());
|
|
|
|
Webserver->sendHeader(F("Location"), redirect);
|
|
|
|
Webserver->send(303);
|
|
|
|
}
|
|
|
|
#endif // USE_WEBSERVER
|
|
|
|
|
2024-01-06 16:54:23 +00:00
|
|
|
/*********************************************************************************************\
|
|
|
|
* Interface
|
|
|
|
\*********************************************************************************************/
|
|
|
|
|
|
|
|
bool Xdrv121(uint32_t function) {
|
|
|
|
bool result = false;
|
|
|
|
|
2024-01-09 13:58:33 +00:00
|
|
|
switch (function) {
|
2024-01-09 16:08:13 +00:00
|
|
|
case FUNC_COMMAND:
|
|
|
|
result = DecodeCommand(kGVCommands, GVCommand);
|
|
|
|
break;
|
2024-01-09 13:58:33 +00:00
|
|
|
#ifdef USE_WEBSERVER
|
|
|
|
case FUNC_WEB_ADD_MANAGEMENT_BUTTON:
|
|
|
|
if (XdrvMailbox.index) {
|
|
|
|
XdrvMailbox.index++;
|
|
|
|
} else {
|
|
|
|
WSContentSend_PD(HTTP_BTN_MENU_GV);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case FUNC_WEB_ADD_HANDLER:
|
|
|
|
Webserver->on(PSTR("/" WEB_HANDLE_GV), GVSetupAndStart);
|
|
|
|
break;
|
|
|
|
#endif // USE_WEBSERVER
|
|
|
|
}
|
2024-01-06 16:54:23 +00:00
|
|
|
if (GV.active) {
|
|
|
|
switch (function) {
|
|
|
|
case FUNC_LOOP:
|
|
|
|
if (GV.WebServer) { GV.WebServer->handleClient(); }
|
|
|
|
break;
|
|
|
|
case FUNC_EVERY_100_MSECOND:
|
2024-01-10 14:51:19 +00:00
|
|
|
if (GV.sse_ready && (100 == GV.sampling)) {
|
|
|
|
GVMonitorTask();
|
|
|
|
}
|
2024-01-06 16:54:23 +00:00
|
|
|
break;
|
|
|
|
case FUNC_ACTIVE:
|
|
|
|
result = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif // USE_GPIO_VIEWER
|