Created
September 30, 2025 11:56
-
-
Save fxprime/1fe9d3d1fe9e2424e74cab5d520fe23a to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /* | |
| ESP8266 Board Tester (I/O + WiFi) | |
| - Arduino core for ESP8266 | |
| - Features: | |
| * Serial menu for quick tests | |
| * Simple web UI to view digital inputs & ADC, toggle outputs | |
| * WiFi scan, connect (via Serial), start SoftAP | |
| Pin notes: | |
| - Be careful with GPIO0, GPIO2, GPIO15: they affect boot. Use them carefully. | |
| - A0 is ADC (analogRead) | |
| - Modify pin assignments below to match your board | |
| Author: ChatGPT (GPT-5 Thinking mini) | |
| */ | |
| #include <Arduino.h> | |
| #include <ESP8266WiFi.h> | |
| #include <ESP8266WebServer.h> | |
| // ----- Configure pins (change if your board differs) ----- | |
| // Prefer pins that won't break boot mode: | |
| // Common safe pins on NodeMCU: D1(GPIO5), D2(GPIO4), D5(GPIO14), D6(GPIO12), D7(GPIO13) | |
| const uint8_t OUT_PINS[] = {5, 4, 14, 12, 13}; // digital outputs (GPIO5=D1, GPIO4=D2, ...) | |
| const uint8_t OUT_COUNT = sizeof(OUT_PINS) / sizeof(OUT_PINS[0]); | |
| const uint8_t IN_PINS[] = {16, 0, 2}; // digital inputs (GPIO16=D0, GPIO0, GPIO2) - watch boot pins | |
| const uint8_t IN_COUNT = sizeof(IN_PINS) / sizeof(IN_PINS[0]); | |
| const uint8_t PWM_PIN = 15; // (GPIO15) if you want PWM; watch boot config - change if needed | |
| const uint8_t ADC_PIN = A0; // analog input | |
| // Webserver | |
| ESP8266WebServer server(80); | |
| // WiFi state | |
| bool wifiConnected = false; | |
| // Debounce state for inputs | |
| unsigned long lastDebounceTime[10]; | |
| bool lastStableState[10]; | |
| const unsigned long debounceDelay = 30; // ms | |
| // Helper: pin index lookups | |
| int findOutIndex(uint8_t gpio) { | |
| for (uint8_t i = 0; i < OUT_COUNT; ++i) if (OUT_PINS[i] == gpio) return i; | |
| return -1; | |
| } | |
| int findInIndex(uint8_t gpio) { | |
| for (uint8_t i = 0; i < IN_COUNT; ++i) if (IN_PINS[i] == gpio) return i; | |
| return -1; | |
| } | |
| // ----- Web UI HTML (simple) ----- | |
| const char INDEX_HTML[] PROGMEM = R"rawliteral( | |
| <!doctype html> | |
| <html> | |
| <head> | |
| <meta charset="utf-8"> | |
| <title>ESP8266 I/O Tester</title> | |
| <meta name="viewport" content="width=device-width,initial-scale=1"> | |
| <style> | |
| body { font-family: Arial, Helvetica, sans-serif; margin:12px; } | |
| .card { border:1px solid #ccc; padding:10px; margin-bottom:10px; border-radius:6px;} | |
| button { padding:8px 12px; margin:4px; } | |
| .pin { display:flex; align-items:center; justify-content:space-between; margin:6px 0;} | |
| </style> | |
| </head> | |
| <body> | |
| <h2>ESP8266 I/O Tester</h2> | |
| <div class="card"> | |
| <strong>WiFi</strong> | |
| <div id="wifiInfo">loading...</div> | |
| <button onclick="fetch('/scan').then(()=>alert('scan started on server console'))">Start Scan (server console)</button> | |
| </div> | |
| <div class="card"> | |
| <strong>Digital Outputs</strong> | |
| <div id="outs">loading...</div> | |
| </div> | |
| <div class="card"> | |
| <strong>Digital Inputs</strong> | |
| <div id="ins">loading...</div> | |
| </div> | |
| <div class="card"> | |
| <strong>Analog</strong> | |
| <div>ADC (A0): <span id="adc">-</span></div> | |
| </div> | |
| <script> | |
| function update() { | |
| fetch('/status').then(r=>r.json()).then(data=>{ | |
| document.getElementById('wifiInfo').innerText = data.wifi; | |
| // outputs | |
| let outs = document.getElementById('outs'); | |
| outs.innerHTML = ''; | |
| data.outputs.forEach(function(o){ | |
| let d = document.createElement('div'); | |
| d.className = 'pin'; | |
| d.innerHTML = '<div>GPIO' + o.gpio + ' (out)</div><div><button onclick="toggle('+o.gpio+')">'+(o.level? 'ON':'OFF')+'</button></div>'; | |
| outs.appendChild(d); | |
| }); | |
| // inputs | |
| let ins = document.getElementById('ins'); | |
| ins.innerHTML = ''; | |
| data.inputs.forEach(function(i){ | |
| let d = document.createElement('div'); | |
| d.className = 'pin'; | |
| d.innerHTML = '<div>GPIO' + i.gpio + ' (in)</div><div>' + (i.level? 'HIGH':'LOW') + '</div>'; | |
| ins.appendChild(d); | |
| }); | |
| document.getElementById('adc').innerText = data.adc; | |
| }); | |
| } | |
| function toggle(gpio) { | |
| fetch('/toggle?gpio=' + gpio).then(()=>update()); | |
| } | |
| setInterval(update, 1000); | |
| update(); | |
| </script> | |
| </body> | |
| </html> | |
| )rawliteral"; | |
| // ----- Setup and handlers ----- | |
| void handleRoot() { | |
| server.send_P(200, "text/html", INDEX_HTML); | |
| } | |
| void handleStatus() { | |
| // Build JSON status | |
| String s = "{"; | |
| s += "\"wifi\":\""; | |
| if (WiFi.status() == WL_CONNECTED) { | |
| s += WiFi.SSID(); | |
| s += " ("; | |
| s += WiFi.localIP().toString(); | |
| s += ")"; | |
| } else if (WiFi.getMode() == WIFI_AP) { | |
| s += "SoftAP "; | |
| s += WiFi.softAPIP().toString(); | |
| } else { | |
| s += "Not connected"; | |
| } | |
| s += "\","; | |
| // outputs | |
| s += "\"outputs\":["; | |
| for (uint8_t i=0;i<OUT_COUNT;i++){ | |
| uint8_t g = OUT_PINS[i]; | |
| s += "{\"gpio\":" + String(g) + ",\"level\":" + String(digitalRead(g) ? 1:0) + "}"; | |
| if (i < OUT_COUNT-1) s += ","; | |
| } | |
| s += "],"; | |
| // inputs | |
| s += "\"inputs\":["; | |
| for (uint8_t i=0;i<IN_COUNT;i++){ | |
| uint8_t g = IN_PINS[i]; | |
| s += "{\"gpio\":" + String(g) + ",\"level\":" + String(digitalRead(g) ? 1:0) + "}"; | |
| if (i < IN_COUNT-1) s += ","; | |
| } | |
| s += "],"; | |
| // ADC | |
| s += "\"adc\":" + String(analogRead(ADC_PIN)); | |
| s += "}"; | |
| server.send(200, "application/json", s); | |
| } | |
| void handleToggle() { | |
| if (!server.hasArg("gpio")) { | |
| server.send(400, "text/plain", "missing gpio"); | |
| return; | |
| } | |
| int gpio = server.arg("gpio").toInt(); | |
| int idx = findOutIndex(gpio); | |
| if (idx < 0) { | |
| server.send(400, "text/plain", "pin not configured as output"); | |
| return; | |
| } | |
| int level = digitalRead(gpio); | |
| digitalWrite(gpio, !level); | |
| server.send(200, "text/plain", "ok"); | |
| } | |
| void handleScan() { | |
| server.send(200, "text/plain", "scan started"); | |
| Serial.println("WiFi scan starting..."); | |
| int n = WiFi.scanNetworks(); | |
| Serial.printf("Found %d networks:\n", n); | |
| for (int i=0;i<n;i++){ | |
| Serial.printf("%d: %s (RSSI %d) %s\n", i+1, WiFi.SSID(i).c_str(), WiFi.RSSI(i), WiFi.encryptionType(i)==ENC_TYPE_NONE ? "OPEN":"SEC"); | |
| } | |
| WiFi.scanDelete(); | |
| } | |
| // ----- Serial menu helpers ----- | |
| void serialMenu(); | |
| void wifiConnectFromSerial(); | |
| // ----- Setup ----- | |
| void setup() { | |
| Serial.begin(115200); | |
| delay(50); | |
| Serial.println("\n=== ESP8266 I/O + WiFi Tester ==="); | |
| // Initialize outputs | |
| for (uint8_t i=0;i<OUT_COUNT;i++){ | |
| pinMode(OUT_PINS[i], OUTPUT); | |
| digitalWrite(OUT_PINS[i], LOW); | |
| } | |
| // Initialize inputs with pullups where appropriate (user can wire external pull-downs) | |
| for (uint8_t i=0;i<IN_COUNT;i++){ | |
| pinMode(IN_PINS[i], INPUT_PULLUP); | |
| lastDebounceTime[i] = 0; | |
| lastStableState[i] = digitalRead(IN_PINS[i]); | |
| } | |
| // PWM pin init (analogWrite works after pinMode OUTPUT) | |
| pinMode(PWM_PIN, OUTPUT); | |
| analogWriteRange(1023); // full range; analogWrite(0-1023) | |
| // WiFi default: station mode but not connected | |
| WiFi.mode(WIFI_STA); | |
| WiFi.disconnect(); // ensure clean state | |
| // Web server routes | |
| server.on("/", handleRoot); | |
| server.on("/status", handleStatus); | |
| server.on("/toggle", handleToggle); | |
| server.on("/scan", handleScan); | |
| server.begin(); | |
| Serial.println("Web server started. Connect to http://<esp-ip>/"); | |
| delay(100); | |
| // Show initial menu | |
| serialMenu(); | |
| } | |
| // debounce helper for inputs | |
| bool readDebounced(uint8_t inIndex) { | |
| uint8_t gpio = IN_PINS[inIndex]; | |
| bool reading = digitalRead(gpio); | |
| if (reading != lastStableState[inIndex]) { | |
| lastDebounceTime[inIndex] = millis(); | |
| } | |
| if ((millis() - lastDebounceTime[inIndex]) > debounceDelay) { | |
| // stable | |
| lastStableState[inIndex] = reading; | |
| } | |
| return lastStableState[inIndex]; | |
| } | |
| // ----- Main loop ----- | |
| unsigned long lastSerialCheck = 0; | |
| void loop() { | |
| server.handleClient(); | |
| // Print inputs occasionally | |
| static unsigned long lastPrint = 0; | |
| if (millis() - lastPrint > 1000) { | |
| lastPrint = millis(); | |
| Serial.print("[Inputs] "); | |
| for (uint8_t i=0;i<IN_COUNT;i++){ | |
| bool s = readDebounced(i); | |
| Serial.printf("GPIO%d:%d ", IN_PINS[i], s?1:0); | |
| } | |
| Serial.print(" ADC:"); | |
| Serial.println(analogRead(ADC_PIN)); | |
| } | |
| // Process serial menu commands (non-blocking) | |
| if (Serial.available()) { | |
| String cmd = Serial.readStringUntil('\n'); | |
| cmd.trim(); | |
| if (cmd.length()) { | |
| if (cmd.equalsIgnoreCase("menu") || cmd.equalsIgnoreCase("?")) { | |
| serialMenu(); | |
| } else if (cmd.equalsIgnoreCase("scan")) { | |
| Serial.println("Starting WiFi scan..."); | |
| int n = WiFi.scanNetworks(); | |
| Serial.printf("Found %d networks:\n", n); | |
| for (int i=0;i<n;i++){ | |
| Serial.printf("%d: %s (RSSI %d) %s\n", i+1, WiFi.SSID(i).c_str(), WiFi.RSSI(i), WiFi.encryptionType(i)==ENC_TYPE_NONE ? "OPEN":"SEC"); | |
| } | |
| WiFi.scanDelete(); | |
| } else if (cmd.startsWith("connect ")) { | |
| // format: connect SSID,PASSWORD (password optional for open) | |
| String arg = cmd.substring(8); | |
| arg.trim(); | |
| int comma = arg.indexOf(','); | |
| String ssid = arg; | |
| String pass = ""; | |
| if (comma >= 0) { | |
| ssid = arg.substring(0, comma); | |
| pass = arg.substring(comma+1); | |
| } | |
| ssid.trim(); pass.trim(); | |
| Serial.printf("Connecting to '%s' ...\n", ssid.c_str()); | |
| WiFi.begin(ssid.c_str(), pass.length()?pass.c_str():NULL); | |
| unsigned long start = millis(); | |
| while (WiFi.status() != WL_CONNECTED && millis() - start < 10000) { | |
| delay(200); | |
| Serial.print("."); | |
| } | |
| if (WiFi.status() == WL_CONNECTED) { | |
| Serial.printf("\nConnected! IP: %s\n", WiFi.localIP().toString().c_str()); | |
| } else { | |
| Serial.println("\nFailed to connect (timeout)."); | |
| } | |
| } else if (cmd.equalsIgnoreCase("ap")) { | |
| // start softAP | |
| String apName = "ESP8266-Test"; | |
| bool ok = WiFi.softAP(apName); | |
| if (ok) { | |
| Serial.printf("SoftAP started: %s - IP %s\n", apName.c_str(), WiFi.softAPIP().toString().c_str()); | |
| } else { | |
| Serial.println("Failed to start SoftAP"); | |
| } | |
| } else if (cmd.equalsIgnoreCase("outs")) { | |
| // toggle all outputs on/off sequence for quick test | |
| Serial.println("Toggling outputs ON->OFF"); | |
| for (uint8_t i=0;i<OUT_COUNT;i++) { | |
| digitalWrite(OUT_PINS[i], HIGH); | |
| } | |
| delay(500); | |
| for (uint8_t i=0;i<OUT_COUNT;i++) { | |
| digitalWrite(OUT_PINS[i], LOW); | |
| } | |
| Serial.println("Done."); | |
| } else if (cmd.startsWith("set ")) { | |
| // set GPIO:value e.g. set 5:1 | |
| String arg = cmd.substring(4); | |
| arg.trim(); | |
| int colon = arg.indexOf(':'); | |
| if (colon<0) { | |
| Serial.println("Usage: set <gpio>:<0|1>"); | |
| } else { | |
| int gpio = arg.substring(0,colon).toInt(); | |
| int val = arg.substring(colon+1).toInt(); | |
| int idx = findOutIndex(gpio); | |
| if (idx<0) { | |
| Serial.println("Pin not configured as output"); | |
| } else { | |
| digitalWrite(gpio, val?HIGH:LOW); | |
| Serial.printf("GPIO%d set to %d\n", gpio, val); | |
| } | |
| } | |
| } else if (cmd.startsWith("pwm ")) { | |
| // pwm <0-1023> | |
| String arg = cmd.substring(4); | |
| arg.trim(); | |
| int v = arg.toInt(); | |
| if (v<0) v=0; | |
| if (v>1023) v=1023; | |
| analogWrite(PWM_PIN, v); | |
| Serial.printf("PWM on GPIO%d = %d/1023\n", PWM_PIN, v); | |
| } else { | |
| Serial.println("Unknown command. Type 'menu' for options."); | |
| } | |
| delay(10); | |
| } | |
| } | |
| } | |
| // ----- Serial menu text ----- | |
| void serialMenu() { | |
| Serial.println("Commands:"); | |
| Serial.println(" menu - show this menu"); | |
| Serial.println(" scan - WiFi scan (prints to serial)"); | |
| Serial.println(" connect SSID,PASS - connect to WiFi (pass optional for open)"); | |
| Serial.println(" example: connect MySSID,mypassword"); | |
| Serial.println(" ap - start SoftAP (SSID: ESP8266-Test)"); | |
| Serial.println(" outs - quick toggle all outputs ON then OFF"); | |
| Serial.println(" set <gpio>:<0|1> - set output GPIO to 0 or 1. e.g. set 5:1"); | |
| Serial.println(" pwm <0-1023> - set PWM on configured PWM pin"); | |
| Serial.println("Web UI: http://<your-esp-ip>/ (or connect to SoftAP then visit 192.168.4.1)"); | |
| Serial.print("Configured outputs: "); | |
| for (uint8_t i=0;i<OUT_COUNT;i++){ Serial.print("GPIO"); Serial.print(OUT_PINS[i]); Serial.print(" "); } | |
| Serial.println(); | |
| Serial.print("Configured inputs: "); | |
| for (uint8_t i=0;i<IN_COUNT;i++){ Serial.print("GPIO"); Serial.print(IN_PINS[i]); Serial.print(" "); } | |
| Serial.println(); | |
| Serial.printf("PWM pin: GPIO%d ADC: A0\n", PWM_PIN); | |
| Serial.println("NOTE: Be careful with GPIO0,2,15 - they affect boot mode. See pin mapping before wiring."); | |
| Serial.println("-----------------------------------------------------------"); | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment