太陽光+3Gモデム+防水ケースで屋外用RaspberryPiデータロガーを作る

レシピ
  • RaspberryPiZero
  • ラズパイ用カメラモジュール
  • SIM用USBモデム
  • 防水ケース
  • 12v鉛蓄電池
  • 太陽光パネル(10W~30W)
  • PWMチャージコントローラ
  • 12v to 5v DC-DCコンバータ
  • ケーブル類

今回データロガー用に使う使う基盤

  • ina219(電圧・電流センサー)
  • bme280(温湿度・気圧センサー)
概要

自作したラズパイ用防水ケースの性能をチェックするため、bme280で防水ケース内の温湿度を測定します。また、太陽光の発電量を確認するため、ina219でバッテリー電圧および電流を測定します。取得したデータはSIM用USBモデムを用いた3Gモバイルデータ通信を通してPython3でMachinistに送信します。また、ラズパイカメラモジュールで撮影した画像をdropbox_uploader.shを使ってDropboxに送信します。

RaspberryPi Zero用屋外防水ケースを3Dプリンターで自作する

配線図

後で書く。

データログ送信スクリプト

Machinistにデータを送信するプログラムをPython3で書きます。jsonで送るようになっているので公式リファレンス通りに作ります。APIKEYが必要なのでユーザーページからコピーします。

ina219とbme280ではi2cを利用します。sudo raspi-configからi2cを有効にしてください。

またライブラリもインストールしておきます。

sudo pip3 install pi-ina219 RPi.bme280

#Python3
import smbus2
import bme280
from ina219 import INA219
import urllib.request
import json


bme280_address=0x76 #0x76 or 0x77
SHUNT_OHMS = 0.1 #milli ohm
APIKEY = "xxxxxxxxxxxxxxxxxx"
Url = "https://gw.machinist.iij.jp/endpoint"
method = "POST"

def machinist_body_property(agent: str):
    data = {}
    data['agent'] = agent
    data['metrics'] = []
    return data

def machinist_metrics_property(name: str, value):
    metric = {}
    metric['name'] = name
    metric['data_point'] = {'value': value}
    return metric

def sendMachinist(data):

    headers = {
        "Content-Type": "application/json",
        "Authorization": "Bearer " + APIKEY,
        "User-Agent": "Python3"
    }

    senddata = json.dumps(data).encode("ascii")
    try:
        req = urllib.request.Request(Url, senddata, headers)
        data = urllib.request.urlopen(req)
        html = data.read().decode("ascii")
        if "Succeeded" in html:
            print("Send succeeded")
        data.close()
    except Exception as e:
        print(e)


#ina219
ina = INA219(SHUNT_OHMS)
ina.configure()
#bme280
i2c = smbus2.SMBus(1)
calibration_params = bme280.load_calibration_params(i2c,bme280_address)
data = bme280.sample(i2c, bme280_address, calibration_params)
#make_senddata
senddata=machinist_body_property('SolarPi')
senddata['metrics'].append(machinist_metrics_property('BatteryVoltage',ina.voltage()))
senddata['metrics'].append(machinist_metrics_property('BatteryCurrent',ina.current()))
senddata['metrics'].append(machinist_metrics_property('Temperature',data.temperature))
senddata['metrics'].append(machinist_metrics_property('Humidity',data.humidity))
senddata['metrics'].append(machinist_metrics_property('Pressure',data.pressure))
sendMachinist(senddata)
通信制御スクリプト

USBモデムはアイドル時でもやや消費電力を食うので、通常はバスパワーをオフにしておき、データを送信する時のみオンにしてデータ送信を行えるようにスクリプトを書きます。スクリプトはcrontabで10分毎に動作させます。バスパワーON時の消費電力は20mA、OFFにすると5mA程度です。bashで書きます。

#!/bin/bash

#指定したUSBモデムの接続が確立しているか判定
function check_network_connection () {
	/sbin/ifconfig | grep -q "$1"
}

#main 通信確立後に実行したいやつを書く
function main () {
	python3 /home/pi/sendMachinist.py #データログ送信
	raspistill -o /home/pi/image.jpg -q 10 -w 1200 -h 900 #写真撮影
	/home/pi/dropbox_uploader.sh upload /home/pi/image.jpg pi/`date +%Y-%m-%d_%H:%M:%S`.jpg #dropbox送信
}

SOC_USB=/sys/devices/platform/soc/20980000.usb/buspower


echo 1 | sudo tee SOC_USB >/dev/null #USBバスパワーON
sudo /sbin/ifup ppp0 #USBモデムアップ

#接続確立まで15秒待機
for ((i=0; i<15; i++))
do
	if check_network_connection ppp0; then
		echo "Online"
		main
		break
	else
		sleep 1
	fi
done

sudo /sbin/ifdown ppp0 #USBモデムダウン
echo 0 | sudo tee SOC_USB >/dev/null #USBバスパワーOFF

cronに登録します。

crontab -e

*/10 * * * * /home/pi/script.sh

動作確認

送信された温湿度、大気圧、バッテリー電流、電圧はこのように表示されます。

くもり、雨を考えると太陽光は30W欲しいです。バッテリーも容量多ければ多いほどよい。

ssh通信で遠隔メンテナンス

遠隔でssh接続ができれば屋外に設置してあるラズパイを取り外して持って帰ってコード編集・・・などの手間がなくなります。

sshポートフォワーディングを利用して遠隔でssh通信ができるようにします。一般的なSIMのデータ通信は外部からアクセスできないので、踏み台となるsshサーバーを用意する必要があります。

①ラズパイからリモートフォワードでsshポートをsshサーバー内部に転送後、②アクセス端末側からsshサーバーにsshログインし、③さらに先ほどsshサーバー内部に転送されたポートにsshログイン(多段ssh)することで、踏み台sshサーバーを介してラズパイに接続できます。踏み台サーバーは条件を満たせば永久無料で使えるGoogleComputeEngineサービスを使いました。自宅で固定IPもしくはDDNSを設定しているのであれば、宅内にsshサーバーを置いて外部公開(ポート開放)でも良いです。

なお、先ほど紹介したbashスクリプトはデータ送信時にしか通信を行わないため、ラズパイとsshサーバー間のssh接続を維持できません。ssh通信したいときだけネットワーク接続を遮断しないようにしておく必要があります。今回はGCEにhttpサーバーを立て、sshの有効・無効状態(1か0)を返すページを作っておいて、スクリプト動作時にそのページにアクセスし、1が返ってきた場合は通信を切らずにautosshでリモートフォワード接続を維持、0が帰ってきたら通信を切るようスクリプトを書きます。これで必要な時だけssh通信ができるようになります。

RaspberryPi Zero用屋外防水ケースを3Dプリンターで自作する

ラズパイ用防水ケースの作成

屋外でラズパイが使えると色々便利なのでケース3Dプリンターで自作してみた。

レシピ
  • 自作防水ケース 
  • 硬質塩ビシートorアクリル 30mm角-1mm厚x1枚
  • M4ネジ 皿ねじ全長16mm x8
  • M4ナット x8
  • シリコンゴム紐 φ2mm
  • ケーブルグランド PG11 x1

※ケース作成には3Dプリンターが必要です。

印刷と設計

湿気が入らないようにシリコンゴム紐を使用して密閉できるよう設計します。

また、カメラを使用する為の窓を付けたいので30mm角-厚み1mmの硬質塩ビシートを使用します。

カメラ部の穴は塩ビシートとシリコンをシールカバーで挟み込みこむことで防水性を確保します。

ケース蓋も同様にシリコンを使用して防水します。

ケーブルを通す部分は配線用防水コネクタを使用します。

印刷時の材質はABS。ノズル0.6mm、レイヤ高さ0.48mm、印刷速度50mm/sで約8時間。

ABSは紫外線に強くないので、屋外で長時間使用するなら耐候塗装等をすると良いと思います。


動作確認

蓋を締めた状態でケーブル入り口から息を吹き込んでも漏れは無く、しっかり密閉できているようです。

試しにケース内にモデム、温湿度計(bme280)、シリカゲル(重要)を入れ、温湿度データをMachinistに送信するシステムを組んで屋外で動作させてみました。

1週間ほど動作させましたが、結露するような湿度にはならず、うまく機能しているようです。

監視カメラ・環境測定など、屋外でのIoTに役立てると思います。


RaspberryPiにSIMカードを挿して通信する

遠隔操作・監視するときに色々と便利なのでメモ

動作確認済みUSBモデム

△L-02C ドコモ LTE対応(一応通信できるが、クセがあり時々通信不能になったりするのでオススメしない)
〇L-03D ドコモ LTE対応 動作良好
〇E3372h-153 LTE対応 外国製なのでバンドの問題あり docomosimでSMS付きでないものはLTE不可。フルMVNOのIIjmioIoTsimはSMSなくてもLTE通信できた。
〇W903 TIANJIE ノーブランド品? aliexpressで1000円。3Gのみ。速度を必要としなければ十分使える。

※ドコモのは大体SIMロックがかかっているがフルMVNOのIIjmioはdocomosimの扱いではないのでロックが掛かっている機器では使えない

レシピ

・RaspberryPi
・USBモデム(今回はL-03Dで設定)

・使用するソフト
wvdial または pppconfig

udevの設定(必要に応じて)

L-03Dは通常接続するとCDROM(VID=0x1004, PID=0x6327)として認識してしまうが、標準インストールされているusb_modeswitchによって
USBデバイス(VID=0x1004, PID=0x6326)として再認識されるように設定されている。(/lib/udev/rules.d/40-usb_modeswitch.rules参照)
この時、modprobe usbserialを実行しないとデバイスを認識しないため、モデムを認識したときにコマンドを実行するudevルールを作る。

設定ファイルを新規作成 /etc/udev/rules.d/50-l03d.rules
またデバイス名はそのままだと/dev/ttyUSB2だが、状況によって数字が変わりアクセスできなくなる可能性があるので、シンボリックリンクでmodemとして固定する。
また、モデム認識時にifupサービスを実行し、自動でネット接続できるようにしておく。今回の設定ではモデムのネットワークインターフェス名をppp0とする。
自動接続不要ならENV{SYSTEMD_WANTS}=”ifup@ppp0.service”は消してok

ACTION=="add", ATTRS{idVendor}=="1004", ATTRS{idProduct}=="6326", RUN+="/sbin/modprobe usbserial vendor=0x1004 product=0x6326"
KERNEL=="ttyUSB*", ATTRS{../idVendor}=="1004", ATTRS{../idProduct}=="6326",ATTRS{bNumEndpoints}=="03", ATTRS{bInterfaceNumber}=="02", SYMLINK+="modem", ENV{SYSTEMD_WANTS}="ifup@ppp0.service"

※最初からUSBデバイスとして認識できているものなら2行目のみでOK。
※idVendor,idProductなど緑色箇所はモデムによって異なるのでlsusbで確認し、書き換えること。

参考にしたサイト

・Raspberry Piで、起動後にUSBモデムを自動認識する
https://qiita.com/air-gh/items/93c67c175e6519129a7f
・SORACOM Interstellar – 40光年先の遠隔監視 –
https://blog.soracom.jp/blog/2017/05/02/interstellar-1/
・Raspberry PiでUSBモデム挿入時にSORACOMへ自動接続する
https://qiita.com/gimy/items/1d49f8e08210e9c9f20a

wvdialの設定

wvdialが設定しやすいのでこちらを使用します。
sudo apt-get install wvdialのあと、
設定ファイルを編集 sudo nano /etc/wvdial.conf
今回はbmobileのapnで設定

[Dialer Defaults]
Init1 = ATZ
Init2 = ATH
Init3 = AT+CGDCONT=1,"IP","bmobile.ne.jp"
Init4 = ATQ0 V1 E1 S0=0 &C1 &D2 +FCLASS=0
Dial Attempts = 3
Stupid Mode = 1
Modem Type = Analog Modem
Dial Command = ATDT
New PPPD = yes
APN = bmobile.ne.jp
Modem = /dev/modem
Baud = 460800
ISDN = 0
Phone = *99***1#
Username = bmobile@4g
Password = bmobile
Carrier Check = no
Auto Reconnect = yes

作成後、sudo wvdialで通信できる。

/etc/network/interfacesの設定

sudo nano /etc/network/interfacesでネットワークインターフェイスを登録する。

allow-hotplug ppp0
iface ppp0 inet wvdial
 pre-up sleep 10 
ほかの設定

ここまでやれば再起動して動くはず。sudo ifup ppp0またはsudo ifdown ppp0で手動ONOFFできる。
また、udevで設定した自動ifupがうまく機能しなかったので下記ページをもとにifupサービスを修正した。

・Raspbianのifup@.serviceがおかしくてUSB 3G/4G モデムが動かなかった
https://signal-flag-z.blogspot.com/2017/01/raspbianifupserviceusb-3g4g.html

備考

L-03Dは大きなファイルを送信しようとするとハングアップするので出力帯域を制限して使用します。
高度なトラフィック制御
https://wiki.archlinux.jp/index.php/高度なトラフィック制御

Token Bucket Filter (TBF)

特定のレート制限に達するまでパケットの送信を許可します。

仮想バケットを作成してバケットが満杯になったときに特定の速度でトークンを破棄することで機能します。各パッケージはバケットから仮想トークンを取得して、トークンを使って送信許可を得ます。大量のパケットが来た場合、バケットはトークンを発行することができず新しいトークンができるまで待機することになります。トークンが十分高速に到達しない場合、パケットが破棄されます。逆の場合 (送信されるパケットが少なすぎる場合)、トークンを使うことで一時的に通信速度を爆発させることができます。

上記より、インターフェイスを遅くしたい場合に役立つ qdisc です。例:

アップロードによってモデムのキューが満杯になってしまうことがあるため、巨大なファイルをアップロードしようとすると通信が破壊されます。

tc qdisc add dev ppp0 root tbf rate 220kbit latency 50ms burst 1540