LoginSignup
3
0

More than 1 year has passed since last update.

M5StackCore2とロードセルTAL220B-5kg/HX711とGASで猫が餌を食べた量を自動記録する

Posted at

前記事

感圧センサだと上手く測定できなかったので、
今回はロードセルを使用する

背景

  • 猫を2匹飼っている
  • 1匹は軽度の脂肪肝の疑い
  • 肝臓の負荷を下げるため、毎日食事量を一定に保つ必要がある
  • キャットフードは1日3回(朝、昼、夜)
  • 1匹あたり目安は45g/日

問題

誰がどれだけ食べたか分からない

  • 2匹ともムラ食い(食べたいときに食べたい量だけ食べる)
  • 給餌皿は分けているが、自分の皿という認識はない(食べたい皿から食べる)

仕方なく、ムラ食いの度に食べた量の記録を付ける
ムラ食いの記録

とても大変

アプローチ

参考記事

色々参考にしたので漏れがあるかも?

構成
https://qiita.com/siroitori0413/items/e45c5b5d3952ef32fff3

プログラム
https://knt60345blog.com/m5stack-googlespreadsheet/
https://hack-tnr.hatenablog.com/entry/2020/06/06/173558

必要部品

  • M5 Stack Core 2 1個
  • ロードセルユニット 1個(Amazonで購入1,698円)
  • ジャンパ線 オス―オス 4本
  • GROVEケーブル オス―オス 1本

成果物

M5Stackにロードセルを繋いで、Arduino IDEからプログラムを流し込めば完成
最初UIFlowで実装しようとしましたが、HX711の取り扱いに悩み、サンプルコードが少ないので止めました。
いつか再チャレンジしたい。

外観

回路の部分がめっちゃ襲われるのでケースなどで対策必須です

餌皿と猫と計測器 猫に襲われる計測器

プログラム

M5Stack側

sample.c
#include <M5Core2.h>
#include <HTTPClient.h>
#include <ArduinoJson.h>

// loadcell
void AE_HX711_Init(void);
void AE_HX711_Reset(void);
long AE_HX711_Read(void);
long AE_HX711_Averaging(long adc,char num);
float AE_HX711_getGram(char num);

#define pin_dout  32    // GROVE Yellow
#define pin_slk   33    // GROVE White

// LOAD CELL (TAL220B-5kg)
#define OUT_VOL   0.001f   //定格出力 [V]
#define LOAD      5000.0f    //定格容量 [g]

float offset;

// wifi
char const *ssid = "your ssid";
char const *password = "your password";

// gas
char const *published_url = "your gas endpoint";

void setup_serial(){
  Serial.begin(115200);
  while (!Serial) continue;
}

void setup_wifi(){
  
  Serial.println("Connecting to ");
  Serial.print(ssid);

  // WiFi開始
  WiFi.begin(ssid, password);
 
  // Wi-Fi接続待ち
  while (WiFi.status() != WL_CONNECTED){
    delay(1000);
    M5.Lcd.print(".");
  }

  // WiFi接続成功メッセージの表示
  Serial.println("\nWiFi Connected.");
  M5.Lcd.setCursor(10, 40);
  M5.Lcd.setTextColor(WHITE);
  M5.Lcd.setTextSize(2);
  M5.Lcd.println("WiFi Connected.");

  // M5StackのIPアドレスを表示
  M5.Lcd.print("IP address: ");
  M5.Lcd.println(WiFi.localIP());
}

void setup(){

  // M5Stack objectの初期化
  M5.begin();

  setup_serial();

  // 画面初期設定
  M5.Lcd.fillScreen(TFT_NAVY);
  M5.Lcd.setCursor(10, 10);
  M5.Lcd.setTextColor(WHITE);
  M5.Lcd.setTextSize(3);
  M5.Lcd.printf("START");

  // Wi-Fi処理の開始
  Serial.println("setup wifi");
  setup_wifi();

  Serial.println("setup AE-HX711");
  AE_HX711_Init();
  AE_HX711_Reset();
  offset = AE_HX711_getGram(50); 
}

void loop(){

    StaticJsonDocument<500> doc;
    char pubMessage[256];
    
    M5.update();
    
    float data;
    char S1[20];
    char s[20];
    data = AE_HX711_getGram(10);
    sprintf(S1,"%s",dtostrf((data-offset), 5, 1, s));
 
    M5.Lcd.setCursor(0, 0, 1);
    M5.Lcd.fillScreen(BLACK);
    M5.Lcd.setTextColor(WHITE);
    M5.Lcd.println("Sending...");
    
    doc["weight"] = S1;
    M5.Lcd.println(S1);
    
    serializeJson(doc, pubMessage);
    
    // HTTP通信開始
    HTTPClient http;
    
    Serial.print(" HTTP通信開始 \n");
    http.begin(published_url);
    
    Serial.print(" HTTP通信POST \n");
    int httpCode = http.POST(pubMessage);
    
    if(httpCode > 0){
      M5.Lcd.printf(" HTTP Response:%d\n", httpCode);
    
      if(httpCode == HTTP_CODE_OK){
      //if(httpCode == 302){
        M5.Lcd.println(" HTTP Success!!");
        String payload = http.getString();
        Serial.println(payload);
      }
    }else{
      M5.Lcd.println(" FAILED");
      Serial.printf(" HTTP failed,error: %s\n", http.errorToString(httpCode).c_str());
    }
    
    http.end();
    delay(30000);
}

void AE_HX711_Init(void)
{
  pinMode(pin_slk, OUTPUT);
  pinMode(pin_dout, INPUT);
}
 
void AE_HX711_Reset(void)
{
  digitalWrite(pin_slk,1);
  delayMicroseconds(100);
  digitalWrite(pin_slk,0);
  delayMicroseconds(100); 
}
 
long AE_HX711_Read(void)
{
  long data=0;
  while(digitalRead(pin_dout)!=0);
  delayMicroseconds(10);
  for(int i=0;i<24;i++)
  {
    digitalWrite(pin_slk,1);
    delayMicroseconds(5);
    digitalWrite(pin_slk,0);
    delayMicroseconds(5);
    data = (data<<1)|(digitalRead(pin_dout));
  } 
  digitalWrite(pin_slk,1);
  delayMicroseconds(10);
  digitalWrite(pin_slk,0);
  delayMicroseconds(10);
  return data^0x800000; 
}
 
 
long AE_HX711_Averaging(long adc,char num)
{
  long sum = 0;
  for (int i = 0; i < num; i++) sum += AE_HX711_Read();
  return sum / num;
}
 
float AE_HX711_getGram(char num)
{
  #define HX711_R1  20000.0f
  #define HX711_R2  8200.0f
  #define HX711_VBG 1.25f
  #define HX711_AVDD      4.2987f//(HX711_VBG*((HX711_R1+HX711_R2)/HX711_R2))
  #define HX711_ADC1bit   HX711_AVDD/16777216 //16777216=(2^24)
  #define HX711_PGA 128
  #define HX711_SCALE     (OUT_VOL * HX711_AVDD / LOAD *HX711_PGA)
   
  float data;
 
  data = AE_HX711_Averaging(AE_HX711_Read(),num)*HX711_ADC1bit;   
  data =  data / HX711_SCALE;
 
  return data;
}

GAS側

js.sample.js
var sheet_id="your sheet id";

//デバイスからのPOSTで実行される関数
function doPost(e) {
  
  //POSTのpayloadを格納
  var parsed_data;
  
  try { 
    parsed_data = JSON.parse(e.postData.contents); 
  } catch(_exception){ 
    // JSON以外がPOSTされたら例外を返して終了
    return ContentService.createTextOutput("JSONデータの解釈が出来ません。: " + _exception.message); 
  }
  
  
  var spread_file_object = SpreadsheetApp.openById(sheet_id);
  var sheet_name = "debug";   
  var sheet_object = spread_file_object.getSheetByName(sheet_name);
  
  // スプレッドに行を追記
  sheet_object.appendRow([new Date(), parsed_data.weight]);
  
  SpreadsheetApp.flush(); //描かなくても動く。一応、遅延対策。
  
  //ESP32にレスポンスを返す
  return ContentService.createTextOutput("post success");
  
}

function doGet(e) {  
  //ESP32にレスポンスを返す
  return ContentService.createTextOutput("get success");
  
}

課題

圧力センサシートを使っていた時と比較して、1g単位で測れるようになりました。
一方で、基盤が猫に襲われたり、ご飯を食べていないときの無駄データの多さが気になります。
重量に変化があったタイミングだけデータを飛ばす、襲われないようにカバーをつける等の工夫が必要そうです。

3
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
0