Skip to content

Instantly share code, notes, and snippets.

@rin-ofumi
Created January 6, 2023 07:09
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rin-ofumi/2441540d6d8fc6e85f02d3b286b538ce to your computer and use it in GitHub Desktop.
Save rin-ofumi/2441540d6d8fc6e85f02d3b286b538ce to your computer and use it in GitHub Desktop.
ATOMS3用 Wi-SUN HATモニター子機プログラム
// M5Stack社のATOMS3をWi-SUN HAT用ESPNOW受信モニタ子機にするサンプルプログラム
// 2023.01.05 @rin_ofumi
//
// ESPNOWコードは「Lang-Ship」さんのESPNOWサンプルスケッチを参考にしています。
// https://lang-ship.com/blog/work/m5stickc-esp-now-1/
//
// ESPNOW受信データの処理は「がじぇっとろぐ」さんを参考にしてます。
// https://b.okadajp.org/2020/05/08/pinku-ji-xiao7seguledshirudoni/
//
// PlatformIO IDE用のテンプレートは「さいとてつや」さんのを使っています。
// https://github.com/3110/m5stack-platformio-boilerplate-code
//
// 別途、下記のライブラリが必要となります。
// M5Unified (テスト時のVerは0.1.1)
#include <M5Unified.h>
#include <WiFi.h>
#include <esp_now.h>
WiFiClient client;
esp_now_peer_info_t slave;
// ユーザー毎のカスタマイズ項目(ここから ↓)
// YOUR-SSID と YOUR-PASSWORD をご自身のWiFi環境に合わせて修正して下さい。
const char* ssid = "YOUR-SSID"; // WiFiのSSID
const char* password = "YOUR-PASSWORD"; // WiFiのパスワード
const int npd_threshold = 2000; // 電力使い過ぎ警告(赤文字)の閾値(単位[W])
unsigned long disp_timeout = 30000; // ESPNOW受信タイムアウトリミット(単位[ms])超えると非表示になります。
// ユーザー毎のカスタマイズ項目(ここまで ↑)
// 以下、プログラム内で使用するグローバル変数
const char* npd_unit = "W"; // 瞬時電力の単位文字列[w]
int d_size_x; // LCDサイズ
int d_size_y; // LCDサイズ
const float f_size_x = 0.5; // 値のフォントXサイズ倍率
const float f_size_y = 0.7; // 値のフォントYサイズ倍率
const float f_u_size_x = 0.5; // 単位のフォントXサイズ倍率
const float f_u_size_y = 0.6; // 単位のフォントYサイズ倍率
const float f_s_size_x = 0.3; // ステータスアイコンのフォントXサイズ倍率
const float f_s_size_y = 0.4; // ステータスアイコンのフォントYサイズ倍率
boolean disp_update = false; // true = 画面更新有り
boolean disp_off = false; // 受信タイムアウト非表示フラグ(false:表示、true:非表示)
int disp_r = 0; // 画面回転(0 to 3)"0"がUSBコネクタが下にある向き
class Recv_data { // ESPNOW受信データの格納用クラス定義
public:
unsigned long recv_Time; // 受信時刻キャッシュ(単位[ms])
String unit_Type; // 機器種別(NPDの英数3文字)
String val_Data; // 受信データ
};
Recv_data r_d; // 受信データクラス宣言
// 送信コールバック関数
void OnDataSent(const uint8_t* mac_addr, esp_now_send_status_t status) {
char macStr[18];
snprintf(macStr, sizeof(macStr), "%02X:%02X:%02X:%02X:%02X:%02X",
mac_addr[0], mac_addr[1], mac_addr[2], mac_addr[3], mac_addr[4],
mac_addr[5]);
Serial.print("ESPNOW Sent to: ");
Serial.println(macStr);
Serial.print("ESPNOW Send Status: ");
Serial.println(status == ESP_NOW_SEND_SUCCESS ? "Delivery Success"
: "Delivery Fail");
}
// 受信コールバック関数
void OnDataRecv(const uint8_t* mac_addr, const uint8_t* data, int data_len) {
Serial.print(">>>Free Heap Size = ");
Serial.println(esp_get_free_heap_size());
char macStr[18];
snprintf(macStr, sizeof(macStr), "%02X:%02X:%02X:%02X:%02X:%02X",
mac_addr[0], mac_addr[1], mac_addr[2], mac_addr[3], mac_addr[4],
mac_addr[5]);
Serial.printf("> ESPNOW Recv MAC Addr = %s\n", macStr);
Serial.printf("> ESPNOW Recv Data(%d byte)\n", data_len);
char* dataStr;
dataStr = (char*)malloc(data_len + 1);
memcpy(dataStr, data, data_len);
dataStr[data_len] = 0;
// 受信データ解析
// UIFlowから受信した先頭10バイトの謎データは捨てる
String data_s = String(dataStr + 10);
Serial.printf("> Recv Data String = [%s]\n", data_s);
// 残りデータの先頭3バイトにWi-SUN HATからの瞬間電力値のヘッダー[NPD]が含まれてるか確認
r_d.unit_Type = data_s.substring(0, 3);
if (r_d.unit_Type == "NPD") {
Serial.printf("-> r_d.unit_Type = %s\n", r_d.unit_Type);
r_d.val_Data = data_s.substring(4);
Serial.printf("-> r_d.val_Data = %s\n", r_d.val_Data);
// 「受信時刻(ms)」をキャッシュに格納し、LCD更新フラグをtrueに
r_d.recv_Time = millis(); // タイマーリセット
Serial.printf("-> ESPNOW recv time = %d(ms)\n", r_d.recv_Time);
disp_off = false; // ”LCD表示”に
disp_update = true; // ”画面更新有り”に
}
free(dataStr);
Serial.print(">>>Free Heap Size = ");
Serial.println(esp_get_free_heap_size());
Serial.print("\n");
}
// 画面描画関数
void draw_lcd() {
uint16_t val_color = WHITE; // 値・単位のフォントカラー
uint16_t back_color = BLACK; // 画面背景色
M5.Display.startWrite();
// 画面回転
M5.Display.setRotation(disp_r);
// 通信タイムアウト時の黒画処理
if (disp_off) { // タイムアウト時は値が黒文字(非表示)
val_color = BLACK;
back_color = BLACK;
}
// ESPNOWで受信したデータ種別確認
String val_buf = r_d.val_Data;
String unit_buf;
if (r_d.unit_Type == "NPD") { // NPD(Wi-SUN HATからの電力値受信)なら
unit_buf = npd_unit;
if (val_buf.toInt() >= npd_threshold) { // 電力使い過ぎ閾値超えの場合は
val_color = BLACK;
back_color = RED;
} else {
val_color = WHITE;
back_color = BLACK;
}
}
// 背景色描画
M5.Display.fillScreen(back_color);
// 値の描画
M5.Display.setTextColor(val_color, back_color);
M5.Display.setFont(&fonts::Font8);
M5.Display.setTextSize(f_size_x, f_size_y);
M5.Display.setTextDatum(
textdatum_t::middle_center); // 真ん中寄せ、中央原点
M5.Display.drawString(val_buf, (int)(d_size_x / 2), (int)(d_size_y / 2));
// 単位の描画
M5.Display.setFont(&fonts::FreeSansBold24pt7b);
M5.Display.setTextSize(f_u_size_x, f_u_size_y);
M5.Display.setTextDatum(textdatum_t::bottom_right); // 右寄せ、下原点
M5.Display.drawString(unit_buf, (int)(d_size_x - 8), (int)(d_size_y - 3));
M5.Display.endWrite();
disp_update = false; // 画面更新フラグをクリア
}
void setup() {
Serial.begin(115200);
auto cfg = M5.config();
cfg.clear_display = true; // 起動時に画面クリア
M5.begin(cfg);
M5.Display.setTextSize(1.5); // 老眼にはキツいので文字サイズは1.5倍
M5.Display.setBrightness(128); // LCD輝度を半分に
d_size_x = M5.Display.height();
d_size_y = M5.Display.width();
Serial.printf("height=%d width=%d\n", d_size_x, d_size_y);
M5.Display.fillScreen(BLACK);
M5.Display.setRotation(disp_r);
M5.Display.print("Wi-SUN HAT ESPNOW monitor\n");
Serial.print("Wi-SUN HAT ESPNOW monitor\n");
// 変数群初期化
r_d.unit_Type = "";
r_d.val_Data = "";
WiFi.begin(ssid, password); // Wi-Fi に接続
while (WiFi.status() != WL_CONNECTED) { // Wi-Fi 接続待ち
delay(500);
M5.Display.print(".");
}
M5.Display.print("WiFi connected\r\nIP address: ");
M5.Display.println(WiFi.localIP());
// ESP-NOW初期化
// WiFi.mode(WIFI_STA); //送信しないなら不要みたい
WiFi.disconnect();
if (esp_now_init() == ESP_OK) {
Serial.println("ESPNow Init Success");
M5.Display.println("ESPNow Init Success");
} else {
Serial.println("ESPNow Init Failed");
M5.Display.println("ESPNow Init Failed");
ESP.restart();
}
// マルチキャスト用Slave登録
memset(&slave, 0, sizeof(slave));
for (int i = 0; i < 6; ++i) {
slave.peer_addr[i] = (uint8_t)0xff;
}
esp_err_t addStatus = esp_now_add_peer(&slave);
if (addStatus == ESP_OK) {
// Pair success
Serial.println("Pair success");
}
// ESP-NOWコールバック登録
// esp_now_register_send_cb(OnDataSent); //送信しないなら不要みたい
esp_now_register_recv_cb(OnDataRecv);
}
void loop() {
M5.update();
// 一定時間指定モードのデータを受信してなければ値を非表示にする
if ((millis() - r_d.recv_Time) >= disp_timeout) {
if (disp_off == false) {
disp_off = true;
disp_update = true;
draw_lcd();
Serial.print("ESPNOW timeout! disp_off!\n");
}
}
// ボタンBを押したら画面回転
if (M5.BtnA.wasPressed()) {
if (disp_r < 3) {
disp_r = disp_r + 1;
} else {
disp_r = 0;
}
disp_update = true;
draw_lcd();
Serial.print("disp_rotate!\n");
}
// 画面更新有りなら描画処理へ
if (disp_update == true) {
draw_lcd();
}
delay(1);
}
@rin-ofumi
Copy link
Author

実行時の画像サンプル

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment