Raspberry Pi 再入門
OSは64bitがいいの?
を見ると、クローズドソースのソフトウェアが64bitに移行しているし将来的にパフォーマンスが良いから64bitも用意したよとあるけど、まだ時期尚早なのか?
をみて、64bitでインストールすることにした。
MacからRaspberry PiにSSH接続、VNC接続できるようにしたので、これでRaspberry Piにマウスとキーボードの接続が不要になった。
続いて、MacのVSCodeからRaspberry Piに接続して開発できるようにする。
いよいよCO2センサーをRaspberry Pi 4に接続する。
の記述に従うと
ラズパイとは以下のような形で接続します。VCCはラズパイの3.3Vに、GNDはラズパイのGNDに、SDAは3ピンに、SCLは5ピンに接続するあたりは通常のI2C接続と同じですが、これに加えてRSTをVCCに、WAKEをGNDに接続する必要があります。
とあるが、Raspberry Pi 4 のピン配置がわからん。ブレットボードの使い方もわからない。
ピン配置は、pinout
コマンドで調べることができた。
$ pinout
bash: warning: setlocale: LC_ALL: cannot change locale (en_US.UTF-8)
,--------------------------------.
| oooooooooooooooooooo J8 +======
| 1ooooooooooooooooooo PoE | Net
| Wi 1o +======
| Fi Pi Model 4B V1.4 oo |
| ,----. +---+ +====
| |D| |SoC | |RAM| |USB3
| |S| | | | | +====
| |I| `----' +---+ |
| |C| +====
| |S| |USB2
| pwr |hd| |hd| |I||A| +====
`-| |---|m0|---|m1|----|V|-------'
Revision : d03114
SoC : BCM2711
RAM : 8GB
Storage : MicroSD
USB ports : 4 (of which 2 USB3)
Ethernet ports : 1 (1000Mbps max. speed)
Wi-fi : True
Bluetooth : True
Camera ports (CSI) : 1
Display ports (DSI): 1
J8:
3V3 (1) (2) 5V
GPIO2 (3) (4) 5V
GPIO3 (5) (6) GND
GPIO4 (7) (8) GPIO14
GND (9) (10) GPIO15
GPIO17 (11) (12) GPIO18
GPIO27 (13) (14) GND
GPIO22 (15) (16) GPIO23
3V3 (17) (18) GPIO24
GPIO10 (19) (20) GND
GPIO9 (21) (22) GPIO25
GPIO11 (23) (24) GPIO8
GND (25) (26) GPIO7
GPIO0 (27) (28) GPIO1
GPIO5 (29) (30) GND
GPIO6 (31) (32) GPIO12
GPIO13 (33) (34) GND
GPIO19 (35) (36) GPIO16
GPIO26 (37) (38) GPIO20
GND (39) (40) GPIO21
POE:
TR01 (1) (2) TR00
TR03 (3) (4) TR02
For further information, please refer to https://pinout.xyz/
ターミナル上では、カラーで表示されてます:
ブレッドボードの使い方
RSTをVCCに
は、センサー側のピンを繋ぐことになるのだろうが、VCCはすでにラズパイの3.3Vと繋がっているので、混線しないのか?大丈夫なのか?
を見ても接続の仕方が異なる。
なおSEN-CCS811で測定するには、測定前48時間から接続して通電している必要があるほか、接続後20分間放置したあとから測定することになっているので注意しましょう。
とあるので、48時間放置してから確認することにしよう。
続いて気温気圧湿度センサーとRaspberry Pi picoの設定を終えた。
AWS IoTの設定のため、まずルートアカウントの作成から始める。
続いて、待っている間にRasberry Pi Picoと温度センサーの設定を行う。
scpコマンドで、ローカルからssh接続先にディレクトリごと転送できるのか。。便利。
ありがてえ。AWS IoT Coreで証明書をデバイス側に設置する必要があり、VSCodeでssh接続がうまくいかないので困っていた。
こちらも粘っていたら
に書かれていた方法で remote sshが復活した。
AWS IoTの証明書ファイルをRaspberry Pi Pico内のcertsディレクトリに配置するのに手間取った。
Thonneyのファイルペインからファイルを転送するのだが、転送先のディレクトリを右クリックして、「Focus into」してから転送元にある転送したいファイルを選択するのがコツ。
Raspberry Pi PicoからUSBブラグを抜こうとしたら、ブレットボードから端子が抜けてしまった。
慌てて撮っておいた画像を元に挿し直してテストする。センサーが認識しなかったものの、再度ブレットボードに挿し直して事なきを得た。バックアップは必要。
すったもんだがあったものの、なんとかRaspberry Pi 4からテストメッセージを送信、Iot Core側でメッセージ受信を確認した。
Raspberry Pi Picoは制約がきつくて間に合いそうにない。いったんRaspberry Pi 4で試すことを考えよう。
(追記)
AWS IoT CoreとRaspberry Pi Picoで検索すると、一応事例は出てきた。
組み込みとRustの組み合わせは熱いな。
(追加2)
Raspberry Piを使いたい環境では無線LANは使用できないことが判明。
携帯回線モジュール付きの基盤を買うか、モバイルルーターを別途用意しなければならない。
幸い、電源はあるそうなので、どちらでもいけそう。
raspberry pi 4をもう一台買って、ネットワークが弱い環境なのでMacと直接USBケーブルで接続する方法を試そうとした。
VSCodeのリモートssh接続が例によってうまくいかない。64bit osの時とはまた違った症状のようだ。
を読んで、nodeをaptで入れてみることにした。新しく買ったRasspberry Pi 4にBME280搭載のセンサーモジュールを配線したものの、一向に認識しない。Raspberry Pi 4を再起動したり配線を見直すも効果なし。
改めて見直すと、Raspberry Piのピンを180度反転して捉えていた可能性に気がつき、急ぎ点対象に差し替えたら見事にセンサーが認識された。
試行錯誤の途中でvscodeのSSH設定をすることも出来て良かった。
BME280の扱い方
サンプルプログラムprint
文を修正しないと環境によっては動作しません。
Traceback (most recent call last):
File "/home/<username>/Documents/aws-iot-device-sdk-python/samples/basicPubSub/basicPubSub.py", line 18, in <module>
from AWSIoTPythonSDK.MQTTLib import AWSIoTMQTTClient
ModuleNotFoundError: No module named 'AWSIoTPythonSDK'
を参考に、
# サンプルレポジトリのルートディレクトリで
$ sudo python setup.py install
した上で、basicPubSub.py
を実行する。
$ python3 samples/basicPubSub/basicPubSub.py --endpoint foo.iot.ap-northeast-1.amazonaws.com --rootCA ./certs/AmazonRootCA1.pem --cert ./certs/bar-certificate.pem.crt --key ./certs/bar-private.pem.key
これで、AWS IoT Coreに送信できた。
タスクをスケジュールに登録するのは、pythonスクリプトだけでも実現できるらしい
に沿って、センサーで取得した値をAWS IoT Coreに送信するところまでを試行してみた。
一応、測定値以外に想定している単位も送信するようにした。
ぱっと見で改善できそうな点は
- User Propertiesに、送信元の端末の情報を付与しても良さそう
- messageは変えたほうが良さそう
- topicも変えたい
- 測定地点の情報
ひとまず、サンプルコード中のsleep関数の引数を30分にする方式で測定と送信を定期実行することにした。
それでもAWS側のサブスクライブは解除されていないので一安心。
ラズパイ本体とセンサーの位置が近いと、温度が高い方にふれてしまうことが分かった。
BME280のセンサーが異常終了するようになった。
writeReg
bus.write_byte_data(i2c_address, reg_address, data)
OSError: [Errno 121] Remote I/O error
同様の症状は、他の事例でも起こっているようだ。
電源ケーブルを変えたせいか?
他のラズパイとの距離を離したり、電源ケーブルが触れているのを解消し、センサーとラズパイの距離も離し、ラズパイの再起動(これは効果なかった)をかけることでエラーは解消した。
実地で起こった時に備えて、携帯回線でもIPを固定したり、リモートで通信して復旧できるようにはしたいが。。
電源ケーブルも、蜂に攻撃されることも考慮してそれなりの被覆されたものにする必要がありそう。
測定 & 値の送信の定期実行
どうもpythonのスケジューラ利用では、ssh接続が切れたらおしまいのようなので、crontabによる定期実行にチャレンジ。
cronの確認
設定内容の編集は crontab -e
。
設定内容の確認は crontab -l
。設定ファイルの中身が標準出力に出力される。
動作の確認はsudo /etc/init.d/cron status
。
$ sudo /etc/init.d/cron status
● cron.service - Regular background program processing daemon
Loaded: loaded (/lib/systemd/system/cron.service; enabled; vendor preset: enabled)
Active: active (running) since Sat 2023-11-11 20:48:58 JST; 21min ago
出来てるみたいでよかった。
異常が生じた時に検知できる仕組みもいずれ導入したい。
閾値を越えたら通知
- 閾値を越えたら通知する
- TODO: 一度通知したら、1時間は通知を抑制する
上のページを見ながら、topicの新規作成・sandbox内で電話番号の登録・テストメッセージの送信に成功した。
通知するルールを後から編集する
AWS IoT コンソールの [Rules] (ルール) ハブを開き、該当するルールを編集する。
せっかくRaspberry Pi Picoを買ったのだからもっと活用したい。せっかくだから、Rustで書きたいな。
Groveデバイスを使うと、接続が簡単に
測定値の保存と可視化
とりあえず、自宅の湿度を測定して、通知するように組んでみよう。
Grafana
次は携帯電話回線経由の通信やってみよう。
ついでに、起動したらスクリプトを自動実行するように設定しよう。
SSH接続でパスワード認証を無効にする
携帯電話回線への疎通
ハードウェア
12V2A 2.1mmの電源を別途購入する。
まず、柱を取り付けた。
ネジとナットを締めるために、ミニドライバーとレンチが必要。
続いて、アンテナ・ラズパイ本体とのUSBケーブルでの接続・電源ケーブルの接続を行い、ラズパイを起動する。
ソフトウェア
前掲のリンク先に記載のある手順を実施する。
curl https://mechatrax.github.io/setup.sh | sudo bash
sudo raspi-config nonint do_netconf 2
sudo apt install 4gpi-utils 4gpi-net-mods 4gpi-networkmanager
sudo nmcli con add type gsm ifname "*" con-name {任意の接続名} apn {apn} user {user-name} password {password}
動作確認
nmcli connection show
で、接続を確認する。
$ nmcli connection show
NAME UUID TYPE DEVICE
musen-lan xxxxx-yyyy-zzzz-aaaa-asfjklkrewo wifi wlan0
xxxx-sim 56b4ee0c-xxxx-yyyy-zzzz-95d87bvb5u77 gsm cdc-wdm0
コマンドで設定したcon-name
の行で DEVICE
が ----
になっていなければOK!
計測値の確認
どうやら、ラズパイを再起動してもcrontabで登録したタスクは実行されるらしく、無事携帯電話回線からセンサーの計測値がAWSに送信されていることを確認した🎉
物理デプロイの前に、仕上げとしてファイヤーウォールの設定を入れようとするも、utw
を apt-get
で導入できず。さらに、システム関係のアップデートコマンドを実行したら、イメージが壊れるという事態に。
もはやMacからSDカードを認識することもできない状態になったため、SDカードを用意するところからやり直すことに。とほほ。。
気を取り直して、4GPiの開発元が公開しているOSイメージをRaspberry Pi Imager経由で別のSDカード(8GB)にインストールした。
再セットアップ中、無線LANモジュール (wlan0)をスキャンしようとすると
sudo iwlist wlan0 scan | grep ESSID
wlan0 Interface doesn't support scannning : Device or resource busy
となり、スキャンできない。
ifup
もできない。
4G Piを外すことで wlan0
が使えるようになった。もののすぐ使えなくなった。
をみながら、ssh周りの設定を行う。
SSH接続周りの設定変更
SSH接続がすぐに切れなくなるようにするために、リモート側の/etc/ssh/sshd_config
ファイルを編集した。
ClientAliveInterval 60
ClientAliveCountMax 60
ただ、ローカルのVSCode側で編集すると権限不足で怒られて保存できない。
そこで、sudoで上書きしてくれるVSCode拡張を導入して解決した。
さらに、パスワードでのログインを禁止する。
#rootユーザでのログインを禁止
#上の方に記載されている
#「#PermitRootLogin prohibit-password」を「PermitRootLogin no」に変更
PermitRootLogin no
#パスワード認証を無効化して公開鍵認証のみ許可
#真ん中付近に記載されている
#「#PasswordAuthentication yes」を「PasswordAuthentication no」に変更
PasswordAuthentication no
#空パスワードを無効に
#PasswordAuthenticationのすぐ下に記載されている
#「#PermitEmptyPasswords no」を「PermitEmptyPasswords no」に変更
PermitEmptyPasswords no
最後にsshサービスを再起動して、ログインし直す。
sudo /etc/init.d/ssh restart
ufwの設定は、
の記事が詳しい。
そうか pip
はRaspberry Pi OSではインストールされていないのか。
sudo apt install pip
でインストールする。
アンインストールするときは
sudo apt remove python3-pip
を実行する。
i2c通信をPythonから扱うライブラリsmbus2
をようやっとインストールできるようになった。
$ pip3 install smbus2
...
Installing collected packages: smbus2
Successfully installed smbus2-0.4.3
一部のドキュメントでは、python-smbus
が必要と書いてあるものの、後述する手順においてはsmbus2
だけで充分だった。
git
も入っていなかったので
sudo apt-get install git
でインストールする。
センサーの値をPythonで取得する(再)
git clone https://github.com/SWITCHSCIENCE/BME280.git
Switch Scienceさんのサンプルプログラムはpython2で書かれていて、いつの間にかインストールしていたPythonはpython3なので、print
文を print
関数に変更する。
これでやっとPythonコマンドを使って、センサーの値を取得できるようになった。すでに23時過ぎ。
$ python Python27/bme280_sample.py
temp : 23.71 ℃
pressure : 1009.40 hPa
hum : 20.60 %
やっとセンサーからの取得値をAWS IoT Coreに送信することができた。
Pythonで、深い階層にあるファイルから別の深い階層にあるファイルの関数をインポートする方法でハマったのでメモ。
3つ上のディレクトリに上がってから2つ下がる場合、
import os
import sys
sys.path.append(os.path.join(os.path.dirname(__file__),'../../../'))
で3つ上のディレクトリに上がり、
from directory1.directory2 import filename
filename.function()
とすることで、絶対パスを書かずにインポートできます。
なお、上記はシェルスクリプトをかます場合、シェルスクリプト側にもディレクトリに関する配慮が必要だった。
スクリプトの最初に、 スクリプトのあるディレクトリへの移動をかます。
script_dir=$(cd $(dirname ${BASH_SOURCE:-$0}); pwd)
python3 ${script_dir}/path/to/file.py
# 以下、ファイルパスを指定する箇所にはもれなく${script_dir}を付加して絶対パスに展開されるようにする
MQTTでラズパイが再起動するようにしよう。
ラズパイ側がサブスクライブすれば良いはず。
デバイスを遠隔で再起動・シャットダウンできるようにする
basicPubSub.pyを改造して、AWS IoT Core側で定義しておいたトピックにサブスクライブし、無限ループで待ち受けることにした。
# サブスクライブしていたトピックにメッセージが投稿されたら呼ばれる関数
def customCallback(client, userdata, message):
print("Received a new message: ")
print(message.payload)
print("from topic: ")
print(message.topic)
print("--------------\n\n")
parsed_json = json.loads(message.payload)
if ("cmd" in parsed_json):
cmd = ''
if(parsed_json["cmd"] == "shutdown"):
cmd = 'sudo shutdown -h now'
elif(parsed_json["cmd"] == "reboot"):
cmd = 'sudo reboot'
if(cmd != ''):
print(cmd)
subprocess.Popen(cmd, shell=True)
# Connect and subscribe to AWS IoT
myAWSIoTMQTTClient.connect()
if args.mode == 'both' or args.mode == 'subscribe':
myAWSIoTMQTTClient.subscribe(topic, 1, customCallback)
while True:
time.sleep(1)
で、このpythonスクリプトを起動時に実行するようにすればよい。
/etc/systemd/system
以下に、次のようなファイル(説明上、 some.service
とする)を設置する。
[Unit]
Description = Subscribe and wait for MQTT topic
Requires = network-online.target
After = network-online.target # After=network.targetではうまく動かなかった
[Service]
ExecStart = /usr/bin/bash subscribe.sh # pythonスクリプトの場合は、 /usr/bin/python3 subscribeTopic.py のようにする
WorkingDirectory = /path/to/script
User = user-name
Restart = always
RestartSec = 5
Type = simple
[Install]
WantedBy=multi-user.target
最後に、サービスを有効にする
sudo systemctl enable some.service
サービスを無効化するには
sudo systemctl disable some.service
再起動したらSSHでログイン出来なくなったぞ。
センサー計測&送信は出来ている。
初期設定の自動化
SDカードごとバックアップする
SDカードがMacにマウントできたら、
$ diskutil list
/dev/disk26 (external, physical):
#: TYPE NAME SIZE IDENTIFIER
0: FDisk_partition_scheme *7.9 GB disk26
1: Windows_FAT_32 boot 268.4 MB disk26s1
2: Linux 7.6 GB disk26s2
でディスク名を把握し、
$ diskutil umountDisk /dev/disk26
$ sudo dd if=/dev/disk26 of=backup.img bs=1m
7497+0 records in
7497+0 records out
7861174272 bytes transferred in 1147.083625 secs (6853183 bytes/sec)
でバックアップ完了。このときは20分かかった。
crontabに登録したジョブがうまく動作しなくなったので、
grep CRON /var/log/syslog
でログを検索すると、
No MTA installed
なるエラーメッセージが出るので
sudo apt-get install postfix mailutils
でMTAをインストールした。
Postfix (main.cf) was not set up. Start with
cp /usr/share/postfix/main.cf.debian /etc/postfix/main.cf
. If you need to make changes, edit /etc/postfix/main.cf (and others) as
needed. To view Postfix configuration values, see postconf(1).
After modifying main.cf, be sure to run 'systemctl reload postfix'.
Created symlink /etc/systemd/system/multi-user.target.wants/postfix.service → /lib/systemd/system/postfix.service.
またしても、センサーが動かなくなることがある。
にあるように、
OSError: [Errno 121] Remote I/O error
が出ているときは素直に電源を入れ直すのが吉。
AWS LambdaでIoT Coreからの情報を受け取ってゴニョゴニョしたい
に沿って進めていきます。今回はLambdaにデプロイして動作確認するところまで。
Serverless FrameworkでLambdaにデプロイする
AWS CLIで認証する
基本的には、IAMのWeb コンソールからアクセスキーを発行して、CLI用のプロファイルを作成すれば良い。
私の場合はIAMユーザーにMFAを適用しているので、さらに一時的な認証情報を取得し、 ~/.aws/credentials
内のプロファイルの値を書き換える必要がある。
デプロイする
sls deploy --aws-profile aws-profile-name
でデプロイする。
動作確認は、以下の通り:
% sls invoke -f hello -d '{"key":"value"}' --aws-profile aws-profile-name
{
"statusCode": 200,
"body": "{\"message\": \"Go Serverless v1.0! Your function executed successfully!\", \"event\": {\"key\": \"value\"}}"
}
デプロイした関数を削除する:
% sls remove --aws-profile <aws-profile-name>
Removing <service-name> from stage dev (ap-northeast-1)
✔ Service <service-name> has been successfully removed (26s)
Cloud Watchで動作を確認する
Cloud Watch > Log groupsから、/aws/lambda/subako-dev-hello
を選択する。
Log streamsから、Log streamを選択する。
これで、ログの値が見れる🎉。
やっとRaspberry PiからLambdaまで値が到達した。。あとはTimestream経由で可視化ツールに流し込めれば一通りの設定は完了。。長かった。
Timestream
資料
Composite partition key については、
クラスメソッドの記事に沿って、dev_name
をPartition key nameとして使用してみることにした。
紛失した4G PiのSIMカードスロットを公式サイトから購入することが出来た。注文から3日で届いたので、早速開封しSIMカードの疎通確認をSDカードの交換以来初めて実施した。