見出し画像

M5StackCore2+モバイルデータ通信+体重計+LINEAPIで重量通知システムを作ってみる!!

Hello World!

大阪ヒートクールでは、最近M5Stackを利用してIoTデバイス等の検証を行うことが多いです。その一巻として、モバイルデータ通信とロードセルを使った体重計を結合したシステムを試作する機会があったので記事にしました。

構築するアーキテクチャ

今回構築するアーキテクチャ
M5 SCALE KITとM5stack Core2

M5Stack用はかりキット(重さユニット付き)を使用して重さを計測します。M5Stack Core2はI2C通信を使用してセンサのデータを取得します。 

M5Stack Core2ではCAT-M対応の通信モジュールSoracomが提供するSoracom Airを使用することで、モバイルデータ通信によりLINEに通知を送れます。またモバイルデータ通信が不安定な場合も考えてwifi通信モードも実装しました。

プログラムの開発にはPlatformIOを使用し、言語は C++で記述します。

スキャンした重さの閾値を上回ったら、LINEに通知を送るように設計します。

実装にあたり満たすべき条件


M5StackCore2はWifiモードで接続しています。

※以下、体重計をweight scanner、センサ出力値をweightDataと呼称します。
仕様として、weight scannerの出力値が 2kg以上になった場合に、LINEの特定のchannelまたはトークに通知を送ることを考えます。この時、weight scannerは以下の2つの状態を持ちます。

状態1: weight scanner が2 kg以上の重量を感知した時に、即時通知を飛ばすようにする

weightData < 2kgの状態から、weightData >= 2kgになった場合、一定期間経ってから送信されるのではなく、即時通知を送るようにします。

状態2: 2 kg以上の重量を検知して即時通知した後は、30sec間隔で通知が送られるようにする。

正し、weightData < 2kgになった後は、状態1に戻るようにします。

これらをdiagram flowに表すと以下のflow chartになります。

Flow chart

実装コード

今回はwifiモードで実装しています。

#include <M5Core2.h>
#include <WiFi.h>
#include <WiFiClientSecure.h>
#include <AXP192.h>
#include "HX711.h"

AXP192 power;
HX711 weightScannerClient;

#define TINY_GSM_MODEM_SIM7080

#define SerialMon Serial
#define SerialAT Serial1

#define LOADCELL_DOUT_PIN 33 //NOTE: this use weight module
#define LOADCELL_SCK_PIN 32 //NOTE: this use weight module

#define GSM_AUTOBAUD_MIN 9600 //NOTE: this use sim module
#define GSM_AUTOBAUD_MAX 115200 //NOTE: this use sim module

#define WIFI_SSID "" //NOTE: please change SSID
#define WIFI_PASSWORD "" //NOTE: please change password

//NOTE: sim mode
// #include <TinyGsmClient.h>
// TinyGsm modem(SerialAT);
// TinyGsmClient client(modem);

const char* line_notify_host     = "notify-api.line.me";
const char* line_notify_token = "";

bool isWightAlert = false;
bool sendLineNotifyLock = true;
float weightData;

void initWifi() {
  WiFi.begin(WIFI_SSID, WIFI_PASSWORD);

  M5.Lcd.print("Connecting to wifi");
  // loading
  int _cursorX = 0;
  M5.Lcd.setTextFont(4);
  M5.Lcd.setTextColor(WHITE);
  M5.Lcd.setCursor(0, 0);

  while (WiFi.status() != WL_CONNECTED) {
    Serial.print(".");
    M5.Lcd.setCursor(0 + 5 * _cursorX, 30);
    M5.Lcd.print(".");
    delay(300);
    _cursorX++;
    if (_cursorX > 320) {
      _cursorX = 0;
    }
  }
  M5.Lcd.fillScreen(BLACK);
  M5.Lcd.setCursor(0, 0);
  M5.Lcd.println("connected with IP");
  M5.Lcd.println(WiFi.localIP());
}

//SIM Mode
// void initSim(){
//     delay(300);
//     M5.Lcd.println("modem.init()");

//     modem.restart();
//     M5.Lcd.println("modem.getModemInfo()");
//     String modemInfo = modem.getModemInfo();
//     M5.Lcd.println(modemInfo);

//     bool result;
//     do {
//         result = modem.setNetworkMode(2);//2 Automatic, 13 GSM only,  38 LTE only,  51 GSM and LTE only
//         delay(500);
//     } while (result != true);
//     M5.Lcd.println("modem.setNetworkMode(38)");
//     do {
//         result = modem.setPreferredMode(1);
//         delay(500);
//     } while (result != true);
//     M5.Lcd.println("modem.setPreferredMode(38)");

// //ここでsoracomのAPNを登録する
//     while (!modem.gprsConnect("soracom.io", "sora", "sora")) {
//         M5.Lcd.println("NG.");
//         delay(1000);
//         M5.Lcd.println("retry");
//         break;
//     }
//     M5.Lcd.println("modem.gprsConnect");
//     M5.Lcd.println("OK.");

//     M5.Lcd.println(" success");
//     bool res = modem.isGprsConnected();
//     M5.Lcd.println(res);

//     //各種情報を出力
//     String ccid = modem.getSimCCID();
//     M5.Lcd.println("CCID: " + ccid);
//     String imei = modem.getIMEI();
//     M5.Lcd.println("IMEI: " + imei);
//     String cop = modem.getOperator();
//     M5.Lcd.println("Operator: " + cop);
//     IPAddress local = modem.localIP();
//     M5.Lcd.println("Local IP: ");
//     M5.Lcd.println(local);
//     int csq = modem.getSignalQuality();
//     M5.Lcd.println("Signal quality: ");
//     M5.Lcd.println(csq);

//     if (modem.isNetworkConnected()) {
//         M5.Lcd.println("Network connected");
//     }

//     SerialMon.println("Waiting for network...");
//     if (!modem.waitForNetwork()) {
//         M5.Lcd.println("fail");
//         delay(1000);
//         return;
//     }

//     if (modem.isNetworkConnected()) {
//         M5.Lcd.println("Network connected");
//     }
// }

void setup() {
  M5.begin();
  weightScannerClient.begin(LOADCELL_DOUT_PIN,LOADCELL_SCK_PIN);
  weightScannerClient.set_gain();
  weightScannerClient.set_scale(27.61f); //NOTE: set scale
  weightScannerClient.tare(); //NOTE: Auto Set Offset

  // Serial.begin(115200);
  // initSim();
  initWifi();
}

void _sendLINENotification(String message) {
  //LINE
  WiFiClientSecure wifiClient;
  wifiClient.setInsecure();

 if (!wifiClient.connect(line_notify_host,443)) {
    M5.Lcd.println("Connection failed");
    return;
  }

  String query = String("message=") + message;
  String request = String("")
              + "POST /api/notify HTTP/1.1\r\n"
              + "Host: " + line_notify_host + "\r\n"
              + "Authorization: Bearer " + line_notify_token + "\r\n"
              + "Content-Length: " + String(query.length()) +  "\r\n"
              + "Content-Type: application/x-www-form-urlencoded\r\n\r\n"
              + query + "\r\n";
  wifiClient.print(request);
}

unsigned long previousMilles = 0;

void sendLineNotificationCronJob(String msg){
  const long interval= 30000; //NOTE: 30sec
  unsigned long currentMillis = millis();
  if(!sendLineNotifyLock){
    _sendLINENotification(msg);
    sendLineNotifyLock = true;
  }else{
    //NOTE: 最初の一回の通知移行は定期実行処理になる。
    if(currentMillis - previousMilles >= interval){
      _sendLINENotification(msg);
      previousMilles = currentMillis; //NOTE: reset
    }
  }
}

void loop() {
  M5.update();
  weightData = weightScannerClient.get_units(10) / 1000.0; //NOTE: 単位は`kg`

  if(weightData >= 2){ //NOTE: 2kg
    sendLineNotificationCronJob("乗りました。");
  }else{
    sendLineNotifyLock = false;
  }
}

各項目の説明

LINE API TOKEN

LINEに通知を送るには、LINE_NotifyというTokenが必要です。
下記のURLからログインして、送信したいLINEのチャンネルの選択とTokenを確定してTokenを発行します。

取得したTokenを/src/main.cppの下記にコピーペーストしてください

//C

~~省略~~
const char* line_notify_host     = "notify-api.line.me";
const char* line_notify_token = ""; //NOTE: ここに追加する

bool isWightAlert = false;
float weightData;
~~省略~~

実際に送信する部分に関しては、下記のやり方を引用させていただきました。

実際に送られる部分に関しては、requestを一つの変数にまとめて送る形になっています。

String request = String("")
              + "POST /api/notify HTTP/1.1\r\n"
              + "Host: " + line_notify_host + "\r\n"
              + "Authorization: Bearer " + line_notify_token + "\r\n"
              + "Content-Length: " + String(query.length()) +  "\r\n"
              + "Content-Type: application/x-www-form-urlencoded\r\n\r\n"
              + query + "\r\n";

VScodeにPlatformIOを読み込ませてProjectを追加する

今回、M5stackStackCore2をC++かつPlatformIOかつvscodeで動かすので、
platformプラグインを入れてrepositoryをcloneしてきます。

git cloneでcloneしたら、Open Projectでcloneしてきたrepositoryを開きます。

weight scannerについて

実際に届くはかりキット

M5Stack Core2に接続するweight scannerは以下を使用すると上記で述べましたが、こちらを動かすmoduleはbogde/HX711のrepositoryを使用します。

~~~~省略~~~~
void loop() {
  M5.update();

//実際に毎秒のweightは以下で取得する。weightDataの単位はkgで取得できるように単位調整。
  weightData = weightScannerClient.get_units(10) / 1000.0; 

  if(weightData >= 2){ //NOTE: 2kg
    sendLineNotificationCronJob("乗りました。");
  }else{
    sendLineNotifyLock = false;
  }
}
~~~~省略~~~~
weightScannerとM5stackCore2はI2Cで行うので、groveコネクタに接続する図
4つ端子を私たちが用意した天板に接続した図

モジュールに含まれているのはI2Cコネクタと4つのロードセルで、水平な天板は別途用意する必要があります。

モバイルデータ通信について

このスモール開発は遠隔で行われたため、実際にはwifi通信モードで実験しました。モバイルデータ通信の方法については下記のリンクにて詳細を説明しています。


この記事が気に入ったらサポートをしてみませんか?