Open46

Raspberry Pi 再入門

saharasahara

MacからRaspberry PiにSSH接続、VNC接続できるようにしたので、これでRaspberry Piにマウスとキーボードの接続が不要になった。

saharasahara

いよいよCO2センサーをRaspberry Pi 4に接続する。

https://www.itmedia.co.jp/news/articles/2109/18/news032.html

の記述に従うと

ラズパイとは以下のような形で接続します。VCCはラズパイの3.3Vに、GNDはラズパイのGNDに、SDAは3ピンに、SCLは5ピンに接続するあたりは通常のI2C接続と同じですが、これに加えてRSTをVCCに、WAKEをGNDに接続する必要があります。

とあるが、Raspberry Pi 4 のピン配置がわからん。ブレットボードの使い方もわからない。

ピン配置は、pinoutコマンドで調べることができた。

https://www.bioerrorlog.work/entry/raspberry-pi-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/

ターミナル上では、カラーで表示されてます:
board

pins

ブレッドボードの使い方
https://denshi-kousaku.nazotoki-k.com/kiso/eic-801.htm

RSTをVCCに

は、センサー側のピンを繋ぐことになるのだろうが、VCCはすでにラズパイの3.3Vと繋がっているので、混線しないのか?大丈夫なのか?

https://tsaitoh.net/wp/2020/11/14/co2sensor-raspberry-pi/
を見ても接続の仕方が異なる。

なおSEN-CCS811で測定するには、測定前48時間から接続して通電している必要があるほか、接続後20分間放置したあとから測定することになっているので注意しましょう。

とあるので、48時間放置してから確認することにしよう。

saharasahara

続いて気温気圧湿度センサーとRaspberry Pi picoの設定を終えた。

saharasahara

AWS IoTの設定のため、まずルートアカウントの作成から始める。

saharasahara

続いて、待っている間にRasberry Pi Picoと温度センサーの設定を行う。

saharasahara

AWS IoTの証明書ファイルをRaspberry Pi Pico内のcertsディレクトリに配置するのに手間取った。

Thonneyのファイルペインからファイルを転送するのだが、転送先のディレクトリを右クリックして、「Focus into」してから転送元にある転送したいファイルを選択するのがコツ。

saharasahara

Raspberry Pi PicoからUSBブラグを抜こうとしたら、ブレットボードから端子が抜けてしまった。
慌てて撮っておいた画像を元に挿し直してテストする。センサーが認識しなかったものの、再度ブレットボードに挿し直して事なきを得た。バックアップは必要。

saharasahara

すったもんだがあったものの、なんとかRaspberry Pi 4からテストメッセージを送信、Iot Core側でメッセージ受信を確認した。
Raspberry Pi Picoは制約がきつくて間に合いそうにない。いったんRaspberry Pi 4で試すことを考えよう。

(追記)
AWS IoT CoreとRaspberry Pi Picoで検索すると、一応事例は出てきた。

https://www.hackster.io/sandeep-mistry/connect-your-raspberry-pi-pico-w-to-aws-iot-core-8868b7
https://docs.aws.amazon.com/ja_jp/iot/latest/developerguide/connecting-to-existing-device.html
https://note.com/euskace/n/n4780b880857c

組み込みとRustの組み合わせは熱いな。

(追加2)
Raspberry Piを使いたい環境では無線LANは使用できないことが判明。
携帯回線モジュール付きの基盤を買うか、モバイルルーターを別途用意しなければならない。
幸い、電源はあるそうなので、どちらでもいけそう。

saharasahara

raspberry pi 4をもう一台買って、ネットワークが弱い環境なのでMacと直接USBケーブルで接続する方法を試そうとした。

https://dev.classmethod.jp/articles/raspberry-pi-4-develop-with-mac-using-usb-c-cable-2/

VSCodeのリモートssh接続が例によってうまくいかない。64bit osの時とはまた違った症状のようだ。
https://itc-engineering-blog.netlify.app/blogs/vscode_ssh_python
を読んで、nodeをaptで入れてみることにした。

saharasahara

新しく買ったRasspberry Pi 4にBME280搭載のセンサーモジュールを配線したものの、一向に認識しない。Raspberry Pi 4を再起動したり配線を見直すも効果なし。

改めて見直すと、Raspberry Piのピンを180度反転して捉えていた可能性に気がつき、急ぎ点対象に差し替えたら見事にセンサーが認識された。
試行錯誤の途中でvscodeのSSH設定をすることも出来て良かった。

saharasahara
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'

https://docs.aws.amazon.com/ja_jp/greengrass/v1/developerguide/IoT-SDK.html

を参考に、

# サンプルレポジトリのルートディレクトリで
$ 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に送信できた。

saharasahara

https://dev.classmethod.jp/articles/for-begginer-temp-hum-puressure-data-send-to-awsiotcore-via-raspberrypi/

に沿って、センサーで取得した値をAWS IoT Coreに送信するところまでを試行してみた。

values

一応、測定値以外に想定している単位も送信するようにした。

ぱっと見で改善できそうな点は

  • User Propertiesに、送信元の端末の情報を付与しても良さそう
  • messageは変えたほうが良さそう
  • topicも変えたい
  • 測定地点の情報
saharasahara

ひとまず、サンプルコード中のsleep関数の引数を30分にする方式で測定と送信を定期実行することにした。

それでもAWS側のサブスクライブは解除されていないので一安心。

ラズパイ本体とセンサーの位置が近いと、温度が高い方にふれてしまうことが分かった。

saharasahara

BME280のセンサーが異常終了するようになった。

writeReg
    bus.write_byte_data(i2c_address, reg_address, data)
OSError: [Errno 121] Remote I/O error

同様の症状は、他の事例でも起こっているようだ。

https://remoteroom.jp/diary/2021-08-22/

電源ケーブルを変えたせいか?

他のラズパイとの距離を離したり、電源ケーブルが触れているのを解消し、センサーとラズパイの距離も離し、ラズパイの再起動(これは効果なかった)をかけることでエラーは解消した。

実地で起こった時に備えて、携帯回線でもIPを固定したり、リモートで通信して復旧できるようにはしたいが。。

電源ケーブルも、蜂に攻撃されることも考慮してそれなりの被覆されたものにする必要がありそう。

saharasahara

測定 & 値の送信の定期実行

どうもpythonのスケジューラ利用では、ssh接続が切れたらおしまいのようなので、crontabによる定期実行にチャレンジ。

https://k99-tech.com/blog/archives/1141

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

出来てるみたいでよかった。

異常が生じた時に検知できる仕組みもいずれ導入したい。

saharasahara

閾値を越えたら通知

  • 閾値を越えたら通知する
  • TODO: 一度通知したら、1時間は通知を抑制する

https://docs.aws.amazon.com/ja_jp/iot/latest/developerguide/iot-sns-rule.html

https://docs.aws.amazon.com/sns/latest/dg/welcome.html

上のページを見ながら、topicの新規作成・sandbox内で電話番号の登録・テストメッセージの送信に成功した。

通知するルールを後から編集する

AWS IoT コンソールの [Rules] (ルール) ハブを開き、該当するルールを編集する。

saharasahara

次は携帯電話回線経由の通信やってみよう。
ついでに、起動したらスクリプトを自動実行するように設定しよう。

saharasahara

携帯電話回線への疎通

https://note.com/avakansai/n/n868a9e67060e

ハードウェア

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に送信されていることを確認した🎉

saharasahara

物理デプロイの前に、仕上げとしてファイヤーウォールの設定を入れようとするも、utwapt-getで導入できず。さらに、システム関係のアップデートコマンドを実行したら、イメージが壊れるという事態に。
もはやMacからSDカードを認識することもできない状態になったため、SDカードを用意するところからやり直すことに。とほほ。。

https://qiita.com/yoh-nak/items/46935af3c5c4036e93b3

saharasahara

再セットアップ中、無線LANモジュール (wlan0)をスキャンしようとすると

sudo iwlist wlan0 scan | grep ESSID
wlan0 Interface doesn't support scannning : Device or resource busy

となり、スキャンできない。

ifupもできない。

4G Piを外すことで wlan0が使えるようになった。もののすぐ使えなくなった。

saharasahara

https://zenn.dev/karaage0703/books/80b6999d429abc8051bb/viewer/4e6567

をみながら、ssh周りの設定を行う。

SSH接続周りの設定変更

SSH接続がすぐに切れなくなるようにするために、リモート側の/etc/ssh/sshd_configファイルを編集した。

ClientAliveInterval 60
ClientAliveCountMax 60

ただ、ローカルのVSCode側で編集すると権限不足で怒られて保存できない。

そこで、sudoで上書きしてくれるVSCode拡張を導入して解決した。

https://stackoverflow.com/questions/56291492/how-to-save-a-file-in-vscode-remote-ssh-with-a-non-root-user-privileges

https://marketplace.visualstudio.com/items?itemName=yy0931.save-as-root

さらに、パスワードでのログインを禁止する。

#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
saharasahara

そうか pip はRaspberry Pi OSではインストールされていないのか。

https://qiita.com/nanbuwks/items/315f4281cbad51c73510

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だけで充分だった。

saharasahara

gitも入っていなかったので

sudo apt-get install git

でインストールする。

saharasahara

センサーの値を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
saharasahara

やっとセンサーからの取得値をAWS IoT Coreに送信することができた。

saharasahara

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()

とすることで、絶対パスを書かずにインポートできます。

参考:https://note.nkmk.me/python-relative-import/

saharasahara

なお、上記はシェルスクリプトをかます場合、シェルスクリプト側にもディレクトリに関する配慮が必要だった。

スクリプトの最初に、 スクリプトのあるディレクトリへの移動をかます。

script_dir=$(cd $(dirname ${BASH_SOURCE:-$0}); pwd)

python3 ${script_dir}/path/to/file.py
# 以下、ファイルパスを指定する箇所にはもれなく${script_dir}を付加して絶対パスに展開されるようにする
saharasahara

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

https://monomonotech.jp/kurage/raspberrypi/systemd_autostart.html

最後に、サービスを有効にする

sudo systemctl enable some.service

サービスを無効化するには

sudo systemctl disable some.service

再起動したらSSHでログイン出来なくなったぞ。
センサー計測&送信は出来ている。

saharasahara

SDカードごとバックアップする

https://zenn.dev/dsl_gunma/articles/1f2456582db81c

https://jp.easeus.com/todo-backup-resource/backup-raspberry-pi-sd-card.html

https://www.pc-koubou.jp/magazine/52103

https://raspi.taneyats.com/entry/backup-sdcard

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分かかった。

saharasahara

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.

https://tech.kurojica.com/archives/17514/

saharasahara

AWS LambdaでIoT Coreからの情報を受け取ってゴニョゴニョしたい

https://zenn.dev/thorie/articles/548iot_room_condition_aws_timestream_grafana_cloud

に沿って進めていきます。今回はLambdaにデプロイして動作確認するところまで。

Serverless FrameworkでLambdaにデプロイする

AWS CLIで認証する

https://zenn.dev/akkie1030/articles/aws-cli-setup-tutorial

基本的には、IAMのWeb コンソールからアクセスキーを発行して、CLI用のプロファイルを作成すれば良い。

私の場合はIAMユーザーにMFAを適用しているので、さらに一時的な認証情報を取得し、 ~/.aws/credentials内のプロファイルの値を書き換える必要がある。

https://zenn.dev/akkie1030/articles/aws-cli-mfa-account

デプロイする

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 group

Log streamsから、Log streamを選択する。

Log streams

これで、ログの値が見れる🎉。

logs

やっとRaspberry PiからLambdaまで値が到達した。。あとはTimestream経由で可視化ツールに流し込めれば一通りの設定は完了。。長かった。