重さを量ってlineにnotifyした話

GWなのに、あいかわらず工作活動してるなぁ

# はじめに
- M5Stackの[miniscales](https://docs.m5stack.com/ja/unit/Unit-Mini%20Scales)で重さを量ってみた。
- それだけだと、ネタとしては薄すぎるから、量った重さをlineに飛ばしてみた。

# 重さを量る
- こんなコードで量れる。公式のサンプルスケッチは、[こちら](https://github.com/m5stack/M5Unit-Miniscale)。
```cpp
#include <M5Unified.h>
#include "UNIT_SCALES.h"

UNIT_SCALES scales;

void setup() {
  M5.begin();
  M5.Lcd.setRotation(2);
  M5.Lcd.setTextFont(&lgfxJapanGothic_36);

  while (!scales.begin(&Wire, /*SDA*/ 2, /*SCL*/ 1, DEVICE_DEFAULT_ADDR)) {
    Serial.println("scales connect error");
    M5.Lcd.print("scales connect error");
    delay(1000);
  }
  scales.setLEDColor(0x001000);
  M5.Lcd.clearDisplay();
}

float offset = 0;

void loop() {
  float weight = scales.getWeight() - offset;

  M5.Lcd.setCursor(10, 10);
  M5.Lcd.println("重さ");
  M5.Lcd.printf("%7.1f\n", weight);
  M5.Lcd.print(" グラム");

  M5.update();
  if (M5.BtnA.wasPressed()) {
    offset = scales.getWeight();
    M5.Lcd.clearDisplay();
  }

  delay(500);
}
```
- コードの説明はするまでもないかな。getWeight()で重さを量るけど、ゼロ点補正のために変数offsetを設定して、scaleに何も載せてないときにボタンA(M5AtomS3の場合は、液晶画面)を押してoffset値を取得するようにした。
- こんな感じの表示になるけど、0.1g単位でブレブレだから、1g単位表示で使うのがいいみたい。最大重量5kgで1g単位の測定精度なら十分なのかな？最大重量500gにして0.1g単位の精度が出るモードも用意してくれればいいのに。
![GIF_20240427_144205_520.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2733720/1d2444d2-ee3c-6406-af6f-b9ef7a1b7441.gif)
- この後、10分以上放置してたら、g単位でズレとった。量る直前にゼロリセット（液晶画面押下）をしないとならないね。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2733720/20680983-1a5e-0dc9-a3ff-e8ee6931655e.png)

# lineにnotifyする
- [line notify](https://notify-bot.line.me/ja/)を使って飛ばした。
- アクセス用のトークンは[ここ](https://notify-bot.line.me/my/)から取得する。今回、トークンの名称は「notify_test」とした。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2733720/0f401a7e-38d7-d6d5-a79d-26a1a6dfe72c.png)
- ちなみに、macosならターミナルでcurlコマンドを打ち込むだけで飛ばせる。下記の`[access_token]`を上記で取得したトークンに塗り替えて実行すると、lineに「hello」と通知される。トークンを塗り替えるときに、[ ]を消すのを忘れずに。
```
curl -X POST -H 'Authorization: Bearer [access_token]' -F 'message=hello' https://notify-api.line.me/api/notify
```
- M5AtomS3を使って飛ばすときは、こんな感じ。
```cpp
#include <M5Unified.h>
#include <WiFi.h>
#include <WiFiClientSecure.h>

//設定
char* ssid = "****";                              //wifiのssid
char* wfpw = "****";                              //wifiのパスワード
char* host = "notify-api.line.me";                //line_notifyのapiサーバアドレス
char* post = "/api/notify";                       //line_notifyのpost先
char* token = "****";                             //line_notifyのトークン
char* type = "application/x-www-form-urlencoded"; //HTMLのデフォルトのフォームの送信形式

void setup() {
  M5.begin();
  WiFi.begin(ssid, wfpw);

  while (WiFi.status() != WL_CONNECTED) {
    delay(1000);
    Serial.print(".");
  }
  Serial.print("\nwifi接続成功!");

  M5.Lcd.setFont(&lgfxJapanGothic_24);
  M5.Lcd.print("押したら\n通知!");
}

void send_msg(char* msg) {
  // httpsアクセス用イニシャライザ
  WiFiClientSecure client;
  client.setInsecure();//サーバー証明書不要

  //SSL接続(443)
  if (!client.connect(host, 443)) {
    Serial.println("apiサーバ接続失敗");
    return;
  }
  Serial.println("apiサーバ接続成功");

  //notify_apiへポスト
  String query = String("message=") + String(msg);
  String request = String("")
                   + "POST " + post + " HTTP/1.1\r\n"
                   + "Host: " + host + "\r\n" + "Authorization: Bearer " + token + "\r\n"
                   + "Content-Length: " + String(query.length()) + "\r\n"
                   + "Content-Type: " + type + "\r\n\r\n" + query + "\r\n";
  client.print(request);
}

void loop() {
  M5.update();
  if (M5.BtnA.wasPressed()) {
    char* msg = "notifyテスト";
    send_msg(msg);
  }
  delay(10);
}
```
- apple watchを持っていれば、こんな風に通知される。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2733720/96524d54-0139-3313-3325-84896fb1dddf.png)

# 重さを量ったらlineに飛ばす
[重さを量る](https://qiita.com/kei2y/items/8d174bb3f97f0ee40420#%E9%87%8D%E3%81%95%E3%82%92%E9%87%8F%E3%82%8B)と[lineにnotifyする](https://qiita.com/kei2y/items/8d174bb3f97f0ee40420#line%E3%81%ABnotify%E3%81%99%E3%82%8B)を合体させる。
```cpp
#include <M5Unified.h>
#include <WiFi.h>
#include <WiFiClientSecure.h>
#include "UNIT_SCALES.h"

UNIT_SCALES scales;

//設定
char* ssid = "****";                              //wifiのssid
char* wfpw = "****";                              //wifiのパスワード
char* host = "notify-api.line.me";                //line_notifyのapiサーバアドレス
char* post = "/api/notify";                       //line_notifyのpost先
char* token = "****";                             //line_notifyのトークン
char* type = "application/x-www-form-urlencoded"; //HTMLのデフォルトのフォームの送信形式

void setup() {
  M5.begin();
  WiFi.begin(ssid, wfpw);

  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("\nwifi接続成功!");

  M5.Lcd.setRotation(2);
  M5.Lcd.setFont(&lgfxJapanGothic_16);
  M5.Lcd.setTextSize(2);

  while (!scales.begin(&Wire, /*SDA*/ 2, /*SCL*/ 1, DEVICE_DEFAULT_ADDR)) {
    Serial.println("scales connect error");
    M5.Lcd.print("scales connect error");
    delay(1000);
  }
  scales.setLEDColor(0x001000);
  M5.Lcd.clearDisplay();
}

void send_msg(char* msg) {

  //httpsアクセス用イニシャライザ
  WiFiClientSecure client;
  client.setInsecure();//サーバー証明書不要

  //SSL接続(443)
  if (!client.connect(host, 443)) {
    Serial.println("apiサーバ接続失敗");
    return;
  }
  Serial.println("apiサーバ接続成功");

  //notify_apiへポスト
  String query = String("message=") + String(msg);
  String request = String("")
                   + "POST " + post + " HTTP/1.1\r\n"
                   + "Host: " + host + "\r\n" + "Authorization: Bearer " + token + "\r\n"
                   + "Content-Length: " + String(query.length()) + "\r\n"
                   + "Content-Type: " + type + "\r\n\r\n" + query + "\r\n";
  client.print(request);
}

float offset = 0;
float oldWt = 0;
float sendWt = 0;

void loop() {

  float weight = scales.getWeight() - offset;

  M5.Lcd.setCursor(10, 10);
  M5.Lcd.println("重さ");
  M5.Lcd.printf("%7.1f\n", weight);
  M5.Lcd.print(" グラム");

  if (fabs(oldWt - weight) > 10) {
    delay(2000);
    weight = scales.getWeight() - offset;
    oldWt = weight;
    if (weight > 50.0) {
      char wt[32];
      sprintf(wt, "重さ%.0fグラム", weight);
      char* msg = wt;
      send_msg(msg);
    }
  }

  M5.update();
  if (M5.BtnA.wasPressed()) {
    offset = scales.getWeight();
    oldWt = 0;
    M5.Lcd.clearDisplay();
  }

  delay(500);
}
```
- 合体する際、プログラムは若干変えている。
- 具体的に言えば、重さの変動が10g超発生したとき、自動的にlineにnotifyすることとした。ただし、変動後の重さが50g超のときのみに限定している。
- 次の写真は、iphone_se3の重さを量った結果。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2733720/30ea3fbc-3480-16d8-5c7a-fe51b02b58be.png)

# 最後に
- 初めてline_notifyのapiを利用してみたけど、apple watchと連動させると色々と便利な使い道が見つかりそう。でも、普段、iphoneを持ち歩かないからなぁ。