ESP32のI2CとI/Oのhttpサーバデバッガ

技術関連

ESP32のI2CとI/Oのhttpサーバデバッガ

ESP32のI2C簡易デバッガを作成しました。

できること

・I2Cアレススキャナ
  I2Cのアドレスをスキャンして今接続されているI2Cデバイスを確認する

・I/Oスキャナ
  現在のI/O状態を表示する。(使用できるピンだけ)

ESP32にデータをダウンロードして

http://esp32-debug.local/

をアドレスバーに入力すると接続できます。

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に出してもらいました)

タイトルとURLをコピーしました