前記事
感圧センサだと上手く測定できなかったので、
今回はロードセルを使用する
背景
- 猫を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単位で測れるようになりました。
一方で、基盤が猫に襲われたり、ご飯を食べていないときの無駄データの多さが気になります。
重量に変化があったタイミングだけデータを飛ばす、襲われないようにカバーをつける等の工夫が必要そうです。