From af9d4cadd8ef524d3156e9e509ab935d87bf572c Mon Sep 17 00:00:00 2001
From: Theo Arends <11044339+arendst@users.noreply.github.com>
Date: Sat, 27 Jan 2024 23:58:00 +0100
Subject: [PATCH] GPIO Viewer update from 1.0.7 to 1.5.0
---
tasmota/html_compressed/HTTP_GV_PAGE_150.h | 25 +++
tasmota/html_uncompressed/HTTP_GV_PAGE_150.h | 24 +++
tasmota/my_user_config.h | 3 +-
tasmota/tasmota_support/support_esp32.ino | 28 +++
tasmota/tasmota_support/support_esp8266.ino | 62 +++++++
.../xdrv_121_gpioviewer.ino | 163 ++++++++++++++----
6 files changed, 273 insertions(+), 32 deletions(-)
create mode 100644 tasmota/html_compressed/HTTP_GV_PAGE_150.h
create mode 100644 tasmota/html_uncompressed/HTTP_GV_PAGE_150.h
diff --git a/tasmota/html_compressed/HTTP_GV_PAGE_150.h b/tasmota/html_compressed/HTTP_GV_PAGE_150.h
new file mode 100644
index 000000000..b0200b768
--- /dev/null
+++ b/tasmota/html_compressed/HTTP_GV_PAGE_150.h
@@ -0,0 +1,25 @@
+/////////////////////////////////////////////////////////////////////
+// compressed by tools/unishox/compress-html-uncompressed.py
+/////////////////////////////////////////////////////////////////////
+
+const size_t HTTP_GV_PAGE_SIZE = 465;
+const char HTTP_GV_PAGE_COMPRESSED[] PROGMEM = "\x3D\x0F\xE1\x10\x98\x1D\x19\x0C\x64\x85\x50\xD0\x8F\xC3\xD0\x55\x0D\x09\x05\x7C"
+ "\x3C\x7C\x3E\xDF\x1F\x67\xE1\xE8\x29\xD8\x87\xE1\xE9\x5B\x41\x33\xF0\xFA\xF2\x3A"
+ "\xD1\x0F\x10\xC2\xD2\xC4\x46\x7A\xC7\xDF\xCF\x43\xB0\x10\x6A\x1E\x83\x5D\x5A\x0C"
+ "\x55\xFF\xCE\x8F\x87\xD9\x0B\x2A\x2B\x23\x07\x59\xCF\x9F\x67\xE1\xE8\x43\xAF\xCD"
+ "\x0A\xFB\x30\x7C\x3E\xCF\xAF\x1F\x67\xE1\xE8\x16\xF1\xA2\xFB\x08\xF8\x7D\xE8\x79"
+ "\xC7\xD8\x21\x57\x31\x66\x7A\x1E\x71\xD3\x43\xC3\xEC\x10\xB2\x6E\xB1\xAC\xF8\x7D"
+ "\x99\xEB\x1F\x19\x9F\xA3\xED\x07\x9D\x4F\xA8\xF8\x7D\x8F\xE8\x94\x28\xF8\x23\x33"
+ "\xD0\xD9\xD6\x3F\xA2\x50\xA3\xA9\x6F\x6D\x6D\x84\x75\xF8\x3B\x09\x9F\x0E\x43\xA3"
+ "\x3E\xCF\xC3\xD0\xBC\x1F\xF4\x65\x2A\x2B\x32\x18\xCF\x87\xD8\xDC\x10\x58\x4C\xFB"
+ "\x41\xFF\x3D\xEC\xFE\x8F\x6F\x2F\x7C\x33\xE1\xF6\x43\xC4\x30\xB4\xB0\x10\x78\x25"
+ "\x23\x31\x6C\xE8\x6B\xF1\xF6\x7E\x1E\x87\x60\x20\xE2\xC0\x8F\xC0\x47\xA8\xC8\x27"
+ "\xE1\x4D\xD0\x24\xE3\xE0\x83\xC0\xD5\xFB\xCE\xBC\x76\x0D\xBD\xE3\xA0\x7E\xF1\xF6"
+ "\x7E\x1E\x87\x60\x20\xF1\x1C\x3B\x04\x32\x3F\x0F\x41\x34\xCD\x68\x87\xC3\xEE\xC6"
+ "\x0C\x3E\xCF\xC3\xD0\xEC\x13\x4C\xC1\x0F\x79\x3F\x07\xF7\x84\xC1\xF3\xA0\xF0\xCD"
+ "\xC3\xF7\xE7\x55\xBC\x3D\xE4\x7C\x47\xB7\x46\x1E\x67\xD9\xF5\xE3\xEC\xEA\x19\x9F"
+ "\xA3\xCC\xFB\x3E\x84\x3E\xCE\xA3\x1F\x6C\xBC\x68\xE8\x31\x45\xFB\x1A\x79\x9F\x67"
+ "\xD0\x88\x8D\x08\x71\xF6\x7B\x8F\x2A\x01\x8B\xC0\x49\xD8\x08\x3A\x49\xD8\x2A\x2B"
+ "\x86\x84\x7E\x1B";
+
+#define HTTP_GV_PAGE Decompress(HTTP_GV_PAGE_COMPRESSED,HTTP_GV_PAGE_SIZE).c_str()
\ No newline at end of file
diff --git a/tasmota/html_uncompressed/HTTP_GV_PAGE_150.h b/tasmota/html_uncompressed/HTTP_GV_PAGE_150.h
new file mode 100644
index 000000000..8fed388bd
--- /dev/null
+++ b/tasmota/html_uncompressed/HTTP_GV_PAGE_150.h
@@ -0,0 +1,24 @@
+const char HTTP_GV_PAGE[] PROGMEM =
+ ""
+ ""
+ "
"
+ "%s - GPIO Viewer" // SettingsTextEscaped(SET_DEVICENAME).c_str()
+ ""
+ "" // GV_BASE_URL
+ ""
+ ""
+ ""
+ ""
+ ""
+ ""
+ ""
+ ""
+ ""
+ "";
diff --git a/tasmota/my_user_config.h b/tasmota/my_user_config.h
index 93da42e15..ce1a1f3f2 100644
--- a/tasmota/my_user_config.h
+++ b/tasmota/my_user_config.h
@@ -470,8 +470,7 @@
#define USE_ENHANCED_GUI_WIFI_SCAN // Enable Wi-Fi scan output with BSSID (+0k5 code)
// #define USE_WEBSEND_RESPONSE // Enable command WebSend response message (+1k code)
// #define USE_WEBGETCONFIG // Enable restoring config from external webserver (+0k6)
-// #define USE_GPIO_VIEWER // Enable GPIO Viewer to see realtime GPIO states (+4k code)
-// #define GV_BASE_URL "https://thelastoutpostworkshop.github.io/microcontroller_devkit/gpio_viewer/assets/"
+// #define USE_GPIO_VIEWER // Enable GPIO Viewer to see realtime GPIO states (+6k code)
// #define GV_SAMPLING_INTERVAL 100 // [GvSampling] milliseconds - Use Tasmota Scheduler (100) or Ticker (20..99,101..1000)
#define USE_EMULATION_HUE // Enable Hue Bridge emulation for Alexa (+14k code, +2k mem common)
#define USE_EMULATION_WEMO // Enable Belkin WeMo emulation for Alexa (+6k code, +2k mem common)
diff --git a/tasmota/tasmota_support/support_esp32.ino b/tasmota/tasmota_support/support_esp32.ino
index f3e362ee3..365f4d0f7 100644
--- a/tasmota/tasmota_support/support_esp32.ino
+++ b/tasmota/tasmota_support/support_esp32.ino
@@ -501,6 +501,10 @@ uint32_t ESP_getFreeSketchSpace(void) {
return ESP.getFreeSketchSpace();
}
+uint32_t ESP_getHeapSize(void) {
+ return ESP.getHeapSize();
+}
+
uint32_t ESP_getFreeHeap(void) {
// ESP_getFreeHeap() returns also IRAM which we don't use
return heap_caps_get_free_size(MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT);
@@ -558,6 +562,18 @@ uint8_t* FlashDirectAccess(void) {
return data;
}
+uint32_t ESP_getPsramSize(void) {
+ return ESP.getPsramSize();
+}
+
+uint32_t ESP_getFreePsram(void) {
+ return ESP.getFreePsram();
+}
+
+uint32_t ESP_getMaxAllocPsram(void) {
+ return ESP.getMaxAllocPsram();
+}
+
extern "C" {
#if ESP_IDF_VERSION_MAJOR >= 5
// bool IRAM_ATTR __attribute__((pure)) esp_psram_is_initialized(void)
@@ -948,6 +964,18 @@ String GetCodeCores(void) {
#endif
}
+uint32_t ESP_getChipCores(void) {
+ return ESP.getChipCores();
+}
+
+uint32_t ESP_getChipRevision(void) {
+ return ESP.getChipRevision();
+}
+
+String ESP_getEfuseMac(void) {
+ return String(ESP.getEfuseMac());
+}
+
/*********************************************************************************************\
* High entropy hardware random generator
* Thanks to DigitalAlchemist
diff --git a/tasmota/tasmota_support/support_esp8266.ino b/tasmota/tasmota_support/support_esp8266.ino
index 00df1731b..d21184d74 100644
--- a/tasmota/tasmota_support/support_esp8266.ino
+++ b/tasmota/tasmota_support/support_esp8266.ino
@@ -85,6 +85,10 @@ uint32_t ESP_getSketchSize(void) {
return ESP.getSketchSize();
}
+uint32_t ESP_getHeapSize(void) {
+ return 32768; // Using default heap (No PIO_FRAMEWORK_ARDUINO_MMU_CACHE16_IRAM48_SECHEAP_SHARED)
+}
+
uint32_t ESP_getFreeHeap(void) {
return ESP.getFreeHeap();
}
@@ -98,6 +102,10 @@ float ESP_getFreeHeap1024(void) {
}
*/
+uint32_t ESP_getMaxAllocHeap(void) {
+ return ESP.getFreeHeap();
+}
+
uint32_t ESP_getFlashChipId(void) {
return ESP.getFlashChipId();
}
@@ -110,6 +118,18 @@ uint32_t ESP_getFlashChipSize(void) {
return ESP.getFlashChipSize();
}
+uint32_t ESP_getPsramSize(void) {
+ return 0;
+}
+
+uint32_t ESP_getFreePsram(void) {
+ return 0;
+}
+
+uint32_t ESP_getMaxAllocPsram(void) {
+ return 0;
+}
+
void ESP_Restart(void) {
// ESP.restart(); // This results in exception 3 on restarts on core 2.3.0
ESP.reset();
@@ -197,6 +217,48 @@ String GetCodeCores(void) {
return F("");
}
+uint32_t ESP_getChipCores(void) {
+ return 1;
+}
+
+uint32_t ESP_getChipRevision(void) {
+ return 1;
+}
+
+String ESP_getEfuseMac(void) {
+ uint32_t mac0 = *(uint32_t*)(0x3FF00050);
+ uint32_t mac1 = *(uint32_t*)(0x3FF00054);
+ uint32_t mac3 = *(uint32_t*)(0x3FF0005C);
+
+ uint32_t mach = 0;
+ uint32_t macl = 0;
+ if (mac3 != 0) {
+ mach = ((mac3 >> 16) & 0xFF) << 16;
+ mach |= ((mac3 >> 8) & 0xFF) << 8;
+ mach |= mac3 & 0xFF;
+ }
+ else if (((mac1 >> 16) & 0xFF) == 0) {
+ mach = 0x18FE34;
+ }
+ else if (((mac1 >> 16) & 0xFF) == 1) {
+ mach = 0xACD074;
+ }
+ String macStr = "";
+ if (mach > 0) {
+ macl = ((mac1 >> 8) & 0xFF) << 16;
+ macl |= (mac1 & 0xFF) << 8;
+ macl |= (mac0 >> 24) & 0xFF;
+
+ uint64_t maca = ((uint64_t)mach << 24) | macl;
+ // Need uint64ToString with base 10 as ESP8266 WStrings does not support uint64_t
+ while (maca > 0) {
+ macStr = String((uint32_t)(maca % 10), 10) + macStr;
+ maca /= 10;
+ }
+ }
+ return String(macStr);
+}
+
/*********************************************************************************************\
* High entropy hardware random generator
* Thanks to DigitalAlchemist
diff --git a/tasmota/tasmota_xdrv_driver/xdrv_121_gpioviewer.ino b/tasmota/tasmota_xdrv_driver/xdrv_121_gpioviewer.ino
index 96281511c..e7f7508e9 100644
--- a/tasmota/tasmota_xdrv_driver/xdrv_121_gpioviewer.ino
+++ b/tasmota/tasmota_xdrv_driver/xdrv_121_gpioviewer.ino
@@ -17,6 +17,15 @@
#define XDRV_121 121
+//#define GV_INPUT_DETECTION // Report type of digital input
+
+#define GV_USE_ESPINFO // Provide ESP info
+#ifdef ESP32
+#ifndef GV_USE_ESPINFO
+#define GV_USE_ESPINFO // Provide ESP info
+#endif
+#endif
+
#ifndef GV_PORT
#define GV_PORT 5557 // SSE webserver port
#endif
@@ -27,29 +36,15 @@
#define GV_KEEP_ALIVE 1000 // milliseconds - If no activity after this do a heap size event anyway
#ifndef GV_BASE_URL
-#define GV_BASE_URL "https://thelastoutpostworkshop.github.io/microcontroller_devkit/gpio_viewer/assets/"
+#define GV_BASE_URL "https://thelastoutpostworkshop.github.io/microcontroller_devkit/gpio_viewer_1_5/"
#endif
-/*
-#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
-*/
-const char *GVRelease = "1.0.7";
+const char *GVRelease = "1.5.0";
#ifdef USE_UNISHOX_COMPRESSION
- #include "./html_compressed/HTTP_GV_PAGE.h"
+ #include "./html_compressed/HTTP_GV_PAGE_150.h"
#else
- #include "./html_uncompressed/HTTP_GV_PAGE.h"
+ #include "./html_uncompressed/HTTP_GV_PAGE_150.h"
#endif
const char HTTP_GV_EVENT[] PROGMEM =
@@ -62,7 +57,12 @@ const char HTTP_GV_EVENT[] PROGMEM =
enum GVPinTypes {
GV_DigitalPin = 0,
GV_PWMPin = 1,
- GV_AnalogPin = 2
+ GV_AnalogPin = 2,
+#ifdef GV_INPUT_DETECTION
+ GV_InputPin = 3,
+ GV_InputPullUp = 4,
+ GV_InputPullDn = 5
+#endif // GV_INPUT_DETECTION
};
struct {
@@ -78,6 +78,26 @@ struct {
bool sse_ready;
} GV;
+#ifdef GV_INPUT_DETECTION
+
+int GetPinMode(uint8_t pin) {
+#ifdef ESP8266
+ if (pin > MAX_GPIO_PIN -2) { return -1; } // Skip GPIO16 and Analog0
+#endif // ESP8266
+#ifdef ESP32
+ if (pin > MAX_GPIO_PIN) { return -1; }
+#endif // ESP32
+
+ uint32_t bit = digitalPinToBitMask(pin);
+ uint32_t port = digitalPinToPort(pin);
+ volatile uint32_t *reg = portModeRegister(port);
+ if (*reg & bit) { return OUTPUT; } // ESP8266 = 0x01, ESP32 = 0x03
+ volatile uint32_t *out = portOutputRegister(port);
+ return ((*out & bit) ? INPUT_PULLUP : INPUT); // ESP8266 = 0x02 : 0x00, ESP32 = 0x05 : x01
+}
+
+#endif // GV_INPUT_DETECTION
+
void GVStop(void) {
GV.sse_ready = false;
GV.ticker.detach();
@@ -100,6 +120,9 @@ void GVBegin(void) {
GV.WebServer->on("/", GVHandleRoot);
GV.WebServer->on("/release", GVHandleRelease);
GV.WebServer->on("/free_psram", GVHandleFreePSRam);
+ GV.WebServer->on("/sampling", GVHandleSampling);
+ GV.WebServer->on("/espinfo", GVHandleEspInfo);
+ GV.WebServer->on("/partition", GVHandlePartition);
GV.WebServer->on("/events", GVHandleEvents);
GV.WebServer->begin();
@@ -112,24 +135,21 @@ void GVHandleRoot(void) {
char* content = ext_snprintf_malloc_P(HTTP_GV_PAGE,
SettingsTextEscaped(SET_DEVICENAME).c_str(),
GV_BASE_URL,
- GV_PORT,
WiFi.localIP().toString().c_str(),
- WiFi.localIP().toString().c_str(), GV_PORT,
- GV.sampling,
-#ifdef ESP32
- ESP.getPsramSize() / 1024,
-#else
- 0,
-#endif // ESP32
+ GV_PORT,
ESP_getFreeSketchSpace() / 1024);
if (content == nullptr) { return; } // Avoid crash
GV.WebServer->send_P(200, "text/html", content);
free(content);
}
+void GVWebserverSendJson(String &jsonResponse) {
+ GV.WebServer->send(200, "application/json", jsonResponse);
+}
+
void GVHandleRelease(void) {
String jsonResponse = "{\"release\":\"" + String(GVRelease) + "\"}";
- GV.WebServer->send(200, "application/json", jsonResponse);
+ GVWebserverSendJson(jsonResponse);
}
void GVHandleFreePSRam(void) {
@@ -140,7 +160,85 @@ void GVHandleFreePSRam(void) {
} else
#endif
jsonResponse += "No PSRAM\"}";
- GV.WebServer->send(200, "application/json", jsonResponse);
+ GVWebserverSendJson(jsonResponse);
+}
+
+void GVHandleSampling(void) {
+ String jsonResponse = "{\"sampling\": \"" + String(GV.sampling) + "\"}";
+ GVWebserverSendJson(jsonResponse);
+}
+
+void GVHandleEspInfo(void) {
+#ifdef GV_USE_ESPINFO
+ const FlashMode_t flashMode = ESP.getFlashChipMode(); // enum
+
+ String jsonResponse = "{\"chip_model\":\"" + GetDeviceHardware();
+ jsonResponse += "\",\"cores_count\":\"" + String(ESP_getChipCores());
+ jsonResponse += "\",\"chip_revision\":\"" + String(ESP_getChipRevision());
+ jsonResponse += "\",\"cpu_frequency\":\"" + String(ESP.getCpuFreqMHz());
+ jsonResponse += "\",\"cycle_count\":" + String(ESP.getCycleCount());
+ jsonResponse += ",\"mac\":\"" + ESP_getEfuseMac();
+ jsonResponse += "\",\"flash_mode\":" + String(flashMode);
+#ifdef ESP8266
+ jsonResponse += ",\"flash_chip_size\":" + String(ESP.getFlashChipRealSize());
+#else
+ jsonResponse += ",\"flash_chip_size\":" + String(ESP.getFlashChipSize());
+#endif
+ jsonResponse += ",\"flash_chip_speed\":" + String(ESP.getFlashChipSpeed());
+ jsonResponse += ",\"heap_size\":" + String(ESP_getHeapSize());
+ jsonResponse += ",\"heap_max_alloc\":" + String(ESP_getMaxAllocHeap());
+ jsonResponse += ",\"psram_size\":" + String(ESP_getPsramSize());
+ jsonResponse += ",\"free_psram\":" + String(ESP_getFreePsram());
+ jsonResponse += ",\"psram_max_alloc\":" + String(ESP_getMaxAllocPsram());
+ jsonResponse += ",\"free_heap\":" + String(ESP_getFreeHeap());
+ jsonResponse += ",\"up_time\":\"" + String(millis());
+ jsonResponse += "\",\"sketch_size\":" + String(ESP_getSketchSize());
+ jsonResponse += ",\"free_sketch\":" + String(ESP_getFreeSketchSpace());
+ jsonResponse += "}";
+#else
+ String jsonResponse = "{\"chip_model\":\"" + GetDeviceHardware() + "\"}";
+#endif // GV_USE_ESPINFO
+ GVWebserverSendJson(jsonResponse);
+}
+
+void GVHandlePartition(void) {
+ String jsonResponse = "["; // Start of JSON array
+#ifdef ESP32
+ bool firstEntry = true; // Used to format the JSON array correctly
+
+ esp_partition_iterator_t iter = esp_partition_find(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_ANY, NULL);
+// esp_partition_iterator_t iter = esp_partition_find(ESP_PARTITION_TYPE_ANY, ESP_PARTITION_SUBTYPE_ANY, NULL);
+
+ // Loop through partitions
+ while (iter != NULL) {
+ const esp_partition_t *partition = esp_partition_get(iter);
+
+ // Add comma before the next entry if it's not the first
+ if (!firstEntry)
+ {
+ jsonResponse += ",";
+ }
+ firstEntry = false;
+
+ // Append partition information in JSON format
+ jsonResponse += "{";
+ jsonResponse += "\"label\":\"" + String(partition->label) + "\",";
+ jsonResponse += "\"type\":" + String(partition->type) + ",";
+ jsonResponse += "\"subtype\":" + String(partition->subtype) + ",";
+ jsonResponse += "\"address\":\"0x" + String(partition->address, HEX) + "\",";
+ jsonResponse += "\"size\":" + String(partition->size);
+ jsonResponse += "}";
+
+ // Move to next partition
+ iter = esp_partition_next(iter);
+ }
+
+ // Clean up the iterator
+ esp_partition_iterator_release(iter);
+#endif // ESP32
+
+ jsonResponse += "]"; // End of JSON array
+ GVWebserverSendJson(jsonResponse);
}
void GVHandleEvents(void) {
@@ -240,7 +338,6 @@ void GVMonitorTask(void) {
else {
// Read digital GPIO
- pintype = GV_DigitalPin;
int value = digitalRead(pin);
originalValue = value;
if (value == 1) {
@@ -248,6 +345,12 @@ void GVMonitorTask(void) {
// } else {
// currentState = 0;
}
+#ifdef GV_INPUT_DETECTION
+ int pin_mode = GetPinMode(pin);
+ pintype = (INPUT == pin_mode) ? GV_InputPin : (INPUT_PULLUP == pin_mode) ? GV_InputPullUp : GV_DigitalPin;
+#else
+ pintype = GV_DigitalPin;
+#endif
}
if (originalValue != GV.lastPinStates[pin]) {