#include <M5StickCPlus.h>
#include <WiFiClientSecure.h>
#include <Ticker.h>
// 使用するGPIOポート --------
#define WATER_PIN 36 // 雪センサー
const char *WIFI_PASS = "***********"; //Wi-Fiのパスワード
// LINEの設定 -------- トークン発行 さっき紹介したサイトを参考に
bool USE_LINE_NOTIFY = true;
const char* LINE_HOST = "notify-api.line.me";
const char* LINE_TOKEN = ""; //ゲットしたライントークンを入力
// 設定 --------
const int BATT_LOW_ALERM = 20; // バッテリー残量アラーム(%) 低下時に通知
const int MUSIC_BPM = 120;
// マクロ定義 --------
#define sp(x) Serial.println(x)
#define spn(x) Serial.print(x)
#define spf(fmt, ...) Serial.printf(fmt, __VA_ARGS__)
#define lp(x) M5.Lcd.println(x)
#define lpn(x) M5.Lcd.print(x)
#define lpf(fmt, ...) M5.Lcd.printf(fmt, __VA_ARGS__)
#define array_length(x) (sizeof(x) / sizeof(x[0]))
// ================================================================================
int i, stat;
delay(100);
while (true) {
// 接続を試みる
lpn("WiFi connecting...");
spn("WiFi connecting...");
for (i = 0; i < 150; i++) {
spn(stat);
if (stat == WL_DISCONNECTED) { //6
spn(".");
if (i == 149) {
lpn("! ");
sp("abort");
}
} else if (stat == WL_CONNECT_FAILED) { //2 (WL_NO_SSID_AVAIL=1)
lpn("<<FAIL>>\n");
sp(" err=" + String(stat) + " Failed");
delay(3000);
break;
} else if (stat == WL_CONNECTED) { //3
lp("<<OK>> connected!\n");
lpn("IP= ");
sp("\nWiFi connected!");
spn("IP address: ");
break;
}
delay(200);
}
// 接続できたか?
if (stat == WL_CONNECTED) { //接続成功 //3
break;
} else { //接続失敗
delay(3000);
}
}
}
// バッテリー残量の計算
int get_battery() {
return round(((M5.Axp.GetBatVoltage() - 3.0f) / (4.2f - 3.0f)) * 100);
}
// ================================================================================ https POST
// LINE Notify メッセージ 絶対に変えるな!!
bool send_line(String message) {
bool res = false;
spn("connecting LINE server...");
WiFiClientSecure sclient;
sclient.setInsecure(); // 証明書の検証をしない
if (sclient.connect(LINE_HOST, 443)) {
String query = "message=" + message;
String request = String("")
+ "POST /api/notify HTTP/1.1\r\n"
+ "Host: " + LINE_HOST + "\r\n"
+ "Authorization: Bearer " + LINE_TOKEN + "\r\n"
+ "Content-Length: " + String(query.length()) + "\r\n"
+ "Content-Type: application/x-www-form-urlencoded\r\n\r\n"
+ query + "\r\n";
spn("sending...");
sclient.print(request);
//sp(request);
while (sclient.connected()) {
String buff = sclient.readStringUntil('\n');
//sp("|"+buff);
break;
}
sp("done");
res = true;
} else {
sp("failed");
}
sclient.stop();
return res;
}
// ================================================================================ タイマー割込み
Ticker ticker1;
int start_play_music = 0;
void play_music(int musicno) {
start_play_music = musicno;
}
// バックグラウンドでBEEP音を再生する(10msごとに実行される関数)
void play_background() {
static int musicno = 0;
static int step = 0;
static unsigned long stop_ms = 0;
// 再生開始
if (start_play_music > 0) {
musicno = start_play_music;
start_play_music = 0;
step = 0;
stop_ms = 0;
}
// 次の音階を設定する
}
// ================================================================================ セットアップ
void setup() {
M5.begin();
Serial.begin(115200);
// LED設定
pinMode(M5_LED, OUTPUT);
digitalWrite(M5_LED, HIGH); //OFF
// 水位センサー ピン設定
pinMode(WATER_PIN, INPUT);
//ディスプレイ&フォント設定
M5.Lcd.setRotation(3); // 横向き・ホームボタンが左
M5.Lcd.setTextColor(TFT_WHITE);
M5.Beep.setBeep(2000, 100);
// タイマー割込みの開始:
ticker1.attach_ms(10, play_background);
delay(1000);
delay(200);
}
// ================================================================================ メインループ
void loop() {
static int mode = 0;
static int detect_cnt = 0;
int i;
bool res;
M5.update();
// 1秒おきにフラグを立てる
static uint32_t tm = 0;
static uint32_t old_ms = 0;
bool refresh = false;
if (tm == 0 || tm+1000 < millis()) {
refresh = true;
tm = millis();
}
if (millis() < old_ms) tm = 0;
old_ms = millis();
// メインボタンを1回押したらお湯はり開始
if(mode == 0 && M5.BtnA.wasPressed()) {
sp("Start");
res = send_line("%0D%0A計測開始!"); // LINEに通知する
if (!res) lp("Line Error!");
mode = 1;
refresh = true;
} else if(mode >= 1 && M5.BtnA.wasPressed()) {
// メインボタンをもう1回押したら終了
sp("Shutdown...");
mode = 3;
refresh = true;
}
// 雪の状態をチェック
int yuki = digitalRead(WATER_PIN);
digitalWrite(M5_LED, yuki ? LOW : HIGH);
if (mode == 1) {
if (detect_cnt == 1) {
// ゆき
}
if (detect_cnt >= 500) { // 5秒連続して検出したら (100=1s)
mode = 2;
refresh = true;
sp("雪かき");
send_line("%0D%0A雪かき!"); // LINEに通知する
}
}
// モードに応じた画面表示
static bool battnotified = false;
if (refresh) {
int battery = get_battery(); // バッテリー残量取得
lp("Battery "+String(battery)+"%\n\n");
switch (mode) {
case 0:
lp("Push to Start\n");
break;
case 1:
lp("KEISOKU\n");
lp("count="+String(detect_cnt)+"\n");
break;
case 2:
lp("YUKIKAKI");
break;
case 3:
lp("Shutdown...");
break;
}
// バッテリー残量警告
if (!battnotified && battery < BATT_LOW_ALERM) {
battnotified = true;
send_line("%0D%0Aモバイルバッテリーが接続されていません "+String(battery)+"%70"); // LINEに通知する
}
}
// 電源オフ
if (mode == 3) {
delay(2000);
M5.Axp.PowerOff(); // 電源オフ
}
// サイドボタンでモード戻す
if(M5.BtnB.wasPressed()) {
mode = 0;
detect_cnt = 0;
battnotified = false;
}
delay(10);
}