はじめに
「Wiki IoT/Bot Computing」 というのを開発中です。これは一種の並列計算機構で、「Wiki Bot」と名付けた、IoTのエッジ端末がこの計算機構の演算装置群であり、インターネット上に分散配置されたWikiのページをこの計算機構の記憶装置として使おうとするものです。
従来、Wiki Bot はJavaを中心にして作っていましたが、従来のWiki Botの機能を、今回の、ページ変更機能とページ挿入機能の追加により、ほぼ、Python に移し替えました。
なお、前回以下の記事で、Java版にはなかった、画像などのファイルをuploadする機能を加えています。
これにより、以下のように、毎時ごとにその時間のデータを、その時間のページ(合計24ページ)に書き込むとともに、毎日、その日のデータを、その日のページ(合計31ページ)に書き込むシステムを実現することができます。また、このとき、毎時ごとに行う処理のスクリプトを1つのページにまとめて書いておき、それを毎時ごとのページに挿入することができます。同じように、毎日行う処理のスクリプトを1つのページにまとめて書いておき、それを日々のページに挿入することができます。
Wiki Bot
エッジ端末がWikiのページの記述で遠隔操作されるBot となります。これをWiki Botと呼んでいます。
ページ変更
IoTシステムでは、エッジシステムにおいて獲得したデータを、毎時ごと、または毎日ごと、に保存して、あとから参照したい場合があります。今回の機能追加により Wiki IoT/Bot Computer で、あらかじめ、毎時ごとに、または毎日ごとに、データとスクリプトを格納するWikiページ(これをobject ページと呼ぶことにします)を作っておき、Botが、時間が変わるたびに、または日が変わるたびに、そのBotが読み書きするobject ページを変更することができます。これは、Object ページのスクリプトに、
command: set pageName="<次に実行されるページ>"
を書き加えることにより、実現することができます。<次に実行されるページ> には
<hour>
または
<day>
を含むことができ、<hour>には、そのときの時間の時、<day>にはその日の日が置き換わります。例えば以下のコマンド
command: set pageName="ex01_h<hour>"
がスクリプトに書かれていて、これが18時に実行されると、
実行されたときの時間が<hour>と置き換わり、従って、
"ex01_h<hour>"
は、
"ex01_h18"
になり、Wiki Botが次にこのページを読むとき、前と同じWiki サーバの、
ex01_h18 のページを読むことになります。
なお、このとき、あらかじめ
"ex01_h00", "ex01_h01", ..., "ex01_h23" の24枚のページすべてに、実行すべきスクリプトを書いておく必要があります。
例えば、ex01_h06 は、以下のようになります。
object_page http://192.168.1.37/pukiwiki/?ex01_h06 or http://192.168.1.37/pukiwiki/?ex01_h06
device yama_bot_0000_0000_0000_0001 or yama_bot_0000_0000_0000_0001 start after no write for 10 min.
command: set read_interval=60000
command: set exec_interval=0
command: set report_length=400
command: py test
py: import time
py: v=self.ex_pico("ex self.sensing.get_motion()")
py: line="device=motion, Date="+self.Pico_Time.get_now()+", v="+str(v)
py: self.write_line(line)
py: v=self.ex_pico("ex self.sensing.get_lx()")
py: line="device=lx, Date="+self.Pico_Time.get_now()+", v="+str(v)
py: self.write_line(line)
py: v=self.ex_pico("ex self.sensing.get_co2()")
py: if v.startswith("Traceback"):
py: v=0
py: line="device=co2, Date="+self.Pico_Time.get_now()+", v="+str(v)
py: self.write_line(line)
py: v=self.ex_pico("ex self.sensing.get_pressure()")
py: line="device=pressure, Date="+self.Pico_Time.get_now()+", v="+str(v)
py: self.write_line(line)
py: v=self.ex_pico("ex self.sensing.get_humidity()")
py: line="device=humidity, Date="+self.Pico_Time.get_now()+", v="+str(v)
py: self.write_line(line)
py: v=self.ex_pico("ex self.sensing.get_temperature()")
py: line="device=temp, Date="+self.Pico_Time.get_now()+", v="+str(v)
py: self.write_line(line)
py: self.send_result()
command: end test
command: run test
command: set pageName="ex01_h<hour>"
result:
device=temp, Date=2025-02-07 00:14:58.634112, v=13.69C
device=motion, Date=2025-02-06 23:53:43.508638, v=0
device=lx, Date=2025-02-06 23:53:45.542352, v=0
device=pressure, Date=2025-02-06 23:53:47.585749, v=1016.75hPa
device=humidity, Date=2025-02-06 23:53:49.641112, v=47.11%
device=temp, Date=2025-02-06 23:53:51.726429, v=16.34C
device=motion, Date=2025-02-06 23:54:43.955486, v=0
...
current_device="yama_bot_0000_0000_0000_0001". Date=2025-02-07 00:00:55.012417
また、ex01_h07 は以下のようになります。
object_page http://192.168.1.37/pukiwiki/?ex01_h07 or http://192.168.1.37/pukiwiki/?ex01_h07
device yama_bot_0000_0000_0000_0001 or yama_bot_0000_0000_0000_0001 start after no write for 10 min.
command: set read_interval=60000
command: set exec_interval=0
command: set report_length=400
command: py test
py: import time
py: v=self.ex_pico("ex self.sensing.get_motion()")
py: line="device=motion, Date="+self.Pico_Time.get_now()+", v="+str(v)
py: self.write_line(line)
py: v=self.ex_pico("ex self.sensing.get_lx()")
py: line="device=lx, Date="+self.Pico_Time.get_now()+", v="+str(v)
py: self.write_line(line)
py: v=self.ex_pico("ex self.sensing.get_co2()")
py: if v.startswith("Traceback"):
py: v=0
py: line="device=co2, Date="+self.Pico_Time.get_now()+", v="+str(v)
py: self.write_line(line)
py: v=self.ex_pico("ex self.sensing.get_pressure()")
py: line="device=pressure, Date="+self.Pico_Time.get_now()+", v="+str(v)
py: self.write_line(line)
py: v=self.ex_pico("ex self.sensing.get_humidity()")
py: line="device=humidity, Date="+self.Pico_Time.get_now()+", v="+str(v)
py: self.write_line(line)
py: v=self.ex_pico("ex self.sensing.get_temperature()")
py: line="device=temp, Date="+self.Pico_Time.get_now()+", v="+str(v)
py: self.write_line(line)
py: self.send_result()
command: end test
command: run test
command: set pageName="ex01_h<hour>"
result:
device=temp, Date=2025-02-07 00:14:58.634112, v=13.69C
device=motion, Date=2025-02-06 23:53:43.508638, v=0
device=lx, Date=2025-02-06 23:53:45.542352, v=0
device=pressure, Date=2025-02-06 23:53:47.585749, v=1016.75hPa
device=humidity, Date=2025-02-06 23:53:49.641112, v=47.11%
device=temp, Date=2025-02-06 23:53:51.726429, v=16.34C
device=motion, Date=2025-02-06 23:54:43.955486, v=0
...
current_device="yama_bot_0000_0000_0000_0001". Date=2025-02-07 00:00:55.012417
ページ挿入
上の例で、それぞれの時間のobjectページで、同じ処理を行うスクリプトを、それぞれのobjectページに書くのは面倒です。そこで、今回の機能拡張により、別のページに処理だけを書いておき、それをobjectページに挿入することも可能になっています。この、挿入されるスクリプトを記述したページを class ページと呼ぶことにします。
例えば、以下の class ページ
command: set read_interval=60000
command: set exec_interval=0
command: set report_length=360
command: py test
py: import time
py: v=self.ex_pico("ex self.sensing.get_motion()")
py: line="device=motion, Date="+self.Pico_Time.get_now()+", v="+str(v)
py: self.write_line(line)
py: v=self.ex_pico("ex self.sensing.get_lx()")
py: line="device=lx, Date="+self.Pico_Time.get_now()+", v="+str(v)
py: self.write_line(line)
py: v=self.ex_pico("ex self.sensing.get_co2()")
py: if v.startswith("Traceback"):
py: v=0
py: line="device=co2, Date="+self.Pico_Time.get_now()+", v="+str(v)
py: self.write_line(line)
py: v=self.ex_pico("ex self.sensing.get_pressure()")
py: line="device=pressure, Date="+self.Pico_Time.get_now()+", v="+str(v)
py: self.write_line(line)
py: v=self.ex_pico("ex self.sensing.get_humidity()")
py: line="device=humidity, Date="+self.Pico_Time.get_now()+", v="+str(v)
py: self.write_line(line)
py: v=self.ex_pico("ex self.sensing.get_temperature()")
py: line="device=temp, Date="+self.Pico_Time.get_now()+", v="+str(v)
py: self.write_line(line)
py: self.send_result()
command: end test
command: run test
command: set pageName="ex01_h<hour>"
を、http://192.168.1.37/pukiwiki/?class_ex01_h に書いておき、objectページ、ex01_h06 とex01_h07 の、それぞれのページのスクリプトの部分を、以下のinclude 文
include http://192.168.1.37/pukiwiki/?class_ex01_h or http://192.168.1.37/pukiwiki/?class_ex01_h
に書き換えることで、いままでそれぞれのページに同じスクリプトを書いていたものを一つのclass ページにまとめて書くことができます。
ここで、object ページ ex01_h06 は以下のようになります。
object_page http://192.168.1.37/pukiwiki/?ex01_h06 or http://192.168.1.37/pukiwiki/?ex01_h06
device yama_bot_0000_0000_0000_0001 or yama_bot_0000_0000_0000_0001 start after no write for 10 min.
include http://192.168.1.37/pukiwiki/?class_ex01_h or http://192.168.1.37/pukiwiki/?class_ex01_h
result:
device=motion, Date=2025-02-17 06:01:44.025406, v=0
device=lx, Date=2025-02-17 06:01:46.058132, v=272
device=co2, Date=2025-02-17 06:01:48.090722, v=40191
...
object ページ ex01_h07 は以下のようになります。
object_page http://192.168.1.37/pukiwiki/?ex01_h07 or http://192.168.1.37/pukiwiki/?ex01_h07
device yama_bot_0000_0000_0000_0001 or yama_bot_0000_0000_0000_0001 start after no write for 10 min.
include http://192.168.1.37/pukiwiki/?class_ex01_h or http://192.168.1.37/pukiwiki/?class_ex01_h
result:
device=motion, Date=2025-02-17 07:01:47.333278, v=0
device=lx, Date=2025-02-17 07:01:49.363504, v=256
device=co2, Date=2025-02-17 07:01:51.403447, v=35583
ページ変更機能とページ挿入機能を追加したWiki Bot のpython プログラム
以下のリンク先のpython のプログラム wiki_bot_07.py が、ページ変更機能とページ挿入機能を加えた Wiki Bot のプログラムです。これを、Raspberry Pi で動かします。
上のプログラムをダウンロードして、 Raspberry Pi の /home/pi/pythonの直下に置きます。
python3 の仮想環境の構築と使用するパッケージの install
このプログラムは requests_toolbelt パッケージの、MultipartEncoder クラスやpyserialパッケージを利用しているのですが、最近のRaspberry Pi OSでは、pip3コマンドが使えないので、
$ sudo apt install virtualenv python3-virtualenv -y
$ virtualenv -p /usr/bin/python3 testpip
を実行して、python3の仮想環境が使えるようにします。
$ source testpip/bin/activate
を実行して、仮想環境に入ります。
pip3 install requests_toolbelt
pip3 install pyserial
を実行して、 requests_toolbelt と pyserial を install しておきます。
参考:
毎日自動起動するように設定
Wiki bot のプログラムは文字列処理を行いますが、python は文字列処理を行うとゴミが溜まっていき、そのうちメモリが足りなくなることがあります。この問題を緩和するため、以下の設定を行って、毎日、定時に再起動し、自動的にwiki bot が起動されるようにします。
systemd の設定
/etc/systemd/system 直下に、(テキスト)ファイル reboot.service を作成し、エディタで中身を以下のようにします。
[Unit]
Description = Reboot
RefuseManualStart = true
RefuseManualStop = true
[Service]
ExecStart = /sbin/reboot
同じく、/etc/systemd/system 直下に、(テキスト)ファイル reboot.timer を作成し、エディタで中身を以下のようにします。このようにした場合は毎朝午前 10:05分に再起動が行われます。
[Unit]
Description = Weekly Reboot Timer
[Timer]
OnCalendar=*-*-* 10:05:00
[Install]
WantedBy = timers.target
以下を実行して、reboot.timer を有効にし、毎時再起動するタイマーを開始します。
sudo systemctl enable reboot.timer
sudo systemctl start reboot.timer
同じく、/etc/systemd/system 直下に、再起動後に、wiki bot を自動起動するため、(テキスト)ファイル wiki_bot.service を作成し、エディタで中身を以下のようにします。
[Unit]
Description=wiki_bot
After=network.target
[Service]
User=pi
Type=simple
WorkingDirectory=/home/pi/python
ExecStart=/home/pi/python/start_wiki_bot.sh
[Install]
WantedBy=multi-user.target
以下を実行して、実行を有効にします。
sudo systemctl enable wiki_bot.service
reboot後に自動起動されるbash スクリプト, start_wiki_bot.sh を作成
/home/pi/python ディレクトリ直下に、systemd でreboot後に自動起動される以下のスクリプト、start_wiki_bot.sh を作ります。
#!/usr/bin/bash
sleep 120
xhost +
export DISPLAY=:0.0
cd /home/pi/python
source testpip/bin/activate
python3 wiki_bot_07.py -s
応用例: 情報処理学会IOT研究会
山之上卓, "IoTのためのWiki Bot in Python", 2025-IOT-068
場所を知ることができる Wiki Bot
Wiki Botを動かすRaspberry Pi にGPSデバイスをシリアルインターフェースで接続することで、Wiki Botの場所を知ることができるようになります。これを実現するための Wiki Bot を以下のリンク先に置いています。
また、このWiki Botを使って、場所によって表示を変える、着る電光掲示板を作成しました。
https://protopedia.net/prototype/6774
参考: