最終回で連載は終わりました。温度測定をなぜRaqspberryPiにしたのか。なぜwlan0とwlan1と二重のWifiにしているのか。なぜFlaskサーバーになっているのか。これを現場に出動させると、そこに行かないとダッシュボードが見られないことです。
いまの構成では、ブラウザで温度を見るには Pi が立てている AP に繋ぐPi必要があります。つまりその部屋にいなければ見えません。 温度を自動で集めているのに、確認するには足を運ぶ。これでは半分しか自動化できていません。
このPi には WiFi が 2 つあります。ESP 用の AP にしている wlan1 と、第5回で「インターネット出口」に使った wlan0 です。wlan0 使って出動現場のWifiアクセスポイントからPiに接続します。
この記事の構成 — 何を検証したか
| 部分 | 出どころ | 状態 |
|---|---|---|
| 3 節の導入手順 | この Pi で実際に実行 | 2026-09-11 実機で検証済み |
| 2 節の AP 直結表示 | AP に繋いだノート PC で実際に開いた | 実機。第10回で CDN を外したあと |
| 4 節のダッシュボード外部表示 | 実際に開いた画面 | 実機。スクリーンショットあり |
| 5 節の SSH | 実際に接続した | 実機 |
| 6.1 キー期限の無効化 | 管理コンソールで実施 | 実施済み |
| 6.2 MagicDNS / 6.3 SSH 鍵認証 | この記事では実施していません | 手順の紹介のみ |
| 9 節の失敗表 | 公式の対処と、私が過去に踏んだもの | 一部は今回踏んでいません(明記します) |
| 11 節の液晶 | 旧版(現地出動済)の Pi で稼働中 | 実機。ただし連載版の Pi には未実装 |
最初に断っておきます。 私は以前、別の Pi で同じことをやったときに 1 つ罠を踏んでいます(9 節)。今回はそれを踏みませんでした。踏まなかったものを「踏んだ」とは書きませんが、既知の罠として載せておきます。 同じ症状で止まる人がいるはずなので。
1. なぜ Tailscale か
1.1 従来のやり方が使えない
外から自宅や現場の機械に入る、といえば普通は「ルータでポートを開けて、DDNS を契約する」です。個人の家ならこれで済みます。
ビル管理の現場では、これができません。 ルータは建物側の設備で、管理権限がありません。「温度を見たいのでポートを開けてください」と申請して通る話でもありません。そもそも企業の基幹システムに個人で作ったDIYのデバイスを接続させてくれるわけもありません。
1.2 Tailscale が解決すること
| 内容 | |
|---|---|
| ルータの設定が要らない | NAT 越えが内蔵されている。外向きの通信だけで繋がる |
| ポート開放も DDNS も要らない | 固定 IP も不要 |
| 直接繋がらなければ中継する | P2P を試みて、無理なら中継サーバー経由に落ちる |
| 認証は Google アカウント | 個人利用なら無料(100 台まで) |
| 既存のネットワークを触らない | tailscale0 という仮想インターフェースが 1 本増えるだけ |
最後の 1 行が重要です。hostapd も dnsmasq も Flask も、何も書き換えません。 ここまで積み上げてきたものを壊さずに、経路が 1 本増えます。詳しくは 7 節。
2. 何が変わるか
念のため用語を先に。AP を出しているのは Pi です。 wlan1 は Pi に挿さっている 2 枚目の WiFi(USB のドングル)の名前で、ESP 子機はそこに繋ぎに行く側です。ESP 側は AP を出していません。
導入前。見る方法は 3 つありました。ただし、どれも「その場所にいる」ことが前提です。
Pi は WiFi の口を 2 つ持っている
wlan1 = Pi 自身が立てている AP (192.168.4.1) ← USB の WiFi ドングル
├─ ESP 子機 ──→ ここへ HTTP POST
└─ 見る側の端末 ──→ http://192.168.4.1:5000/
wlan0 = 家/現場のルータ側の口 ← オンボードの WiFi
└─ 同じ LAN の PC ──→ http://(Pi のアドレス):5000/
Pi 本体にモニタとキーボードを付ける ──→ http://localhost:5000/
3 つとも、Pi のそばか、Pi と同じ LAN にいないと届かない
いちばん現場向きなのは AP に直接繋ぐ形です。インターネットが要らないので、WiFi の繋がるノート PC でもタブレットでもスマートフォンでも、持ち込んで AP に入れば画面の確認もデータの吸い出しもできます。ただし、これがちゃんと動くようになったのは第10回でグラフのライブラリを CDN から Pi の中に移してからです。 それまでは AP 経由だと表しか出ませんでした。
2 番目は開発中に使っていた形です。Pi を隣の部屋に置いて職場の同じルータに繋ぎ、自分のデスクの PC から開いて作っていました。これは「同じ LAN にいる」から届いているだけで、現場に置いた Pi には通用しません。
2.1 やりがちな間違い — AP に繋いだ端末で localhost を開く
ノート PC を Pi の AP に繋いで、http://localhost:5000/ を開いてみたことがあります。接続を拒否されました。 それで私はしばらく「AP 側からは見られないものだ」と思い込んでいました。
localhost は「そのブラウザが動いている機械自身」を指す名前です。PC で打てば PC が自分自身の 5000 番を探しに行くだけで、Pi は一切関係ありません。Pi 本体の画面で localhost:5000 が開けるのは、そのブラウザが Pi の中で動いているからです。同じ文字列でも、打つ機械が変われば指す先が変わります。
正しくは http://192.168.4.1:5000/ です。AP 側から Pi を名指しするアドレスを打たなければいけません。
導入後。
wlan1 = Pi 自身が立てている AP (192.168.4.1)
└─ ESP 子機 ──→ ここへ HTTP POST ← 今までどおり。何も変わらない
wlan0 = 家/現場のルータ ──→ インターネット
└─ tailscale0 (100.x.x.x) ──→ tailnet
└─ 自宅の PC / 出先のスマホ から見える
ESP から Pi への経路は何も変わりません。 見る側の経路が 1 本増えるだけです。
3. Pi に入れる
3.1 リポジトリを推測しない
Tailscale の APT リポジトリは、ディストリビューションとコードネームで URL が変わります。ここを推測で書くと 404 になり、原因探しで時間が溶けます。 実際に叩いて確かめます。
dpkg --print-architecture
grep -E '^ID=|^ID_LIKE=' /etc/os-release
curl -s -o /dev/null -w "debian/trixie : %{http_code}\n" https://pkgs.tailscale.com/stable/debian/trixie.noarmor.gpg
curl -s -o /dev/null -w "raspbian/trixie : %{http_code}\n" https://pkgs.tailscale.com/stable/raspbian/trixie.noarmor.gpg
私の Pi ではこう出ました。
arm64
ID=debian
debian/trixie : 200
raspbian/trixie : 200
両方 200 が返ります。 どちらでも取れてしまうので、アーキテクチャで決めます。64bit(arm64)で ID=debian なので debian 側を使います。raspbian は 32bit(armhf)向けです。
コードネームが違う人(bookworm など)は、URL の trixie をそこに置き換えて同じことをしてください。
3.2 インストール
sudo mkdir -p --mode=0755 /usr/share/keyrings
curl -fsSL https://pkgs.tailscale.com/stable/debian/trixie.noarmor.gpg | sudo tee /usr/share/keyrings/tailscale-archive-keyring.gpg >/dev/null
curl -fsSL https://pkgs.tailscale.com/stable/debian/trixie.tailscale-keyring.list | sudo tee /etc/apt/sources.list.d/tailscale.list
sudo apt-get update
sudo apt-get install -y tailscale
このブロックを貼る前に、これを単独で実行しておいてください。
sudo -v
パスワードを聞かれるので入力します。こうしておくと、上のブロックの途中で sudo のパスワード入力が挟まりません。入力待ちが次の行を食べてしまう事故は、この連載で何度もやりました。
確認します。
tailscale version
systemctl is-active tailscaled
cat /etc/apt/sources.list.d/tailscale.list
1.102.4
tailscale commit: 3caf7d9e7dcaba589cfc58beda596929733e4fea
long version: 1.102.4-t3caf7d9e7-g084ee3b64
other commit: 084ee3b64537a1276e56fc38cdf0a711da9f4936
go version: go1.26.6 (tailscale/go 7275f792d4)
active
# Tailscale packages for debian trixie
deb [signed-by=/usr/share/keyrings/tailscale-archive-keyring.gpg] https://pkgs.tailscale.com/stable/debian trixie main
tailscaled は インストールした時点で勝手に起動します。 自分で enable する必要はありません。
3.3 tailnet に参加する
この 1 行だけを実行します。
sudo tailscale up
こう表示されて、プロンプトが戻らない状態になります。
To authenticate, visit:
https://login.tailscale.com/a/xxxxxxxxxxxx
この URL を、別の PC のブラウザで開いてください。 Pi にブラウザが無いためです。ここで固まったと思って Ctrl+C を押さないこと。私は最初これで戸惑いました。
先に PC 側に Tailscale を入れて同じ Google アカウントでログインしておくと、確認画面で Connect を押すだけで済みます。承認すると Pi 側に Success. が出てプロンプトが戻ります。
3.4 確認
tailscale ip -4
tailscale status
私の環境ではこう出ました(他の端末の行と、IP の実値は伏せています)。
100.x.x.x
100.x.x.x takemetothehospital-1 <あなたのアカウント>@ linux -
100.y.y.y ray <あなたのアカウント>@ windows -
100. で始まるアドレスが Pi の tailnet 内 IP です。このアドレスは、あなたの tailnet の中からしか届きません。 インターネットに公開されるわけではありません。
3.5 名前に -1 が付いた
よく見ると takemetothehospital-1 と、末尾に -1 が付いています。 ホスト名は takemetothehospital なのに、です。
理由は単純で、同じホスト名の機械が既に tailnet にいるからです。私の場合、会社から引き揚げてきた完成品の Pi が同じホスト名で稼働していて、ベランダの温度を取っています。名前が衝突したので、後から入れたほうに -1 が付きました。
困るのは、この名前がそのまま MagicDNS の名前になることです。
takemetothehospital-1.tailXXXXXX.ts.net
実害はありませんが、気になるなら管理コンソールで Machine name を変えられます。そもそもホスト名を機械ごとに変えておくのが本筋です。私はこの連載で「device_id は MAC から自動生成して重複させない」と散々書いておきながら、Pi のホスト名では同じことをやっていませんでした。
3.6 Pi の GUI でも接続状況が見える
Pi にモニターとキーボードを繋いでいる場合は、コマンドを打たなくても状態が見られます。 デスクトップ右上のネットワークアイコンから Connection Information を開くだけです。

ここに tailscale0 のアドレスが出ていれば、Tailscale は上がっています。出ていなければ上がっていません。これだけで切り分けが済みます。
地味ですが、現場ではこれが効きます。「外から見えない」というとき、原因が Tailscale なのかネットワークなのかサーバーなのか分かりません。Pi の画面を見て tailscale0 のアドレスが出ていれば、少なくとも Tailscale は生きていると分かります。最終回の 9 節で「モニターとキーボードを繋げば Pi だけで完結する」と書いたのと同じ考え方です。
4. 本題 — 外からダッシュボードを見る
ここまで来たら、あとはブラウザにアドレスを打つだけです。
http://100.x.x.x:5000/

PC は家の LAN にいます。Pi の AP には繋いでいません。 それでもダッシュボードが出ます。
追加の設定は一切していません。 第8回で書いた 1 行が効いているからです。
app.run(host="0.0.0.0", port=5000, debug=False, use_reloader=False)
host="0.0.0.0" は「どのインターフェースから来た接続でも受ける」という意味です。wlan1 から来ようが tailscale0 から来ようが、Flask から見れば同じです。もしここが host="127.0.0.1" だったら、tailnet 経由では開けませんでした。
スマートフォンに Tailscale アプリを入れて同じアカウントでログインすれば、出先からでも同じアドレスで開けます。
5. SSH も通る
ダッシュボードが目的でしたが、副産物として外から SSH できるようになります。
ssh pi@100.x.x.x
これが効くのは、現場で「サービスが落ちていないか」を確認したいときです。ログを流すだけなら出先からでもできます。
journalctl -u tempserver -n 30 --no-pager
6. やっておくこと
6.1 キーの期限を無効化する(常設サーバーでは必須)
既定では、各デバイスの鍵は 90 日で期限切れになります。 期限が切れると tailnet から外れ、再認証するまで繋がりません。
Pi のような常設サーバーでこれをやられると、ある日突然「外から見えない」状態になります。しかも現場に行かないと直せません。本末転倒です。
| 手順 | |
|---|---|
| 1 | 管理コンソール(login.tailscale.com/admin/machines)を開く |
| 2 | Pi の行の右端「…」メニュー |
| 3 | Disable key expiry をクリック |
日常的に使う PC 側は、期限ありのままで構いません。切れてもその場で再ログインできます。
6.2 MagicDNS(任意)
有効にすると、IP ではなく名前で繋げるようになります。
ssh pi@takemetothehospital-1
http://takemetothehospital-1:5000/
管理コンソールの DNS ページで有効化します。この記事では使っていません。 IP を直打ちで済ませました。名前を覚えるのが面倒なら入れてください。
6.3 SSH を鍵認証にする(推奨、この記事では未実施)
パスワード認証のままでも、tailnet の外からは届きません。ただ、鍵認証にしておけば、tailnet に入られた場合の二重の壁になります。Google アカウントと秘密鍵の両方が漏れないと入れない構成です。
手順はリポジトリの docs/Tailscale_導入ガイド.md に書いてあります。順番だけ注意してください。
| やること | |
|---|---|
| 1 | PC で鍵ペアを作る |
| 2 | 公開鍵を Pi の authorized_keys に登録する |
| 3 | 鍵で入れることを必ず確認する |
| 4 | そのあとで PasswordAuthentication no にする |
3 を飛ばして 4 をやると詰みます。 鍵が効いていない状態でパスワードを無効化すると、SSH ではもう入れません。モニターとキーボードを繋いで直すことになります。
なお pi ユーザーのパスワードは第9回で変更済みです。デフォルトのまま外から届く状態にはしていません。
7. 既存の構成への影響
いちばん気にしていたのはここでした。結論から言うと、何も壊れませんでした。
| 対象 | 影響 |
|---|---|
wlan1(ESP 用の AP、192.168.4.1) | なし |
wlan0(インターネット出口) | なし |
| hostapd | なし |
| dnsmasq | なし |
Flask(tempserver) | なし。設定も変えていない |
| 新しく増えるもの | tailscale0 という仮想インターフェース 1 本 |
Tailscale は物理インターフェースを触りません。仮想の口が 1 つ増えるだけです。だから ESP からの POST の経路も、AP の DHCP も、これまでどおり動きます。
ESP 子機からは tailnet が見えない
念のため書いておくと、ESP 側から 100.x.x.x には繋がりません。 子機は wlan1 の AP(192.168.4.1)にしか繋がっていないからです。
これは仕様どおりで、問題ありません。 子機は Pi に温度を送れればよく、tailnet を知る必要はありません。むしろ、電池で動く子機に VPN を喋らせる理由がありません。
8. セキュリティで気をつけること
| 内容 | |
|---|---|
| Google アカウントの 2 段階認証 | tailnet への入口そのものです。必ず有効にしてください |
| 他人を招待しない | 招待した相手は tailnet 内の機械に届きます。個人利用なら招待しないこと |
| インターネットには公開されない | 100.x.x.x は tailnet の外からは到達できません |
pi のパスワード | 第9回で変更済み。デフォルトのままにしないこと |
| 使わなくなった端末は消す | 管理コンソールから Delete。残しておく理由がありません |
9. 詰まりやすいところ
| 症状 | 原因 | 対処 |
|---|---|---|
apt-get install tailscale が 404 | リポジトリの系統かコードネームが違う | 3.1 のとおり curl で 200 が返る URL を確かめる |
| 認証 URL を開いても何も起きない | 既に別アカウントに紐付いている | sudo tailscale logout してから sudo tailscale up をやり直す |
tailscale ip -4 が何も返さない | デーモンが上がっていない | systemctl status tailscaled と journalctl -u tailscaled -n 50 --no-pager |
| プロンプトが戻らない | 異常ではありません | 認証待ちです。URL を PC のブラウザで開く |
名前に -1 が付く | 同じホスト名の機械が既にいる | 3.5 参照。管理コンソールで改名できる |
| SSH がパスワード入力の前後で切られる | 逆引き DNS のタイムアウト | 下記 9.1 |
tailscale status に relay と出る | UDP が塞がれていて中継経由になっている | 遅くなるが動く。SSH やダッシュボードなら実用上問題なし |
| ダッシュボードが開かない | Flask が落ちている、またはポート違い | systemctl is-active tempserver と、末尾の :5000 を確認 |
9.1 Timeout before authentication(今回は踏みませんでした)
今回は起きませんでしたが、以前に別の Pi でやったときに踏んだ罠なので載せておきます。
症状は、tailnet 経由の SSH でパスワードを入れる前後にサーバー側から切られるというものです。
Connection closed by 100.x.x.x port 22
Pi 側のログにはこう残ります。
Timeout before authentication for connection from 100.x.x.x
原因は、sshd が接続元 IP の逆引き DNS を引こうとしてタイムアウトし、LoginGraceTime(既定 120 秒)に達して切断していることです。パスワードが間違っているわけでも、鍵が悪いわけでもありません。
対処は 1 行です。
echo "UseDNS no" | sudo tee -a /etc/ssh/sshd_config.d/no-usedns.conf
sudo systemctl reload ssh
逆引きに依存しない設定はセキュリティ上も勧められているので、症状が出ていなくても入れておいて損はありません。
10. やめるとき
将来 Tailscale をやめる場合の手順です。今回は実行していません。 公式の手順の転記です。
sudo tailscale down
sudo tailscale logout
sudo apt-get purge -y tailscale
sudo rm -f /etc/apt/sources.list.d/tailscale.list
sudo rm -f /usr/share/keyrings/tailscale-archive-keyring.gpg
sudo apt-get update
そのあと管理コンソールから対象の機械を Delete します。tailscale0 が消えるだけなので、wlan0 も wlan1 も元のままです。
11. おまけ — 目の前にも小さな画面を付ける(I2C キャラクタ液晶)

ここまでは遠くから見る話でした。最後に逆の話を 1 つだけ書きます。Pi の目の前で、ブラウザを開かずに状態を見るための小さな画面です。
先に断っておきます。これはこの連載で組んだブログ用の Pi ではありません。 先に現地へ出した旧版の Pi で実装して、そのまま動いているものです。連載版にはまだ入れていません。ですのでこの節は「こういうものも足せる」という紹介で、手順をそのままなぞっても連載版の Pi では動きません(理由は 11.6 に書きます)。
11.1 なぜ付けたか
現地で最初に知りたいのは、たいてい 2 つだけです。
| 知りたいこと | |
|---|---|
| 1 | 子機は今ちゃんと送ってきているか |
| 2 | 電波は届いているか |
これを見るためだけに、ノート PC を出して AP に繋いでブラウザを開くのは面倒です。Pi のそばに 20 桁 4 行の液晶が 1 枚あれば、それで済みます。
実際に効くのは 1 のほうでした。温度の数字だけを見ても、それが 1 分前の値なのか 3 時間前の値なのか分かりません。 電池切れも、DeepSleep からの復帰失敗も、WiFi を掴み損ねたのも、画面の上ではすべて「値が古いまま更新されない」という同じ形で出ます。だから経過時間を出すようにしました。
11.2 部品と配線(4 本だけ)
HD44780 系の 20×4 キャラクタ液晶に、PCF8574 の I2C バックパックが付いたモジュールです。秋葉原でも通販でも安く手に入ります。配線は 4 本しかありません。
| LCD 側 | Pi のピン | 備考 |
|---|---|---|
| VCC | Pin 2(5V) | 3.3V 版もあるので実物の動作電圧を必ず確認 |
| GND | Pin 6(GND) | |
| SDA | Pin 3 | GPIO2 |
| SCL | Pin 5 | GPIO3 |
I2C を有効にします。
sudo raspi-config
# Interface Options → I2C → Enable → 再起動
再起動したらアドレスを調べます。ここも推測しないこと。
ls /dev/i2c-1
i2cdetect -y 1
0 1 2 3 4 5 6 7 8 9 a b c d e f
30: -- -- -- -- -- -- -- 27 -- -- -- -- -- -- -- --
0x27 か 0x3F のどちらかであることがほとんどです。何も出ないなら配線か電源です。コントラストはバックパック上の半固定抵抗で回します。 「バックライトは点くが文字が出ない」ときは、たいていこれが振り切れているだけです。
11.3 何を出しているか
20 桁 4 行にこう詰めました。
+--------------------+
|2026/08/18 16:32:44|
|AP@ WAN@ CAM@ 1/3 |
|POD-01 26.9C #|
|POD-02 25.9C #|
+--------------------+
| 行 | 内容 |
|---|---|
| 1 | 日付と時刻(秒まで) |
| 2 | AP / wlan0 / カメラ の状態と、ページ番号 |
| 3・4 | センサーの表示名、温度、そして末尾 1 枠 |
2 行目の @ の位置には、CGRAM に登録したアンテナ・地球儀・カメラのアイコンが出ます。動いていないものは x になります。「空白」ではなく「x」にしたのは、現場で「消えている」と「出ていない」を見間違えたからです。
センサーが 3 台以上あるときは 5 秒ごとにページが送られます(右端の 1/3)。
11.4 電波強度と経過時間を交互に出す
ここが本題です。 20 桁しかないので、行の末尾には 1 つしか置けません。RSSI も経過時間もどちらも見たい。そこで末尾だけを 3 秒ごとに入れ替えることにしました。


上の 2 枚は同じ液晶を続けて撮ったものです。行が入れ替わっているのではなく、末尾の 4 文字だけが入れ替わっています。
| 電波強度ビュー | 経過時間ビュー | |
|---|---|---|
| 行末 | RSSI を 4 段のバーで表示 | 最後の POST からの経過時間 |
| 例 | 棒 4 本(強い)/空(弱い or 不明) | now / 9m / 1.5h |
| 切替間隔 | 3 秒 | 3 秒 |
RSSI を数値(-55)ではなくバーにしたのは、現場では符号付きの数字を読み取るより、棒が何本立っているかを見るほうが速いからです。CGRAM に 5 段階分の絵を焼いてあります。
| RSSI | バー | 目安 |
|---|---|---|
| -50 dBm より強い | 4 段 | 良好 |
| -50 〜 -60 | 3 段 | 問題なし |
| -60 〜 -70 | 2 段 | 実用範囲 |
| -70 〜 -80 | 1 段 | 弱い |
| -80 より弱い / 不明 | 空 | 置き場所を見直す |
経過時間は 4 文字に収まるように丸めています。
| 経過 | 表示 | |
|---|---|---|
| 60 秒未満 | now | 正常に受信している |
| 1 〜 59 分 | 12m | DeepSleep の間隔しだいでは正常 |
| 1 〜 10 時間未満 | 1.5h | 疑わしい |
| 10 〜 23 時間 | 10h | 止まっている |
| 24 時間以上 | >1d | 止まっている |
| 時刻が取れない | --- | データなし、または時刻ずれ |
11.5 名前は半角カナで出す
HD44780 に漢字はありません。 ダッシュボードで付けた表示名をそのまま出すと、冷却塔1 は .....1 になってしまいます。
そこで jaconv で全角カナを半角カナに変換してから送っています。液晶側の 0xA1〜0xDF に半角カナが入っている CGROM なら、そのまま読めます。
| ダッシュボードでの表示名 | 液晶での表示 |
|---|---|
| レイキャクトウ 02 | レイキャクトウ 02 |
| ガイキオン | ガイキオン |
| Reactor-1 | Reactor-1 |
| 冷却塔1(漢字) | .....1(読めない) |
つまり運用の決めごとがひとつ増えます。 液晶を付けるなら、表示名はカタカナか英数字で付ける。 漢字で付けたい気持ちは分かりますが、現地で読めないと意味がありません。
なお、液晶に焼ける自作文字(CGRAM)は 8 個が上限です。この実装では 5 段階のバーで 5 個、アンテナ・地球儀・カメラで 3 個、ちょうど 8 個すべて使い切っています。 これ以上増やすなら、どれかを捨てることになります。
11.6 サーバー側には何も足していない
液晶のスクリプトは Flask とは別のプロセスで、localhost の API を叩いているだけです。
[LCD スクリプト] ──> http://localhost:5000/api/sensors ← 温度・RSSI・時刻
└─> http://localhost:5000/api/status/ap ← AP が上がっているか
ip -4 addr show wlan0 ← WAN の口
/sys/class/video4linux ← カメラの有無
だからサーバー側のコードは 1 行も変えていません。 止めたければ液晶のサービスを止めるだけで、温度収集には何の影響もありません。
ただし、ここが冒頭で断った理由です。 この液晶スクリプトが叩いている /api/sensors と /api/status/ap は旧版のサーバーのエンドポイント名です。第10回で公開した連載版の app.py は /api/latest という名前になっています。連載版に載せるなら、まずここを合わせる必要があります。 動かしていないものを「動きます」とは書けないので、はっきり書いておきます。
常駐は systemd です。Flask のあとに起動して、落ちたら 10 秒後に上がり直します。
[Unit]
After=network-online.target temperature-server.service
[Service]
Environment="LCD_I2C_ADDR=0x27"
ExecStart=/home/pi/i2c_lcd_display/venv/bin/python3 /home/pi/i2c_lcd_display/i2c_lcd_display.py
Restart=always
RestartSec=10
表示の間隔は環境変数で変えられるようにしてあります。数字は現地で決めるものなので、コードに埋めませんでした。
| 環境変数 | 既定 | 意味 |
|---|---|---|
LCD_REFRESH_SEC | 2 | 画面を書き直す間隔(秒) |
LCD_ROTATE_SEC | 5 | センサーのページ送り(秒) |
LCD_VIEW_TOGGLE_SEC | 3 | 電波強度 ↔ 経過時間の切替(秒) |
LCD_SENSORS_PER_PAGE | 2 | 1 ページに出すセンサー数 |
LCD_I2C_ADDR | 0x27 | i2cdetect で見えたアドレス |
11.7 詰まったところ
| 症状 | 原因 | 対処 |
|---|---|---|
| バックライトは点くが文字が出ない | コントラスト | バックパックの半固定抵抗を回す |
LCD init failed | I2C アドレス違い | i2cdetect -y 1 で見えた値を入れる |
| カナが化ける | CGROM が欧州版 | charmap を A00 / A02 で入れ替えて実機で見る |
表示名が . だらけ | 漢字を入れている | カタカナか英数字に付け替える |
| カメラが常に「あり」になる | Pi 内蔵の /dev/video10 以降を数えていた | /sys/class/video4linux の名前で bcm2835- と rpi- を除外する |
| センサーが出てこない | Flask が落ちている | 右端が FLASK になっていないか見る |
最後から 2 つ目は自分で埋めた落とし穴です。/dev/video* があるかどうかで判定していたので、カメラを繋いでいなくても必ず「あり」になっていました。 Pi は内蔵の映像処理デバイスを常に /dev/video10 以降に出しているからです。液晶にアイコンが出ているので動いていると思い込み、しばらく気づきませんでした。「表示が出ている」ことと「判定が正しい」ことは別だという、ありふれた話です。
11.8 これは必要か
無くても困りません。 ブラウザで見れば同じ情報が、もっと詳しく出ます。
それでも付けたのは、現地に立ったときに手ぶらで確認できるからです。点検のついでに前を通って、now が並んでいるのを見て通り過ぎる。それだけのために付けました。Tailscale が「行かなくても見える」なら、これは「行ったときに一瞬で見える」ほうの解決です。 どちらか片方でも十分ですが、両方あると、現場でデバイスを見たときに、ふとちゃんと動作してるのかな?とそわそわすることもなくなります。

これで本当に終わりです
[ESP8266 子機] ──HTTP POST──┐
├→ [Pi: Flask + SQLite] ──→ 現場のブラウザ
[XIAO 子機] ─ESP-NOW→ [Master] ┘ └──→ tailnet ──→ 自宅・出先
・ユニバーサル基板に固定され、持ち運べる
・LED 3 色で、PC 無しに一次切り分けができる
・電源を入れれば勝手に上がり、落ちても戻ってくる
・GitHub から clone すれば、他人の Pi でも同じものが動く
・現場に行かなくても、温度が見られる
序章から数えて 13 本になりました。しまい込んでいた部品が、現場に置いて、遠くから見られるものになりました。
この連載で書いてきたことのうち、技術的に難しいものはほとんどありません。難しかったのは、動かない原因が「そこ」ではないと気づくまでの時間でした。抜けたジャンパー線、切れかけたリード線、効いていない設定、逆引きのタイムアウト。どれもコードを読んでも分かりません。
AI は、コードは読めますが現物は見られません。 代わりに、こちらが現物を見て、出力をそのまま渡せば、そこから先は一緒に考えられます。要約せずに貼る。推測させない。手順は自分で通す。この 3 つだけで、独学より遥かに速く進めました。
コードを精査すれば冗長な部分や反復的な改変中に行方不明になっている関数は有るかと思いますが、コードを自分で書けともAIに支援をしてもらってここまでモノづくりが出来るという事を知ってもらいたくてこの記事を作成しております。
長らくお付き合いいただき、ありがとうございました。


コメント