課題の整理
スタックチャンを開発する環境の要件は次の通り
- Windows
- Mac
- Linux
バージョンは明記されていませんでしたが、Windows10 の WSL で試していたところ、最後の書き込み部分でWSL側にスタックチャンデバイスをUSB経由で見えるようにしないとならないのですが、Windows10ではそれができなさそうというところがこの記事の出発点です。
どうするか
現在Windows10のPCから確認できているのは、株式会社アールティが公開している Web Flashという書き込み方法です。
オリジナルである stack-chan のGitHubページにも同じような書き込み用のWebサイトがありましたが、そちらから書き込みをしてもバイナリが合わないのかうまく動きませんでした。
とはいえ、Webから書き込むことができるので、Windows10でビルドさえできれば、このWebからの書き込みができるのではないかと考えてます。
調査
ディレクトリ構成
flash.css # CSSファイル
index.html # 書き込み画面
manifest_esp32_m5stack fire.json
manifest_esp32_m5stack.json
manifest_esp32_m5stack_core2.json
manifest_esp32_m5stack_cores3.json
index.html
中を見る
まずは index.html
の中身を確認します。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Flash firmware</title>
<link rel="stylesheet" href="../global.css">
<link rel="stylesheet" href="flash.css">
<script type="module" src="https://unpkg.com/esp-web-tools@9/dist/web/install-button.js?module"></script>
<script>
document.addEventListener('DOMContentLoaded', () => {
const button = document.querySelector('esp-web-install-button')
const select = document.querySelector('.select-target')
select.addEventListener('change', (event) => {
const target = event.target.value
button.setAttribute("manifest", `manifest_${target}.json`)
})
})
</script>
</head>
<body>
<div class="app">
<select class="select-target">
<option value="esp32_m5stack">M5Stack</option>
<option value="esp32_m5stack_core2">M5Stack Core2</option>
<option value="esp32_m5stack_fire">M5Stack Fire</option>
<option value="esp32_m5stack_cores3">M5Stack CoreS3</option>
</select>
<esp-web-install-button manifest="manifest_esp32_m5stack.json" class="button-flash-container">
<button slot="activate" class="button-flash">
Flash Stack-chan firmware [・_・]
</button>
<span slot="unsupported">Ah snap, your browser doesn't work!</span>
<span slot="not-allowed">Ah snap, you are not allowed to use this on HTTP!</span>
</esp-web-install-button>
</div>
</body>
</html>
ChatGTPに解説させる
それほど大きくないですが、JavascriptやらHTMLには疎いのでChatGPTに聞いてみます。
このHTMLファイルは、ESP32搭載デバイス(主にM5Stackシリーズ)にStack-chanのファームウェアをWebブラウザ経由で書き込む(Flashする)ためのWeb UIを提供します。その仕組みを順を追って説明します。
🔧 主な構成要素とその役割
1 . カスタム要素
esp-web-tools ライブラリが提供するWeb Componentsの一つ。
Web Serial APIを使って、ブラウザから直接ESP32にファームウェアを書き込めます。
manifest 属性に指定された .json ファイル(ファームウェアの構成情報)を参照して、書き込みを行います。
2 . JavaScript(DOMContentLoadedイベント内)
デバイスの種類を ドロップダウンで選べるようにしています。
ユーザーが選択肢を変更すると、 の manifest 属性を動的に変更します。
button.setAttribute("manifest", `manifest_${target}.json`)
→ 例: esp32_m5stack_core2 を選んだら manifest_esp32_m5stack_core2.json が使用されます。
3 . スロット(の中)
slot="activate":実際のフラッシュボタンのUI。
slot="unsupported":ブラウザが非対応だった場合に表示。
slot="not-allowed":HTTPでは動作しないことを警告(Web SerialはHTTPS必須)。
🧠 処理の流れ
ユーザーがページを開く。
初期状態で manifest_esp32_m5stack.json が選択された状態。
でデバイスの種類を選ぶと、JavaScriptが manifest の指定を切り替える。
Flash Stack-chan firmware [・_・] ボタンを押すと、ブラウザがシリアル接続可能なデバイスを探し、選択させる。
対応したファームウェアが選択されたESP32デバイスに書き込まれる。
🌐 必要な環境
Google Chrome, Edge など Web Serial API に対応したブラウザ
HTTPSページであること(セキュリティ制約によりHTTPでは動作不可)
manifest_xxx.json ファイルが適切にホスティングされていること
🔍 まとめ
機能 | 説明 |
---|---|
デバイス選択 | により選択。JavaScriptでmanifestファイルを切り替え |
書き込みボタン | esp-web-tools のWeb Componentを使用 |
書き込み対象 | ESP32(M5Stackシリーズ) |
利用API | Web Serial API(ブラウザ→USB経由でESP32と通信) |
HTTPSのページであることと、manifest_xxx.json がちゃんとホスティングされていると良さそうですね。
json
では、json の中身はどうなっているのでしょうか。
中をみる
{
"name": "Stack-chan",
"version": "1.0.0",
"builds": [
{
"chipFamily": "ESP32-S3",
"parts": [
{
"path": "tech.moddable.stackchan/com.m5stack.cores3/bootloader.bin",
"offset": 0
},
{
"path": "tech.moddable.stackchan/com.m5stack.cores3/partition-table.bin",
"offset": 32768
},
{
"path": "tech.moddable.stackchan/com.m5stack.cores3/xs_esp32.bin",
"offset": 65536
}
]
}
]
}
ChatGPT に解説させる
これは、esp-web-tools の で使用される manifest JSON ファイルで、ESP32デバイス(この場合は M5Stack CoreS3)にフラッシュするためのファームウェア情報が記述されています。
🧾 全体構成の概要
このJSONファイルは、以下のような情報を含んでいます:
フィールド | 説明 |
---|---|
name | プロジェクトや製品の名前(例:Stack-chan) |
version | ファームウェアのバージョン |
builds | 書き込むファームウェアの構成(チップ種別や各バイナリのオフセットなど) |
🔍 詳細解説
{
"name": "Stack-chan",
"version": "1.0.0",
"builds": [
{
"chipFamily": "ESP32-S3",
"parts": [
{
"path": "tech.moddable.stackchan/com.m5stack.cores3/bootloader.bin",
"offset": 0
},
{
"path": "tech.moddable.stackchan/com.m5stack.cores3/partition-table.bin",
"offset": 32768
},
{
"path": "tech.moddable.stackchan/com.m5stack.cores3/xs_esp32.bin",
"offset": 65536
}
]
}
]
}
📌 chipFamily: "ESP32-S3"
このファームウェアが対応しているESPチップの種類。
今回は ESP32-S3 → M5Stack CoreS3 用であることを示しています。
📌 parts 配列
各ファイルと書き込み先のアドレス(フラッシュオフセット)を定義します。
ESP32に書き込む際の典型的な構成です:
path | offset(書き込み先) | 説明 |
---|---|---|
bootloader.bin | 0x0000 | ブートローダー。起動時に実行される |
partition-table.bin | 0x8000 (32768) | パーティションテーブル。フラッシュ内のレイアウトを定義 |
xs_esp32.bin | 0x10000 (65536) | 実際のアプリケーション(Stack-chan本体) |
💡 Web Serial API + manifest の役割
このJSONは、Web Serial経由でESP32にファームウェアを書き込む際に、どのファイルをどのアドレスに書くかをブラウザに指示するための「地図」です。
esp-web-install-button コンポーネントがこのJSONを読み取り、USB経由でESP32に自動的に書き込みを行います。
✅ 要件
上記ファイルパス (path) は、ブラウザからアクセス可能なHTTP(S) URLに解決される必要があります(相対パスとして指定されている場合、manifestのある場所が基準)。
バイナリファイルは予めビルド・配置されている必要があります。
ホスト先の検討
GitHub でホストしたらHTTPSを備え、かつバイナリも保管できそうですね。試してみましょう
GitHub でホストする。
ソースコードはほぼ同じものを使うので、リポジトリをフォークしておきます。
ブランチの管理とかはまだ考えてませんが、本家のブランチとは別に用意して管理しようと思います。ここでは dev/1.0-kaneko
としておきます。
GitHub Pages を有効にする
リポジトリの Settings から Pages を選択して Github Pages を有効にしてみました。
GitHub Actions
ちゃんと見てなくて気付いていませんでしたが、バックエンドでActionsでビルドが走っていたようです。何をしているのか確認してみましょう
GitHub Actions の調査
build.yml
ここではビルドをしているようです。正常に完了する限りは触らなさそうだと思いましたので放置です。
name: Build Stack-chan Firmware
on: [push, pull_request, workflow_dispatch]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: ./.github/actions/setup
- name: Check Format
run: npm run format
working-directory: ./firmware
- name: Lint
run: npm run lint
working-directory: ./firmware
- name: Build
run: source "$HOME/.local/share/xs-dev-export.sh" && npm run build
working-directory: ./firmware
- name: Build CoreS3
run: source "$HOME/.local/share/xs-dev-export.sh" && npm run build --target=esp32/m5stack_cores3
working-directory: ./firmware
bundle.yml
ここでビルドの呼び出しのタイミングなどを確認していそうです。
./firmware/
ディレクトリ以下に差分があれば、ビルドなければビルドをしない様子。
できあがったものを gh-pages
ブランチに保存して公開しているようです。
gh-pages ブランチを用意しましょう。
また、ブランチのチェック時に本家でのデフォルトのブランチである dev/1.0
をみています。私はブランチを dev/1.0-kaneko
と変えたので少々修正が必要そうですね。
name: Bundle Stack-chan Firmware
on:
push:
branches:
- dev/v1.0
pull_request:
branches:
- dev/v1.0
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Check for changes in firmware
id: diff_check
run: |
git diff --quiet HEAD^ HEAD -- ./firmware/ || echo "diff_detected=true" >> $GITHUB_ENV
- name: Cache build results
id: cache
uses: actions/cache@v4
with:
path: ./firmware/stackchan/tech.moddable.stackchan
key: ${{ github.sha }}
- name: Setup
if: ${{ env.diff_detected}} || steps.cache.outputs.cache-hit != 'true'
uses: ./.github/actions/setup
- name: Bundle
if: ${{ env.diff_detected}} || steps.cache.outputs.cache-hit != 'true'
run: source "$HOME/.local/share/xs-dev-export.sh" && npm run bundle
working-directory: ./firmware
- name: Upload Firmware Bundle
if: ${{ env.diff_detected}} || steps.cache.outputs.cache-hit != 'true'
uses: actions/upload-artifact@v4
with:
name: firmware-bundle
path: ./firmware/stackchan/tech.moddable.stackchan
deploy:
needs: build
if: github.event_name == 'push' && github.ref == 'refs/heads/dev/v1.0'
runs-on: ubuntu-latest
steps:
- name: Checkout Pages Branch
uses: actions/checkout@v4
with:
ref: gh-pages
- name: Download Firmware Bundle
uses: actions/download-artifact@v4
with:
name: firmware-bundle
path: ./firmware-bundle
- name: Move Bundle
run: |
mkdir -p ./web/flash/tech.moddable.stackchan
rm -rf ./web/flash/tech.moddable.stackchan/*
mv firmware-bundle/* ./web/flash/tech.moddable.stackchan
- name: Commit and Push
run: |
git config --global user.name 'GitHub Action'
git config --global user.email 'action@github.com'
git add .
git commit -m "Deploy firmware bundle from ${{ github.sha }}"
git push
GitHub Actions のエラー
ブランチを修正してActionsが動くようになったものの、エラーがいくつか出てきて困りました。
遭遇したエラーと、対処を書いていきます。対処の中には適当にChatGPTがいったままを対応しているものもあるので、将来的には見直しが必要そうです。まずはビルドをパスすることから。
sdkconfig がファイルじゃないエラー
ビルドのActionで発生したものです。
Requirement files:
- /home/runner/.local/share/esp32/esp-idf/tools/requirements/requirements.core.txt
Python being checked: /home/runner/.espressif/python_env/idf5.3_py3.12_env/bin/python
Traceback (most recent call last):
File "/home/runner/.local/share/esp32/esp-idf/tools/idf.py", line 844, in <module>
main()
File "/home/runner/.local/share/esp32/esp-idf/tools/idf.py", line 725, in main
cli = init_cli(verbose_output=checks_output)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/runner/.local/share/esp32/esp-idf/tools/idf.py", line 687, in init_cli
all_actions = merge_action_lists(all_actions, extension.action_extensions(all_actions, project_dir))
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/runner/.local/share/esp32/esp-idf/tools/idf_py_actions/dfu_ext.py", line 70, in action_extensions
return dfu_actions if is_target_supported(project_path, SUPPORTED_TARGETS) else {}
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/runner/.local/share/esp32/esp-idf/tools/idf_py_actions/tools.py", line 721, in is_target_supported
return get_target(project_path) in supported_targets
^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/runner/.local/share/esp32/esp-idf/tools/idf_py_actions/tools.py", line 103, in get_target
return get_sdkconfig_value(path, 'CONFIG_IDF_TARGET')
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/runner/.local/share/esp32/esp-idf/tools/idf_py_actions/tools.py", line 709, in get_sdkconfig_value
with open(sdkconfig_file, 'r') as f:
^^^^^^^^^^^^^^^^^^^^^^^^^
IsADirectoryError: [Errno 21] Is a directory: '/home/runner/work/stack-chan/stack-chan/firmware/stackchan/sdkconfig'
なぜか本家のリポジトリでも同じエラーを見ますが、処理は継続してます。私の場合はエラーで止まります。悲しい。
原因を調べたところ、npm run build
で読み込む ./firmware/stackchan/manifest.json
に sdkconfig
がディレクトリであることを期待しているものの、その後の esp-idf
がsdkconfig をファイルで欲しがっていることが直接原因でした。
正しい対処が分からないので、manifeset.json
が参照するディレクトリを sdkconfig.d
と変更し、esp-idf
が参照するファイルを新たに登録しました。
中身は以下の通りです。(ChatGPTにきいた)CONFIG_IDF_TARGET
を指定しろというので入れました。ほかのパラメタは sdkconfig/sdkconfig.default
を持ってきてみました。
# Target MCU
CONFIG_IDF_TARGET="esp32s3"
# Flash size
CONFIG_ESPTOOLPY_FLASHSIZE_4MB=n
CONFIG_ESPTOOLPY_FLASHSIZE_8MB=y
CONFIG_ESPTOOLPY_FLASHSIZE="8MB"
esp-idf のバージョンが期待と合わない
同じくビルドです。
rm: cannot remove '/home/runner/.local/share/moddable/build/tmp/esp32/m5stack/debug/stackchan/xsProj-esp32/main/idf_component.yml': No such file or directory
# bles2gatt bleservices
*** Update required to ESP-IDF v5.4 (found v5.3.1)
See update instructions at: https://github.com/Moddable-OpenSource/moddable/blob/public/documentation/devices/esp32.md
Expected ESP IDF v5.4, found v5.3.1
make: *** [/home/runner/.local/share/moddable/build/tmp/esp32/m5stack/debug/stackchan/makefile:1558: idfVersionCheck] Error 1
make: *** Waiting for unfinished jobs....
Error: Process completed with exit code 2.
v5.4 を期待しているのに v5.3.1 だったといわれました。
これは本家にすでにあがっていたので助かりました。
ライブラリのバージョンを変更して完了です。
(未解決)
こちらはGenerate Stack-chan Schematics Files
で発生しましたが何のことなのか、何に必要なのかわかってないので未解決です。
Current runner version: '2.323.0'
Operating System
Runner Image
Runner Image Provisioner
GITHUB_TOKEN Permissions
Secret source: Actions
Prepare workflow directory
Prepare all required actions
Getting action download info
Download immutable action package 'actions/checkout@v3'
Version: 3.6.0
Digest: sha256:942562b9c7d4b1f2557f1da21f4d3eb4eb4fd01f2ad78b2a6e187d1b193e0a0f
Source commit SHA: f43a0e5ff2bd294095638e18286ca9a3d1956744
Download action repository 'INTI-CMNB/KiBot@v2_k7' (SHA:8a00b90949bad65e36658d4551820edee9c0f5d6)
Error: Missing download info for actions/upload-artifact@v2
とりあえず
ビルドは成功したので、あとはGithub Pages をホストするだけだと思います。
GitHub Pages は設定済みなので、以下にアクセスして書き込みを試してみます。
結果
書き込みは成功しましたが、スタックチャンは起動しませんでした。
本家の書き込みのサイトから試しても同じ状況なので、なにか潜在的な課題があるのかも知れません。
課題
GitHub Actionのエラー
1つだけ失敗しているジョブがあるので原因を調査する
スタックチャンが起動しない(こっちのが問題)
高度なことをやりたいわけではないので、例えばバージョンを落として動かないかを見てみるなど。(昨年時点のバージョンにしてみるとか)