九州・福岡・東京ときどきIoT

21年間のはてなダイアリー&アメブロからの避難所

ESP32でLLMを動かす計画ー1

下記URLでM5STACKにソフトを入れLLM動作しているHPがある。

https://github.com/78/xiaozhi-esp32

このURLの内容を精査し、 ESP32S3+320X240LCDで動作させたい。

MEMSマイクINMP441を使用したい。

TFT

// --- Pin Configuration ---

#define TFT_CS    14

#define TFT_DC    47

#define TFT_RST   21

#define TFT_MOSI  45

#define TFT_MISO  46

#define TFT_SCLK  3

で、38,39,40のGPIOは空いている。

この条件で、ESP32S3+アルファのハードウェアに移植したいと思う。

 

指定されたGitHubリポジトリ xiaozhi-esp32 の内容を精査し、ご提示のカスタムハードウェア(ESP32-S3 + 320x240 LCD + INMP441マイク)へ移植するための計画と具体的な手順を以下にまとめます。

1. ソースプロジェクト (xiaozhi-esp32) の内容精査

 

まず、指定されたGitHubリポジトリを分析しました。

  • プロジェクトの概要:
    これは、M5Stack(初代ESP32ベースの統合開発デバイス)で動作する、中国語の対話型AIアシスタントです。愛称は「小智 (Xiao Zhi)」。
  • 主要な機能:
  • 音声認識: 内蔵マイクで音声を録音します。
  • ウェイクワード: 特定の言葉(おそらく "小智" など)で起動します。
  • クラウドAI対話: 録音した音声をBaidu(百度)の音声認識APIに送信してテキストに変換し、そのテキストをTuring Robot (图灵机器人) などの対話AI APIに送り、返答を得ます。
  • 音声合成 (TTS): AIからのテキスト返答をBaiduの音声合成APIで音声データに変換し、スピーカーで再生します。
  • 情報表示: 天気、時刻、カレンダー、AIの返答などをLCDに表示します。天気情報はSeniverse (心知天气) APIを利用しています。
  • ハードウェア依存性 (M5Stack):
  • MCU: ESP32 (デュアルコア)
  • ディスプレイ: 内蔵TFT LCD (ILI9341, 320x240)
  • マイク: 内蔵マイク (SPM1423, PDM接続)
  • スピーカー: 内蔵スピーカー (I2S接続)
  • ボタン: 物理ボタン A, B, C
  • ライブラリ: M5Stack.h というM5Stack専用の統合ライブラリに強く依存しています。
  • 開発環境:
    PlatformIOをベースに構築されています。

2. ハードウェアの差異と移植方針

 

M5Stackとご提示のカスタムハードウェアには、いくつかの重要な違いがあります。これを乗り越えるのが移植の鍵です。

機能

M5Stack (元プロジェクト)

ESP32-S3 (ターゲット)

移植方針

MCU

ESP32

ESP32-S3

ESP32-S3はより高性能ですが、I2Sなどのペリフェラル設定が若干異なります。コードのコアロジックは流用可能ですが、ピン設定やドライバ初期化部分は書き換えが必要です。

ディスプレイ

内蔵LCD (M5.Lcd)

汎用SPI LCD (320x240)

M5.Lcd に関連する描画コードを、Adafruit_GFX と Adafruit_ST7789 を使ったコードに全面的に書き換えます。これは前回までのプロジェクトで経験済みなので、比較的スムーズに進められるはずです。

マイク

内蔵マイク (PDM)

INMP441 (I2S)

I2Sのピン設定をESP32-S3とINMP441に合わせて変更します。ご提示の空きピン 38, 39, 40 はI2Sに最適です。

スピーカー

内蔵スピーカー (I2S)

未定

このプロジェクトは音声合成で返答するため、スピーカーが必須です。I2S接続のDACモジュール (MAX98357A など) と小型スピーカーを別途用意する必要があります。

ボタン

物理ボタンA, B, C

未定

M5Stackのボタンに割り当てられた機能を、無効化するか、別途タクトスイッチを接続して実装します。

3. ソフトウェアの移植手順と修正コード案

 

この移植は大規模な作業になるため、ステップバイステップで進めるのが賢明です。

 

ステップ1: 開発環境のセットアップ (PlatformIO)

 

元のプロジェクトがPlatformIOベースなので、それに倣うのが最も近道です。

  1. VSCodeにPlatformIO IDE拡張機能をインストールします。
  2. xiaozhi-esp32 プロジェクトをダウンロードし、PlatformIOで開きます。
  3. プロジェクト内の platformio.ini ファイルを、ESP32-S3用に書き換えます。

platformio.ini の修正案:

 

Ini, TOML



[env:esp32s3_custom]
platform = espressif32
board = esp32s3_generic      ; ボードを汎用ESP32-S3に変更
framework = arduino
upload_speed = 921600
monitor_speed = 115200
board_build.mcu = esp32s3
board_build.f_cpu = 240000000L
board_build.flash_mode = qio
board_build.flash_size = 16MB ; お使いのモジュールに合わせて変更

lib_deps =
    ; 元のライブラリ
    ArduinoJson
    PubSubClient
    https://github.com/zhouhanj/HTTPClient.git
    ; M5Stackライブラリは不要なので削除
    ; m5stack/M5Stack
   
    ; ★新たに追加するライブラリ
    adafruit/Adafruit GFX Library
    adafruit/Adafruit BusIO
    adafruit/Adafruit ST7735 and ST7789 Library

 

ステップ2: ディスプレイ表示の置き換え

 

main.cpp や関連ファイル内の、M5.Lcd.xxx という形式のコードをすべて tft.xxx に書き換えます。

main.cpp の冒頭部分の修正案:

 

C++



// #include <M5Stack.h> // ← 削除

// ★ここから追加
#include <SPI.h>
#include <Adafruit_GFX.h>
#include <Adafruit_ST7789.h>

// --- Pin Configuration ---
#define TFT_CS    14
#define TFT_DC    47
#define TFT_RST   21
#define TFT_MOSI  45
#define TFT_MISO  46
#define TFT_SCLK  3

SPIClass hspi = SPIClass(HSPI);
Adafruit_ST7789 tft = Adafruit_ST7789(&hspi, TFT_CS, TFT_DC, TFT_RST);
// ★ここまで追加

// ... (他のincludeはそのまま)

void setup() {
    // M5.begin(); // ← 削除
    // M5.Power.begin(); // ← 削除

    // ★ここから追加
    Serial.begin(115200);
    hspi.begin(TFT_SCLK, TFT_MISO, TFT_MOSI, TFT_CS);
    tft.init(240, 320); // 解像度をターゲットに合わせる
    tft.setRotation(3); // 320x240の横長表示にするための向き設定
    tft.invertDisplay(true); // 色が反転する場合に備える
    tft.fillScreen(ST77XX_BLACK);
    // ★ここまで追加
   
    // ... setup()内の残りのコード
}

注意: tft.setRotation() や tft.invertDisplay() の設定値は、お使いのLCDに合わせて調整が必要になる場合があります。

 

ステップ3: マイク (INMP441) の設定

 

元のコードはM5Stackの内蔵マイクを初期化しています。これを汎用のI2SドライバでINMP441を初期化するコードに書き換えます。

マイク初期化部分の修正案:

元のプロジェクトの音声処理部分 (voice.cpp など) を探し、I2Sの初期化コードを以下のように置き換えます。

 

C++



#include "driver/i2s.h"

// I2Sピン設定
#define I2S_WS_PIN   38
#define I2S_SD_PIN   39
#define I2S_SCK_PIN  40

// I2Sポート番号
#define I2S_PORT_NUM I2S_NUM_0

// I2S設定
i2s_config_t i2s_config = {
    .mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_RX),
    .sample_rate = 16000,
    .bits_per_sample = I2S_BITS_PER_SAMPLE_32BIT,
    .channel_format = I2S_CHANNEL_FMT_ONLY_LEFT, // INMP441は片チャネル
    .communication_format = I2S_COMM_FORMAT_STAND_I2S,
    .intr_alloc_flags = ESP_INTR_FLAG_LEVEL1,
    .dma_buf_count = 8,
    .dma_buf_len = 64,
    .use_apll = false, // ESP32-S3ではAPLLが使えない場合がある
    .tx_desc_auto_clear = false,
    .fixed_mclk = 0
};

i2s_pin_config_t pin_config = {
    .bck_io_num = I2S_SCK_PIN,
    .ws_io_num = I2S_WS_PIN,
    .data_out_num = I2S_PIN_NO_CHANGE, // TXは使わない
    .data_in_num = I2S_SD_PIN         // RXピン
};

void Mic_Init() {
    esp_err_t err = i2s_driver_install(I2S_PORT_NUM, &i2s_config, 0, NULL);
    if (err != ESP_OK) {
        Serial.printf("Failed installing I2S driver: %d\n", err);
        return;
    }
    err = i2s_set_pin(I2S_PORT_NUM, &pin_config);
    if (err != ESP_OK) {
        Serial.printf("Failed setting I2S pin config: %d\n", err);
        return;
    }
    Serial.println("I2S Mic driver installed.");
}

この Mic_Init() 関数を setup() の中で呼び出すようにします。

 

4. 移植の難所と注意点

 

  • APIキーの取得:
    Baidu、Turing Robot、SeniverseのAPIを利用するには、ご自身で各サービスに登録し、APIキー(またはAppID/Secret Key)を取得する必要があります。取得したキーは、コード内の設定箇所 (config.h など) に記述します。
  • スピーカーの実装:
    前述の通り、音声で返答を聞くためにはスピーカーが必要です。I2S DAC (MAX98357Aなど) をESP32-S3の空きピンに接続し、音声データを再生するコードを追加する必要があります。
  • M5Stack固有機能の削除:
    M5.update() (ボタン状態の更新) や、M5.BtnA.wasPressed() などのコードは、コンパイルエラーになるため、すべて削除するか、同等の機能を自前で実装する必要があります。
  • メモリとパフォーマンス:
    ESP32-S3は高性能ですが、音声認識とAI対話は多くのメモリと処理能力を必要とします。PSRAM付きのESP32-S3モジュールを使用すると、より安定して動作する可能性があります。

 

5. 推奨する進め方

 

このプロジェクトは非常に大規模で複雑なため、一度にすべてを移植しようとすると困難を極めます。以下の段階的なアプローチをお勧めします。

  1. 【第1段階】基本動作の確認:
  • まずは、ディスプレイ表示の移植を完了させます。時刻や簡単なテキストが画面に表示できることを目標とします。
  • 次に、INMP441マイクのテストをします。I2Sでマイクからの音声データをシリアルプロッタに表示させるだけの簡単なスケッチを作成し、マイクが正常に動作することを確認します。
  1. 【第2段階】API接続の確認:
  • Wi-Fiに接続し、天気API (Seniverse) からJSONデータを取得してシリアルモニタに表示させるプログラムを作成します。APIキーが正しく設定できているかを確認します。
  1. 【第3段階】コア機能の統合:
  • マイクで録音した短い音声(.wav形式)を、Baiduの音声認識APIにPOSTリクエストで送信し、認識結果のテキストが返ってくるかを確認します。ここが最大の難関です。
  1. 【第4段階】全機能の統合:
  • すべての機能を結合し、UIを整えます。

まずは第1段階の、ディスプレイとマイクをそれぞれ個別に動かすことから始めるのが最も確実です。頑張ってください!