電池なしで光る名刺基板を作る(2)届いた基板に書き込んで光らせるまで
電池を持たず、スマホをかざすと LED が光る名刺を、電子回路の板(基板)で作っています。今回は、工場から届いた基板に小さなコンピュータ(マイコン)のプログラムと、スマホで開くページの URL を書き込みました。Android ではかざしている間光り続けましたが、iPhone では何も光りませんでした。その原因を探して iPhone でも一瞬光るように直し、光り方を 9 種類に増やすまでの記録です。
- 届いた基板は Android でかざすと光った。iPhone 17 のホーム画面のままの読み取りは電波が約 0.1 秒で切れるため光らず、起動直後に黄色の LED を点けるように直した
- 光り方を 9 種類に増やした。計算を速くすると明るさと電流が増えるので、描く間隔を 6 ms(0.006 秒)に固定し、空いた時間はマイコンを休ませた
- 届いたときの輸入消費税を含めた確定費用は 35,043 円(1 枚あたり約 1,168 円)
企画から発注までは (1)企画から発注まで に書きました。今回はその続きです。

表

裏
届いた実物です。(1)の図は、設計データから描いた絵でした。
01届いた基板
2026 年 9 月 18 日に発注した 30 枚が、10 月 1 日に届きました。
(1)で「刷った実物で読めるかは(2)で確かめます」と書いた QR コードは、実物でもスマホで読めました。工場の審査で指摘を受けたアンテナ微調整用のコンデンサ(C1)は、0402 サイズ(1.0×0.5 mm)の部品(C1552)への変更が反映されたと、9 月 21 日に JLCPCB(基板を作った中国のメーカー)から回答をもらっていました。実物でも、すぐ下にある同じ 0402 サイズのコンデンサ(C2)と同じ大きさの部品が載っていました。30 枚のうち、届いた時点で見た目でわかる不良はなく、書き込みでつまずいた基板もありませんでした。
ただ、届いたあとの 10 月 5 日に、片付けの途中で束を落とし、何枚かの LED を割ってしまいました。紙の名刺と違って表面がつるつる滑るので、重ねて手で持つのには向きません。
届いた基板には、電源の LED の横にあった稲妻マークが入っていません。シルク(表面の印刷)を差し替えたときに、意図せず消していました。残す範囲の境目を、図形の中心がわずか 0.05 mm 越えていたためです。気づいたのは発注のあと、9 月 30 日に設計データを公開する前の見直しのときでした。
技術メモ:消えた稲妻マーク
- シルクを差し替えるスクリプト
artwork.pyは、元のシルクのうち残す範囲を「中心の x 座標が 123 mm 未満」で判定していた - 稲妻マークの中心は x ≒ 123.05 mm で、消す側に入った
- 出典:リポジトリの
docs/CHANGES.md(2026-09-16)と README の「主な変更」

02マイコンへの書き込み:3 つのパッドと 3.3V
マイコンは、裏面の 3 つのパッドをクリップではさんで、アダプタとつなぎます。マイコンは電気を節約するため 1 MHz という低めの速さで動かしていて、書き込むプログラムもその速さを前提にしています。
大事なのは、アダプタの電源を 3.3V にすることです。同じ基板に載っている NFC のチップ(スマホと通信する部品)は、電源の上限が 3.6V です。アダプタによくある 5V のまま当てると、このチップの上限を超えるので、基板でいちばん弱い部品に合わせました。
30 枚に同じ作業をくり返すので、手順は「基板をクリップではさむ → Enter キーを押す → 成功か失敗かを色で見る」だけで済むようにしました。書き込みのあとには、書いた中身を読み出して照合(ベリファイ)します。製造の現場でいう、治具(作業を正確にくり返すための専用の道具)と作業手順書を小さく作ったようなものです。パソコンの画面には次のように出ます。
基板をはさんで Enter(q で終了):
✔ 成功(この回 1 枚目) LED が回るのを確かめて外してください
基板をはさんで Enter(q で終了):
✘ 失敗 ピンの当たりを確かめて、もう一度 Enter
(中略)
成功: N 枚
成功は緑、失敗は赤で表示し、どちらのときもベルが鳴るので、画面を見ていなくても終わったことが分かります。最後に、その回に成功した枚数を表示します。
アダプタは、スイッチサイエンスの「USB シリアル UPDI 変換アダプタ(FTDI)」(税込 1,760 円)を使いました。電源の出力を 5V と 3.3V で切り替えられるので、3.3V にしています。クリップは、2.54 mm 間隔・4 ピンの、ばねで伸び縮みするピン(ポゴピン)が付いたもの(Amazon で購入時 1,054 円)で、4 本のうち 3 本を使います。洗濯ばさみのような形で上下の両方にピンが付いていますが、表側のピンは LED に当たるので外し、裏面のパッドにだけ当たるようにしました。
1 枚の書き込みは約 3 秒、照合は 0.3 秒ほどです。1 枚目で動作を確かめたあと、翌日、光り方を仕上げた版(firmware-2)を 30 枚すべてに書き込みました。途中で一度、書き込み用のスクリプト(手順を自動で進めるプログラム)が止まりましたが、立ち上げ直して最後まで終えています。書き込みを成功させるには、基板をクリップでしっかり固定することが大事だと分かりました。
技術メモ:書き込みの道具と設定
- arduino-cli と megaTinyCore 2.6.11、コンパイラは avr-gcc 7.3.0-atmel3.6.1-azduino7b1
- FQBN は
megaTinyCore:megaavr:atxy6:chip=816,clock=1internal(内部クロック 1 MHz) - プログラマは
serialupdi230k_wd1(SerialUPDI、230400 baud、write delay あり) - ファームウェア側でも
static_assert(F_CPU == 1000000UL)で 1 MHz を前提にしている - Arduino のスケッチは「フォルダ名=.ino の名前」でないとビルドできないので、作業用の
nfc_card/にコピーしてから 1 回だけビルドする - 成功の判定は、書き込みの出力に
Verify successfulがあるかどうか - スクリプトは software/flash_cards.sh(macOS と SerialUPDI のアダプタが前提)。ビルドの方法は
THIRD_PARTY_NOTICES.mdの「ビルドの方法」にある

03かざしてみる:iPhone は光らず、Android は光った
10 月 1 日の夜、プログラムを書き込んだ 1 枚目に、まず iPhone 17 をかざしました。何も光りません。NXP(NFC チップのメーカー)の書き込みアプリ「NFC TagWriter」で読み取ると、「No NDEF records available」(読み取れるデータがない)と出ました。NFC チップの中は、まだ空のままでした。
次に、手元の古い Android で NFC をオンにしてかざしました。すると、かざしている間、外周の赤い LED が光り続けました。電池を持たない基板が、スマホの電波だけで光った瞬間です。このとき書き込んでいたのは、元の作者のプログラムをほぼそのまま使った版(違いは、黄色の LED で光らせるモールス信号の文字列だけ。06 章で書きます)で、赤の光り方は、尾を引いた光が外周を回る元の「彗星」1 種類でした。
(1)で「この基板でうまく動くかは(2)で確かめる」と書いた電源まわりの工夫も、元の設計のままで動きました。LED を光らせるための電気をためる部品(10µF のコンデンサ)を、最初は抵抗を通してゆっくりため、約 0.3 秒後に電子スイッチで抵抗を迂回する仕組みです。今回確かめたのは「光り続けた」ところまでで、電流は測っていません。
Android で光ったので、基板と電源まわりは動いています。iPhone で光らない理由は、別にありそうです。チップへの URL の書き込みは、次の章で書きます。

04NFC への書き込み:工場出荷のままでは書けなかった
かざしたスマホに開いてもらうページの URL を、NFC チップに書き込みます。最初は、同じ NFC TagWriter で書こうとしました。
ところが、iPhone からは書き込めず、Android の同じアプリでもうまく書けないことがありました。調べると、このチップは工場から出荷された時点で初期化されておらず、データを書く場所の「目次」にあたる部分が空のままでした。
そこで、Android で動く NXP の「NTAG I2C Demoboard」というアプリを使い、Config の「Reset tag」で初期化してから書きました。初期化したあとは、iPhone の NFC TagWriter でも書けました。リポジトリの書き込み手順も、この形に書き換えています。
技術メモ:NFC チップの初期化
- チップは NXP NT3H2111(NTAG I2C plus 1k)。出荷時は CC(Capability Container)が 0 で、NDEF(URL などを入れる標準の形式)を書けない状態だった
- NTAG I2C Demoboard の Config →「Reset tag」で初期化する
- 元の作者によると、エネルギーハーベスト(電波から電気を取り出して外に出す機能)は初期状態で有効
NFC には、かざした人だけが開く名刺ページの URL だけを書きました。このページは検索には出さず、HP からもリンクしていません。「名刺にスマホをかざしてくださって、ありがとうございます。」で始まり、「連絡先に追加」と「フォームで相談する」の 2 つのボタンを置きました。「連絡先に追加」から、名前・肩書・サイトと SNS の URL などを、スマホの連絡先に登録できます。URL と連絡先(vCard)の両方を NFC に入れる案も試しましたが、アプリでうまくいかなかったので、連絡先はこのページから渡す形にしました。
(1)の 09 章で、名刺を配る前に、かざして開くページに公開場所へのリンクを置くと書いた件も、このページで済ませました。書き込んだプログラムのソースの置き場所(github.com/chofu-lab/nfc-card)へのリンクと、名刺をお渡ししてから少なくとも 3 年間はどなたでも無料で入手できるようにしておくこと、ライセンス表示のページへのリンクを載せています。配る基板に入っているのは、firmware-2 という版のプログラムです。

050.1 秒のうちに光らせる
URL を書いたあと、iPhone をホーム画面のまま(アプリを使わずに)かざすと、URL を開く通知は出るようになりました。ところが、LED は相変わらず何も光りません。
試した iPhone 17 では、ホーム画面での読み取りのとき、電波が約 0.1 秒で切れました。一方、元のプログラムは、起動してから 0.3 秒待ち、そこから 0.3 秒かけて LED を明るくする作りでした。光り始める前に、電気が来なくなっていたのです。
確かめるため、iPhone のアプリ「NFC Tools」で、チップからの読み出し(READ)を 2,500 回続けて送りました。すると約 14 秒の間、iPhone でも光り続けました。光らない原因は、電波が出ている時間の短さだと分かりました。
技術メモ:iPhone で読み出しを続ける
- NFC Tools の「高度なコマンド」で READ を続けて送ると、その間は電波が出続ける
- 続けて送れる時間の目安は、100 回で 1 秒足らず、500 回で約 3 秒、2,500 回で約 14 秒
- iPhone のアプリで読み取りを続けられる時間には上限があるので、余裕を持たせて 2,500 回にした
そこで、マイコンが起動した直後に、黄色の LED を半分の明るさで点けるようにしました。これで iPhone でも、ホーム画面のままかざすと、黄色の LED が一瞬光ります。
代わりに、最初の 0.3 秒にも黄色の LED に電流が流れるようになりました。赤の演出と、突入電流(電源を入れた瞬間に流れ込む大きな電流)の対策は元のままです。この直しは、次の章以降の変更と一緒に firmware-2 に入っています。
技術メモ:起動の流れ(firmware-2)
| 時刻 | 起きること |
|---|---|
| t=0 | analogWrite(powerLedPin, 127)(PWM 約 50%)で黄 LED を点ける |
| 0〜0.3 秒 | delay(300)。C3(10µF)を R3(2.2kΩ)経由で充電する |
| 0.3 秒 | P-ch MOSFET の Q1 をオンにして R3 を迂回する(元と同じ) |
| 0.3〜0.6 秒 | 1 フレームを 13.5 ms から 6 ms へ縮めて、赤をフェードインする |
| 約 0.6 秒〜 | 黄 LED を 0.5 秒で消し、彗星が回り始める |
出典:software/nfc_card.ino(firmware-2 の 93〜147 行)、docs/CHANGES.md(2026-10-01)。元の版は 0.3 秒の待ちのあと、黄 LED を 0.3 秒かけてフェードインしていた。
直したあと(firmware-2)の、スマホによる反応の違いをまとめます。試したのは iPhone 17 と古い Android の 2 台です。
| Android | iPhone 17(ホーム画面のまま) | |
|---|---|---|
| 電波が出ている時間 | かざしている間 | 約 0.1 秒 |
| かざしたとき | 外周の赤い LED が 9 種類のパターンで光り続ける | 黄色の LED が一瞬光り、名刺ページを開く通知が出る |

06光り方を 9 種類に
元のプログラムの光り方は、03 章で書いた「彗星」1 種類でした。これを 9 種類に増やしました。1 種類を 6 秒ずつ、1 巡 54 秒でくり返します。
| 名前 | 見え方 |
|---|---|
| 彗星 | 尾を引いた光が 2 周する |
| 双子の彗星 | 反対側にもう 1 つの彗星 |
| スピナー | 4 つの点が回る |
| すれ違い | 2 つの点が逆向きに回り、4 回すれ違う |
| 交互点灯 | 300 ms ごとに半分ずつ入れ替わる |
| 満ちて引く | 3 秒で全部点き、同じ順に消える |
| 積み上げ | 速い点が終点に積もっていく |
| きらめき | ばらばらに明滅する |
| チャージ | 5.2 秒かけて明るくなり、最後に 2 回点滅する |
赤の光り方として、ほかに心拍・呼吸・ルーレット・衝突・流れ星なども試しましたが、残したのは上の 9 種類です。心拍は、全部の LED を点けて明るさを変えると、なめらかに変わらず、段差のようにカクカクと変わって見えたので外しました。
黄色の LED の役割も変えました。元のプログラムには、黄色の LED で作者のサイト名をモールス信号で光らせる、小さな遊びが入っていました。03 章で触れたとおり、10 月 1 日に一度、その文字列を当社の CHOFU-LAB.COM に変えましたが、翌日の firmware-2 では、このモールス信号と、起動時の演出のあとに 10 秒間ゆっくり明るくなったり暗くなったりする動きを、両方やめました。
今の黄色の LED は「パターンが変わる合図」です。パターンが切り替わるたびに光り、0.5 秒で消えます。チャージの最後の点滅では、赤と一緒に光ります。
技術メモ:9 パターンの数値
- パターンは 25 ms ごと(1 tick)に更新する。各パターンは 240 tick=6 秒
- 彗星は 6 tick=150 ms で 1 歩、6 秒で 40 歩=2 周。元は約 0.4 秒で 1 歩だったので、約 2.7 倍速い
- 1 つの光で点く個数(先頭を含む)は、彗星が最大 17 個、双子の彗星 8 個ずつ、スピナー 4 個ずつ、すれ違い 6 個ずつ
- 積み上げは 20+19+…+1=210 tick(5.25 秒)で 20 個がそろい、そのあと全体が 2 回点滅する
- きらめきは 16 ビットの LFSR(初期値 0xACE1)による疑似乱数なので、電源を入れるたびに同じ並びで光る
- チャージは、明るさの段階ごとの点灯時間の表がほぼ指数関数的に並んでいるので、段階を一定の速さで変えるとなめらかに明るくなって見える
- 黄 LED の消え方は、値 r を 127→0 へ直線で下げ、明るさを r²/128 にしている。終わりにかけて柔らかく消える
- 交互点灯のコメントにある「20 blinks」は 20 区間の意味で、各 LED が光るのは 10 回
- LED の番号順が外周を回る順になっていることは、(1)で基板の配線とプログラムを突き合わせて確かめた。実物でも、元の彗星と同じく外周を順に回る
07速くしたら明るくなりすぎる:6 ms の決まりを守る
その 1:1 個ずつ順に光らせている
外周の 20 個の LED は、同時には光っていません。1 個ずつ、ごく短い時間だけ順に光らせ、それを速くくり返すことで、目には全部が光っているように見せています。そのため、LED の明るさは「1 フレームのうち、点いている時間の割合」で決まります。点いている時間が同じなら、フレームが短いほど明るくなります。
その 2:元の 1 フレームが約 6 ms だった理由
元のプログラムでは、1 フレームが約 6 ms でした。中身を見ると、LED ごとに割り算(20 で割った余り)を計算していて、これに時間がかかっていました。このマイコンには割り算の命令がなく、割り算をするたびに、たくさんの手順を踏む必要があるからです。1 フレームの大半は、光らせることではなく計算に使われていました。
その 3:割り算をなくすと 3 倍明るい → 6 ms に固定して休ませる
パターンを増やすにあたって、割り算を比較と引き算に置き換え、パターンの計算も 25 ms ごとにまとめました。すると 1 フレームが約 3 分の 1 に縮みます。点いている時間は同じなので、点灯の割合が約 3 倍になり、LED の明るさも、赤い LED に流れる平均の電流も約 3 倍になってしまいます(計算上の見込み)。
そこで、タイマーで 6 ms ごとに 1 回だけ描くことにしました。彗星を描くのにかかるのは約 1.3 ms で、残りの時間は、次の 6 ms の区切りまでマイコンをスリープさせます(20 個全部が点くパターンでは描く時間が延び、休む時間はそのぶん短くなります)。これで、LED 1 個あたりの明るさと電流は、元とほぼ同じになりました。実物でも、20 個全部が点くパターンの 1 個あたりの明るさは、ほかのパターンと同じに見えます。
その 4:電流はどうなったか
基板全体の平均電流は、約 0.69 mA から約 0.52 mA に下がる見込みです(細かい動作を計算でなぞったシミュレーションによる推定)。理由は 2 つで、マイコンがフレームの残りの時間を休むようになったことと、黄色の LED がふだん消えていることです。
一方、赤の LED は点いている数が増えるパターンがあるので、赤の合計電流は平均で元の約 1.2 倍、20 個全部が点くパターンの間は平均で約 3 倍になります(どちらも推定)。
ついでに見つけた、元からの小さな不具合
プログラムを読んでいて、起動時のフェードの最後に、まれに約 65 ms、外周の赤い LED が消えたままになる箇所を見つけました。時計を 2 回読んでいて、その間に時刻がちょうど 0.3 秒の区切りをまたぐと、マイナスになるはずの待ち時間が、とても大きな数として扱われる作りでした。時計を 1 回だけ読むように直しています。コードを読んで見つけたもので、実機でどのくらいの頻度で起きるかは確かめていません。
技術メモ:割り算をなくした 1 行と、6 ms ごとに眠る仕組み
LED ごとの % 20 を、比較と引き算に置き換えた(元の 173 行 → firmware-2 の 270 行)。
// 元: LED ごとに割り算(AVR には割り算命令がない)
uint8_t dist = (headIdx - i + 20) % 20;
// 新: 比較と引き算だけ
uint8_t d1 = head >= i ? head - i : head + 20 - i;
6 ms ごとに起きて描画し、あとは眠る(firmware-2 の 196〜207 行)。
void waitFrame() {
cli();
while (!frameDue) {
sleep_enable();
sei();
sleep_cpu();
sleep_disable();
cli();
}
frameDue = false;
sei();
}
cliで割り込みを止めてからフラグを確かめ、seiの直後にsleep_cpuで眠る。seiの直後の 1 命令は割り込みより先に実行されるので、確かめてから眠るまでの間に合図が来て、そのまま眠り続けてしまうことがない- TCB0 を周期割り込みモードにし、
CCMP = FRAME_US - 1(6000−1)、1 カウント=1 µs - IDLE スリープでは CPU だけが止まり、タイマーと黄 LED の PWM は動き続ける
- パターンの更新は
tickStartTime += TICK_MSと前回の時刻に足すので、6 ms 刻みでも平均 25 ms からずれない
1.3 ms は起動時の彗星 1 回分の描画時間(firmware-2 のコードのコメントの値)、「約 3 倍」「約 1.2 倍」「約 0.69 mA → 約 0.52 mA」は docs/CHANGES.md にあるサイクル単位のシミュレーションによる推定値です。電流の値はシミュレーションによる推定です。
技術メモ:65 ms 消灯の原因
元のコード(元の 98〜105 行の一部)。
while (millis() - startupStart < 300) {
uint32_t elapsed = millis() - startupStart;
uint16_t blankingDelay = (300 - elapsed) * 25;
...
if (blankingDelay > 0) delayMicroseconds(blankingDelay);
millis()をループの条件と本体で 2 回読んでいる。1 回目が 299 以下でループに入り、2 回目までに 301 以上に進むと、300 - elapsedが符号なしで回り込む- elapsed=301 のとき、25 倍して 16 ビットに切り詰めた値は 65511 µs になり、約 65.5 ms、LED が消えたまま待つ
- firmware-2(133〜135 行)は
millis()を 1 回だけ読み、if (elapsed >= 300) break;を先に判定する。待ち時間はdelayMicrosecondsではなく TCB0 の周期で作る loop()も 1 フレームに 1 回だけ時刻を読み、その値を使い回す(153 行)
08確定した費用
(1)では、届いたときに消費税と通関の手数料がかかるので、確定額は(2)で書くとしていました。
| 内訳(30 枚) | 金額 |
|---|---|
| 基板 | 58.06 ドル |
| 部品の実装(部品代を含む) | 104.86 ドル |
| 送料 | 9.82 ドル |
| 追加(治具費など) | 25.63 ドル |
| 小計(JLCPCB への支払い。カード明細で 32,143 円) | 198.37 ドル |
| 関税 | 0 円 |
| 輸入消費税(地方消費税を含む) | 2,900 円 |
| 通関手数料 | 0 円 |
| 合計(小計+関税+輸入消費税+通関手数料) | 35,043 円 |
上の 5 行(小計まで)は(1)の表と同じです。届いたときにかかったのは輸入消費税の 2,900 円だけで、関税と通関手数料はかかりませんでした。小計の円額は、9 月 18 日と 21 日の 2 回に分けて払った分(27,952 円と 4,191 円)の、カード明細の合計です。
このほかに、書き込みの道具として、アダプタ 1,760 円とクリップ 1,054 円の合わせて 2,814 円がかかりました(どちらも購入時の税込価格で、送料は別)。何度でも使える道具なので、名刺 1 枚あたりの費用には入れていません。
1 枚あたりは約 1,168 円になりました((1)の時点の見込みは約 1,050 円)。見込みより高くなった分の大半は、輸入消費税(1 枚あたり約 97 円)です。
09届いた基板を配る前のチェックリスト
今回の経験から、電池のない NFC の基板を書き込んで配るまでに確かめることをまとめました。
- 書き込みの電源電圧を、基板に載っているすべての部品の動作範囲に収める(今回は 3.3V。NFC チップの上限は 3.6V)
- 書き込みのクリップは、基板にしっかり固定できるものにし、ピンが部品に当たらないかも確かめる
- 書き込みのあとに照合し、成功と枚数を 1 枚ずつ色で表示する
- NFC チップは、書く前に初期化が要るかを確かめる
- Android と iPhone の両方で、アプリを使わない読み取りを試す
- 光り始めるまでの時間を、使うスマホのいちばん短い読み取り時間(今回の iPhone 17 では約 0.1 秒)と比べる
- 処理を速くしたら、1 フレームの長さと平均電流を見直す
- 時刻は 1 回だけ読んで使い回す
- ソースの置き場所とライセンス表示を、配る前にかざして開くページに載せる
- 基板は重ねて手で持たない(表面が滑りやすく、落とすと LED が割れる)
10この経験が役立つご相談
今回やったことは、仕事の場面に置き換えると、次のようなご相談に役立ちます。
- 電池を使わない機器や、NFC でスマホと連携する試作の、仕組みの確認から試作まで
- 既存のファームウェアの読み解きと、省電力化や不具合の原因探し(今回の 6 ms や 65 ms の話のように)
- 少量の基板を手配したあとの、書き込みや動作確認の手順づくり
電子機器の試作や、ファームウェアの読み解き・改良のご相談は、お問い合わせからどうぞ。
この基板は WiHarper/nfc_card(CERN-OHL-S v2 / GPL-3.0)をもとにしています。名刺に書き込んだファームウェア(firmware-2)は GPL-3.0-or-later で、megaTinyCore(LGPL 2.1)と AVR-LibC(修正 BSD)の一部を含みます(表示は THIRD_PARTY_NOTICES.md)。変更後の設計データとソースは github.com/chofu-lab/nfc-card にあります。作業には arduino-cli と、AI の Claude Code を使いました。前回は (1)。