TuBrief
Subscribed Channels
Videos
Community

ESP32S3と16MBフラッシュでリアルタイム入力を受け取るオンデバイスLLMの作成

TuBrief Editorial
August 12, 2026
0
Computing/Software

Written with AI assistance from the source video. The video is the authority.

日本語한국어中文العربيةEspañolDeutschEnglishFrançaisहिन्दीPortuguêsРусскийBahasa Indonesia

Related Video

たった8ドルのマイコンで言語モデルがローカル動作!8:14

たった8ドルのマイコンで言語モデルがローカル動作!

Better Stack

More from the community

사내 시스템에 llm api 붙일 때 마주하는 현실적인 한계와 대응법

September 13, 2026

레거시 백엔드에 GPT-6 Astra 붙일 때 예산 승인과 보안 통과를 먼저 끝내는 법이 있습니다

September 13, 2026

에이전트끼리 대화하다 6천만 원 청구서가 나오는 이유

September 13, 2026

사내 RAG 벡터 검색에 Okta 권한 필터를 직접 거는 방법

September 13, 2026

브라우저 에이전트에게 내 구글 계정을 통째로 넘기면 안 되는 이유

September 12, 2026

Apple Won the AI Race

September 12, 2026

Comments (0)

Log in to leave a comment

No posts yet

© 2026 . All rights reserved.

TuBrief
Subscribed Channels
Videos
Community
Log in

ESP32S3と16MBフラッシュでリアルタイム入力を受け取るオンデバイスLLMの作成

8ドルのESP32-S3ボードで2,890万パラメータの言語モデルを動かそうと決意すると、すぐに途方に暮れることになります。デモ映像ではスムーズに動くモデルが、自分が作ったハードウェア周辺機器と組み合わさると途端にフリーズしてしまうからです。SRAMはわずか512KBしかなく、文章を一つ変えるたびに15MBのパーティションを書き直さなければならないと、開発中についイライラしてしまいます。この制約を突破して実際にキーパッド入力を受け付けて動作するデバイスを作るには、メモリキャッシュと入出力バッファを最初から設計し直す必要があります。

毎回リフレッシュせずにシリアルでプロンプトを注入する

テスト文を一つ変えるたびにボードを焼き直すようなやり方は、時間の無駄です。非同期UART割り込みとFreeRTOSセマフォを組み合わせれば、ボードを再起動する必要はありません。シリアルモニターやキーパッドで文章を入力するそばから、モデルが即座に理解します。

  1. GPIO 16/17ピンをUART_NUM_2ポートに割り当て、ハードウェアFIFOの閾値を120バイトに設定して受信割り込みを有効にします。
  2. 内蔵SRAMに2048バイトのリングバッファを作成します。推論演算の実行中にデータがあふれてクラッシュするのを防ぎます。
  3. 改行文字を検出するストリーミングパーサーを追加し、43,056バイトのBTK1エンコーダーライブラリを使用して入力テキストを直接トークンID配列に変換します。
  4. xPromptSemaphoreのバイナリセマフォを設定します。受信タスクがトークンの書き込みを完了してxSemaphoreGiveを呼び出すと、待機中の推論タスクがxSemaphoreTakeでメモリロックを取得して演算を開始します。

この構成を整えておけば、プロンプトの修正とテストにかかる時間を80%以上削減できます。

`
+------------------+ +-------------------+ +--------------------+
| External UART | ---> | HW FIFO Buffer | ---> | SW Ring Buffer |
| (Keypad/Monitor) | | (120 Bytes) | | (2048 Bytes) |
+------------------+ +-------------------+ +--------------------+
|
v
+------------------+ +-------------------+ +--------------------+
| LLM Forward Pass | <--- | xPromptSemaphore | <--- | BTK1 Tokenizer |
| (Inference Task) | | (Binary Lock) | | (43,056 B Library) |
+------------------+ +-------------------+ +--------------------+

`

階層別埋め込み分散とSRAMキャッシュで14トークンの速度を出す

Google Gemma 3nの研究陣が提案したPer-Layer Embeddings (PLE)構造を使用すると、4ビットに圧縮した14.9MBのモデルを分割して配置できます。2,500万パラメータに達する埋め込みテーブルは16MBのSPIフラッシュに置き、Output Headの重み(310万パラメータ)とKVキャッシュは8MBのPSRAMに配置します。最も頻繁に使用される559KパラメータのDense Compute CoreとHot Activationバッファのみを512KBのSRAMに埋め込みます。

速度を秒間14トークン以上に引き上げる作業は、想像以上に手強いものです。

  1. matvec_i8_range演算関数に__attribute__((noinline))属性をしっかりと付与します。コンパイラが勝手にインライン化を進めてしまうと、I-RAMのキャッシュミスが発生し、演算時間が94.9msから155.2msにかえって悪化してしまいます。
  2. ESP32-S3のDual Xtensa LX7コアにple_model_proj演算とqkv演算を分散させます。デュアルコアを同時に稼働させないと、本来の速度が出ません。
  3. 初期レイヤーの埋め込みデータをSRAMの空き領域に事前に読み込んでおくプリフェッチバッファを拡張し、フラッシュメモリのボトルネックを解消します。

C言語でただ移植しただけでは、秒間0.57トークンという絶望的な速度になります。しかし、INT8ステージングとSRAMプリフェッチまで完了させると、秒間14.0トークンを超えてきます。

最適化段階 トークンあたりの演算遅延時間 秒間トークン生成数 主な適用技術
C純粋移植 (Baseline) 1,757.2 ms 0.57 tok/s シングルコア、FP32演算
PSRAM Head & スカラー最適化 193.9 ms 4.61 tok/s Output Head PSRAM配置
デュアルコアFP32適用 139.4 ms 6.22 tok/s デュアルコアレイヤー分割演算
INT8 Staging + SRAM最適化 94.9 ms 9.88 tok/s INT8量子化、noinline適用
SRAMプリフェッチバッファ拡張 ~71.4 ms 14.0+ tok/s 初期レイヤーSRAMプリフェッチ

210mAの電流を抑えるライトスリープ制御

240MHzのクロックでデュアルコアを稼働させ続けると、消費電流が210mA (777mW)まで跳ね上がります。ボードが熱くなることは言うまでもなく、バッテリーが瞬く間に消耗してしまいます。esp_pm_configureコマンドを使用して、推論が行われていないときはクロックを40MHzに下げ、自動ライトスリープをかける必要があります。

  1. esp_pm_config_esp32s3_t構造体でmax_freq_mhzを240、min_freq_mhzを40に設定し、light_sleep_enableをtrueに有効化します。
  2. esp_sleep_pd_config(ESP_PD_DOMAIN_VDDSDIO, ESP_PD_OPTION_ON)を実行します。スリープ状態に入ってもSPIフラッシュとRAMの電源は維持し、データが消えないようにする必要があります。
  3. uart_set_wakeup_thresholdとesp_sleep_enable_uart_wakeupを使用して、シリアル信号が入力された瞬間にCPUがスリープから復帰するように復帰割り込みを設定します。

3.7V 1000mAhのリチウムポリマーバッテリーで演算を続けさせると、4.7時間しか持ちません。しかし、ライトスリープ待機モードでは電流が0.24mA (0.88mW)まで急減します。待機時間が混ざり合う実際の使用環境では、平均電流が15〜30mAのレベルに維持され、バッテリーの駆動時間が3倍以上に延びます。