釣り竿センサーの検知結果をLINEへ通知

キャストした釣り竿の「引き」をセンサーで検知し、一定の敷居値を超えた場合に、LINEにメッセージを通知する内容を実装しましたので、まとめてみました。 f:id:camelrush:20200617233215p:plain

加速度センサーの実装

基本的な機器の実装と、加速度の取得は、以下の記事を参考にしてください。今回はx,y,z軸への移動距離を「振動のふり幅」とするので、取得した値のうち、Acceloretor値を使用します。 camelrush.hatenablog.com

システム構成図

今回の構成図は以下の通りです。MobilePC上でグラフを表示しつつ、スマホ(SmartPhone)のLINEにAPI通知をPushメッセージを通知します。

f:id:camelrush:20200617235834p:plain

LINE「Messaging API」の使用

LINEのAPIのうち、今回は「Messaging API」を使用します。作成したチャネルのエントリポイント(WebHook)に対して、JSON形式でHTTPリクエストをPOSTすると、あらかじめチャネルを登録しているユーザに対してメッセージを発信することができます(発信先は、Toパラメータで個別指定ができますが、今回はブロードキャスト(全体)発信とします)詳細の仕様は以下に書かれています。
Messaging APIリファレンス | LINE Developers

LINEアカウントがあれば、LINE Developersにログインすることで、フリープランのAPIチャネルを使用することができます。

※アカウントの登録方法、チャネルの作成方法は省略します。

プログラム

RaspberryPiには、以下のPythonプログラムを配置します。

  • mqttAccelerometer.py
from devices.accelerometer import AccelerometerMPU6050 as MPU6050
import paho.mqtt.client
import ssl
import asyncio
import json
import time
import datetime
import requests
import random

# Mqtt Define
AWSIoT_ENDPOINT = "[AWS IoT Entry Point]"
MQTT_PORT = 8883
MQTT_TOPIC_PUB = "mqttAccelerometer"
MQTT_ROOTCA = "./awscert/AmazonRootCA1.pem"
MQTT_CERT = "./awscert/xxxxxxxxxxx-certificate.pem.crt"
MQTT_PRIKEY = "./awscert/xxxxxxxxxxx-private.pem.key"

LINE_THRESHOLD = 5
MSG_LIST = ['ヒットしてない!?',
            '来てる来てるw',
            'Fish on!',
            'Gone!']

def mqtt_connect(client, userdata, flags, respons_code):
    print('mqtt connected.') 
    # Entry Mqtt Subscribe.
    client.subscribe(MQTT_TOPIC_SUB)
    print('subscribe topic : ' + MQTT_TOPIC_SUB) 
 
def mqtt_message(client, userdata, msg):
    # Get Received Json Data 
    json_dict = json.loads(msg.payload)
    # if use ... json_dict['xxx']

def line_post(asum,tmstr):
    access_token = [LINE API Access Token]
    headers = {'Authorization': 'Bearer {}'.format(access_token)}
    uri = "https://api.line.me/v2/bot/message/broadcast"
    msg = random.choice(MSG_LIST) + "\n" + tmstr + "(" + asum + ")"
    print(msg)
    item_data = {
            "messages":[
                {
                    "type":"text",
                    "text":msg 
                }
            ]
        }
    r_post = requests.post(uri, headers=headers, json=item_data)
    print(r_post)
    
# Publish Loop
async def pub_loop():
    acmeter = MPU6050()
    sendtime = time.time()
    while True:
        # get Sensor data
        temp = acmeter.get_temp()
        ax, ay, az = acmeter.get_accel()
        gx, gy, gz = acmeter.get_gyro()
        tm = datetime.datetime.now()
        asum = abs(ax)+abs(ay)+abs(az)
        
        print('ax:{0:> .6f} ,ay:{1:> .6f} ,az:{2:> .6f} ,asum:{3:> .6f}'.format(ax,ay,az,asum))
        json_msg = json.dumps({"dt":tm.strftime('%Y-%m-%d %H:%M:%S.%f'),"ax":ax,"ay":ay,"az":az,"asum":asum,"threshold":LINE_THRESHOLD,"gx":gx,"gy":gy,"gz":gz,"temp":temp})

        # Line Message Send
        if (asum > LINE_THRESHOLD):
            if (time.time() - sendtime) > 1:
                sendtime = time.time()
                line_post('G:{0:>.2f}'.format(asum),
                    tm.strftime('%H時').lstrip("0") + 
                    tm.strftime('%M分').lstrip("0") + 
                    tm.strftime('%S秒').lstrip("0"))

        # mqtt Publish
        client.publish(MQTT_TOPIC_PUB ,json_msg)
        time.sleep(0.01)

# Main Procedure
if __name__ == '__main__':
    # Mqtt Client Initialize
    client = paho.mqtt.client.Client()
    client.on_connect = mqtt_connect
    client.on_message = mqtt_message
    client.tls_set(MQTT_ROOTCA, certfile=MQTT_CERT, keyfile=MQTT_PRIKEY, cert_reqs=ssl.CERT_REQUIRED, tls_version=ssl.PROTOCOL_TLSv1_2, ciphers=None)

    # Connect To Mqtt Broker(aws)
    client.connect(AWSIoT_ENDPOINT, port=MQTT_PORT, keepalive=60)

    # Start Mqtt Subscribe 
    client.loop_start()

    # Start Publish Loop 
    loop = asyncio.get_event_loop()
    loop.run_until_complete(pub_loop())

加速度センサー「MPU6050」を制御する処理は、以下のとおりとなります。

  • devices/accelerometer.py
from abc import ABCMeta
import smbus
import math
from time import sleep

# MPU6050 Register Map
DEV_ADDR = 0x68
ACCEL_XOUT = 0x3b
ACCEL_YOUT = 0x3d
ACCEL_ZOUT = 0x3f
TEMP_OUT = 0x41
GYRO_XOUT = 0x43
GYRO_YOUT = 0x45
GYRO_ZOUT = 0x47
PWR_MGMT_1 = 0x6b
PWR_MGMT_2 = 0x6c   

class AbstractAccelerometer(metaclass=ABCMeta):
    def __init__(self ,devadr):
        self._devadr = devadr
        self._bus = smbus.SMBus(1)
        acc_conf = self._bus.read_byte_data(self._devadr ,0x1C)
        acc_conf = acc_conf | 0x18
        self._bus.write_byte_data(self._devadr, 0x1C, acc_conf)

    def startup(self ,pwr_mgmt_adr):
        self._bus.write_byte_data(self._devadr, pwr_mgmt_adr, 0)

    def _read_word(self ,adr):
        high = self._bus.read_byte_data(self._devadr, adr)
        low = self._bus.read_byte_data(self._devadr, adr+1)
        val = (high << 8) + low
        return val

    def read_word_sensor(self ,adr):
        val = self._read_word(adr)
        if (val >= 0x8000):         # minus
            return -((65535 - val) + 1)
        else:                       # plus
            return val

class AccelerometerMPU6050(AbstractAccelerometer):

    def __init__(self):
        super().__init__(DEV_ADDR)
        super().startup(PWR_MGMT_1)

    def get_temp(self):
        temp = super().read_word_sensor(TEMP_OUT)
        x = temp / 340 + 36.53      # data sheet(register map)記載の計算式.
        return x

    def get_gyro(self):
        x = super().read_word_sensor(GYRO_XOUT)/ 131.0
        y = super().read_word_sensor(GYRO_YOUT)/ 131.0
        z = super().read_word_sensor(GYRO_ZOUT)/ 131.0
        return [x, y, z]

    def get_accel(self):
        #x = super().read_word_sensor(ACCEL_XOUT)/ 16384.0
        #y = super().read_word_sensor(ACCEL_YOUT)/ 16384.0
        #z = super().read_word_sensor(ACCEL_ZOUT)/ 16384.0
        x = super().read_word_sensor(ACCEL_XOUT)/ 2048.0
        y = super().read_word_sensor(ACCEL_YOUT)/ 2048.0
        z = super().read_word_sensor(ACCEL_ZOUT)/ 2048.0
        return [x, y, z]

結果

以下のとおり、加速度センサーのふり幅(水色)が敷居値(灰色:5.0G)を超えた場合だけ、LINEに通知が送られてきました。

f:id:camelrush:20200617233527g:plain

べからず

LINE API は、フリープランのうちは、60メッセージ/日、合計でも1.000メッセージが上限であり、それ以上を送ると通知してくれなくなります。センサーの通知メッセージをノーウエイトで送るとあっという間に上限に達し「429 Too Many Requests」を応答するようになりますので、注意してください。