Skip to content

Instantly share code, notes, and snippets.

@fxprime
Created September 30, 2025 11:56
Show Gist options
  • Select an option

  • Save fxprime/1fe9d3d1fe9e2424e74cab5d520fe23a to your computer and use it in GitHub Desktop.

Select an option

Save fxprime/1fe9d3d1fe9e2424e74cab5d520fe23a to your computer and use it in GitHub Desktop.
/*
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