Logo address

XBee (2)

2023/07/12 公開

はじめに

ここでは XBee series 2 (以下 XBee S2 あるいは単に XBee2) の標準的な構成、すなわち

のシステムについて議論する。読者は XBee2 についてある程度知っていると想定される。

XBee1(XBee series 1) に対する XBee2 の特徴は API モードで動作することにある。API モードとは通信がパケット方式になっていることにある。パケットには長さ情報と check sum が含まれているので、任意の byte 列が送信でき、さらに通信の信頼性が格段に向上する1

日本語の参考書としては文献[1,2,3]がある。これらは僕が持っていて、よく知っているという理由で選ばれた。従って今となっては古い感が否めない。近頃 IoT が盛んに言われる様になっているので、書籍はもっと多くなっている。

このシステムを選んだのは、XBee が話題になった頃(2010年頃)の標準システムであり、僕のジャンクボックスの中に当時のいくつかの XBee モジュールが転がっていたからである。大学に教員として在籍していたときに、ゼミ生の一人が XBee を卒論のテーマにしたいと言ってきたが、僕は当時は XBee については何も知らなかったので、XBee を理解するために必要な投資(金と時間)をした訳である。Plan9 には XBee に関するツールが存在しなかったので、ついでに作ることにした。しかし作ったものが中途半端な状態になっていた。今回 それらを Lua で書き直し、公開することにした2

書き直す前のコードは C 言語だったのであるが、C は可読性が乏しい上に、修正のたびにコンパイルが必要になり、柔軟性に欠ける。Lua は十分な可読性を持っていて、実行速度が高速で、また非常に柔軟である。書き直すだけの価値を持っている。


注1: Internet のパケットと考え方は似ているが、(非力なCPUによる処理を想定して)簡略化されている。
注2: http://p9.nix.link/netlib/xbee/

Ref

[1] 濱原、佐藤「超お手軽無線モジュールXBee」(CQ出版社)
注釈: これが一番詳しいが、特殊なハード環境を想定している。また重要な説明を欠いていることがある。
[2] Robert Faludi (小林、水原訳)「XBee で作るワイヤレスセンサーネットワーク」(O’REILLY)
注釈: ZigBee ネットワークを概念的に知るのに良い。
[3] 鄭立「ZigBee 開発ハンドブック」(リックテレコム, 2012)
注釈: XBee コマンドリファレンスが載っていない!
[4] Digi International: "Zigbee RF Modules User Guide"
https://www.digi.com/resources/documentation/digidocs/pdfs/90000976.pdf
これが一番基本的。

用語

XBee に関する情報を集めていると、用語が多すぎて混乱する。
ネットで "XBee" を検索するとスズキの自動車(クロスビー)が出てくるが、これは関係ない。

ZigBee

区別しておきたいのは XBee と ZigBee である。ZigBee は近距離無線通信のプロトコルの名称であり、Digi International (https://www.digi.com) が提唱し発展させた。XBee は Digi の商品名称である。

WPAN, LR-WPAN

次の略語もよく使われる。

abbrev. full name ref
WPAN Wireless Personal Area Network [5]
LR-WPAN low-rate wireless personal area network [6]

Connectivity Standards Alliance

最近、Zigbee Alliance が名称を改めて Connectivity Standards Alliance となった[7]。

As of May 11, 2021, the Zigbee Alliance has been rebranded to Connectivity Standards Alliance[8].

Amazon Alexa など WPAN 機器の広がりを受けて、Alliance の範囲を広げたのだろう。また cpu パワーと掲載メモリーの増大のもとで IP ベースの新しい相互接続性が模索されている[9]:

Matter is a unifying, IP-based connectivity protocol built on proven technologies, helping you connect to and build reliable, secure IoT ecosystems, and keeping you focused on developing innovative products and accelerating paths to market.

CSA がこの問題への回答になれば良いのだが

DigiMesh

Digi International がバッテリ駆動ネットワークに最適化された メッシュネットワークプロトコル「DigiMesh」を発表[10]。使いやすそうである:

DigiMeshでは、こうした親子関係のアーキテクチャはありません。すべてのデバイスは節電のためにスリープ状態を実行することができ、そしてすべてデバイスは、同時にルータとエンドデバイスになることができます。
ならば、XBee2 で構成される mesh network は ZigMesh とでも言うべきか?

Ref

[5]: IEEE 802.15
https://en.wikipedia.org/wiki/IEEE_802.15

[6]: IEEE 802.15.4
https://en.wikipedia.org/wiki/IEEE_802.15.4

[7]: CSA
https://csa-iot.org

[8]: Connectivity Standards Alliance
https://en.wikipedia.org/wiki/Connectivity_Standards_Alliance

[9]: Matter
https://csa-iot.org/all-solutions/matter/

[10]: DigiMesh
https://japan.zdnet.com/release/10389538/

XBee

xbeeD.png pins.png
図1: XBee S2 図2: XBee Pins

XBee S2 と pin 番号規則

これは初期のもの。現在は売られていないようだ。

手持ちの XBee には、どれも電圧を測定するための端子が4個備わっている。(以下 D0,D1,D2,D3 と言う)

Pin name Physical pin # Parameter
DIO0, AD0 20 D0
DIO1, AD1 19 D1
DIO2, AD2 18 D2
DIO3, AD3 17 D3
DIO6,RTS 16 D6
DIO5, AD5 15 D5
VREF 14
ON,SLEEP 13
DIO7,CTS 12 D7
DIO4, AD4 11 D4
VCC 1
VOUT 2
VIN/CONFIG 3
RESET 5
PWM0 6 P0
PWM1 7 P1
DI8 9 D8
GND 10
https://www.digi.com/resources/documentation/Digidocs/90001456-13/Default.htm#reference/r_xbee_s3b_io_pins.htm

VCC と GND は XBee の駆動電源の口である。標準的にはここに 3.3V を与えて駆動するのであるが、かなりの幅を許している。

シリーズ2の普通の(つまり PRO ではない) end device の場合には、Vcc の許容範囲は 2.1~3.6V と特に広い。XBee では end device が測定に使用されると想定されている。そして end device は電源に電池を使うことが想定されている。電池は時間が経つにつれて電圧が低下する。従って長期間に渡って測定が継続できるためには動作可能な電圧の幅が大きくなるように設計する必要がある。

そこで駆動電圧の違いが、電圧の測定に影響を与えるのではないかと心配になる。

文献[4]によれば、電圧の分解能は 10bit つまり1/1000である。これは精度の限界である。実際の精度は如何? この記事のテーマの一つである。

XBee の電圧は 0V から 1.2 V の範囲で測定できる。これより高い電圧の測定は、抵抗器を使って適当に分圧すればよい。分圧の状況はテスターで測定すればよいが、僕の手持ちの MOSTECH のテスターの精度は、マニュアルによれば、常温(18℃~28℃)、5V 程の直流電圧の測定で、

型名 精度 測定電圧
MS8218 0.03% DC 5V
MS8268 0.7% DC 4V
ほどである。

ちなみに手持ちのバッテリーの電圧を測定してみると(同一のバッテリーに対して)

MS8218	2.8351V
MS8268	2.822V
であった。MS8268 は 0.5% ほど低く出ている。

XBee ネットワーク環境

我が家のネットワーク環境は複雑である。以下の XBee2(XBee series 2) の実験に関係した部分を図3に示す。

図3: 我が家の XBee ネットワーク環境2

Coordinator はサーバと USB で接続されている。サーバーの役割は電源の供給と、コマンドを通じての XBee の制御である。
Router はノートPC(ASUS 904x1) と USB で接続されている。ノートPC の役割は電源の供給と、コマンドを通じての XBee のデータの読み取りである。ノートPC は必要に応じて起動すればよい。電源停止中でも、Router には USB ポートを通じて電源が供給されている。
実はサーバも長期に渡ってデータを XBee から採取している。従って何かちょっとした実験をしたい場合に、サーバーの邪魔にならないよう、ノートPCからアクセスすることにしている。
End Device は、Router にも Coordinator にも接続できる。Coordinator と End Device の実際のデータ転送の経路は電波状況に依存する。遮蔽物の存在は経路決定に強く影響するはずである。我が家の環境では、どの EndDevice も Router を通じて Cordinator と繋がっているのではないかと思われる。明確な言い方ができないのは、僕は確認する方法を知らないからである3。XBee のファームウェアが最適な経路でデータを転送してくれるので、かようなことを知らなくてもよいとするのが、ZigBee の設計者の立場なのであろう。我々が Internet を使うときに、通常はサーバーまでの経路を知る必要はない。それとよく似ている。しかし、世の中には、知らなくてもよいと言われると返って知りたくなる人間もいるのだ。

管理を容易にするために、この図の4個の XBee には C,D,E,G とラベルが貼られている。


注1: ASUS 904x については http://ar.nyx.link/blog/EeePC.html を見よ
注2: 図3において、フリーの SVG を使わせて頂きました。(正しく表示されるように、少し修正が加わっています)
https://illustration-free.net/
注3: ATND コマンドで確認できる。これによると End Device は Router を通じて Coordinator に接続している場合もあるが、直接 Coordinator と接続している場合もある。詳しくは http:index3.html を見よ。

ソフトウェア

図3において、サーバーもノートPCも、Plan9 の下で動いている。ところが現状では Plan9 の下で動く XBee 用のソフトウェアは(僕のもの以外には)存在しない。僕のものは 10 年ほど前に書かれたもので、C で記述されていた。公表されることもなく、眠っていた状況である。今回は公表にあたって Lua で書き直した。なぜ Lua を? 次の2つの点から説明しておく。
(a) C ではなぜダメなのか?
(b) Python ではなぜダメなのか?

(a) の答: C は生産性が悪すぎる。コードの変更や追加にコンパイル作業が要求され、柔軟に対応できない。
(b) の答: Python は仕様が大き過ぎて、あの大きなシステムは持て余す。そのために Plan9 のような小さな開発グループでは Python の最新版は手に入らない。Lua は非常にこじんまりとした言語であり、それでいて強力である。Lua はミニマリズムの見本のようなもので、高速であり、Python の 10 倍ほど早い。移植性も極めて良い。Lua で足りる問題であれば Lua を使わない手はない。(問題によっては Python よりも使いやすい)

Plan9 用の Lua は僕のサーバー
http://p9.nyx.link/netlib/lua/
から手に入る。Plan9 に特有な機能を追加した 9lua も含まれている。

Plan9 で動く ZigBee 用のソフトウェアは
http://p9.nyx.link/netlib/xbee/
から手に入る。ここには MAN_XBEEAPI, README, pub.tgz が置かれている。pub.tgz を展開すると att.c, api.c, api.lua などが現れる。pub.tgz を展開したディレクトリを $xbee としよう。今後、ここを XBee 関係の仕事場とする。 $xbee において

mk install
とすれば、 /usr/local/bin/$objtype/xbeeattapi がインストールされる。api.lua は 9lua で書かれているために、9lua をインストールしておく必要がある。実行は各々
xbee/att $device
xbee/api $device
api.lua $device
のようにする。ここに $device はデバイスファイルの名称である。詳しくは以下に説明する。

att.c は透過モードで動く普通の通信プログラムであり、XBee2 では使わない。api.capi.lua は各々 C と Lua で書かれた API モード用のプログラムである。att.capi.c は昔書いたプログラムに、今回最小限の手を加えたものである。これらのプログラムは非同期で動く。つまり XBee に命令を送るプログラムと、その結果を受取るプログラムは同期していない。これは通信プログラムの普通のスタイルである。他方 api.lua は同期通信を行う。同期通信のメリットは、XBee から送られてきたデータが何の命令に対応しているのかが分かりやすいことにあるが、1つ躓くと通信が止まる欠点を持っている。つまり通信の信頼性がないと採用しづらい方法である。2つの通信方式は目的によって使い分ける必要がある1

これらの XBee 用のプログラムはどれもケーブルで接続された XBee と通信するプログラムである2。現在では RS232C のシリアルケーブルが使われることは稀で、普通は USB ケーブルが使われ、USB ケーブルの中をシリアルデータが流れる。従って XBee は USB の口を通じて接続される。もちろん、そのままでは無理なので、IC チップが介在し、適当な変換作業が行われている。Plan9 では FTDI と Prolific のチップがサポートされている。ここでは、このどちらかを使って XBee と通信していると仮定する。


注1: 図4の出力を得るapi.luaは同期通信のプログラムである。このプログラムは Vcc の電圧を調べる命令 AT%V に続けて、 XBee のアナログ入力端子に掛かっている電圧を調べる命令 ATIS を発行し、2つの命令の応答を合わせて結果を1つの行で表示している。非同期でもやれなくはないかも知れないが、かなり面倒である。
注2: ケーブルで接続されていない XBee との通信は、Coordinator や Router を通じて行なう。

setup

XBee と通信するプログラムはデバイスファイル $device を引数に指定してで起動される。例えば api.lua の場合には

api.lua $device
とする。他のプログラムが $device を使っていると正しく働かない。主な理由は、データの取り合いが発生するからである。従って何らかの排他制御が必要になる。いくつかのプログラムが排他制御に関わっている。その関係はかなり複雑である。$xbee には利用可能な $device を見つけるプログラム setup が含まれている。$xbee において
setup
を実行すると
# select:
# getdataB
# getdataB.rc
# getdataC
# getdataC.rc
# xbee/api /mnt/consoles/usb6
# api.lua /mnt/consoles/usb6
のように、次に実行するコマンドが例示される。$device に相当しているのが /mnt/consoles/usb6 である。usb6 の部分は環境によって異なるであろう1getdata で始まる行は、僕の環境でのメニューである。各自の環境に合わせればよい2


注1: setup コマンドの中で使われているシェルスクリプト getdev$deviceを決定している。そして $xbee の中のプログラムの中で最も難しい。改善の余地があるのかも知れないが、僕の現在の知識では、確実な方法は出てこない。
注2: getdataX, getdataX.rc は簡単なシェルスクリプトである。行われている内容はシェルスクリプトを見ればよい。X の部分は XBee に貼り付けたラベルであり、もちろん僕の環境に合わせられている。従って単なるサンプルである。

xbee/api

XBee に対する命令は大文字と小文字の区別をしない。

xbee/api の実行例として ATND コマンド(node discovery)を取り上げる1。このコマンドは XBee のネットワークに接続されている Coordinator 以外のすべての node アドレスと、親子関係を教えてくれる便利な(デバッグ用の)コマンドである。
このコマンドを発行すると、(調べるのに多少の時間が必要なために)ちょっと間をおいて応答が返ってくる。どれだけの応答が発生するのか、前もって判らないために、同期通信は不可能である。このような場合にはプロンプトは正確には出せず、煩わしくもある。従って -p フラグで "no prompt" の選択ができるようにしてある。

hebe# xbee/api -p /mnt/consoles/usb6
ATND
7E 00 04 08 01 4E 44 64
* 7E 00 19 88 01 4E 44 00 D8 77 00 13 A2 00 40 72 16 7C 20 00 FF FE 01 00 C1 05 10 1E 8A
* 7E 00 19 88 01 4E 44 00 59 3F 00 13 A2 00 40 A7 45 09 20 00 D8 77 02 00 C1 05 10 1E FD
* 7E 00 19 88 01 4E 44 00 8D DF 00 13 A2 00 40 AD D4 5D 20 00 D8 77 02 00 C1 05 10 1E 40

発行したコマンドが ATND であり、プロードキャストで送られた API パケットの内容が

7E 00 04 08 01 4E 44 64
で示されている。これを受け取った node からの応答が "*" で始まる行である。ここには3つの行が表示されている。つまり Coordinator 以外に3つの XBee がネットワークに参加していることが判る。応答の内容を具体的に見ていこう。

まず

7E 00 19 88 01 4E 44 00 D8 77 00 13 A2 00 40 72 16 7C 20 00 FF FE 01 00 C1 05 10 1E 8A
の内容は次のようになっている。
"7E 00 19"	-- DELIM + size
"88"		-- frame type -- AT resp.
"01"		-- frame ID
"4E 44"		-- "ND"  -- AT command name
"00"		-- command status (OK)
"D8 77 00 13 A2 00 40 72 16 7C 20 00 FF FE 01 00 C1 05 10 1E"	-- command data
"BA"		-- check sum

command data の内容については簡単な解説しか手に入らない。しかも文献[2](p.315) の説明は間違いを含む。文献[1](p.175)の説明には問題は無い。この内容は文献[4](p.172)と一致している。

文献[1](p.175)あるいは文献[4](p.172)によれば command data の部分は

2: MY
4: SH
4: SL
n: NI		-- ちゃんと説明されていない
2: MP
1: device type
1: status
2: profile ID
2: maker ID
ここに ":" の左の数字は、情報が占める byte 数である。"n" となっているのは可変長を意味する。
また MY, SH, SL, NI, MP の意味は文献[1,2,4]に従う。
具体的には、この例では
"D8 77"		--> MY of xbeeG, affr16 of parent of xbeeC and xbeeD
"00 13 A2 00 40 72 16 7C"		-- xbeeG
"20 00"		-- What is this? probably this means "no NI"
"FF FE" MP of xbeeG
"01" -- router, "02" end device
"00"	-- status OK
"C1 05"	-- profile ID
"10 1E"	-- maker ID
である。従って XBee の SH,SL が判っていれば、無線ネットワーク上での位置関係が判るのである。


注1: "Network discovery" となっていることもある。

api.lua

api.lua を起動すると

>
のプロンプトが表示される。次に実行例を示す。
hebe# api.lua /mnt/consoles/usb6
> ATSH
7E 00 04 08 03 53 48 59
* 7E 00 09 88 03 53 48 00 00 13 A2 00 24
# note that "00 13 A2 00" is the first half of MAC address in use
> ATSL
7E 00 04 08 04 53 4C 54
* 7E 00 09 88 04 53 4C 00 40 A7 46 07 A0
# note that "40 A7 46 07" is the last half of MAC address in use
> ATID	# show the current PAN ID
7E 00 04 08 0D 49 44 5D
* 7E 00 0D 88 0D 49 44 00 00 00 00 00 00 00 00 0A D3
ここに "*" で始まる行は XBee からの応答である。また "#" は筆者による(後付の)コメントである。

命令名を除いてすべて 16進数表記である。送信と受信パケットの中身が判るようになっている。XBee にかんする書物や ZigBee の仕様書とつき合わせやすいが、人間にとって優しいとは言い難い。この点に関しては後に議論する。

以上は local な XBee すなわち api.lua を実行しているコンピュータと接続している XBee との交信結果である。残りの XBee を remote な XBee と言うことにする。remote な XBee については アドレスを指定する必要がある。"0013A200:40A74509" をそのアドレスとする。(":" は入れなくてもよいが、見やすくするために入れてもよいことにしている)

> dest 0013A200:40A74509
> ATID	# show the current PAN ID of the dest
7E 00 0F 17 03 00 13 A2 00 40 A7 45 09 FF FE 02 49 44 6F
* 7E 00 17 97 03 00 13 A2 00 40 A7 45 09 3E 8D 49 44 00 00 00 00 00 00 00 00 01 22
> ATIS	# show digital and analog data of the dest
7E 00 0F 17 04 00 13 A2 00 40 A7 45 09 FF FE 02 49 53 5F
* 7E 00 17 97 04 00 13 A2 00 40 A7 45 09 3E 8D 49 53 00 01 00 10 01 00 00 02 22 DD
こうした 16進数のデータ列の意味を読み取るのは慣れていないと非常に困難である。
api.lua は(api.lua のコードに手を付けなくても)新たな命令を組み込む機能を持っている。mylib.lua が外付けのライブラリで、その中に新しい命令 "_ATIS" が含まれている。 それは次のように使う。
> load mylib.lua
> _ATIS
1682473127 3.141 0010 01 0000 0.643

この表示の意味は(このケースでは)

のように整理されて(Pyplot などのツールで処理しやすいように)表示される1

次に示すのは "_ATIS" を利用して10分ごとのアナログデータを採取した記録の一部である。

1683374976 3.491 0010 01 0000 0.764
1683375576 3.489 0010 01 0000 0.751
1683376177 3.487 0010 01 0000 0.744
1683376777 3.487 0010 01 0000 0.739

図4:_ATIS の出力


注1: ZigBee の API では転送されるデータ量を減らすために "0000" に相当する部分は省かれている。しかし可変長データは処理しにくいので、ここでは省略された部分を補っている。

api.lua は(形式的には) ZigBee のすべての命令をサポートしているはずである。命令の詳細に関しては文献 [1,2,4] に詳しい。「形式的には」と言っているのは、ATIR のようなコマンドは同期式の api.lua には馴染まない。 xbee/api を使う必要がある。

アナログデータ測定の精度

Vcc

AT%V コマンドで Vcc の電圧を見ることができる。これはあくまでも XBee から見た Vcc 電圧で、テスターで測定した値と一致している保証はない。

手持ちの XBee の一つ(xbeeD)で調べた。
Vcc 電源に単3アルカリ電池2個を使うと、(テスター MS8218)では Vcc=3.167VAT%V コマンドは 3.060V を示していた。従って誤差は 1/30 と評価できる。
我が愛用の三和のアナログテスターの誤差はこの程度あるから、満足すべしと思える。

電圧変換モジュール(5V to 3.3V など)を XBee の end device の電源として使っている場合には、問題が発生する可能性がある。(待機中ではなく)通信中には XBee は多くの電力を使う。その電力消費に電圧変換モジュールが耐えられないときにはVcc電圧が瞬間的に落ちる。AT%V コマンドは落ちた値を送信している可能性がある。電源がバッテリーであれば、そのような影響は少ないであろう。ZigBee の仕様書は安定した Vcc を仮定していると考えるべし。

XBee のネットワークにおいてアナログ測定を行っているのは、実際的にはバッテリー駆動の End Device であろうから、問題はむしろ、バッテリー電圧の低下が測定にいかなる影響を与えるかが問題になる。

IS の精度

XBee2 では D0~D3 の4つの端子を通じて電圧を測定できる。
適当な電源を使って D1 端子の電圧を測定してみる。
そのためには ATD1
ATD1 2
を実行しておく。
IS コマンドはその結果を見る最も簡単なコマンドである。
報告される数値の分解能は10bit

精度は文献[1~4]には載っていない1
IS の精度の問題は多分 Zigi 規格の外なのであろう。


注1: IS コマンドの記述を英文の PDF 文書から探すのは苦労する。Mac の Safari で PDF 文書を表示させた場合、(初心者向けに設計されているので)大文字と小文字を区別できない。"is" も一致するために膨大な量が候補として挙げられ、"IS" を探すのは事実上不可能である。Chrome も同様である。この点に関しては玄人向けの Firefox は流石に良くできている。

実験装置

実験に使われた XBee(xbeeD) と ブレッドボード(xbeeDB)を図5と6に示しておく。
このブレッドボードは、電池などの電圧を調べるように設計されている。テスターと異なり、xbee を噛ませることによって電圧の時間変化の記録を採ることが可能である。

XBee の ADC 端子は 1.2V までしか測定できない。より高い電圧を測定したい場合には、potentio meter を利用する。xbeeDB では ADC ピン D1 が利用されており、測定対象の電圧 V は potentio meter によって V*6783/9921 に減圧されている。従って、例えば V = 1.3145 であれば、D1 ピンには 0.8987V が掛かっていることになる1

ex1.svg xbeeDB-pin.png
図5: 回路図 図6: ボードピン

図5の回路図のマル印はボードと外部との接続口を表している。これは図6(この図は図1の一部)の右上に並んだ4つのピンである。番号は右から順に 1,2,3,4 なっている2


注1: 実際はどうか? ボードピン3に何も接続しない、あるいは GND と接続して、D1 の電圧をテスターで測定しても 0V にはならない。原因は D1 端子が周囲の電場(特に D0 端子と D2 端子の電圧)の影響を受けるからである。この現象は Arduino でも観察された。しかし、IS コマンドで報告される D1 端子の電圧は正しく 0V になっている。XBee 恐るべし!

注2: 図のピン配置は合理的ではない。ピン1~4を Vcc,GND,GND,V の順に並べた方が、率直で、配線もやりやすいのである。しかるにこの配置になってしまったのは、かって、このボードの電源に 5V to 3.3V の変換モジュールを使用していたからである。


実験(1)

電源 Vcc がどの程度アナログ測定に影響を与えているかを調べる。

電源

Vcc = 3.3734	-- two NiZn batteries

測定対象

NiCd
V = 1.3133
V*6783/9921	-- 0.89790483822195

測定結果は

1686818289 3.178 0000 02 0000 0.868
であった。
3.154/3.3734 -- 0.93496175964902
0.868/0.89824668884185 -- 0.96632696872966
つまり 97% 精度が出ている。これは満足すべき結果であろう。

実験(2)

電源 Vcc がどの程度アナログ測定に影響を与えているかを調べる。

電源

Vcc = 2.695	-- two eneloops

測定対象

NiCd
V = 1.3133
V*6783/9921	-- 0.89790483822195

測定結果は

1686829600 2.590 0000 02 0000 0.866
であった。実験(1)の "0.868" と比較すると、Vcc 依存性は非常に小さいことが判る。

実験(3)

電源 Vcc がどの程度アナログ測定に影響を与えているかを調べる。

電源

Vcc = 3.433	-- new two NiZn batteries

測定対象

NiCd
V = 1.3133
V*6783/9921	-- 0.89790483822195

測定結果は

1686830064 3.293 0000 02 0000 0.866
であった。実験(1)の "0.868" と比較すると、Vcc 依存性は非常に小さいことが判る。

NiZn 電池は XBee の実験には非常に良い。実験は電池を浪費する。しかし NiZn は充電できるし、充電すれば、数週間の継続した実験に耐える。

測定

実際に温度の記録を採ってみることにする。測定に使った温度センサーは MCP9700A で、仕様書は
https://www.mouser.jp/datasheet/2/268/MCP9700_Family_Data_Sheet_DS20001942-3132871.pdf
にある。この仕様書には
Accuracy:

と書かれている。MCP9700A については (Vdd = Vcc に設定すれば) XBee の動作範囲の電圧で ±2°C の誤差をみておればよいわけだ。使いやすいセンサーである。

念のためにセンサーの出力電圧 VoutVcc 依存性を調べておいた。(依存しないのが理想である)

Vcc = 2.566	--> Vout = 0.761
Vcc = 3.539	--> Vout = 0.760
僅かに依存しているが、この違いは(後に見るように)温度に換算すると 0.1°C 程である。この実験の電源は一組の eneloop と NiZn である。ほぼ同時刻の測定であるが、その間、空調で室温は保たれている。

仕様書の FIGURE 2-17 には温度と Vout との関係がグラフで示されている:

これによるとほぼ直線的な関係で
100°C	--> 1.5V
 50°C	--> 1.0V
  0°C	--> 0.5V
となっている。つまり
Vout = 0.01*T + 0.5
T = 100*(Vout - 0.5)
と考えてよい。

測定に使った XBee は xbeeC で、このブレッドボード(以下 xbeeCB と書く)の回路の要点を図7に示す。

図7: xbeeCB の回路図の要点

中央の端子が Vout である。仕様書をしっかり読んでいなかったので、図7の配線になってしまった。MCP9700A を使っている限り Vout に接続されている potentio meter は要らなかったのだ。抵抗値を測定すると、実際の減圧比は 10.478/10.491 でほぼ1であった。以下 1 とする。従って Vout を直接 D0 に接続したのと同じである。

NiZn 電池(2本組)の電圧範囲は XBee と相性が良い。そこで NiZn を XBee の駆動電源とした場合、駆動可能な時間を知りたくなる。
XBee に満充電の NiZn 電池を接続し、Vcc の時間変化を 10 分ごとに調べる。その間、XBee は信号待機状態になっている。

横軸に経過時間をとったグラフを図8に示す。記録の開始時刻は 2023-05-07 22:11:51 である。

図8: 1100時間(45日間)の電源電圧の変化

同時に温度センサーモジュール(MCP9700A)の出力電圧(Vout)も測定されている。この電圧は温度が高いと高くなる。

図9: 1100時間(45日間)の温度センサー電圧の変化

温度センサー電圧は、テスターによる直接的な測定ではなく、IS コマンドの結果として XBee から報告された数値である。電源電圧が低下しすぎると、XBee が機能しなくなる。

図10に温度センサー電圧と実際の温度との関係を示す。温度の方は自動採取ではないので、気が向いたときに記録している。温度が高くなると、温度センサー電圧も高くなる。問題は良質の温度計が手元に無いので、家庭用の安物(ThermoPro TP-49、図12)で代用していることにある。Amazon.com によれば ±1°C の誤差を含む1


注1: Highly Accurate Sensors: This temperature humidity sensor features a high accuracy of ±1°F/°C and ±2%-3%RH, making it ideal for measuring dynamic environments like greenhouses
とある。なお http://www.syouhisya.or.jp/test/kirame52.pdf
によれば、±1°C は現在の家庭用の温度計の標準的な誤差らしい。

図10: D0端子電圧の温度依存性1


注1: あとで判ったことであるが、 MCP9700A の個体差は、このグラフに顕著に現れる程である。従って個体ごとに温度補正のパラメータを決定しなくてはならなくなる。この問題については後に議論する。(「温度センサーの個体差」の節を見よ)

このグラフを見ると、明らかにデータが一つおかしい(19°C,0.7V)。温度の実測は手作業のため、間違えた可能性が高い。このデータを省いて、温度の補正を最小自乗法を使って1次式の範囲で求めることにする。ついでにデータを追加しておいた。結果を図11に示す。
図11では D0 ピンの電圧ではなく、式 T=100*(Vout - 0.5) で換算した温度(T0)を使って実際の温度(Tobs)と比較している。T0Tobs よりも少し小さく出ている。

図11: 温度センサーから求めた温度 T0 と実際の温度 Tobs の比較

1次の最小自乗法で求めた回帰直線が参考のために図示されている。この直線を使って T0 から期待される温度 T2 は (図11の観察データの下で)

(a2,b2) = (1.002366, 0.468104) ;   T2 = a2*T0 + b2
となる。この a2,b2 は次節の最小自乗法の説明における \(\alpha\) と \(\beta\) に相当する。

こうして求められた T2 はどの程度の予測能力を持っているか? T2 と観測 Tobs を比較してももちろん同じにはならない。T0 が同じであったとしてもである。どのような理由が考えられるか?

図12: ThermoPro TP-49

XBee 上の温度センサーはむき出しの状態で置かれているのに対して、Tobs の決定に使用されている温度計はセンサーがパッケージで覆われている。従って後者の場合には、環境の温度が温度計内のセンサーに反映されるのに時間がかかる。この問題は、温度が上昇中、あるは下降中の場合に影響を与える。

T2Tobs の違いを定量的に分析することは、専門的な道具なしには非常に困難である1。プロであれば良質の温度計の他に、気温が自由に制御できる実験環境を要求するだろう。こうしたことは、趣味でやっている筆者の場合には望めない。筆者の観察では、補正後の ThermoPro TP-49 との差異は ±0.5°C 以内に納まっている。これでよしとする。


注1: サンプリングの問題は非常に難しい。恣意的なサンプリングを避けるためには、データを自動採取する方法を持っている必要があろう。

最小自乗法

case (1)

2つの量 \((x,y)\) があって、\(y\) は \(x\) の一次の関係 \( y = ax + b \) で与えられるはずの量とする。 \(a\) と \(b\) はわかっていない。
\(x\) を与えたときの \(y\) の観測値を \((x_i,y_i)\ (i=1,2,...,n) \) とする。もちろん \(y\) は観測誤差を含んでいる。
\( a, b \) はこれらの観測値から推定するものとする。推定には最小自乗法を使うのが普通である。すなわち
\[ Q:=\sum_i ((ax_i + b) - y_i)^2 \] とおいて、 \( a, b \) を
\[ \frac{\partial Q}{\partial a} = 0; \hspace{1cm} \frac{\partial Q}{\partial b} = 0 \] から決めれば良い。

必要な計算の見通しを良くするために、\( X, Y \) を次のように配列として考え
\[ X = (x_1, x_2, ..., x_n) ; \hspace{1cm} Y = (y_1, y_2, ..., y_n) ; \hspace{1cm} XY = (x_1 y_1, x_2 y_1, ..., x_n y_n) ; \hspace{1cm} c X = (c x_1, c x_2, ..., c x_n) \] 最後の例において \( c \) は配列ではなく1個の数値である。ここに述べた「配列」の概念と演算法は numpy に従った。

要素の平均をオーバーラインで表すことにする。例えば
\[ \overline{a} = a;\hspace{1cm} \overline{X} = \frac{1}{n} \sum_i x_i ; \hspace{1cm} \overline{XY} = \frac{1}{n} \sum_i x_i y_i \] である。すると
\[ a=\frac{\overline{(X - \overline X)(Y - \overline Y)}}{\overline{(X - \overline X)^2}} ; \hspace{1cm} b = \overline{Y} - a\overline{X} \] が得られる。

\( x \) を与えて \( y \) を予測したい場合には以上の方法で \( (a, b) \) を求めて \( y = ax + b \) とすればよい。

case (2)

しかし \( y \) を与えて \( x \) を予測する場合には \( x = \alpha y + \beta \) とし、\( X \) と \( Y \) の役割を入れ替えて
\[ \alpha=\frac{\overline{(Y - \overline Y)(X - \overline X)}}{\overline{(Y - \overline Y)^2}} ; \hspace{1cm} \beta = \overline{X} - \alpha\overline{Y} \] となる。

\(a=1\) すなわち \(\alpha =1\) の場合には
\[ Q = \sum_i ((\alpha x_i + \beta) - y_i)^2 \] となっている。この場合、決定すべきは \(b\) と \(\beta\) であり、\(b+\beta=0\) の関係がある。

従って \(a\) が 1 に近い場合、どちらの考え方を採用しても、予測に関してはほとんど同じ結果が得られることになる。

温度センサーの個体差

2023/07/28

折角、最小自乗法でパラメータを求めても、この結果は特定の温度センサーに対してしか働かない。温度センサーの型番が同一であってもだ。ここでは MCP9700A を例にとって議論する。

図4において、2つの MCP9700A のセンサー電圧を D0D1 に接続して電圧を測定すると次のようになった1

hebe# getdataC.rc
1690519854 3.298 0010 03 0000 0.848 0.852
1690520455 3.313 0010 03 0000 0.852 0.856
1690521055 3.322 0010 03 0000 0.852 0.856
1690521656 3.327 0010 03 0000 0.852 0.856
1690522256 3.332 0010 03 0000 0.853 0.857
1690522857 3.335 0010 03 0000 0.854 0.859
1690523458 3.336 0010 03 0000 0.853 0.857
1690524058 3.339 0010 03 0000 0.853 0.857
1690524659 3.339 0010 03 0000 0.853 0.857
1690525259 3.340 0010 03 0000 0.853 0.857
1690525860 3.340 0010 03 0000 0.853 0.857
1690526460 3.340 0010 03 0000 0.852 0.855
1690527061 3.340 0010 03 0000 0.852 0.856
1690527661 3.339 0010 03 0000 0.850 0.855
1690528262 3.337 0010 03 0000 0.848 0.853
1690528862 3.336 0010 03 0000 0.848 0.853
1690529463 3.335 0010 03 0000 0.847 0.852
1690530063 3.334 0010 03 0000 0.847 0.850
1690530664 3.330 0010 03 0000 0.845 0.848
1690531265 3.329 0010 03 0000 0.843 0.847
1690531865 3.327 0010 03 0000 0.843 0.847
1690532466 3.325 0010 03 0000 0.842 0.846
1690533066 3.320 0010 03 0000 0.841 0.845

図13: 2つの MCP9700A の個体差

第6フィールドと第7フィールドが各々 D0D1 の電圧である。理想的には両者は一致していて欲しい。
XBee のアナログ端子はなかなか優秀で、アナログ端子が異なっていても、同一の電圧を表示させると一致している。従って図13における不一致は、センサーの個体差に由来している。

この観測結果を見る限り、D1D0 の差は 0.004V 程度である。1% 以内に留まっているので素晴らしいと思うかも知れない。しかし、これは温度に換算すると 0.4°C に相当する。図11を見れば判るように、回帰直線の決定に有意な影響を与える。


注1: 秋月で買った5つの MCP9700A のうちの2つ。この中で最も個体差が大きかった組は、温度に換算して 0.5°C であった。