カテゴリー: IoT・組込み

  • ArduSimple NEO-D9C Plugin で QZSS L6 raw を Linux から取り出すまでに踏んだ 3 つの罠

    要約

    ArduSimple simpleRTK3B Compass (UM982) に AS-XBEE-LBAND-NEOD9C-SMA (u-blox NEO-D9C-00B-02) を載せて、QZSS L6 帯の生ナビゲーションメッセージ UBX-RXM-QZSSL6 を Linux ホストに引き出すまで、想定外の罠を 3 つ踏んだ。同じ構成で QZSS L6 を触る人の時間を節約するために記録を残す。

    • 罠 1: 「XBee ソケットに刺せば本体 USB-C で取れる」は 。Plugin 本体に付いている micro USB を PC に直結するのが正解
    • 罠 2: u-blox D9 QZS の IDD が v1 に上がっていて payload が 262 → 264 byte に。古い前提で書いた parser は version field の時点で reject される
    • 罠 3: 屋内窓際だと bytes は届くが errStatus=Erroneous 連発。生バイト dump 目的なら十分、CLAS / QZNMA デコードは屋上アンテナが要る

    環境

    • 受信機: ArduSimple simpleRTK3B Compass (UM982 ベース、dual-antenna RTK)
    • L6 オプション: ArduSimple AS-XBEE-LBAND-NEOD9C-SMA (u-blox NEO-D9C-00B-02、CLAS / QZNMA)
    • アンテナ: u-blox ANN-MB2-00 × 1 (L1/L2/L5/L6 全バンド、active、bias-T 内蔵)
    • ホスト: Ubuntu 24.04 + Rust (serialport crate で UART 直読み)
    • 用途: QZSS の CLAS センチ補正と QZNMA 信号認証を ホスト側で公開鍵検証する自社受信機の前段検証

    罠 1: USB-C 経由ルートは詰まる、micro USB が最短

    事前に procurement メモにこう書いていた:

    NEO-D9C Plugin は UART のみ (USB なし)。simpleRTK3B Compass の XBee ソケットに刺せば本体の DIP スイッチで XBee UART を USB-C 側にルーティングできる。

    これは ArduSimple の Plugin datasheet と u-blox NEO-D9C のチップ仕様書 (USB ピンは存在するがソケットに引き出されていない、と読める) から導いた結論で、頭の中ではきれいなはずだった。

    実機で simpleRTK3B Compass + Plugin を組んで USB-C を 2 本繋いだら、lsusb には FTDI FT231X が一個出てくる。OK と思って:

    $ stty -F /dev/ttyUSB0 9600 raw -echo
    $ timeout 5 cat /dev/ttyUSB0 | wc -c
    0

    9600 / 38400 / 57600 / 115200 / 230400 / 460800 / 921600 と全 baud で 0 byte。FTDI までは enumerate されているのに、その先の UART に何も流れていない。本体の DIP スイッチが XBee → USB ではなく XBee → UM982 にルーティングしているか、別経路かは結局未解明。

    ハマって写真を見直していたら、Plugin 基板の真ん中に micro USB コネクタが付いているのに気付いた。Plugin の datasheet にも procurement メモにも「USB なし」と書いたのに、現物には micro USB が刺さる穴がある。試しに micro USB ケーブル (データ対応) を Plugin に直差しすると:

    $ dmesg | tail
    [...] usb 1-3: New USB device found, idVendor=1546, idProduct=01a9
    [...] usb 1-3: Product: u-blox GNSS receiver
    [...] cdc_acm 1-3:1.0: ttyACM0: USB ACM device

    VID 0x1546 は u-blox AG、PID 0x01a9 は u-blox generation 9 (NEO-D9C もここ)。/dev/ttyACM0 として u-blox CDC ACM デバイスが即座に見える。UART よりずっと素直で速い。

    教訓: チップ datasheet の “USB なし” は「チップ単体での話」かつ「IDD バージョン依存」かもしれない。Plugin / 評価ボードのリビジョンによっては USB-UART ブリッジを足して micro USB に引き出してくれている。現物の基板写真を眺めて全コネクタを数えるべき

    罠 2: UBX-RXM-QZSSL6 が version 0x00 → 0x01 で payload 構造ごと差し替わってた

    parser を当てたら全フレームが “unsupported version: 0x1” で reject。コードを書いた時に参照した古い IDD (version 0x00、payload 262 byte、channelName が ASCII ‘a’ .. ‘d’ で channel を表す) のレイアウトと、実機 firmware が吐く version 0x01 (payload 264 byte、channel 情報が bitfield) が違う。

    最新の u-blox D9 QZS 1.01 Interface Description (UBX-21031777-R02) に当たって layout を取り直す:

    offset  size   field
      0      1    version (= 0x01)
      1      1    svId
      2      2    cno (raw × 2^-8 = dBHz)
      4      4    timeTag (ms, ローカル時刻タグ)
      8      1    groupDelay (ns, L2 に対する L6 群遅延)
      9      1    bitErrCorr (Reed-Solomon で訂正したビット数)
     10      2    chInfo (bitfield、下記)
     12      2    reserved0
     14    250    msgBytes (生 L6 メッセージ)

    chInfo bitfield (X2、リトルエンディアン):

    • bits 9..8: chn (受信機チャネル 0/1)
    • bit 10: msgName (0=L6D, 1=L6E)
    • bits 13..12: errStatus (0=unknown, 1=error-free, 2=erroneous)
    • bits 15..14: chName (0=channel A, 1=channel B)

    旧 v0 では「channel 名 = 1 byte ASCII で ‘a’/’b’ = L6D ch1/ch2, ‘c’/’d’ = L6E ch1/ch2」だったのが、v1 では (msgName, chName) の 2 bit ペアで同じ情報を表す。さらに errStatuscno が追加されて、パーサで観測品質が直接見えるようになった。

    Rust の parser を書き直す:

    pub const UBX_RXM_QZSSL6_PAYLOAD_LEN: usize = 264;
    pub const L6_RAW_LEN: usize = 250;
    
    impl QzssL6Frame {
        pub fn parse(payload: &[u8]) -> Result<Self, QzssL6Error> {
            if payload.len() != UBX_RXM_QZSSL6_PAYLOAD_LEN {
                return Err(QzssL6Error::BadLength(payload.len()));
            }
            if payload[0] != 0x01 {
                return Err(QzssL6Error::UnsupportedVersion(payload[0]));
            }
            let sv_id = payload[1];
            let cno_dbhz = (u16::from_le_bytes([payload[2], payload[3]]) as f32) / 256.0;
            let time_tag_ms = u32::from_le_bytes([payload[4], payload[5], payload[6], payload[7]]);
            let ch_info = u16::from_le_bytes([payload[10], payload[11]]);
    
            let msg_name = (ch_info >> 10) & 1;
            let err_status = (ch_info >> 12) & 0b11;
            let ch_name = (ch_info >> 14) & 0b11;
    
            let band = match (msg_name, ch_name) {
                (0, 0) => L6Band::L6dCh1,
                (0, 1) => L6Band::L6dCh2,
                (1, 0) => L6Band::L6eCh1,
                (1, 1) => L6Band::L6eCh2,
                _ => L6Band::L6dCh1, // reserved
            };
    
            let raw = payload[14..14 + L6_RAW_LEN].to_vec();
            // ...
        }
    }

    教訓: u-blox の IDD はリビジョン番号が静かに上がる。自分の parser を書く時は version field を厳密に拒否する側に倒して、拒否ログから IDD 追従漏れを検知できるようにしておいた方がいい。今回は厳密 reject にしておいたおかげで 5 秒で気付いた。緩く受けてしまっていたら、後段の CLAS / QZNMA デコーダで意味不明な落ち方をして探すのに半日溶かしていた。

    罠 3: 屋内窓際だと bytes は出るが errStatus=Erroneous で CRC が通らない

    parser を直して走らせると、60 秒間で:

    final stats: L6D ch1=60 ch2=60, L6E ch1=0 ch2=0, total=120

    QZS-2 (svId=2) と QZS-7 (svId=7) から L6D の A/B 両チャネルが綺麗な 1Hz で届く。CNO は 32〜34 dBHz、bitErrCorr は 0。L6E (QZNMA) は今回ゼロだが、QZNMA は内閣府側で試験運用中のフェーズなので、観測タイミングや SV 依存で見えないことがある (運用情報を確認する必要あり)。

    ただし全フレームで errStatus=2 (Erroneous)。bytes は届いているが L6 フレーム内部の CRC が pass していない、という状態。屋内の窓際 + bias-T 給電のパッチアンテナ机置きでは、CNO が「届くが decode 品質には足りない」レンジに入りやすい。ANN-MB2-00 を屋外の開けた場所に持ち出した別検証では ErrorFree が立つ。

    用途別の判断:

    • 生バイト dump、parser 検証、UBX フレーミング検証: 屋内窓際で十分。バイト列は正しい構造で届く
    • CLAS デコード (raw → SSR/OSR 補正適用): CRC pass が前提なので屋外アンテナ必須
    • QZNMA 公開鍵検証: 同上、加えて L6E が実際に放送されているタイミングを CAO 公開資料で確認

    教訓: GNSS 評価はとりあえず屋内窓際から始めて「電気は来てる、UART は喋ってる」を確認しつつ、本格的な信号品質評価は早めに屋外に移すこと。「bytes が届く」と「decode が成立する」は別の状態であって、errStatus / CRC フラグはそれを区別してくれる重要な指標。

    まとめ

    • ArduSimple NEO-D9C Plugin で L6 raw 取り出しは、Plugin 本体の micro USB に直差しが最短。simpleRTK3B Compass の USB-C ルートは DIP スイッチ周りで詰まりやすい
    • UBX-RXM-QZSSL6 は version 0x01、payload 264 byte が現行 (UBX-21031777-R02)。自前 parser を書くなら version 厳密チェックで IDD 追従漏れを検知できるように
    • 屋内窓際は errStatus=Erroneous が普通。decode 成立を見たいなら屋外

    次は屋外でアンテナを開けた空に向けて errStatus=ErrorFree を取り、その上で CLAS decoder と QZNMA 公開鍵検証層を実装する。「自前で受信機を作る」モチベーションについてはまた別の記事で。

  • 連載 #0 ドラゴンレーダーを真面目に作る

    何を作るか

    ドラゴンボールに出てくる「ドラゴンレーダー」を、現代の UWB (Ultra-Wide Band) 測距技術で本当に動くハードウェアとして作る

    具体的に:

    • プレイヤーが持つ円形 LCD 端末に、周囲のドラゴンボールが距離と方向と共に表示される
    • ドラゴンボール側は小型タグ (UWB トランスポンダ) で、複数個 (理想は 7 個) を会場に散らせる
    • LCD は玩具版ドラゴンレーダー (1980 年代バンダイ) の画面をオマージュした緑グリッド + コーラル三角ポインタ + ゴールド光点
    • 押せばボタン音、近づけば効果音 (SE)、見つけたら BGM

    これを DigiKey Make ONE Challenge 2026 (応募締切 2026-06-22) に出展、その後 9 月の Maker Faire Tokyo にデモ持ち込みする計画で動いている。

    なぜ UWB か

    ドラゴンレーダー作品は過去にもファンメイドで何度か試みられているが、ほぼ全て GPSBLE RSSI画像認識 ベースで作られていて、

    • GPS は屋内で精度が出ない、しかも数 m 単位の誤差
    • BLE RSSI は壁 1 枚で値が乱れる、向き判定不可
    • 画像認識はカメラ視野内しか見えない、隠されたら終わり

    5 cm 精度で距離が分かる、向きも 1〜2° で分かる、見通し外でも壁 1 枚なら抜ける」という条件を満たすのは UWB が唯一の現実解。スマホ (iPhone 11 以降、Pixel 6 Pro 等) にも入っている技術で、AirTag が「家具の裏のものを正確に指し示す」のはこれ。

    ハード的には NXP の SR150 (ホスト + AoA 対応) と SR040 (タグ専用、超低消費電力) を採用する。Murata から評価ボード (Type 2BP / Type 2DK) が出ていて、国内技適も取られている。

    なぜバンダイ風 UI か

    「Apple Find My っぽい現代的な UI」も検討したが、面白くない。

    ドラゴンレーダーは 1984 年に玩具として実物が存在する (バンダイ商品)。緑の LCD に緑のドット、ゴールドの数字、コーラル色の三角ポインタ、円形フィルムを回すスイープ。子供だった世代が大人になった今でも、この画面を見せると「あっ」となる

    技術が当時のフィクションに追いついた瞬間を、当時のビジュアル言語のまま届ける。これが本作品のテーゼ。

    構成の全体像

    [ プレイヤー手持ち端末 ]
      ESP32-P4 (RISC-V 400MHz, PSRAM 32MB)
        + 800x800 円形 IPS LCD (Waveshare 3.4C)
        + LVGL 9 (Bandai 風レーダー UI)
        + microSD (BGM/SE/ボイス)
        + I2S Codec ES8311 + スピーカー
        +─[UART 3Mbps 有線]─ Type 2BP EVK (QN9090 + SR150 UWB)
    
                                        ↕ UWB 6.5GHz (Ch5/9)
    
    [ ドラゴンボール (タグ) × 7 個 ]
      Type 2DK EVK (QN9090 + SR040 UWB)
        + 樹脂筐体 (3D プリント、内側に光ファイバー散らし)
        + 単 4 電池 × 2

    ESP32-P4 と Type 2BP は 物理 UART で直接配線する。WiFi / BLE は技適まわりの判断から使わない。

    工程と現状

    Phase 内容 状態
    0 部品入手、SDK 入手、開発環境構築
    1 ESP32-P4 上で LCD + Bandai UI が動く (ダミーデータ)
    2a NXP UWBIOT SDK の MCUXpresso ヘッドレスビルド検証
    2b QN9090 ファーム改造で UWB ranging 結果を ASCII で吐かせる
    2c 改造ファームを焼いて、UART 3Mbps で距離 + 方位が流れることを実機確認
    2d ESP32-P4 ↔ Type 2BP の物理 UART 配線 + LVGL に実データ反映 進行中
    3 ドラゴンボール側 (Type 2DK) のタグ化 ファーム + 筐体 これから
    4 音声 / 効果音 / BGM これから
    5 全体筐体 CAD + 組立 これから

    開発開始は 2026-04-29、応募締切は 2026-06-22。今のところスケジュールに 1 週間以上の余裕がある。

    なぜここに書くか

    この作品は趣味として作るが、過程で得た技術知見 (UWB / NXP UWBIOT SDK / ESP-IDF / LVGL / 組込ハード設計 / 技適) は 同じことをやろうとする人にも役に立つはず。なので進行中の発見を分割して記事にしていく。

    最初に切り出すのは以下:

    1. dk6prog で QN9090 を USB ISP だけで焼く ← SWD ハード無しでもファーム書ける話
    2. MCUXpresso headless build でファームを 10 秒で回す ← IDE を立ち上げずにビルド
    3. UWB ranging が動かない原因は UCI 世代差だった ← 異種 UWB チップを組ませる時のハマりどころ
    4. ESP32-P4 × LVGL で Bandai ドラゴンレーダー UI を作る ← 玩具っぽい温かみある UI を LVGL で
    5. 技適未確認ボードをコンテストで使うなら ← 国内出展のための設計判断

    本作品の進捗は完成までこのブログで時系列に記録する。完成しなかったらそれもまた記録として残す。

    期待値の調整

    「ドラゴンレーダーが動く」とは何か、最初に明確にしておく:

    • ✅ 5 cm 程度の距離精度で近くにあるボールの距離が分かる
    • ✅ AoA でボールの方向 (左右) が分かる (前後判定は補助的)
    • ✅ 7 個まで同時表示できる (round-robin で測距)
    • ❌ 壁を貫通する透視能力は無い (UWB は壁 2 枚以上で減衰する)
    • ❌ 「ピッピッ」音は出るが、原作のサーチ音とは違う (著作権)

    現代の物理法則の上で実装可能な範囲で最大限フィクションに近づける、それが本作品の制約。

    完成記事 (動作デモ動画 + 全工程まとめ) は 6/22 直前に公開予定。それまでは過程の知見を順次共有する。

  • 技適未確認の WiFi ボードを国内コンテストに出すなら何を捨てるか

    要約

    中国メーカーから個人輸入した ESP32 系ボード (含む WiFi6 モジュール) を、国内のメイカーコンテストに展示することを考えるとき、最初にチェックすべきは「そのボード上の無線モジュールに技適マークがあるか」。

    技適マーク (Telec) は電波法に基づく型式認定で、これ無しの無線機を電波を出す状態で国内使用すると電波法違反になる。特例制度 (180 日試験運用) はあるが、公開イベント常時運用には合わない

    技適未確認ボードを「使うかどうか」を悩むのではなく、技適なしを前提にハードウェアアーキテクチャを設計し直すのが正しい意思決定。本記事ではその意思決定を最近実例ベースで整理する。

    何が問題なのか

    ESP32 系ボード、特に 「ESP32-P4 + ESP32-C6 (WiFi6 コンパニオン)」を組み合わせた製品は、Espressif 単体モジュールでは技適済の variant があるが、第三者メーカーが基板に載せて組み立てた完成品は技適マーク (基板上のシールド表面に R 003-XXXXXX の刻印) がそのまま継承されないことが多い。

    具体例:

    • Espressif の ESP32-C6-WROOM-1 モジュールには技適済 SKU が存在する
    • ところが Waveshare 系の「ESP32-P4-WIFI6-Touch-LCD-XC」のような完成品ボードに載っている ESP32-C6 が、技適済 SKU を使っているとは限らない
    • ボード上のシールドに R 番号が刻印されているか目視確認するしか手段がない

    筆者の手元の Waveshare ESP32-P4-WIFI6-Touch-LCD-3.4C は 目視で技適マーク確認できずだった。

    電波法の建付け

    総務省の電波法では、技適マーク (技術基準適合証明、特定無線設備の技術基準適合認定) 未取得の無線設備を国内で電波を発射する状態で使用すると違反となる (第 4 条 / 第 38 条系)。

    ただし救済として 電波法第 4 条の 2 に基づく「実験等無線局」/「特例制度」 があり、開発・実験目的に限り 180 日間届出ベースで使用できる枠が用意されている。

    ただし これは「自宅・自社の閉じた環境で開発する」想定の制度で、

    • 公開イベント (Maker Faire、コンテスト出展、商業展示) で「常時動いている」状態は実験運用とみなされにくい
    • 違反扱いになると刑事罰 (1 年以下の懲役または 100 万円以下の罰金) の可能性がある
    • 主催者側 (大型イベントは大概スタッフが電波法を理解している) から会場ルールでも止められる

    「コンテストに出す」という目的なら、特例制度を当てにせず WiFi/BT を最初から使わない設計にするのが現実解。

    設計上の意思決定

    筆者の場合は ESP32-P4 系ボードを LCD ホストとして使う前提があり、「ボード上の WiFi/BT を一切初期化せず、有線通信に切り替える」設計を採用した。

    具体的に何を犠牲にして何を残したか:

    機能 当初の構想 技適なし前提後
    無線測距 (UWB) UWB チップ (Murata SR150/SR040) で実装 同じ (UWB は別途技適済モジュールを使用)
    デバッグログ転送 WiFi で開発端末に飛ばす USB シリアルのみ
    センサノードとの通信 BLE / WiFi で散発接続 物理 UART 配線
    OTA ファーム更新 WiFi 経由 SD カード差し替え / USB
    Web 設定 UI スマホブラウザから WiFi 経由 LCD タッチ UI のみ

    物理配線で全部やる」と決めると、設計はむしろシンプルになる。ペアリング・認証・電波輻射の干渉考慮が消える。

    チェックリスト: 自分のボードで何を確認すべきか

    国内コンテスト用に中華製評価ボードを選ぶときは以下を順に:

    1. 基板上の無線モジュール (銀色シールド) 表面に技適マーク or R 003-XXXXXX の刻印があるか目視
    2. メーカー商品ページに「Japan / 技適 / Japanese certification」明示があるか
    3. 国内代理店経由なら、その代理店が技適済バージョンとして売っているか
    4. どれもダメなら → そのボードの WiFi/BT は使わない設計にするか、技適済の別ボード (ESP32-DevKitC-32E など) に置き換える

    技適済 ESP32 系の入手:

    • 国内代理店 (秋月電子、スイッチサイエンス、マルツ等) から正規流通品を買えば技適マーク済 (R 003-XXXXXX 刻印あり)
    • AliExpress / Taobao から個人輸入したものは大概グレー

    まとめ

    • 公開イベント展示が前提なら、WiFi/BT を使う設計を最初から避けるのが安全
    • 技適マークの有無は ボード本体ではなく、基板上の無線モジュールのシールドに刻印されているかで判断
    • ハードウェア構成を「技適なしを前提に再設計」する方が、特例制度を当てにするより早くて確実
    • 開発生産性的にも、有線通信に絞ると ペアリング・認証・電波干渉が消えてシンプルになることが多い

    最近の組込デバイスは「WiFi/BT が付いてるのがデフォルト」になっていて、それを使わない設計を最初から組むのは少し勇気がいるが、コンテスト出展という目的を考えると、技適まわりは設計上の制約ではなく前提として扱うべき項目。

    電波法は古い法律だが、まだ生きている。

  • ESP32-P4 × LVGL で「バンダイ ドラゴンレーダー」風 UI を本気で再現する

    要約

    ESP32-P4 + 800×800 円形 LCD (Waveshare 3.4C / IPS) という贅沢なハードで、1980 年代バンダイから出ていた玩具版「ドラゴンレーダー」の画面表示を再現してみた記録。

    ポイントは 4 つ:

    1. ── 緑 CRT 背景 + 太い黒グリッド + ゴールド数字 + コーラル三角ポインタ
    2. 三角ポインタ ── 「現在地」を表す塗り潰し三角を LVGL の lv_obj 矩形スタックで描く (LVGL 9 にはネイティブの三角プリミティブが無いため)
    3. スイープ ── 円弧 30 度幅の lv_arc を回し続ける
    4. 光点 (ドラゴンボール) ── 距離→対数スケール、AoA 方位→極座標変換で配置

    LVGL は工業 HMI に最適化されていて「アニメ・玩具風の温かみある画面」を作るのが意外と難しい。本記事はその難易度をどう越えたかの記録。

    ハードと前提

    • MCU: ESP32-P4 (RISC-V dual-core 400MHz、PSRAM 32MB)
    • LCD: 800×800 round IPS (Waveshare 3.4C)
    • フレームワーク: ESP-IDF v5.5.4、LVGL 9.5.0、esp_lvgl_adapter 経由
    • 描画モード: triple partial buffer + tear-avoid

    LCD は MIPI-DSI で繋がっていて、フレームバッファは PSRAM に乗る。Waveshare BSP (waveshare/esp32_p4_wifi6_touch_lcd_xc) を managed component で取り込めば LVGL までの配線は数行で済む。

    カラーパレット

    玩具版の写真を観察してパレットを取った:

    // theme.h
    #define DR_COLOR_BG       lv_color_hex(0x2A9040)  // 緑 CRT
    #define DR_COLOR_GRID     lv_color_hex(0x001008)  // ほぼ黒
    #define DR_COLOR_SWEEP    lv_color_hex(0xA0FFB0)  // スイープのトレイル
    #define DR_COLOR_TEXT     lv_color_hex(0xFFD700)  // ゴールド (数字 / ラベル)
    #define DR_COLOR_DOT      lv_color_hex(0xFFC800)  // ドラゴンボール (オレンジゴールド)
    #define DR_COLOR_POINTER  lv_color_hex(0xFF6E50)  // コーラル (中央三角)

    ポイントは 背景の緑をくすませる こと。サチった #00FF00 を使うと「LED マトリクス」っぽくなり玩具感が消える。 #2A9040 くらいのやや暗くて青みのある緑が CRT 蛍光体っぽくて良い。

    グリッド

    LVGL の標準では「画面全体に等間隔の格子線」を引くプリミティブは無いので lv_line_create を縦横ループで配置する。

    #define GRID_STEP 50
    for (int x = GRID_STEP; x < DR_SCREEN_SIZE; x += GRID_STEP) {
        static lv_point_precise_t pts[20][2];
        int i = x / GRID_STEP - 1;
        pts[i][0].x = x; pts[i][0].y = 0;
        pts[i][1].x = x; pts[i][1].y = DR_SCREEN_SIZE;
        lv_obj_t *line = lv_line_create(parent);
        lv_line_set_points(line, pts[i], 2);
        lv_obj_set_style_line_color(line, DR_COLOR_GRID, 0);
        lv_obj_set_style_line_width(line, 2, 0);
        lv_obj_set_style_line_opa(line, LV_OPA_80, 0);
    }

    lv_point_precise_tstatic にしないと描画タイミングでスタック上のメモリが消えて画面に変な線が現れる。LVGL のプリミティブはポインタを保持するだけで、内部コピーしないことが多いので注意。

    三角ポインタ ── 一番苦戦したパーツ

    LVGL 9 に 塗り潰し三角プリミティブは無いlv_canvas で自前描画してもいいが、800×800 のフルキャンバスを PSRAM に保持するのは重い。

    そこで採用したのが 水平スキャンラインで矩形を積む方式:

    #define PT_SIZE 16     // 三角の半幅
    #define PT_STEP 2      // スキャンラインの厚さ
    const int y_top    = DR_CENTER - PT_SIZE;
    const int y_bottom = DR_CENTER + PT_SIZE / 2;
    const int height   = y_bottom - y_top;
    
    for (int y = y_top; y <= y_bottom; y += PT_STEP) {
        int dy = y - y_top;
        int half_w = (dy * PT_SIZE) / height;
        if (half_w < 1) half_w = 1;
    
        lv_obj_t *seg = lv_obj_create(parent);
        lv_obj_remove_style_all(seg);
        lv_obj_set_size(seg, half_w * 2, PT_STEP);
        lv_obj_set_pos(seg, DR_CENTER - half_w, y);
        lv_obj_set_style_bg_color(seg, DR_COLOR_POINTER, 0);
        lv_obj_set_style_bg_opa(seg, LV_OPA_COVER, 0);
    }

    頂点が上、底辺が下の塗り潰し三角を、PT_STEP=2 ピクセル幅の矩形を ~12 個積んで再現する。メモリは矩形 12 個分しか食わない (内部的に lv_obj は数百バイト)。LVGL の合成エンジンが矩形描画はべらぼうに速いので描画コストもタダ同然。

    回転させたい場合は lv_obj_set_style_transform_angle でグループ全体を回せばよい。三角ポインタを「機体の向きに合わせて回転」させる用途にも使える。

    スイープアーク

    円弧を回すのは LVGL のお家芸:

    lv_obj_t *arc = lv_arc_create(parent);
    lv_obj_set_size(arc, DR_RADAR_RING_R2 * 2, DR_RADAR_RING_R2 * 2);
    lv_obj_align(arc, LV_ALIGN_CENTER, 0, 0);
    lv_arc_set_bg_angles(arc, 0, 360);
    lv_arc_set_rotation(arc, 270);
    lv_arc_set_angles(arc, 0, 30);  // 30 度幅
    lv_obj_set_style_arc_color(arc, DR_COLOR_SWEEP, LV_PART_INDICATOR);
    lv_obj_set_style_arc_width(arc, DR_RADAR_RING_R2, LV_PART_INDICATOR);
    lv_obj_set_style_arc_opa(arc, LV_OPA_30, LV_PART_INDICATOR);
    
    lv_anim_t a;
    lv_anim_init(&a);
    lv_anim_set_var(&a, arc);
    lv_anim_set_exec_cb(&a, (lv_anim_exec_xcb_t)lv_arc_set_rotation);
    lv_anim_set_values(&a, 270, 270 + 360);
    lv_anim_set_duration(&a, 1500);
    lv_anim_set_repeat_count(&a, LV_ANIM_REPEAT_INFINITE);
    lv_anim_start(&a);

    arc_width = R2 (リング全径) にして、塗り部分の不透明度を 30% に落とすと「スポットライトが回転する」風になる。トレイル感を出したいなら 2 本目のアークを反対側に置いて opa=10% にする手もある。

    光点 (ドラゴンボール) 配置

    UWB ranging から流れてくる distance_mmazimuth_deg を画面座標に変換する:

    static int32_t distance_to_radius_px(uint16_t distance_mm) {
        if (distance_mm == 0) return 0;
        if (distance_mm >= DR_RANGE_MAX_MM) return DR_RADAR_RING_R2;
        float ratio = logf(1.0f + (float)distance_mm / 1000.0f)
                    / logf(1.0f + (float)DR_RANGE_MAX_MM / 1000.0f);
        return (int32_t)(DR_RADAR_RING_R2 * ratio);
    }
    
    static void polar_to_cartesian(uint16_t distance_mm, int16_t azimuth_deg,
                                   int32_t *x, int32_t *y) {
        int32_t r = distance_to_radius_px(distance_mm);
        float az_rad = azimuth_deg * M_PI / 180.0f;
        *x = DR_CENTER + (int32_t)(r * sinf(az_rad));
        *y = DR_CENTER - (int32_t)(r * cosf(az_rad));
    }

    距離 → 半径は 対数スケールにする。線形だと「近い時は中心にくっつく、遠い時はリングの端に張り付く」となるが、人間の空間感覚に合うのは対数。1m での解像度を大きく取り、10m の精度を犠牲にする。

    光点本体は丸オブジェクトに シャドウで発光感を出す:

    lv_obj_t *dot = lv_obj_create(parent);
    lv_obj_set_size(dot, 30, 30);
    lv_obj_set_style_radius(dot, LV_RADIUS_CIRCLE, 0);
    lv_obj_set_style_bg_color(dot, DR_COLOR_DOT, 0);
    lv_obj_set_style_shadow_color(dot, DR_COLOR_DOT, 0);
    lv_obj_set_style_shadow_width(dot, 28, 0);
    lv_obj_set_style_shadow_opa(dot, LV_OPA_70, 0);

    shadow_width = 28 (本体径とほぼ同じ) で等方ハローができ、ドラゴンボールの「内側から光ってる」雰囲気が出る。

    ハマりどころメモ

    症状 原因 / 対処
    起動時にリンカが --enable-non-contiguous-regions discards section ... 21KB ESP32-P4 v1.x シリコンで CONFIG_SPIRAM_XIP_FROM_PSRAM 有効だと IRAM 溢れ → 無効化
    LCD が真っ黒 CONFIG_LV_USE_PERF_MONITOR=nCOMPILER_OPTIMIZATION_SIZE=y、L2 cache 128KB に落とす
    描画がカクつく LV_COLOR_DEPTH=16、frame buffer を PSRAM に置く、triple_partial モード
    緑がサチった LED 色になる パレットを #2A9040 系のくすんだ緑に。#00FF00 は使わない

    動かしてみた

    LCD のフォトを撮って横で玩具のドラゴンレーダーと比較した時、「電池入れて 30 年経った玩具」っぽい質感が出てしまった (緑が少しくすんでいる + 黒グリッドが太い + フォントがゴールド) ── 結果としてその”古色感”が逆に良かった。

    LVGL は HMI 用ライブラリだけど、プリミティブを工夫すれば「玩具・アニメ的な温かみのある UI」も十分作れる。三角プリミティブが無いから諦める前に、矩形スタックで作れないか考えてみる価値はある。

  • UWB ranging が動かない真犯人は「UCI 世代差」だった話

    要約

    異なる NXP UWB チップ (SR150 / SR040) を載せた 2 つの評価ボードを ranging させようとしたら、

    • 両者の RANGE_DATA_NTF (測距結果通知) に乗っている session ID が違って見える
    • 一方は 0x00000001、もう一方は 0x11223344

    「session ID が違うから pair しないんだ」と最初は誤診したが、SDK のソースを当たると 両方ともデフォルトで 0x11223344 を設定していることが判明。

    実際に違うのは UCI (UWB Command Interface) のプロトコル世代で、UCI v1.x は Session ID を payload にそのまま乗せるが、UCI v2.0 以降は Session Handle というラッパに置き換わる。通知フィールドが見た目同じバイト位置でも、解釈が違うので一致しない

    両方を UCI v1.31 世代の SDK に揃えた瞬間に ranging が成立した。誤診から本質に辿り着くまでの記録。

    環境

    • Initiator: NXP SR150 ベースの評価ボード (Murata Type 2BP EVK Rev 4.1)
    • Responder: NXP SR040 ベースの評価ボード (Murata Type 2DK EVK)
    • 両者にメーカープリビルド済の standalone ranging デモを焼いて、UART 経由で生の UCI トレースを観察

    最初の観測

    電源を入れて並べると、2 つの EVK はとりあえずお互いの存在は認識する (TX/RX が走る) のだが、ranging が完了しない。Murata 提供の Python テストスクリプトも distance を 1 度も print しないまま終わる。

    両方の UART を cat /dev/ttyUSB* で生キャプチャして UCI フレームを抜き出すと、

    • 2BP (SR150 / 新しい SDK) 側: NTF 内の session 識別フィールドが 01 00 00 00 (リトルエンディアン解釈で 0x00000001)
    • 2DK (SR040 / 古い SDK) 側: 同じ位置が 44 33 22 11 (リトルエンディアン解釈で 0x11223344)

    ぱっと見「2 つの session ID が違う、だから ranging が成立しない」と読みたくなる。これが間違いだった。

    SDK のソースを当たる

    それぞれの SDK のサンプルコードを grep -nrR SESSION_ID する。

    demos/SR1XX/demo_ranging_controller/demo_ranging_controller.c:
      #define RANGING_APP_SESSION_ID 0x11223344
    
    demos/SR040/demo_tracker_sr040/app_Ranging_Cfg.h:
      #define RANGING_APP_SESSION_ID 0x11223344

    両方とも 0x11223344 を設定している。つまりアプリケーション層で投げているセッション ID は一致しているはず。

    ということは、UART で見ている「session 識別フィールド」がそもそも別物を指している可能性が高い。

    UCI 仕様書を読み直す

    NXP の SR150 UCI Specification を当たると、v1.x と v2.0 で RANGE_DATA_NTF の最初の数バイトの定義が変わっている:

    バイト位置 UCI v1.x UCI v2.0
    0-3 Sequence Number Sequence Number
    4-7 Session ID (アプリが設定した 32-bit 値そのまま) Session Handle (UCI スタックが割り当てた識別子)
    8-11 Rcr Block Index Rcr Block Index

    v2.0 で導入された Session Handle は SDK 内部で SessionInit 時に動的に発行される (例: 0x00000001 から順に割り当てる) 識別子で、アプリが設定した Session ID とは別物。アプリ層 API では Session ID を渡すが、UCI バイナリ層では Handle に置き換わる。

    つまり、

    • SR150 + 新しい SDK: UCI v2.0 → NTF の 4-7 バイト目には Session Handle (0x00000001) が乗る
    • SR040 + 古い SDK: UCI v1.x → NTF の 4-7 バイト目には Session ID (0x11223344) がそのまま乗る

    両者は同じバイト位置に別の意味の値を入れていて、当然 ranging session の照合に失敗する。「session ID が違う」のではなく「session 識別の規約が違う」が本質だった。

    揃え方

    選択肢は 2 つ:

    1. SR040 側の SDK を v2.0 世代に上げる
    2. SR150 側の SDK を v1.x 世代に下げる

    SR040 はメーカー (Murata) が校正済の standalone バイナリを v1.x 世代でしか出してくれていなかったので、現実解は (2)。SR150 側を NXP UWBIOT SDK v04.04.03 (UCI v1.31) の bare バイナリに差し替え、両者の UCI 世代を v1.31 に統一した。

    結果: 焼き直して電源入れた瞬間、UART に TWR[0].distance : 26 のような distance 値が湯水のように流れ始めた。20 秒で 200+ サンプル、distance は両端で平均 0.5cm 以内に一致 (DS-TWR が正しく完走している証拠)。

    教訓

    • 「session ID が違う」のように『見える』状態を素直に取らない。NTF のバイト位置と UCI 仕様書の世代を必ず照合する
    • 異種チップを組ませる時は UCI 仕様の世代揃えが必須。SR150 と SR040 は同じ NXP 製でも世代が異なる SDK で出てくる
    • デバッグの第 1 歩は SDK ソースの grep。「定数として何を設定しているか」を直接確認すれば、UART で観測した値との差分が見える

    UCI が業界標準 (FiRa Consortium 仕様) になりつつあるとはいえ、世代差を吸収するレイヤはまだ薄い。UWB を触る時は両端の SDK バージョンを意識して揃えることが、ranging を素直に動かす近道。

  • MCUXpresso IDE の headless build で組込ファームを 10 秒で回す

    要約

    NXP が出している MCUXpresso IDE は Eclipse CDT ベースなので、見た目は GUI でも中身は コマンドラインから単独でビルドを叩ける

    mcuxpressoide -application org.eclipse.cdt.managedbuilder.core.headlessbuild という呪文を覚えてしまえば、IDE を立ち上げる時間も、Eclipse のウィンドウマネージャ周りで詰まる時間もゼロにできて、1 回 9 秒 でビルドが回るようになる。

    「IDE で開いて、メニューから Build をクリックして、終わるのを待って」というループを 10 倍速くしたい人向けの記事。

    なぜ GUI で回すと遅いか

    NXP UWBIOT SDK のようなボリュームある SDK を MCUXpresso GUI で開くと、

    1. ワークスペースを開くだけで 30 秒以上
    2. インデックス再構築で CPU 100% が 2〜3 分
    3. Build のたびに「Project Indexer Update」が走り直して数十秒

    ソースを 1 行直して焼き直したいだけなのに毎回これに付き合うのは消耗する。

    Eclipse CDT は元々 IBM (JDT) から派生した CDT 部分でヘッドレスビルダを持っていて、-application org.eclipse.cdt.managedbuilder.core.headlessbuild を渡せば IDE の UI を起動せずに同じツールチェーンで .project / .cproject をビルドできる

    環境

    • Ubuntu 24.04
    • MCUXpresso IDE for Linux (/usr/local/mcuxpressoide-25.6.136/ide/)
    • 内蔵 JRE (OpenJDK 17) と内蔵 arm-none-eabi-gcc を使う

    ヘッドレスビルド呪文

    xvfb-run --auto-servernum --server-args="-screen 0 1024x768x24" \
      /usr/local/mcuxpressoide-25.6.136/ide/mcuxpressoide \
      -nosplash \
      -application org.eclipse.cdt.managedbuilder.core.headlessbuild \
      -data /tmp/mcux-ws \
      -import /path/to/project/MyProject \
      -cleanBuild "MyProject/Debug"

    各オプションの意味:

    オプション 意味
    xvfb-run Linux サーバや CI で X が無くても Eclipse の GTK 依存を満たす仮想ディスプレイを立てる
    -nosplash スプラッシュ画面を出さない (ヘッドレスでは不要)
    -application <id> Eclipse プラットフォームの起動アプリケーションを指定。CDT のヘッドレスビルダはこの ID
    -data <dir> Eclipse ワークスペース。事前に存在しなくても自動作成される
    -import <project> .project のあるディレクトリ。プロジェクト名はその中身から読み取られる
    -cleanBuild "Project/Config" クリーンビルドする対象を「プロジェクト名/ビルドコンフィグ名」で指定

    ポイントは Project/Config がプロジェクト内の .cproject に書かれている名前と完全一致しないとダメ という所。GUI で右クリック → Properties → C/C++ Build → 設定タブのリスト名と一致する。

    .cproject ファイル内で:

    <configuration name="Debug" ...>
    <configuration name="Release" ...>

    これを grep するのが手っ取り早い。

    結果

    筆者のケース (NXP UWBIOT SDK v04.04.03 + Murata Type 2BP 向け demo_ranging_controller) では:

    [1/12] Performing build step for 'bootloader'
    ...
    [10/12] Linking CXX executable RhodesV4_SE.axf
    Memory region         Used Size  Region Size  %age Used
       PROGRAM_FLASH:      353180 B     629471 B     56.11%
                SRAM:       32576 B        87 KB     36.57%
               SRAM1:         45 KB        64 KB     70.31%
    [11/12] Generating binary image
    ...
    Build Finished. 0 errors, 1 warnings. (took 9s.620ms)

    9.6 秒で .axf + .bin まで生成される。GUI で開きっぱなしより速い (差分ビルドなら GUI でも数秒だが、起動時間を含めると勝てない)。

    おまけ: シェル関数化

    毎回タイプするのは流石に辛いので zshrc / bashrc に入れておく:

    mcux_build() {
      local proj="$1"
      local cfg="${2:-Debug}"
      xvfb-run --auto-servernum --server-args="-screen 0 1024x768x24" \
        /usr/local/mcuxpressoide-25.6.136/ide/mcuxpressoide \
        -nosplash \
        -application org.eclipse.cdt.managedbuilder.core.headlessbuild \
        -data /tmp/mcux-ws \
        -import "$proj" \
        -cleanBuild "$(basename "$proj")/$cfg"
    }
    
    # 使う側
    mcux_build /path/to/MyProject Debug

    これで mcux_build . のような短い呼び出しで CI でも開発でも使える。

    ハマりどころメモ

    症状 原因 / 対処
    No projects matched -cleanBuild の引数が .cproject のコンフィグ名と一致してない
    DISPLAY 環境変数が設定されてない xvfb-run を使う、もしくは X 転送 (ssh -X)
    Workspace not initialized -data で指定したパスのパーミッションが書込み不可。/tmp/ 以下が無難
    ビルドが空のサイレント終了 -import のパスが間違っているとログも吐かず終わることがある。絶対パス推奨

    何が良くなったか

    • 編集 → ビルド → 焼き のループが体感 1 桁速くなる
    • CI で組込ファームをビルド できる。Pull Request ごとにファームのバイナリ差分を取れる
    • IDE GUI のメモリ食いが消えるので、開発端末のリソースを LLM チャットや別 IDE に回せる

    NXP に限らず Eclipse CDT ベースの組込 IDE (STM32CubeIDE 等) は同じ仕組みで動く。「IDE を立ち上げてビルドする」習慣をやめると、組込開発の生産性は素直に上がる

  • dk6prog (SPSDK) で QN9090 を USB ISP だけで焼く

    要約

    NXP QN9090 を載せた評価ボード (Murata Type 2BP EVK / 2DK EVK 等) のファームを書き換えるとき、SWD (J-Link / MCU-Link Pro) が標準ルートとされるが、ボードのデバッグピンが折れていたり配線できない事情があるとそこで詰まる。

    そこで便利なのが SPSDK に含まれる dk6prog という Python ツールで、USB ケーブル 1 本だけで ISP (In-System Programming) モードに入って書き換えできる。

    本記事では Linux 上で pip install spsdk だけで導入して、QN9090 のフラッシュ全領域を吸い出してバックアップ → カスタムファームを書き込む手順を 1 枚にまとめる。

    なぜこれが効くか

    QN9090 (および兄弟の DK6 シリーズ) は ROM ブートローダに ISP モードを備えていて、特定ピン (PIO_5 / ISP_ENTRY) を引いた状態でリセットすると UART 経由でフラッシュ操作を受け付ける。Murata / NXP の DK6 EVK はオンボードに FTDI FT230X USB-UART ブリッジが載っていて、USB ケーブル 1 本でこの UART に到達できる。

    SWD で書ける環境を持っていない人 (J-Link を持っていない、MCU-Link Pro が手元にない、開発端末が Windows 縛りで動かないなど) でも、Python が動けば書けるのが大きい。

    手順

    0. 前提

    • 開発端末: Ubuntu 24.04 (Linux 一般、macOS でも同様)
    • ターゲット: NXP QN9090 ベースの DK6 EVK (例: Murata Type 2BP / 2DK)
    • USB ケーブル (データ通信対応 micro-USB、充電専用品はダメ)

    1. SPSDK をインストール

    pip install spsdk pyftdi

    (venv 推奨。SPSDK は依存が多めなので素の Python に入れると他プロジェクトと衝突しやすい。)

    2. USB 接続して認識を確認

    EVK と PC を USB でつないだ状態で:

    dk6prog listdev
    List of available devices:
    DEVICE ID: DM86TTWC, VID: 0x403, PID: 0x6015, ...

    DEVICE ID がそのまま -d の引数になる。Murata 出荷品は全 EVK で同じシリアルが振られていることがあり、複数挿していると後続コマンドで衝突するので、作業時は対象 1 個だけ挿す

    3. ISP モードに入れて疎通確認

    EVK の ISP ボタン (PIO_5 / ISP_ENTRY) を押しながら RESET を離すと ISP モードに入る (ボードによっては「ISP」シルク印刷がある専用ボタン、ない場合は基板上のテストパッドを GND に落とす)。

    dk6prog -d DM86TTWC info
    Chip ID: 0x88888888
    ROM Version: 0x140000cc
    
      Memory   Memory ID   Base Address   Length    Sector Size
    ----------------------------------------------------------------
      FLASH    0           0x0            0x9de00   0x200
      PSECT    1           0x0            0x1e0     0x10
      ...

    info が返ってくれば疎通 OK。

    4. 焼く前に「出荷時 FW」をバックアップする

    これが本記事で一番伝えたい部分。Murata の EVK は出荷時にメーカー校正済の FW が入っており、これを潰すと TX パワーや水晶発振の校正が失われる。

    dk6prog -d DM86TTWC read 0 0x9DE00 -o factory_backup.bin

    0x9DE00 (約 632KB) が FLASH 全長。1 度だけ取って、git に上げる代わりに「火事から助かる」場所に保管しておくと心理的に強い。

    5. カスタムファームを書き込む

    dk6prog -d DM86TTWC erase 0 0x9DE00
    dk6prog -d DM86TTWC write 0 my_custom_fw.bin

    (erase は write の中で勝手にやってくれる版もあるが、明示的に erase してから write する方が事故が少ない。)

    6. 復旧したいとき

    dk6prog -d DM86TTWC erase 0 0x9DE00
    dk6prog -d DM86TTWC write 0 factory_backup.bin

    これで step 0 の状態に戻る。安心。

    ハマりどころメモ

    症状 原因 / 回避
    2 USB devices match URL 同じシリアルの EVK が複数挿さっている。物理的に片方を抜く
    no langid permission issue pyftdi backend は libusb で root 権限要求することがある → sudo -E で実行 (環境変数を保持しないと PATH が消えるので -E 必須)
    Failed to enter ISP mode ISP ボタン押しが遅い / 早い。USB を抜いてボタン押しっぱなしで挿し直す → リセットボタンも併用
    Chip ID: 0x88888888 のまま これは正常 (ISP モードで MAC アドレスが返らない仕様)

    何が良くなったか

    • SWD ハードが要らない (J-Link / MCU-Link Pro は 1 台 1〜2 万円)
    • OS を選ばない (Windows 限定の DK6Programmer.exe に縛られない)
    • CI に組み込める (シェルから叩けるので、ビルド → 焼き → テスト を一筆書きできる)

    「SWD ピンが曲がってた」「IDE のドライバが入らない」みたいな ハードウェアやアーキテクチャに本質的でない問題で停滞している人にとって、dk6prog 経由のフラッシュは数時間〜数日を取り戻すポテンシャルがある。

    参考

  • Meta×AMD 1000億ドル契約とバッテリー不要IoT:海外インフラ・ハードウェアの激動

    2026年2月最終週、テックインフラの世界で歴史的な動きがありました。MetaとAMDの1000億ドル規模のチップ契約と、バッテリー不要IoTの業界標準化を目指すAmbient IoT Allianceの発足です。日本ではあまり報道されていませんが、いずれも今後のテック業界の方向を決定づける重要なニュースです。

    Meta × AMD:史上最大のAIチップ契約

    2月24日、MetaがAMDと最大1000億ドル(約15兆円)の複数年チップ契約を発表しました。AI史上最大の単一契約です。

    契約の詳細

    • ハードウェア:AMD Instinct MI450ベースのカスタムGPU + 第6世代EPYC CPU
    • 規模:最大6ギガワットのAMD GPUキャパシティを展開
    • 出荷開始:2026年下半期
    • 株式ワラント:AMDがMetaに対し、最大1億6000万株(発行済み株式の約10%)を1株あたり0.01ドルで発行するワラントを付与。マイルストーン達成に応じて権利確定

    なぜ重要か

    注目すべきは3つのポイントです。

    第一に、Nvidiaの独占が崩れ始めていること。MetaはNvidiaにも数百万個のGPUを発注していますが、AMDに1000億ドルを投じることで戦略的に分散化を図っています。

    第二に、株式ワラントという前例のないスキーム。ハードウェア契約に株式報酬を組み合わせるのは異例で、AMDの株価(とMetaの交渉力)に大きなインパクトを持ちます。

    第三に、AIインフラ投資の規模感。Alphabet、Amazon、Meta、Microsoftの4社で2026年のAIインフラ投資は合計約6500億ドルに達する見通しです。2025年の約4100億ドルから58%増です。

    Ambient IoT Alliance:バッテリー不要IoTの標準化

    2月23日、Ambient IoT Allianceが設立されました。バッテリー不要のIoTデバイスの標準化を推進する業界団体です。

    参加企業

    設立メンバーはAtmosic、Infineon、Intel、PepsiCo、Qualcomm、VusionGroup、Wiliot。半導体大手からPepsiCoのような消費財企業まで参加しているのが特徴です。

    エナジーハーベスティングIoTとは

    環境中のエネルギー(無線電波、光、振動、温度差など)を収集して動作するIoTデバイスです。バッテリーが不要なため、以下のような用途が想定されています。

    • サプライチェーン:商品の物流トラッキング(バッテリー交換不要)
    • 小売:電子棚札、在庫管理センサー
    • 環境モニタリング:農業、建物の構造監視
    • 医療:使い捨てセンサーパッチ

    PepsiCoの参加は、大手消費財企業がサプライチェーンの可視化にバッテリーレスIoTを本気で検討していることを示しています。ESP32のような汎用IoTチップとは異なるレイヤーですが、IoT業界全体の進化を加速させる動きです。

    まとめ

    ハードウェアとインフラの世界では、AI投資の爆発的な拡大と、IoTの新しいパラダイム(バッテリーレス)が同時進行しています。Nvidiaの独占崩壊とバッテリー不要IoTの標準化は、今後数年のテック業界を大きく変える可能性があります。日本でも注視すべきトレンドです。

  • ESP32の最新動向まとめ(2025-2026):新チップ・Wi-Fi 6・AI対応まで完全解説

    IoT開発者にとって定番のESP32ファミリーですが、2025〜2026年にかけて大きな進化を遂げています。新チップの量産開始、Wi-Fi 6対応、AI推論機能の強化など、注目すべきアップデートが目白押しです。この記事では、ESP32エコシステムの最新動向を網羅的にまとめます。

    新チップラインナップの拡充

    Espressifは従来のESP32シリーズに加え、用途特化型の新チップを続々とリリースしています。

    ESP32-P4:高性能HMI向けSoC

    ESP32-P4はEspressif史上最もパワフルなチップです。デュアルコアRISC-V(最大400MHz)を搭載し、MIPI-CSI/DSIによる1080pカメラ入力・ディスプレイ出力、ハードウェアH.264エンコーダ(1080p@30fps)、55本のGPIOを備えています。

    注目点はWi-Fi/Bluetoothを内蔵しないこと。無線通信はESP32-C6などのコンパニオンチップと組み合わせる設計です。WaveshareやGUITIONからP4+C6を統合した開発ボード(約14ドル〜)が発売されています。

    ESP32-C5:業界初デュアルバンドWi-Fi 6対応MCU

    2025年5月に量産開始されたESP32-C5は、RISC-Vベースで2.4GHz + 5GHzのデュアルバンドWi-Fi 6(802.11ax)に対応した業界初のMCUです。従来のESP32はすべて2.4GHzのみだったため、5GHz帯が使えるのは大きな進化です。さらにBluetooth 5やIEEE 802.15.4(Zigbee/Thread)にも対応しています。

    CES 2026で発表:ESP32-E22とESP32-H21

    ESP32-E22はWi-Fi 6E(2.4/5/6GHz トライバンド)に対応し、2×2 MIMOで最大2.4Gbpsのデータレートを実現。デュアルコア500MHz RISC-Vプロセッサを搭載し、無線コプロセッサとして動作します。

    ESP32-H21は超低消費電力BLE MCUで、96MHz RISC-V、320KB RAM、BLE + IEEE 802.15.4対応。ウェアラブルやバッテリー駆動IoTノード向けです。

    開発フレームワークの進化

    ESP-IDF 6.0(2026年2月リリース)

    ESP-IDFのメジャーアップデートであるv6.0が2026年2月13日にリリースされました。主な変更点はMbedTLS v4シリーズへのアップグレードとPSA Crypto APIへの移行です。暗号化APIの使い方に破壊的変更があるため、既存プロジェクトの移行には注意が必要です。

    Arduino ESP32 Core 3.x

    Arduino ESP32 Core 3.0はESP-IDF 5.1ベースに刷新され、ESP32-C6やESP32-H2のサポートが追加されました。8つの周辺機器APIが更新され、ネイティブI2Cオーディオライブラリ、Ethernet SPIサポート(W5500、DM9051等)も追加されています。2.x系からの移行には破壊的変更があるため注意が必要です。

    セキュリティ:CVE-2025-27840「バックドア」騒動

    2025年3月、ESP32に29個の文書化されていないBluetooth HCIコマンドが見つかり「バックドア」として話題になりました。しかしEspressifは、これらはデバッグ用コマンドであり物理アクセスなしにはリモートで悪用できないと説明。ESP-IDF v5.5でこれらのコマンドをデフォルト無効化する修正が行われました。

    Matter/Thread対応の成熟

    スマートホーム標準プロトコル「Matter」への対応が着実に進んでいます。ESP32ファミリーはMatter 1.0認証を取得済みで、Thread Border RouterソリューションもThread V1.3認証を取得しています。ESPHome 2025.6.0ではOpenThreadサポートが追加され、ESP32-C6/H2をThreadエンドポイントとしてHome Assistantに直接接続できるようになりました。

    まとめ

    ESP32ファミリーは単なるWi-Fiマイコンから、Wi-Fi 6/6E、Thread/Matter、AI推論、1080p映像処理まで対応する総合IoTプラットフォームへと進化しています。用途に応じて最適なチップを選べるラインナップの充実が最大の魅力です。次回はESP32でのAI/TinyMLについて詳しく解説します。

  • ESP32-S3でTinyML入門:エッジAI推論を実装する方法

    クラウドに頼らず、マイコン上で直接AI推論を行う「TinyML」が注目を集めています。ESP32-S3はベクトル命令拡張を備え、TinyMLに最適なチップの一つです。この記事では、ESP32-S3でのエッジAI開発について、ツールチェーンからモデルデプロイまで解説します。

    なぜESP32-S3なのか

    ESP32-S3はデュアルコアXtensa LX7(最大240MHz)に加え、ベクトル命令拡張を搭載しています。これにより畳み込みニューラルネットワーク(CNN)の演算が高速化されます。ESP-NNライブラリがこれらの命令を最適に活用し、推論電力は約130〜157mW、レイテンシはモデルサイズに応じて7ms〜536msです。

    実用的な性能として、小型CNNなら約20FPS、中規模モデルでも約1FPSで推論可能です。顔検出、動体検知、音声コマンド認識などのユースケースに十分な性能です。

    ESP-DL:Espressif公式ディープラーニングライブラリ

    Espressifが提供するESP-DLは、ESP32向けの公式ディープラーニングフレームワークです。2025年に大幅なアップデートが行われました。

    ESP-DL v3.2(2025年10月)の主な特徴:

    • 独自のモデルフォーマット「.espdl」(FlatBuffersベースで高速なゼロコピー読み込み)
    • 8bit/16bit量子化サポート
    • ESP32-S3/P4向けのアセンブリ最適化カーネル(PIE命令活用)
    • Conv、Gemm、Add、Mulなどの主要演算をハードウェアアクセラレーション

    ESPDet-Pico:リアルタイム物体検出

    2025年4月にリリースされたESPDet-PicoはUltralytics YOLOv11ベースの軽量物体検出モデルです。歩行者検出、顔検出、COCO(YOLO11n)、猫・犬・手の検出など、事前学習済みモデルが提供されており、ESP32-S3上でリアルタイム推論が可能です。

    開発フロー

    ESP32でTinyMLを始める基本的な流れは以下のとおりです。

    1. モデル学習:PC上でPyTorch/TensorFlowでモデルを学習
    2. 量子化:esp-ppqツールで8bit/16bitに量子化し、.espdlフォーマットに変換
    3. デプロイ:ESP-DLライブラリを使ってESP32上でモデルをロード・推論
    4. 最適化:ESP-NN/PIE命令による自動最適化が適用される

    より手軽に始めたい場合はEdge Impulseがおすすめです。Webブラウザ上でデータ収集からモデル学習、ESP32へのデプロイまで一貫して行えます。深いML知識がなくても物体検出や音声認識モデルを構築できます。

    ESP32-S3 + カメラの活用例

    ESP32-S3はカメラインターフェースを備えているため、映像系のAIアプリケーションに強みがあります。DFRobotの「ESP32-S3 AI Camera」(8MB PSRAM、16MBフラッシュ)は、顔認識・物体検出・音声対話をエッジで実行できる開発ボードとして2025年に発売されました。

    代表的なユースケース:

    • Ringの代替となるローカル動作のドアベルカメラ
    • ペット検知・見守りカメラ
    • 製造ラインでの外観検査
    • 来客カウンターや人流分析

    まとめ

    ESP32-S3はTinyMLの実用的なプラットフォームとして成熟しました。ESP-DL v3.2とESPDet-Picoにより、YOLOベースの物体検出まで手の届く範囲になっています。数百円のマイコンでAI推論ができる時代を、ぜひ体験してみてください。

IP: 取得中...
216.73.217.150216.73.217.150