\n";
+
+ html += "";
+
+ // Image
+ html += "
\n";
+ html += "
";
+ html += "
![Board Image]()
\n";
+
+ html += "
";
+
+ html += "
";
+
+ // Append the script variables
+ String portScript = "";
+ html += portScript;
+
+ String eventSource = "";
+ html += eventSource;
+
+ String ip = "";
+ html += ip;
+
+ String sampling = "";
+ html += sampling;
+
+ html += "";
+
+ html += "";
+ return html;
+ }
+
+ void resetStatePins(void)
+ {
+ uint32_t originalValue;
+ pinTypes pintype;
+ AddLog(2, "IOV: GPIOViewer Connected, sampling interval is %lums", samplingInterval);
+
+ for (int i = 0; i < maxGPIOPins; i++)
+ {
+ lastPinStates[i] = readGPIO(i, &originalValue, &pintype);
+ }
+ }
+
+ // Monitor GPIO Values
+ void monitorTask()
+ {
+ uint32_t originalValue;
+ pinTypes pintype;
+ while (1)
+ {
+ String jsonMessage = "{";
+ bool hasChanges = false;
+
+ for (int i = 0; i < maxGPIOPins; i++)
+ {
+ int currentState = readGPIO(i, &originalValue, &pintype);
+
+ if (originalValue != lastPinStates[i])
+ {
+ if (hasChanges)
+ {
+ jsonMessage += ", ";
+ }
+ jsonMessage += "\"" + String(i) + "\": {\"s\": " + currentState + ", \"v\": " + originalValue + ", \"t\": " + pintype + "}";
+ lastPinStates[i] = currentState;
+ hasChanges = true;
+ }
+ }
+
+ jsonMessage += "}";
+
+ if (hasChanges)
+ {
+ sendGPIOStates(jsonMessage);
+ }
+
+ size_t heap = esp_get_free_heap_size();
+ if (heap != freeHeap)
+ {
+ freeHeap = heap;
+ events->send(formatBytes(freeHeap).c_str(), "free_heap", millis());
+ }
+
+ vTaskDelay(pdMS_TO_TICKS(samplingInterval));
+ }
+ }
+
+ int getLedcChannelForPin(int pin)
+ {
+ for (int i = 0; i < ledcChannelPinCount; i++)
+ {
+ if (ledcChannelPin[i][0] == pin)
+ {
+ return ledcChannelPin[i][1];
+ }
+ }
+ return -1; // Pin not found, return -1 to indicate no channel is associated
+ }
+ int getChannelResolution(int channel)
+ {
+ for (int i = 0; i < ledcChannelResolutionCount; i++)
+ {
+ if (ledcChannelResolution[i][0] == channel)
+ {
+ return ledcChannelResolution[i][1];
+ }
+ }
+ return -1; // Pin not found, return -1 to indicate no channel is associated
+ }
+
+ int readGPIO(int gpioNum, uint32_t *originalValue, pinTypes *pintype)
+ {
+ int channel = getLedcChannelForPin(gpioNum);
+ int value;
+ if (channel != -1)
+ {
+ // This is a PWM Pin
+ value = mapLedcReadTo8Bit(channel, originalValue);
+ *pintype = PWMPin;
+ return value;
+ }
+ uint8_t analogChannel = analogGetChannel(gpioNum);
+ if (analogChannel != 0 && analogChannel != 255)
+ {
+ // This is an analog pin
+ // Serial.printf("A Pin %d value=%d,channel=%d\n", gpioNum, value,analogChannel);
+
+ value = mapLedcReadTo8Bit(analogChannel, originalValue);
+ *pintype = analogPin;
+ return value;
+ }
+ else
+ {
+ // This is a digital pin
+ *pintype = digitalPin;
+ value = digitalRead(gpioNum);
+ *originalValue = value;
+ if (value == 1)
+ {
+ return 256;
+ }
+ return 0;
+ }
+ }
+
+ int mapLedcReadTo8Bit(int channel, uint32_t *originalValue)
+ {
+ uint32_t maxDutyCycle = (1 << getChannelResolution(channel)) - 1;
+ *originalValue = ledcRead(channel);
+ return map(*originalValue, 0, maxDutyCycle, 0, 255);
+ }
+
+ void sendGPIOStates(const String &states)
+ {
+ events->send(states.c_str(), "gpio-state", millis());
+ }
+
+ String formatBytes(size_t bytes)
+ {
+ if (bytes < 1024)
+ {
+ return String(bytes) + " B";
+ }
+ else if (bytes < (1024 * 1024))
+ {
+ return String(bytes / 1024.0, 2) + " KB";
+ }
+ else
+ {
+ return String(bytes / 1024.0 / 1024.0, 2) + " MB";
+ }
+ }
+};
\ No newline at end of file