t-hom’s diary

主にVBAネタを扱っているブログ…とも言えなくなってきたこの頃。

M5 Stack Basicでラズパイにデータ送信

前回摂取カロリー記録のための3ボタンUIを作ったので、今回はここに通信の仕組みを乗せていく。
thom.hateblo.jp

M5 Stackのコード

※ssidとパスワードは他人に見せられないので英字をすべてx、数字をすべて9に置き換えている。

#include <M5Stack.h>
#include <WiFi.h>

int p;
int digit[4];

const char* ssid     = "xx999x-999x99-9";
const char* password = "99x999xx9999x";

const int port = 49152;
const IPAddress server_ip(192, 168, 1, 101);
WiFiClient client;

void setup() {
  // init lcd, serial, but don't init sd card
  M5.begin(true, false, true);
  
  /*
    Power chip connected to gpio21, gpio22, I2C device
    Set battery charging voltage and current
    If used battery, please call this function in your project
  */
  M5.Power.begin();

  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
      delay(500);
  }
  
  resetNumber();
}

// Add the main program code into the continuous loop() function
void loop() {
  // update button state
  M5.update();
 
  // if you want to use Releasefor("was released for"), use .wasReleasefor(int time) below
  if (M5.BtnA.wasReleased() || M5.BtnA.pressedFor(1000, 200)) {
    if(digit[p]++ >= 9){digit[p]=0;}
    displayNumber();
  } else if (M5.BtnB.wasReleased() || M5.BtnB.pressedFor(1000, 200)) {
    if(++p >= 4){p=0;}
    displayNumber();
  } else if (M5.BtnC.wasReleased() || M5.BtnC.pressedFor(1000, 200)) {
    /*M5.Lcd.setTextSize(2);
    M5.Lcd.println("Connecting...");
    delay(1000);*/
    int x = 0;
    x = digit[0]*1000 + digit[1]*100 + digit[2]*10 + digit[3];
    //M5.Lcd.println("Sending " + String(x) + " kcal.");
    //delay(1000);
    if (sendData(x)){
      M5.Lcd.println("Done!");
      M5.Speaker.tone(659, 200);
      delay(200);
    }
    else {
      M5.Lcd.println("Failed!");
      M5.Speaker.tone(440, 100);
      delay(100);
      M5.Speaker.mute();
      delay(100);
      M5.Speaker.tone(440, 100);
      delay(100);
      M5.Speaker.mute();
      delay(100);
      M5.Speaker.tone(440, 100);
      delay(100);
    }

    M5.Speaker.mute();
    delay(1000);
    resetNumber();
  } else if (M5.BtnB.wasReleasefor(700)) {
    resetNumber();
  }
}

bool sendData(int n) {
  M5.Lcd.setTextSize(2);
  M5.Lcd.println("Connecting...");

  M5.Lcd.println("Sending " + String(n) + " kcal.");
  if (!client.connect(server_ip, port)) {
      return false;
  }
  client.print(n);
  //client.stop();
  //WiFi.disconnect();
  return true;
}

void resetNumber() {
  p = 1;
  for (int i=0; i<4; i++){
    digit[i] = {0};
  }
  displayNumber();
}

void displayNumber() {
  M5.Lcd.clear(BLACK);
  M5.Lcd.setCursor(0, 0);
  M5.Lcd.setTextSize(10);
  for(int i=0; i<4; i++) {
    if(i==p){M5.Lcd.setTextColor(YELLOW);}
    else {M5.Lcd.setTextColor(WHITE);}
    M5.Lcd.print(digit[i]);
  }
  M5.Lcd.setTextColor(WHITE);
  M5.Lcd.setTextSize(5);
  M5.Lcd.setCursor(180, 18);
  M5.Lcd.println("kcal");
}

M5 Stack側コードの説明

UIの挙動については前回の記事を参照して欲しい。
前回からの更新は、起動時にWifiに接続されることと、右ボタンでサーバーに接続してデータを送っている点である。

データ送信の度にサーバーに繋ぎに行くくせに切断するコードが無いことを不思議に思う方もいるかもしれない。
これはクライアント側から通信を切断すると上手く行かなかった為、試行錯誤した結果データを受信する度にサーバー側でコネクションをクローズさせるようにした為。
ネットワークの作法に疎いのでこれで良いのかあんまり分かってないんだけど、個人利用のプログラムだから安定稼働すればとりあえず良しとする。

あと送信失敗したときの音は以下のサイトのコードをそのままいただいた。
msr-r.net

ラズパイ側のコード

import socket
import time

with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
    s.bind(("", 49152))
    while True:
        s.listen(1)
        conn, addr = s.accept()
        try:
            data = conn.recv(16)
            print(data)
            time.sleep(1)
        except socket.error:
            pass
        except KeyboardInterrupt:
            conn.close()
            s.close()
        conn.close()

ラズパイ側のコード解説

こちらは単純に受信データをPrintするだけのコード。
conn.recv(16)でデータを待ちつつデータが来たらプリントし、コネクションを切断するという流れ。
KeyboardInterruptを拾う位置がおかしい気がするので、いつかもう少し知識が付いたら書き直したい。

動作イメージはこんなかんじ。
f:id:t-hom:20210219003059p:plain

見てもつまらないとは思うけど、ここまでが一番の難所だった。
あとはファイルに記録してGUIで表示させれば、摂取カロリー記録システムの完成である。

ファイル記録は経験あるので問題ないけど、GUIは少々苦労しそうな気がする。

以上。

当ブログは、amazon.co.jpを宣伝しリンクすることによってサイトが紹介料を獲得できる手段を提供することを目的に設定されたアフィリエイト宣伝プログラムである、 Amazonアソシエイト・プログラムの参加者です。