0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

ESP32‑S3 × Python で作る小型I/Oデバイス(第4回:通信プロトコル(JSON コマンド))

0
Last updated at Posted at 2026-03-05

連載一覧

はじめに

前回までで、ESP32‑S3 の環境構築と I/O の基本動作を確認しました。
今回は、Python アプリと ESP32‑S3 をつなぐ通信プロトコルを設計します。
このプロジェクトでは、USB シリアルを使って PC と ESP32‑S3 を接続し、JSON 形式のコマンドで Python から I/O を操作できるようにします。

• Python → ESP32:JSON コマンドを送信
• ESP32 → Python:JSON レスポンスを返す

この仕組みを作ることで、次回の「Python API 実装」を実現します。

本記事では VisualStudioCode で Python を扱います。
環境構築についてはこちらの記事がわかりやすいです。
VisualStudioCodeでPython環境構築まとめ

JSON コマンドの考え方

ESP32‑S3 に送るコマンドはすべて JSON で統一します。
例:ADC を読みたい場合
{"cmd": "read_adc", "pin": 1}

詳しく知りたい方はこちらの記事をどうぞ
JSONとは?初心者向けに基本構造と使い方をわかりやすく解説

なぜ JSON なのか

  • Python から扱いやすい
  • 拡張しやすい
  • デバッグしやすい
  • 人間が読める

ESP32‑S3 はこの JSON をパースし、コマンド(cmd)の内容に応じて処理を実行します。

コマンド一覧

コマンド 説明 パラメータ例
read_adc ADC値を取得 {"cmd":"read_adc","pin":1}
set_pwm PWM出力を設定 {"cmd":"set_pwm","pin":2,"duty":128}
read_di デジタル入力を取得 {"cmd":"read_di","pin":3}
set_do デジタル出力を設定 {"cmd":"set_do","pin":4,"value":1}
ping 通信確認 {"cmd":"ping"}

ESP32‑S3 側の受信ループ

ESP32‑S3 は、USB シリアルから 1 行ずつ JSON を受け取り、内容に応じて処理を分岐します。以下のコードをESP32-S3に書き込むと、JSONコマンドを受け付けられる状態になります。

基本の流れ

  • シリアルから 1 行読み取る
  • JSON をパース
  • cmd に応じて処理
  • 結果を JSON で返す

サンプルコード(ESP32-S3側)

以下は「最小構成の JSON コマンド受信ループ」です。

※今回はピン番号をコマンドで指定していますが、自由に指定できてしまうと誤設定やノイズの影響を受けやすくなります。そのため、今後デバイス化する際は「このピンは入力」「このピンはPWM」など役割を固定する予定です。

#include <Arduino.h>
#include <ArduinoJson.h>

void setup() {
    Serial.begin(115200);
}

void loop() {
    if (!Serial.available()) return;

    // 1 行の JSON を受信
    String line = Serial.readStringUntil('\n');

    // JSON パース
    StaticJsonDocument<256> doc;
    auto err = deserializeJson(doc, line);
    if (err) {
        Serial.println("{\"status\":\"error\",\"message\":\"json parse error\"}");
        return;
    }

    const char* cmd = doc["cmd"];

    // ----------------------------------------------------------------------
    //  デジタル入力 read_di
    //  {"cmd":"read_di","pin":5}
    // ----------------------------------------------------------------------
    if (strcmp(cmd, "read_di") == 0) {
        int pin = doc["pin"];
        pinMode(pin, INPUT);

        int value = digitalRead(pin);

        StaticJsonDocument<128> res;
        res["status"] = "ok";
        res["value"] = value;

        serializeJson(res, Serial);
        Serial.println();
    }

    // ----------------------------------------------------------------------
    //  デジタル出力 set_do
    //  {"cmd":"set_do","pin":5,"value":1}
    // ----------------------------------------------------------------------
    else if (strcmp(cmd, "set_do") == 0) {
        int pin = doc["pin"];
        int value = doc["value"];  // 0 or 1

        pinMode(pin, OUTPUT);
        digitalWrite(pin, value ? HIGH : LOW);

        Serial.println("{\"status\":\"ok\"}");
    }

    // ----------------------------------------------------------------------
    //  ADC 読み取り read_adc
    //  {"cmd":"read_adc","pin":1}
    // ----------------------------------------------------------------------
    else if (strcmp(cmd, "read_adc") == 0) {
        int pin = doc["pin"];
        int value = analogRead(pin);

        StaticJsonDocument<128> res;
        res["status"] = "ok";
        res["value"] = value;

        serializeJson(res, Serial);
        Serial.println();
    }

    // ----------------------------------------------------------------------
    //  PWM 出力 set_pwm
    //  {"cmd":"set_pwm","pin":2,"duty":128}
    // ----------------------------------------------------------------------
    else if (strcmp(cmd, "set_pwm") == 0) {
        int pin = doc["pin"];
        int duty = doc["duty"];

        ledcAttach(pin, 5000, 8);  // 5kHz / 8bit
        ledcWrite(pin, duty);

        Serial.println("{\"status\":\"ok\"}");
    }

    // ----------------------------------------------------------------------
    //  ping
    //  {"cmd":"ping"}
    // ----------------------------------------------------------------------
    else if (strcmp(cmd, "ping") == 0) {
        Serial.println("{\"status\":\"ok\",\"message\":\"pong\"}");
    }

    // ----------------------------------------------------------------------
    //  不明コマンド
    // ----------------------------------------------------------------------
    else {
        Serial.println("{\"status\":\"error\",\"message\":\"unknown command\"}");
    }
}

コンパイルには ArduinoJson が必要です。
ライブラリマネージャーから「ArduinoJson by Benoit lanchon ...」をインストールしてから書き込みを行ってください。
image.png

レスポンス形式

ESP32‑S3 はシリアル通信でJSONコマンドを受け取り実行すると、必ず 1 行の JSON を返します。

実行結果 応答例
成功 {"status":"ok","value":1234}
エラー {"status":"error","message":"invalid pin"}

Python 側は この応答をreadline() で受け取り、JSON としてパースします。

デバッグ方法

シリアルモニタで手動テスト

Arduino IDE のシリアルモニタで JSON を送信できます。

送信コマンド 応答(返り値)
{"cmd":"ping"} {"status":"ok","message":"pong"}

メッセージ入力欄に送信コマンドを入力してEnterを押すと、下部にESP32-S3からの応答が返ってきます。
image.png

前述の コマンド一覧 に記載している パラメータ例 を送信し、各コマンドに対しての応答が返ってくることを確認してください。

Python からの簡易テスト

Python からシリアル通信を扱うには pyserial が必要です。
VS Code などのターミナルから pip でインストールしてください。

pip install pyserial

サンプルコード(Python側)

以下は Python から ESP32-S3 に "ping" コマンドを送り、返ってきた JSON を表示する最小コードです。

※極力シンプルにするため、例外処理は省いています。

import serial, json

# "COM5" の部分は、Arduino IDE で書き込みに使ったポート番号に合わせてください
ser = serial.Serial("COM5", 115200, timeout=1)

# ESP32-S3 に ping コマンドを送信
ser.write(b'{"cmd":"ping"}\n')

# 1 行の JSON レスポンスを受信
line = ser.readline().decode().strip()

# JSON を Python の辞書に変換して表示
print(json.loads(line))

このコードを VisualStudioCode から実行して接続に成功すると、ESP32-S3 からの応答がターミナルに書き込まれます。
image.png

可変抵抗・LEDによる入出力確認

第3回で作った回路をそのまま使用して以下の確認ができます。

確認内容 送信コマンド
デジタル出力でLEDが点灯 {"cmd":"set_do","pin":5,"value":1}
可変抵抗値に応じてアナログ入力値が変化 {"cmd":"read_adc","pin":10}
PWM出力のduty値に応じてLEDの明るさが変化 {"cmd":"set_pwm","pin":5,"duty":128}

image.png

まとめ

  • ESP32‑S3 と Python の通信は JSON コマンドで統一
  • ESP32‑S3 は「受信 → パース → 分岐 → JSON 返信」のループ
  • Python 側は次回作る Device クラスから簡単に操作できるようになる
  • デバッグはArduino IDEのシリアルモニタと Python の両方で可能

次回予告

次回は、今回の JSON プロトコルを使って
Python 側の Device クラス(API) を実装します。

  • read_adc / set_pwm / read_di / set_do
  • タイムアウト処理
  • エラー処理
  • 簡易テストツール
    ESP32‑S3 を「USB でつなぐだけで使える I/O デバイス」に近づけていきます。
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?