エイアイ・ラボ・ジャパン TAB5 / CAMERA M5Stack Tab5 · ESP32-P4 · MIPI-CSI Arduino-ESP32 3.3.11+ sample v1.0

sample program / for arduino ide

Tab5 の内蔵カメラを、Arduino で動かす。

映像を表示し、QRコードを読み、暗い場所では画面そのものをライト代わりにする。 Tab5 のカメラは素の Arduino では素直に動いてくれないので、実機で確かめた設定を 1本のスケッチにまとめました。つまずきどころも全部書いてあります。

  • Arduino-ESP32 3.3.11 以降
  • M5Stack Tab5
  • M5Unified
  • ESP_Video
  • quirc(同梱)
  • PSRAM 有効
QR: https://example.com
COLOR byte-swap | Z3 | q720 | cand 1 | blk 47%

緑枠 解析範囲(720×720) 赤枠 QR候補 Z3 使用ズーム

ページを開くと、実機と同じ起動カラーバー(白→赤→緑→青)が流れます。

spec / できること

3つの機能を、1本で

本体に組み込む前の切り分けを目的にしています。カメラだけを取り出して「どこまで動くか」を確かめられます。

view

カメラ映像の表示

1280×720 の映像を5インチ画面へ。c 生 / s バイト入替 / b 2値画像 / g 明暗伸長 の4モードを切り替えられます。

decode

QRコードの読み取り

quirc で解析し、読めた内容をシリアルへ QR,内容 で出力。中央を切り出して拡大するズームで、遠いQRにも当たります。

light

画面をライト代わりに

l キーで画面が真っ白に。5インチ・バックライト最大点灯の光を照明として使います。QRが読めたら自動で解除して結果を見せます。

解析の様子が見えます。 quirc が見ている2値画像(b)、QR候補の赤枠、2値画像の黒の割合(blk)を画面に出します。 「なぜ読めないのか」が目で分かるので、切り分けが速くなります。

trace / 初期化の順序

この順番に意味があります

コードもこの順で読めるように並べてあります。順序を入れ替えると動きません。とくに【1】と【4】は外せません。

  1. 【1】 XCLK 24MHz 必ず M5.begin() より先に設定します。ESP32-P4 の LEDC は全タイマがクロック源を共有するため、後から設定すると M5GFX のバックライト用と衝突して24MHzが出せません。
  2. 【2】 M5Unified 開始
    バックライト救済
    画面を横(1280×720)にし、backlightFix() で GPIO22 を素の出力として HIGH に固定します。M5GFX の調光が失敗して黒画面になるのを防ぎます。
  3. 【3】 カメラ給電
    IOエキスパンダ 0x43
    E1.P6 = CAM_RST を解放します。これをやらないとカメラは SCCB に応答しません(0x36 が出てきません)。
  4. 【4】 ESP_Video 初期化
    (dont_init_ldo)
    SCCB は SCL=G32 / SDA=G31、ポート0。csi.begin(cam_config, true) の true が必須です。MIPI PHY 用 LDO は画面と共用で、M5GFX が取得済みだからです。
  5. 【5】 フレーム取得 captureBuffer() で RGB565 1280×720 を取得します。バッファは2枚で交互に返ってきます。
  6. 【6】 QR解析タスク スタック64KBの専用タスクに隔離します。quirc_decode() は内部で約9KBのローカル配列を使うため、8KBの loopTask から呼ぶとスタック保護違反でリセットします。
  7. 【7】 画面表示 16ラインずつ内蔵RAMのバッファ(g_lbuf)へコピーしてから pushImage()。PSRAMから直接DMAすると黒画面になることがあります。
  8. 【8】 ライトモード 画面を真っ白にして照明にします。ライト中もカメラと解析は動き続け、QRが読めたら自動で解除します。

build / コンパイル・実行に必要な注意点

外すと動かない、8つのポイント

実機で1つずつ確かめた項目です。症状と原因が対応するように書きました。

Tab5 内蔵カメラ / Arduino IDE
注意点外すとどうなるか対処
XCLK を M5.begin() より先に設定 LEDC のタイマ衝突で24MHzが出ず、カメラが応答しない xclkSetup() を setup の最初に置く。ログが 24MHz AUTO t3 : 24000000 Hz なら成功
csi.begin(cam_config, true)=dont_init_ldo LDO の取り合いで初期化失敗(画面が先に取得している) 第2引数を必ず true にする
IOエキスパンダの 0x01 に 0xFF を書かない LCD_RST / TP_RST まで戻り、画面が真っ黒になる camPowerOn() の値(0x0D/0x0B/0x05/0x07/0x03)を変えない
GPIO22(バックライト)を直High に M5GFX のLEDC調光が失敗し、画面が真っ黒のまま backlightFix() を呼ぶ。setBrightness() だけでは直りません
QR解析は専用タスク(64KB)で行う decode.c が約9KBのスタックを使い、loopTask(8KB)では保護違反でリセット xTaskCreatePinnedToCore(qrTask, "qrTask", 64*1024, ...)
解析はバイト入替なしの輝度で行う 画像が壊れ、どんなQRデコーダでも読めなくなる 入替えるのは「画面表示用」だけ。解析と表示は別系統と覚える
表示は16ラインずつ内蔵RAMを経由する PSRAMから直接DMAしようとすると黒画面になることがある g_lbuf[1280*16] にコピーしてから pushImage()
このサンプルでは M5.update()・タッチを使わない 内蔵I2C(G31/G32)とカメラSCCBが同じピンで clear bus failed が多発 モード切替はシリアル入力で行う
いちばん大事な教訓:輝度のバイト順。 カメラが返す RGB565 は標準の並び(赤が上位ビット)です。ここを「バイト入替してから輝度を取る」と画像が壊れ、 どんなデコーダでも読めません。実機の生データで4種類のデコーダ(ZXing-C++ / ZBar / OpenCV / quirc)を比較して確定しました。
一方、画面表示はバイト入替すると色化け(赤青反転)が直ります(s キー)。「解析用」と「表示用」は別物です。

setup / ライブラリとファイルの置き方

ESP_Video の入れ方だけ、少し手がかかります

Arduino-ESP32 コア 3.3.11 以降には ESP_Video が同梱されていますが、環境によってはコア内の libraries\ESP_Video が見当たりません。その場合は配布しているZIPを次の形に展開してください。

# Windows の例
C:\Users\<ユーザー名>\Documents\Arduino\libraries\ESP_Video\src\ESP_Video.h
                                                     ↑ この形になるように

# NG: 1階層深くなっている
...\libraries\ESP_Video\ESP_Video\src\ESP_Video.h

展開後は Arduino IDE を再起動してください。スケッチ冒頭のマクロ定義(CONFIG_ESP_VIDEO_ENABLE_MIPI_CSI_VIDEO_DEVICE など)は消さないでください。

スケッチの置き方 — フォルダ名と .ino 名は必ず一致させます。

Arduino/
└─ Tab5_CameraQR_Sample/
   ├─ Tab5_CameraQR_Sample_20261015.ino   ← これを開く
   ├─ README.md
   ├─ quirc.h
   ├─ quirc.c
   ├─ quirc_internal.h
   ├─ decode.c
   ├─ identify.c
   └─ version_db.c
書き込みが Write timeout で失敗するときは、クラッシュ再起動ループでUSBが掴めなくなっています。 シリアルモニタを閉じる → リセットを約2秒長押し(内部の緑LEDが高速点滅=ダウンロードモード)→ 書き込み → もう一度リセット、の順で復帰します。USBケーブルはデータ通信対応のものをお使いください。

zoom / 小さく遠いQRのために

中心を切り出して、拡大する

遠いQRは、画像の中で1モジュールが2〜3px しかありません。quirc はモジュールが小さいと見失います。 そこで中心の 720/Z px を切り出して 720 に拡大し、モジュールを Z 倍にします。 どれが効くかは、実機の生画像でシミュレーションして決めました。

QRを小さくして「遠い状態」を作り、各ズームで読めるか確認(zx = ZXing-C++ / zb = ZBar / cv = OpenCV)
QRサイズモジュール 従来 Z=1Z=2Z=3Z=4
200px5.1pxzx zb –zx zb cvzx zb cv– zb –
150px3.8px– zb –– zb –zx zb cvzx zb cv
120px3.1px– zb –– zb –– zb cvzx zb cv
100px2.6px– zb –– zb –– zb –– – cv
150pxのQRは、従来は3デコーダ中0。 Z=3 / Z=4 では3デコーダ中3が成功しました。 Z が大きいほど中心だけを見るので、遠いQRは画面の中央に置いてください。
quirc へは常に 720×720を渡します。入力サイズを変えないので作り直しが起きず、使用中のバッファを壊す競合(第24版で起きたクラッシュ)が構造的に発生しません。

keys / 操作

シリアルモニタに1文字

Arduino IDE のシリアルモニタを 115200bps で開き、1文字送るだけで切り替わります。

キー動作使いどころ
cCOLOR(生)そのままの映像
sCOLOR バイト入替色化け(赤青反転)が直る。既定はこれ
bBINARY(quircが見ている画像)読めないときはまずこれ。QRの形が見えるか確認
gGRAY(明暗を伸ばす)暗い・コントラストが弱いとき
k先鋭化 ON/OFFぼやけているとき(既定OFF)
zズーム:自動(Z=1..4巡回)⇔ 固定 Z=1..4遠いQRは Z=3 / Z=4 に固定すると反応が速い
a8秒ごとの表示自動切替比較用
lライトモード ON/OFF暗い場所で画面を白くして照明にする
hヘルプ—

画面の下のバーに、状態が1行で出ます。

COLOR byte-swap | Z3 | q720 | cand 1 | blk 47%
表示モード      ズーム 入力サイズ  QR候補数  2値画像の黒の割合

blk(黒の割合)は明るさの当たり外れが一目で分かります。5%未満=明るすぎ/白飛び、70%超=暗すぎ が目安です。

files / ダウンロード

一式をどうぞ

まずはZIPを落として展開するのが早いです(中のフォルダ名と ino 名が一致しているので、そのまま Arduino で開けます)。中身は個別にも置いてあります。md5 を併記しているので、転送の確認に使えます。本体に組み込んだ版は body/ にあります。

  • Tab5_CameraQR_Sample_20261015.zip 一式(スケッチ + README + quirc 6ファイル) download 46,544 bytes  ·  md5 aa9ed90cb50ce380fc2353ae247ebdcc
  • Tab5_CameraQR_Sample_20261015.ino スケッチ本体(映像表示 + QR + ライトモード) download 37,600 bytes  ·  md5 e60f8277dd0d2556f07978b1eb5ca17f
  • README.md 解説書(コンパイル注意点 / トラブルシューティング / ライセンス) download 20,220 bytes  ·  md5 b803fc44a9cca241a8ef7336b3f3e97c
  • ESP_Video_for_3.3.12.zip ESP_Video ライブラリ(コアに見当たらないとき用) download 14,458 bytes  ·  md5 6c1c757ca3cc5087c1585a78d05116cb

quirc 同梱分(ZIPにも入っています。個別に欲しいとき用)

  • quirc.h quirc ヘッダ download 5,402 bytes  ·  md5 95260956b4e462795c479c87a2232050
  • quirc.c 本体。malloc を PSRAM に向けるため1行だけ手を入れています download 4,598 bytes  ·  md5 0c0dc44ced30934372c2c01423957d84
  • quirc_internal.h 内部定義(QUIRC_MAX_REGIONS 254 など) download 3,720 bytes  ·  md5 1437c864d870d4f82cc1d27288c96dc7
  • decode.c デコード。約9KBのスタックを使うので専用タスクが必須 download 21,512 bytes  ·  md5 fe7274780ba959367f61f19869e39773
  • identify.c QR検出(二値化は大域Otsu) download 28,639 bytes  ·  md5 b537b9590c42f4c822d6e04ab86d72ee
  • version_db.c バージョン情報テーブル download 11,779 bytes  ·  md5 cb832938ce640b66f1cf1af4e2b64770
# quirc.c に手を入れた1行(malloc を PSRAM に割り当てる)
#include <esp_heap_caps.h>
#define malloc(sz) heap_caps_malloc((sz), MALLOC_CAP_SPIRAM)

notes / 正直な記載

確認できたこと・これからのこと

確認済み
実機で動作
未確認
整理後の全機能通し
解像度
1280×720 固定
露出 / ゲイン
カメラ任せ(未制御)

内蔵カメラの映像表示、QR読み取り、IOエクスパンダ経由の給電、ESP_Video の初期化手順は、 実機(Tab5)で動作を確認済みです。本サンプルは、その確認できた処理を1本に整理したもので、 整理後の全機能の通し確認はこれからです。差異があればお知らせください。

既知の制限

  • 解像度は 1280×720 固定。SC202CS は 1600×900 / 1600×1200 も持っていますが未対応です(遠いQRには高解像度が効く可能性があります)。
  • 露出・ゲインはカメラ任せ(setSensorExposureTime() 等は未使用)。
  • バックライトの PWM調光は未実装(LEDC が XCLK と競合するため)。常時最大点灯です。
  • タッチ操作は使いません(内蔵I2C がカメラSCCB とピンを共有するため)。
  • QR読み取りは「はっきり写ったもの」が対象です。極端に小さい・遠い・ぼけたQRは限界があります。