ESP32のI2CとI/Oのhttpサーバデバッガ
ESP32のI2C簡易デバッガを作成しました。
できること
・I2Cアレススキャナ
I2Cのアドレスをスキャンして今接続されているI2Cデバイスを確認する
・I/Oスキャナ
現在のI/O状態を表示する。(使用できるピンだけ)
ESP32にデータをダウンロードして
をアドレスバーに入力すると接続できます。
wifi設定を忘れないでください。
SSIDとPASSWORDが必要です。
ソース
#include <WiFi.h>
#include <WebServer.h>
#include <ESPmDNS.h>
#include <Wire.h>
// WiFi設定
const char* ssid = "SSID";
const char* password = "PASSWORD";
const char* hostname = "esp32-debug";
// Webサーバー
WebServer server(80);
// I2C設定(デフォルトピン: SDA=21, SCL=22)
#define SDA_PIN 21
#define SCL_PIN 22
// GPIO設定用の配列(使用可能なピン)
const int gpioPins[] = {2, 4, 5, 12, 13, 14, 15, 16, 17, 18, 19, 23, 25, 26, 27, 32, 33};
const int numPins = sizeof(gpioPins) / sizeof(gpioPins[0]);
// I2Cスキャン結果を保存
bool i2cDevices[128];
int i2cDeviceCount = 0;
// ===== I2Cアドレススキャン =====
void scanI2C() {
i2cDeviceCount = 0;
for (int i = 0; i < 128; i++) {
i2cDevices[i] = false;
}
Serial.println("I2Cスキャン開始...");
for (byte addr = 1; addr < 127; addr++) {
Wire.beginTransmission(addr);
byte error = Wire.endTransmission();
if (error == 0) {
i2cDevices[addr] = true;
i2cDeviceCount++;
Serial.printf("デバイス検出: 0x%02X\n", addr);
}
}
Serial.printf("検出数: %d台\n", i2cDeviceCount);
}
// ===== I2Cデータ読み取り =====
String readI2CData(byte addr, int numBytes) {
String result = "";
Wire.requestFrom(addr, numBytes);
int count = 0;
while (Wire.available() && count < numBytes) {
byte data = Wire.read();
char hex[4];
sprintf(hex, "%02X ", data);
result += hex;
count++;
}
return result.length() > 0 ? result : "データなし";
}
// ===== ルートページ(ダッシュボード) =====
void handleRoot() {
String html = "<!DOCTYPE html><html><head><meta charset='UTF-8'>";
html += "<meta name='viewport' content='width=device-width,initial-scale=1.0'>";
html += "<style>";
html += "body{font-family:Arial,sans-serif;margin:0;padding:20px;background:#f5f5f5;}";
html += "h1{color:#333;border-bottom:3px solid #4CAF50;padding-bottom:10px;}";
html += "h2{color:#555;margin-top:30px;background:#4CAF50;color:white;padding:10px;border-radius:5px;}";
html += ".container{max-width:1200px;margin:0 auto;background:white;padding:20px;border-radius:10px;box-shadow:0 2px 10px rgba(0,0,0,0.1);}";
html += ".section{margin:20px 0;padding:15px;border:1px solid #ddd;border-radius:5px;}";
html += "table{width:100%;border-collapse:collapse;margin:10px 0;}";
html += "th,td{padding:12px;text-align:left;border-bottom:1px solid #ddd;}";
html += "th{background:#4CAF50;color:white;}";
html += "tr:hover{background:#f5f5f5;}";
html += ".on{color:green;font-weight:bold;}";
html += ".off{color:red;}";
html += "button{padding:8px 15px;margin:2px;border:none;border-radius:4px;cursor:pointer;font-size:14px;}";
html += ".btn-on{background:#4CAF50;color:white;}.btn-on:hover{background:#45a049;}";
html += ".btn-off{background:#f44336;color:white;}.btn-off:hover{background:#da190b;}";
html += ".btn-input{background:#2196F3;color:white;}.btn-input:hover{background:#0b7dda;}";
html += ".btn-scan{background:#FF9800;color:white;padding:10px 20px;font-size:16px;}.btn-scan:hover{background:#e68900;}";
html += ".i2c-addr{font-family:monospace;font-weight:bold;color:#2196F3;}";
html += ".status-info{background:#e3f2fd;padding:15px;border-radius:5px;margin:10px 0;}";
html += "</style>";
html += "<script>";
html += "function refresh(){location.reload();}";
html += "setInterval(refresh, 5000);"; // 5秒ごとに自動更新
html += "</script>";
html += "</head><body><div class='container'>";
// タイトル
html += "<h1>🔧 ESP32 デバッグサーバー</h1>";
// ステータス情報
html += "<div class='status-info'>";
html += "<strong>📡 IP:</strong> " + WiFi.localIP().toString() + " | ";
html += "<strong>⏱ 稼働時間:</strong> " + String(millis()/1000) + "秒 | ";
html += "<strong>💾 Free Heap:</strong> " + String(ESP.getFreeHeap()) + " bytes";
html += "</div>";
// I2Cセクション
html += "<h2>🔌 I2Cデバイス</h2>";
html += "<div class='section'>";
html += "<button class='btn-scan' onclick=\"location.href='/i2c/scan'\">🔍 再スキャン</button>";
html += "<p>検出デバイス数: <strong>" + String(i2cDeviceCount) + "台</strong></p>";
if (i2cDeviceCount > 0) {
html += "<table><tr><th>アドレス</th><th>ステータス</th><th>アクション</th></tr>";
for (int addr = 1; addr < 127; addr++) {
if (i2cDevices[addr]) {
char addrHex[6];
sprintf(addrHex, "0x%02X", addr);
html += "<tr><td class='i2c-addr'>" + String(addrHex) + " (" + String(addr) + ")</td>";
html += "<td><span class='on'>● 接続中</span></td>";
html += "<td>";
html += "<button class='btn-input' onclick=\"location.href='/i2c/read?addr=" + String(addr) + "&bytes=8'\">📖 8バイト読取</button> ";
html += "<button class='btn-input' onclick=\"location.href='/i2c/read?addr=" + String(addr) + "&bytes=16'\">📖 16バイト読取</button>";
html += "</td></tr>";
}
}
html += "</table>";
} else {
html += "<p>I2Cデバイスが検出されませんでした。</p>";
}
html += "</div>";
// GPIOセクション
html += "<h2>📌 GPIO制御</h2>";
html += "<div class='section'>";
html += "<table><tr><th>GPIO番号</th><th>現在の状態</th><th>モード設定</th><th>出力制御</th><th>入力読取</th></tr>";
for (int i = 0; i < numPins; i++) {
int pin = gpioPins[i];
int state = digitalRead(pin);
html += "<tr><td><strong>GPIO " + String(pin) + "</strong></td>";
html += "<td>" + String(state == HIGH ? "<span class='on'>HIGH</span>" : "<span class='off'>LOW</span>") + "</td>";
html += "<td>";
html += "<button class='btn-input' onclick=\"location.href='/gpio/mode?pin=" + String(pin) + "&mode=output'\">出力</button> ";
html += "<button class='btn-input' onclick=\"location.href='/gpio/mode?pin=" + String(pin) + "&mode=input'\">入力</button>";
html += "</td>";
html += "<td>";
html += "<button class='btn-on' onclick=\"location.href='/gpio/set?pin=" + String(pin) + "&state=1'\">HIGH</button> ";
html += "<button class='btn-off' onclick=\"location.href='/gpio/set?pin=" + String(pin) + "&state=0'\">LOW</button>";
html += "</td>";
html += "<td><button class='btn-input' onclick=\"location.href='/gpio/read?pin=" + String(pin) + "'\">読取</button></td>";
html += "</tr>";
}
html += "</table></div>";
html += "<p style='text-align:center;color:#888;margin-top:30px;'>自動更新: 5秒ごと</p>";
html += "</div></body></html>";
server.send(200, "text/html", html);
}
// ===== I2Cスキャンエンドポイント =====
void handleI2CScan() {
scanI2C();
server.sendHeader("Location", "/");
server.send(303);
}
// ===== I2C読み取りエンドポイント =====
void handleI2CRead() {
if (!server.hasArg("addr") || !server.hasArg("bytes")) {
server.send(400, "text/plain", "パラメータ不足");
return;
}
int addr = server.arg("addr").toInt();
int numBytes = server.arg("bytes").toInt();
String data = readI2CData(addr, numBytes);
String html = "<!DOCTYPE html><html><head><meta charset='UTF-8'>";
html += "<style>body{font-family:Arial;padding:20px;background:#f5f5f5;}";
html += ".data{background:white;padding:20px;border-radius:5px;font-family:monospace;font-size:14px;}";
html += "button{padding:10px 20px;background:#4CAF50;color:white;border:none;border-radius:5px;cursor:pointer;margin-top:20px;}";
html += "</style></head><body>";
html += "<h2>I2C データ読み取り</h2>";
html += "<p><strong>アドレス:</strong> 0x" + String(addr, HEX) + " (" + String(addr) + ")</p>";
html += "<p><strong>読取バイト数:</strong> " + String(numBytes) + "</p>";
html += "<div class='data'>" + data + "</div>";
html += "<button onclick=\"location.href='/'\">← 戻る</button>";
html += "</body></html>";
server.send(200, "text/html", html);
}
// ===== GPIOモード設定 =====
void handleGPIOMode() {
if (!server.hasArg("pin") || !server.hasArg("mode")) {
server.send(400, "text/plain", "パラメータ不足");
return;
}
int pin = server.arg("pin").toInt();
String mode = server.arg("mode");
if (mode == "output") {
pinMode(pin, OUTPUT);
} else if (mode == "input") {
pinMode(pin, INPUT);
} else if (mode == "input_pullup") {
pinMode(pin, INPUT_PULLUP);
}
server.sendHeader("Location", "/");
server.send(303);
}
// ===== GPIO出力設定 =====
void handleGPIOSet() {
if (!server.hasArg("pin") || !server.hasArg("state")) {
server.send(400, "text/plain", "パラメータ不足");
return;
}
int pin = server.arg("pin").toInt();
int state = server.arg("state").toInt();
pinMode(pin, OUTPUT);
digitalWrite(pin, state);
server.sendHeader("Location", "/");
server.send(303);
}
// ===== GPIO入力読み取り =====
void handleGPIORead() {
if (!server.hasArg("pin")) {
server.send(400, "text/plain", "パラメータ不足");
return;
}
int pin = server.arg("pin").toInt();
pinMode(pin, INPUT);
int value = digitalRead(pin);
String json = "{\"pin\":" + String(pin) + ",\"value\":" + String(value) + ",\"state\":\"" + String(value ? "HIGH" : "LOW") + "\"}";
server.send(200, "application/json", json);
}
// ===== JSON APIエンドポイント =====
void handleAPI() {
String json = "{";
json += "\"uptime\":" + String(millis()/1000) + ",";
json += "\"freeHeap\":" + String(ESP.getFreeHeap()) + ",";
json += "\"i2cDevices\":" + String(i2cDeviceCount) + ",";
json += "\"wifiRSSI\":" + String(WiFi.RSSI());
json += "}";
server.send(200, "application/json", json);
}
void setup() {
Serial.begin(115200);
// I2C初期化
Wire.begin(SDA_PIN, SCL_PIN);
// GPIOピンの初期化(全て入力モードに)
for (int i = 0; i < numPins; i++) {
pinMode(gpioPins[i], INPUT);
}
// WiFi接続
Serial.println("WiFi接続中...");
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("\nWiFi接続成功!");
Serial.println("IP: " + WiFi.localIP().toString());
// mDNS設定
if (MDNS.begin(hostname)) {
Serial.println("mDNS起動: http://" + String(hostname) + ".local");
MDNS.addService("http", "tcp", 80);
}
// 初回I2Cスキャン
scanI2C();
// ルーティング設定
server.on("/", handleRoot);
server.on("/i2c/scan", handleI2CScan);
server.on("/i2c/read", handleI2CRead);
server.on("/gpio/mode", handleGPIOMode);
server.on("/gpio/set", handleGPIOSet);
server.on("/gpio/read", handleGPIORead);
server.on("/api/status", handleAPI);
server.begin();
Serial.println("HTTPサーバー起動完了!");
}
void loop() {
server.handleClient();
//MDNS.update();ESP8266用
}I2Cセンサーの大体のアドレス表(AIに出してもらいました)



