家庭での電力消費、見える化の魔法
電力消費 目に見えないミステリー
省エネを始めるには家電がどれだけの電力を消費しているかを知ることが最初のステップです。ただし、普段の生活でこの電力消費を確認するのは非常に難しい課題で簡単に確認することが出来ません。
重量課金だというのに、なんと恐ろしい事でしょうか。
Sonyの秘密兵器SpresenseとROHMのWi-SUNボードの登場
ここで、Sonyの秘密兵器であるマイコンボード「Spresense」と無線通信規格「Wi-SUN」が活躍します。これらを駆使し、自宅の電力消費を「見える化」するシステムを構築しました。
ありがたくも、コンテスト開催中でモニターとしてこれらの秘密兵器を受け取ることができました。
お陰でNature Remo E liteを買うお金が浮きました!!貧乏なので本当にありがたいです!ありがたや!ありがたや!
結論というか結果、見える化と省エネに貢献
スマートメーター→Wi-SUN→Spresense→PCの流れでデータを処理し、最終的にProcessingで値をグラフとしてプロットしつつ、過去10件の値を文字列で表示してみました。リアルタイムで更新されるので、家電をOFFした瞬間に消費電力が下がるので何の家電がどれだけの消費電力であったかを簡単に把握することが出来ます!!この画面を常時表示することで子供達も積極的に省エネに協力してくれました、すばらしい!!
こんな感じで時間の経過と共に消費電力がグラフ化され、良い感じに可視化されてく。
ハードウェア構成
簡単です。Spresense メインボードにSPRESENSE-WiSUN-EVK-701を接続するだけのお手軽構成。13250円、お手頃価格ですね。
部品構成
外観
ソースコード (Spresense)
偉大なる先達である、 Spresenseで家庭の消費電力値をスマートメータから取得してEVの充電を制御するデバイス by 84mot | elchika を参考に改変を加えて作成しました。
spresense-wisun.ino
/*
SPRESENSE-WISUN-EVK-701
Copyright (c) 2019 ROHM Co.,Ltd.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANyTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#include "bp35c0-j11.h"
// #define DEBUG
// #define DEBUG2
unsigned char state = 0;
unsigned char state_past = 0;
BP35C0J11 bp35c0j11;
void setup()
{
boolean rc = FALSE;
bp35c0j11.j11_init();
rc = bp35c0j11.wait_msg();
#ifdef DEBUG
Serial.println("wait_mes() 通過");
Serial.print("rc=");
Serial.println(rc);
#endif
if (rc == TRUE)
{
state = 1; // hardware reset end
}
else
{
state = 0;
}
// シリアルモニタ用
#ifdef DEBUG
Serial.println("瞬時電力 [W]");
#endif
s_servo.write(90);
// サーボ駆動用
s_servo.attach(PIN_D03);
s_servo.write(80);
}
void loop()
{
unsigned char msg_length = 0;
boolean rc = 0;
#ifdef DEBUG
// Serial.println(" ");
// Serial.print("State = ");
// Serial.println(state, DEC);
#endif
delay(500);
#ifdef DEBUG2
if (state != state_past)
{
Serial.print("state=");
Serial.println(state);
}
state_past = state;
#endif
switch (state)
{
case (0): // need hardware reset
rc = bp35c0j11.cmd_send(CMD_RESET);
rc = bp35c0j11.wait_msg();
if (rc == TRUE)
{
state = 1;
}
break;
case (1): // init state
rc = bp35c0j11.cmd_send(CMD_INI);
rc = bp35c0j11.wait_msg();
if (rc == TRUE)
{
state = 2;
}
break;
case (2): // B-root 接続初期設定
// Bルート接続認証ステータスを追加 ここから
rc = bp35c0j11.cmd_send(CMD_B_ROOT_INI);
rc = bp35c0j11.wait_msg();
if (rc == TRUE)
{
state = 3;
}
else
{
state = 1;
}
break;
// Bルート接続認証ステータスを追加 ここまで
case (3): // active scan
rc = bp35c0j11.cmd_send(CMD_SCAN);
rc = bp35c0j11.wait_msg();
if (rc == TRUE)
{
rc = bp35c0j11.wait_msg();
}
break;
case (4): // スキャン結果をBルート設定にフィードバック
rc = bp35c0j11.cmd_send(CMD_INI2);
rc = bp35c0j11.wait_msg();
if (rc == TRUE)
{
state = 5;
#ifdef DEBUG
Serial.println("スキャン結果の取り込み完了");
#endif
}
break;
case (5): // Bルート動作開始要求
rc = bp35c0j11.cmd_send(CMD_B_ROOT_ACTIVE);
rc = bp35c0j11.wait_msg();
if (rc == TRUE)
{
#ifdef DEBUG
Serial.println("Bルート動作を開始します");
#endif
state = 6;
}
break;
case (6): // UDPポートOPEN要求
rc = bp35c0j11.cmd_send(CMD_PORTOPEN);
rc = bp35c0j11.wait_msg();
if (rc == TRUE)
{
#ifdef DEBUG
Serial.println("status を7に変更");
#endif
state = 7;
}
else
{
#ifdef DEBUG
Serial.println("CMD_PORTOPENの送信に失敗しました");
#endif
state = 0;
}
break;
case (7): // PANA認証開始
rc = bp35c0j11.cmd_send(CMD_B_ROOT_PANA);
rc = bp35c0j11.wait_msg();
if (rc == TRUE)
{
rc = bp35c0j11.wait_msg();
if (rc == TRUE)
{
rc = bp35c0j11.wait_msg();
if (rc == 1)
{
#ifdef DEBUG
Serial.println("PANA認証開始応答を確認。state を8に変更");
#endif
state = 8;
}
else
{
#ifdef DEBUG
Serial.println("PANA認証開始応答を確認できませんでした。再起動します。");
#endif
state = 0;
}
}
}
break;
case (8):
// スマートメータ―応答値の定義を確認する。
// 動作状態、係数、積算電力量有効桁数、積算電力量単位
rc = bp35c0j11.cmd_send(CMD_UDPSEND);
rc = bp35c0j11.wait_msg();
if (rc == TRUE)
{
#ifdef DEBUG
Serial.println("データ送信要求 完了");
Serial.println("メーターからの応答待ち");
#endif
rc = bp35c0j11.wait_msg();
if (rc == TRUE)
{
#ifdef DEBUG
Serial.println("statusを9に変更");
#endif
state = 9;
}
else
{
#ifdef DEBUG
Serial.println("メーターからの応答がタイムアウトしたため再起動します");
#endif
state = 0;
}
}
else
{
Serial.println("データ送信要求 失敗");
state = 8;
}
break;
case (9):
// 計測時刻付きで積算電力計測値を取得する(30分ごと更新値)
// 瞬時電力値、瞬時電流値を取得する
rc = bp35c0j11.cmd_send(CMD_UDPSEND);
rc = bp35c0j11.wait_msg();
if (rc == TRUE)
{
#ifdef DEBUG
Serial.println("データ送信要求 完了");
Serial.println("メーターからの応答待ち");
#endif
rc = bp35c0j11.wait_msg();
if (rc == TRUE)
{
#ifdef DEBUG
Serial.println("全シーケンス終了。再度計測します");
#endif
state = 9;
}
else
{
#ifdef DEBUG
Serial.println("メーターからの応答がタイムアウトしたため再要求を実施します");
#endif
}
}
else
{
#ifdef DEBUG
Serial.println("データ送信要求 失敗");
#endif
}
break;
default:
break;
}
}
bp35c0-j11.cpp
/********************************************************************************
bp35c0-j11.cpp
Copyright (c) 2019 ROHM Co.,Ltd.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*********************************************************************************/
#include <Arduino.h>
#include "bp35c0-j11.h"
// #define DEBUG
unsigned const char uni_req[4] = {0xD0, 0xEA, 0x83, 0xFC}; // Wi-SUNコマンド デバイスへの要求コマンド定義
unsigned const char uni_res[4] = {0xD0, 0xF9, 0xEE, 0x5D}; // Wi-SUNコマンド デバイスからの応答コマンド定義
unsigned const char ini_data[4] = {0x05, 0x00, 0x04, 0x00}; // Dual(Bルート&HAN) /Sleep 非対応/922.9MHz/20mW出力
unsigned const char scan_CMD[6] = {0x06, 0x00, 0x03, 0xFF, 0xF0, 0x01}; // Active Scan 設定 >> スキャン時間 9.65ms*64 , 4~17CHスキャン, Paring ID 有り の設定
unsigned char pair_id[8] = {0}; // 接続先MACアドレス
unsigned char pan_id[2]; // スマートメータ PAN ID
unsigned char scan_ch[1]; // スキャンした結果スマートメーターからの応答のあったチャンネルを格納する
unsigned char IPv6_adr[16] = {0}; // 接続先IPv6アドレス スキャンした際にスマートメータ―からの応答を格納する
unsigned const char my_port[2] = {0x0E, 0x1A}; // オープンするUDPポート
unsigned const char dist_port[2] = {0x0E, 0x1A}; // 送信先UDPポート
// 電力会社から送られてきた情報をそのまま打ち込む
// b_root_id → 認証ID
unsigned const char b_root_id[32] = {'0', '0', '0', '0', '0', '0', '9', '9', '0', '2', '1', '7', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '1', 'B', 'B', 'A', 'A', 'A', 'A'};
unsigned const char password[12] = {'N', 'X', 'F', 'Y', '2', '2', '3', '4', 'A', 'A', 'A', 'A'};
float Watt = 0; // 瞬時電力値 [W]
float Watt_th = 1000; // 瞬時電力値からEV充電可否判断のための閾値 [W]
// サーボ駆動用
int start_time = 0;
int change_time = 500; // サーボの角度遷移時間 [ms]
CMD_FORMAT cmd_format;
BP35C0J11::BP35C0J11(void)
{
}
/********************************************************************************
Name : j11_init
Function : initial setting bp35c0-j11
input : -
return : -
*********************************************************************************/
void BP35C0J11::j11_init(void)
{
// configure output D20/D21
pinMode(PIN_ENABLE, OUTPUT);
pinMode(PIN_RESET, OUTPUT);
digitalWrite(PIN_ENABLE, HIGH);
delay(1000);
// Serial port initial
Serial2.begin(115200);
Serial.begin(115200);
#ifdef DEBUG
Serial.write("RESET");
Serial.println("");
#endif
digitalWrite(PIN_RESET, LOW); // reset
delay(500);
digitalWrite(PIN_RESET, HIGH);
}
/********************************************************************************
Name : wait_msg
Function : wait for response from bp35c0-j11
input : -
return : TRUE/FALSE
*********************************************************************************/
boolean BP35C0J11::wait_msg(void)
{
unsigned long start_time;
unsigned long current_time;
unsigned char rcvdata[128] = {0};
unsigned char cnt = 0;
start_time = millis();
while (Serial2.available() == 0)
{
current_time = millis();
if ((current_time - start_time) > TIMEOUT)
{
#ifdef DEBUG
Serial.println("receive timeout -> hardware reest");
#endif
state = 0;
return FALSE;
}
}
while (Serial2.available() > 0)
{
delay(5);
rcvdata[cnt] = Serial2.read();
#ifdef DEBUG
if (cnt == 0)
{
Serial.print(" res=");
}
Serial.print(rcvdata[cnt], HEX);
Serial.print(" ");
#endif
cnt++;
if (cnt >= 128)
{
#ifdef DEBUG
Serial.println("receive data over flow");
#endif
return FALSE;
}
}
#ifdef DEBUG
Serial.println("");
#endif
if (rcvdata[0] == uni_res[0] && rcvdata[1] == uni_res[1] &&
rcvdata[2] == uni_res[2] && rcvdata[3] == uni_res[3])
{ // RESPONSE/NORTIFICATION
switch (rcvdata[4] << 8 | rcvdata[5])
{
case (RES_B_ROOT):
if (rcvdata[12] == 0x01)
{
#ifdef DEBUG
Serial.println("B-ROOT認証設定 Success");
#endif
}
else
{
#ifdef DEBUG
Serial.println("B-ROOT認証設定 Error");
#endif
return FALSE;
}
case (NORT_WAKE):
#ifdef DEBUG
Serial.println("起動完了通知受信");
#endif
break;
case (RES_INI):
if (rcvdata[12] == 0x01)
{
#ifdef DEBUG
Serial.println("初期化 成功");
#endif
}
else
{
#ifdef DEBUG
Serial.println("初期化 失敗");
#endif
return FALSE;
}
break;
case (RES_PANA_SET):
if (rcvdata[12] == 0x01)
{
#ifdef DEBUG
Serial.println("PANA Password 設定 成功");
#endif
}
else
{
#ifdef DEBUG
Serial.println("PANA Password 設定 失敗");
#endif
return FALSE;
}
break;
case (CMD_B_ROOT_INI):
if (rcvdata[12] == 0x01)
{
#ifdef DEBUG
Serial.println("Bルート情報認証設定 完了");
#endif
}
else
{
#ifdef DEBUG
Serial.println("Bルート情報認証設定 失敗");
#endif
return FALSE;
}
break;
case (RES_SCAN):
break;
case (NORT_SCAN):
if (rcvdata[12] == 0x00)
{
#ifdef DEBUG
Serial.print(" スキャン応答有り★");
Serial.print(" ch[hex]:");
Serial.println(rcvdata[13], HEX);
#endif
// スキャンされたchを格納
scan_ch[0] = rcvdata[13];
#ifdef DEBUG
Serial.print(" 応答台数:");
Serial.println(rcvdata[14]);
Serial.print(" スマートメータMACアドレス[hex]:");
Serial.print(rcvdata[15], HEX);
Serial.print(" ");
Serial.print(rcvdata[16], HEX);
Serial.print(" ");
Serial.print(rcvdata[17], HEX);
Serial.print(" ");
Serial.print(rcvdata[18], HEX);
Serial.print(" ");
Serial.print(rcvdata[19], HEX);
Serial.print(" ");
Serial.print(rcvdata[20], HEX);
Serial.print(" ");
Serial.print(rcvdata[21], HEX);
Serial.print(" ");
Serial.println(rcvdata[22], HEX);
#endif
// スキャンされたMACアドレスを格納
pair_id[0] = rcvdata[15];
pair_id[1] = rcvdata[16];
pair_id[2] = rcvdata[17];
pair_id[3] = rcvdata[18];
pair_id[4] = rcvdata[19];
pair_id[5] = rcvdata[20];
pair_id[6] = rcvdata[21];
pair_id[7] = rcvdata[22];
#ifdef DEBUG
Serial.print(" スマートメータPAN ID[hex]:");
Serial.print(rcvdata[23], HEX);
Serial.print(" ");
Serial.println(rcvdata[24], HEX);
#endif
// スキャンされたPANアドレスを格納
pan_id[0] = rcvdata[23];
pan_id[1] = rcvdata[24];
#ifdef DEBUG
Serial.print(" 受信強度[dBm]:");
Serial.println((rcvdata[25] - 152) - 104, DEC);
Serial.println("スキャンに応答したデバイスが発見されたため接続を試みます");
#endif
state = 4; // 次のステータスへ移行
// Serial.println("ステータスを4に変更");
}
else
{
#ifdef DEBUG
Serial.print(" スキャン応答なし");
Serial.print(" ch[hex]:");
Serial.println(rcvdata[13], HEX);
#endif
}
break;
case (RES_B_ROOT_Active):
if (rcvdata[12] == 0x01)
{
#ifdef DEBUG
Serial.println(" Bルート動作開始 成功");
Serial.print(" 接続ch[hex]:");
Serial.println(rcvdata[13], HEX);
Serial.print(" 接続PAN ID[hex]:");
Serial.print(rcvdata[14], HEX);
Serial.print(" ");
Serial.println(rcvdata[15], HEX);
Serial.print(" 接続MACアドレス[hex]:");
Serial.print(rcvdata[16], HEX);
Serial.print(" ");
Serial.print(rcvdata[17], HEX);
Serial.print(" ");
Serial.print(rcvdata[18], HEX);
Serial.print(" ");
Serial.print(rcvdata[19], HEX);
Serial.print(" ");
Serial.print(rcvdata[20], HEX);
Serial.print(" ");
Serial.print(rcvdata[21], HEX);
Serial.print(" ");
Serial.print(rcvdata[22], HEX);
Serial.print(" ");
Serial.println(rcvdata[23], HEX);
Serial.print(" 受信強度[dBm]:");
Serial.println((rcvdata[24] - 152) - 104, DEC);
#endif
return TRUE;
}
else
{
#ifdef DEBUG
Serial.println(" Bルート動作応答 失敗");
#endif
return FALSE;
}
break;
case (RES_B_ROOT_PANA):
if (rcvdata[12] == 0x01)
{
#ifdef DEBUG
Serial.println("PANA開始応答 成功");
#endif
}
else
{
#ifdef DEBUG
Serial.println("PANA開始応答 失敗");
#endif
return FALSE;
}
break;
case (RES_HAN):
if (rcvdata[12] == 0x01)
{
#ifdef DEBUG
Serial.println("Bルート応答 成功");
#endif
}
else
{
#ifdef DEBUG
Serial.println("Bルート応答 失敗");
#endif
return FALSE;
}
break;
case (NORT_PANA):
if (rcvdata[12] == 0x01)
{
#ifdef DEBUG
Serial.println("PANA 認証成功");
Serial.print(" 認証先MACアドレス[hex]:");
Serial.print(rcvdata[13], HEX);
Serial.print(" ");
Serial.print(rcvdata[14], HEX);
Serial.print(" ");
Serial.print(rcvdata[15], HEX);
Serial.print(" ");
Serial.print(rcvdata[16], HEX);
Serial.print(" ");
Serial.print(rcvdata[17], HEX);
Serial.print(" ");
Serial.print(rcvdata[18], HEX);
Serial.print(" ");
Serial.print(rcvdata[19], HEX);
Serial.print(" ");
Serial.println(rcvdata[20], HEX);
#endif
}
else
{
#ifdef DEBUG
Serial.println("PANA 認証失敗");
#endif
return FALSE;
}
break;
case (RES_PORTOPEN):
if (rcvdata[12] == 0x01)
{
#ifdef DEBUG
Serial.println("UDP port open 成功");
#endif
}
else
{
#ifdef DEBUG
Serial.println("UDP port open 失敗");
#endif
return FALSE;
}
break;
case (RES_COM_STATUS):
#ifdef DEBUG
Serial.println(" ");
Serial.print("スマートメータIPv6アドレス[hex]:");
#endif
for (int i = 12; i < 28; i++)
{
#ifdef DEBUG
Serial.print(rcvdata[i], HEX);
Serial.print(" ");
#endif
IPv6_adr[i - 12] = rcvdata[i]; // IPv6アドレスを格納
}
#ifdef DEBUG
Serial.println(" ");
Serial.print("送信元ポート番号[hex]:");
#endif
for (int i = 28; i < 30; i++)
{
#ifdef DEBUG
Serial.print(rcvdata[i], HEX);
Serial.print(" ");
#endif
}
#ifdef DEBUG
Serial.println(" ");
Serial.print("送信先ポート番号[hex]:");
#endif
for (int i = 30; i < 32; i++)
{
#ifdef DEBUG
Serial.print(rcvdata[i], HEX);
Serial.print(" ");
#endif
}
#ifdef DEBUG
Serial.println(" ");
Serial.print("送信元PAN ID[hex]:");
#endif
for (int i = 32; i < 34; i++)
{
#ifdef DEBUG
Serial.print(rcvdata[i], HEX);
Serial.print(" ");
#endif
}
#ifdef DEBUG
Serial.println(" ");
Serial.print("送信先アドレス種別[hex]:");
#endif
for (int i = 34; i < 35; i++)
{
#ifdef DEBUG
Serial.print(rcvdata[i], HEX);
Serial.print(" ");
#endif
}
#ifdef DEBUG
Serial.println(" ");
Serial.print("暗号化有無[hex]:");
#endif
for (int i = 35; i < 36; i++)
{
#ifdef DEBUG
Serial.print(rcvdata[i], HEX);
Serial.print(" ");
#endif
}
#ifdef DEBUG
Serial.println(" ");
Serial.print("受信データサイズ[Byte]:");
#endif
for (int i = 36; i < 38; i++)
{
#ifdef DEBUG
Serial.print(rcvdata[i], DEC);
Serial.print(" ");
#endif
}
#ifdef DEBUG
Serial.println(" ");
Serial.print("受信データ[hex]:");
#endif
for (int i = 38; i < rcvdata[36] << 8 | rcvdata[37]; i++)
{
#ifdef DEBUG
Serial.print(rcvdata[i], HEX);
Serial.print(" ");
#endif
if (i > 50)
{
#ifdef DEBUG
Serial.println("");
Serial.print("桁数が50桁を超えたので高速化のため表示をキャンセルします");
#endif
i = rcvdata[36] << 8 | rcvdata[37];
}
}
#ifdef DEBUG
Serial.println(" ");
#endif
// 受信データをフォーマットに従って分解して表示する
if (state == 8)
{
#ifdef DEBUG
// スマートメータ―応答値の定義を確認する。
// 動作状態、係数、積算電力量有効桁数、積算電力量単位
// 動作状態
Serial.print("動作状態[hex] (0x30 でON状態):");
Serial.println(rcvdata[53], HEX);
// 係数
Serial.print("係数:");
Serial.println(rcvdata[56] << 8 * 3 | rcvdata[57] << 8 * 2 | rcvdata[58] << 8 * 1 | rcvdata[59]);
// 積算電力量有効桁数
Serial.print("積算電力量有効桁数:");
Serial.println(rcvdata[62]);
// 積算電力量単位
Serial.print("積算電力量単位(0x00:1kWh , 0x01:0.1kWh, ...:");
Serial.println(rcvdata[65], HEX);
#endif
}
else if (state == 9)
{
// 計測時刻付きで積算電力計測値を取得する(30分ごと更新値)
// 瞬時電力値、瞬時電流値を取得する
#ifdef DEBUG
// 瞬時電力[W] :
Serial.print("瞬時電力[W] : ");
Serial.println(rcvdata[53] << 8 * 3 | rcvdata[54] << 8 * 2 | rcvdata[55] << 8 * 1 | rcvdata[56]);
// 瞬時電流[A] R相 :
Serial.print("瞬時電流[A] R相 : ");
Serial.println((rcvdata[59] << 8 * 1 | rcvdata[60]) / 10);
// 瞬時電流[A] T相 :
Serial.print("瞬時電流[A] T相 : ");
Serial.println((rcvdata[61] << 8 * 1 | rcvdata[62]) / 10);
// 積算電力計測値[kW] :
Serial.print("計測日時: ");
Serial.println(rcvdata[65] << 8 * 1 | rcvdata[66]);
Serial.print("年 ");
Serial.print(rcvdata[67]);
Serial.print("月 ");
Serial.print(rcvdata[68]);
Serial.print("日 ");
Serial.print(rcvdata[69]);
Serial.print("時 ");
Serial.print(rcvdata[70]);
Serial.println("分");
Serial.print("積算電力計測値[kW] : ");
Serial.println((rcvdata[71] << 8 * 4 | rcvdata[72] << 8 * 3 | rcvdata[73] << 8 * 2 | rcvdata[74] << 8 * 1 | rcvdata[75]) * 10);
#endif
// 変数定義確認用としてメモ
// float Watt =0 ; //瞬時電力値 [W]
// float Watt_th = 600; // 瞬時電力値からEV充電可否判断のための閾値 [W]
Watt = float(rcvdata[53] << 8 * 3 | rcvdata[54] << 8 * 2 | rcvdata[55] << 8 * 1 | rcvdata[56]);
Serial.println(Watt, 1); // シリアルプロッタ用に瞬時値のみをアウトプット
if (Watt < Watt_th)
{ // 瞬時電力値が判定値を超えた場合にサーボを動かして充電プラグを外す
start_time = millis();
while (start_time + change_time > millis())
{
s_servo.write(map(millis(), start_time, start_time + change_time, 80, 170)); // 0度から90度までchange_time[ms]かけて遷移させる。
}
}
else
{
start_time = millis();
while (start_time + change_time > millis())
{
s_servo.write(map(millis(), start_time, start_time + change_time, 170, 80)); // 0度から90度までchange_time[ms]かけて遷移させる。
}
}
}
else
{
// 何もしない
}
break;
case (RES_UDPSEND):
if (rcvdata[12] == 0x01)
{
#ifdef DEBUG
Serial.println("UDP send 送信 成功");
#endif
}
else
{
#ifdef DEBUG
Serial.println("UDP send 送信 失敗");
#endif
return FALSE;
}
break;
case (0x2FFF):
#ifdef DEBUG
Serial.println("checksum error");
#endif
return FALSE;
break;
default:
#ifdef DEBUG
Serial.println("uni code error");
#endif
return FALSE;
break;
}
}
else
{
#ifdef DEBUG
Serial.println("recv data error");
#endif
return FALSE;
}
return TRUE;
}
/********************************************************************************
Name : cmd_send
Function : REQUEST command to bp35c0-j11
input : cmd - REQUEST command
return : TRUE/FALSE
*********************************************************************************/
boolean BP35C0J11::cmd_send(unsigned short cmd)
{
unsigned short hdr_chksum = uni_req[0] + uni_req[1] + uni_req[2] + uni_req[3];
unsigned short dat_chksum = 0;
unsigned short msg_length = 0;
unsigned short dat_length = 0;
unsigned short send_dat_size = 0;
unsigned char data[128] = {0};
unsigned char send_data[128] = {0};
unsigned char cnt = 0;
switch (cmd)
{
case (CMD_RESET):
dat_length = 0;
msg_length = (unsigned short)(4 + dat_length);
hdr_chksum += CMD_RESET + msg_length;
dat_chksum = 0;
msg_create(CMD_RESET, msg_length, hdr_chksum, dat_chksum, data, send_data);
Serial2.write(send_data, (msg_length + CMD_HDR_LEN));
#ifdef DEBUG
debugmsg(msg_length + CMD_HDR_LEN, send_data);
#endif
// Serial.println("DEBUG 1-1");
break;
case (CMD_INI):
dat_length = (unsigned short)4;
msg_length = (unsigned short)(4 + dat_length);
hdr_chksum += CMD_INI + msg_length;
for (cnt = 0; cnt < dat_length; cnt++)
{
data[cnt] = ini_data[cnt];
}
for (cnt = 0; cnt < dat_length; cnt++)
{
dat_chksum += data[cnt];
}
msg_create(CMD_INI, msg_length, hdr_chksum, dat_chksum, data, send_data);
Serial2.write(send_data, (msg_length + CMD_HDR_LEN));
#ifdef DEBUG
debugmsg(msg_length + CMD_HDR_LEN, send_data);
#endif
break;
case (CMD_INI2): // スキャン完了後のBルート動作開始
dat_length = (unsigned short)4;
msg_length = (unsigned short)(4 + dat_length);
hdr_chksum += CMD_INI + msg_length;
for (cnt = 0; cnt < dat_length; cnt++)
{
data[cnt] = ini_data[cnt];
if (cnt == 2)
{
data[cnt] = scan_ch[0]; // アクティブスキャンの際に応答のあったチャンネルを設定する
}
}
for (cnt = 0; cnt < dat_length; cnt++)
{
dat_chksum += data[cnt];
}
msg_create(CMD_INI, msg_length, hdr_chksum, dat_chksum, data, send_data);
Serial2.write(send_data, (msg_length + CMD_HDR_LEN));
#ifdef DEBUG
debugmsg(msg_length + CMD_HDR_LEN, send_data);
#endif
break;
case (CMD_B_ROOT_ACTIVE): // Bルート動作開始処理
dat_length = (unsigned short)0;
msg_length = (unsigned short)(4 + dat_length);
hdr_chksum += CMD_B_ROOT_ACTIVE + msg_length;
msg_create(CMD_B_ROOT_ACTIVE, msg_length, hdr_chksum, dat_chksum, data, send_data);
Serial2.write(send_data, (msg_length + CMD_HDR_LEN));
#ifdef DEBUG
debugmsg(msg_length + CMD_HDR_LEN, send_data);
#endif
break;
case (CMD_B_ROOT_PANA): // PANA認証開始要求
dat_length = (unsigned short)0;
msg_length = (unsigned short)(4 + dat_length);
hdr_chksum += CMD_B_ROOT_PANA + msg_length;
msg_create(CMD_B_ROOT_PANA, msg_length, hdr_chksum, dat_chksum, data, send_data);
Serial2.write(send_data, (msg_length + CMD_HDR_LEN));
#ifdef DEBUG
debugmsg(msg_length + CMD_HDR_LEN, send_data);
#endif
break;
case (CMD_PANA_SET):
dat_length = (unsigned short)16;
msg_length = (unsigned short)(4 + dat_length);
hdr_chksum += CMD_PANA_SET + msg_length;
for (cnt = 0; cnt < dat_length; cnt++)
{
data[cnt] = password[cnt];
}
for (cnt = 0; cnt < dat_length; cnt++)
{
dat_chksum += data[cnt];
}
msg_create(CMD_PANA_SET, msg_length, hdr_chksum, dat_chksum, data, send_data);
Serial2.write(send_data, (msg_length + CMD_HDR_LEN));
#ifdef DEBUG
debugmsg(msg_length + CMD_HDR_LEN, send_data);
#endif
break;
// Bルート認証設定のコマンド列生成を追加 ここから
case (CMD_B_ROOT_INI):
dat_length = (unsigned short)(32 + 12);
msg_length = (unsigned short)(4 + dat_length);
hdr_chksum += CMD_B_ROOT_INI + msg_length;
for (cnt = 0; cnt < dat_length; cnt++)
{
if (cnt < 32)
{
data[cnt] = b_root_id[cnt];
}
else
{
data[cnt] = password[cnt - 32];
}
}
for (cnt = 0; cnt < dat_length; cnt++)
{
dat_chksum += data[cnt];
}
msg_create(CMD_B_ROOT_INI, msg_length, hdr_chksum, dat_chksum, data, send_data);
Serial2.write(send_data, (msg_length + CMD_HDR_LEN));
#ifdef DEBUG
debugmsg(msg_length + CMD_HDR_LEN, send_data);
#endif
break;
// Bルート認証設定のコマンド列生成を追加 ここまで
// Bルートのアクティブスキャンを実施 ここから
case (CMD_SCAN):
dat_length = (unsigned short)14;
msg_length = (unsigned short)(4 + dat_length);
hdr_chksum += CMD_SCAN + msg_length;
for (cnt = 0; cnt < dat_length; cnt++)
{
if (cnt < 6)
{
data[cnt] = scan_CMD[cnt];
}
else
{
data[cnt] = b_root_id[cnt - 6 + 24];
}
}
for (cnt = 0; cnt < dat_length; cnt++)
{
dat_chksum += data[cnt];
}
msg_create(CMD_SCAN, msg_length, hdr_chksum, dat_chksum, data, send_data);
Serial2.write(send_data, (msg_length + CMD_HDR_LEN));
#ifdef DEBUG
Serial.print("send=");
debugmsg(msg_length + CMD_HDR_LEN, send_data);
#endif
// Serial.println ("Active scanは通過");
break;
// Bルートのアクティブスキャンを実施 ここまで
case (CMD_PORTOPEN):
dat_length = 2;
msg_length = (unsigned short)(4 + dat_length);
hdr_chksum += CMD_PORTOPEN + msg_length;
for (cnt = 0; cnt < dat_length; cnt++)
{
data[cnt] = dist_port[cnt];
}
for (cnt = 0; cnt < dat_length; cnt++)
{
dat_chksum += data[cnt];
}
msg_create(CMD_PORTOPEN, msg_length, hdr_chksum, dat_chksum, data, send_data);
Serial2.write(send_data, msg_length + CMD_HDR_LEN);
#ifdef DEBUG
debugmsg(msg_length + CMD_HDR_LEN, send_data);
#endif
break;
case (CMD_UDPSEND):
if (state == 8)
{
// スマートメータ―応答値の定義を確認する。
// 動作状態、係数、積算電力量有効桁数、積算電力量単位
send_dat_size = 20; // 下に記載されている送信データサイズと同じ値(DEC)可変
dat_length = 22 + send_dat_size;
msg_length = (unsigned short)(4 + dat_length);
hdr_chksum += CMD_UDPSEND + msg_length;
for (int i = 0; i < 16; i++)
{ // 送信先のIPv6アドレス 16byte
data[i] = IPv6_adr[i];
}
for (int i = 16; i < 18; i++)
{ // 送信元のUDPポート番号 2byte
data[i] = my_port[i - 16];
}
for (int i = 18; i < 20; i++)
{ // 送信先のUDPポート番号 2byte
data[i] = dist_port[i - 18];
}
// 送信データサイズ 2byte
data[20] = (unsigned char)(send_dat_size >> 8);
data[21] = (unsigned char)(send_dat_size & 0xFF); // send data length
// send_dat_size のByte数として数えるのははここから
// 送信データヘッダ部 10 Byte
// ECHONET Lite 電文ヘッダ (2Byte) 基本固定値
data[22] = 0x10;
data[23] = 0x81;
// トランザクションID (2 Byte) 基本固定値
data[24] = 0x00;
data[25] = 0x06;
// 送信元オブジェクト (3 Byte) 基本固定値
data[26] = 0x05;
data[27] = 0xFF;
data[28] = 0x01;
// 相手先オブジェクト (3 Byte) 基本固定値
data[29] = 0x02;
data[30] = 0x88;
data[31] = 0x01;
// 各種コマンド送信 10 Byte
data[32] = 0x62; // サービスコード ESV 0x62=GET
data[33] = 0x04; // 処理プロパティ数 OPC 可変 ※EPC+PDCの組み合わせが何回繰り返されるか
data[34] = 0x80; // ECHONET プロパティ EPC 0x80 : 動作状態
data[35] = 0x00; // プロパティデータカウンタ PDC
data[36] = 0xD3; // ECHONET プロパティ 0xD3 :係数
data[37] = 0x00; // プロパティデータカウンタ PDC
data[38] = 0xD7; // ECHONET プロパティ EPC 0xD7 : 積算電力量有効桁数
data[39] = 0x00; // プロパティデータカウンタ PDC
data[40] = 0xE1; // ECHONET プロパティ 0xE1 :積算電力量単位
data[41] = 0x00; // プロパティデータカウンタ PDC
// send_dat_size のByte数として数えるのははここまで
for (cnt = 0; cnt < dat_length; cnt++)
{
dat_chksum += data[cnt];
}
msg_create(CMD_UDPSEND, msg_length, hdr_chksum, dat_chksum, data, send_data);
Serial2.write(send_data, msg_length + CMD_HDR_LEN);
#ifdef DEBUG
debugmsg(msg_length + CMD_HDR_LEN, send_data);
#endif
}
else if (state == 9)
{
// 計測時刻付きで積算電力計測値を取得する(30分ごと更新値)
// 瞬時電力値、瞬時電流値を取得する
send_dat_size = 20; // 下に記載されている送信データサイズと同じ値(DEC)可変
dat_length = 22 + send_dat_size;
msg_length = (unsigned short)(4 + dat_length);
hdr_chksum += CMD_UDPSEND + msg_length;
for (int i = 0; i < 16; i++)
{ // 送信先のIPv6アドレス 16byte
data[i] = IPv6_adr[i];
}
for (int i = 16; i < 18; i++)
{ // 送信元のUDPポート番号 2byte
data[i] = my_port[i - 16];
}
for (int i = 18; i < 20; i++)
{ // 送信先のUDPポート番号 2byte
data[i] = dist_port[i - 18];
}
// 送信データサイズ 2byte
data[20] = (unsigned char)(send_dat_size >> 8);
data[21] = (unsigned char)(send_dat_size & 0xFF); // send data length
// send_dat_size のByte数として数えるのははここから
// 送信データヘッダ部 10 Byte
// ECHONET Lite 電文ヘッダ (2Byte) 基本固定値
data[22] = 0x10;
data[23] = 0x81;
// トランザクションID (2 Byte) 基本固定値
data[24] = 0x00;
data[25] = 0x06;
// 送信元オブジェクト (3 Byte) 基本固定値
data[26] = 0x05;
data[27] = 0xFF;
data[28] = 0x01;
// 相手先オブジェクト (3 Byte) 基本固定値
data[29] = 0x02;
data[30] = 0x88;
data[31] = 0x01;
// 各種コマンド送信 10 Byte
data[32] = 0x62; // サービスコード ESV 0x62=GET
data[33] = 0x04; // 処理プロパティ数 OPC 可変 ※EPC+PDCの組み合わせが何回繰り返されるか
data[34] = 0xE7; // ECHONET プロパティ 0xE7 :瞬時電力計測値
data[35] = 0x00; // プロパティデータカウンタ PDC
data[36] = 0xE8; // ECHONET プロパティ 0xE8 :瞬時電流計測値
data[37] = 0x00; // プロパティデータカウンタ PDC
data[38] = 0xEA; // ECHONET プロパティ EPC 0xEA : 定時積算電力量計測値 正方向
data[39] = 0x00; // プロパティデータカウンタ PDC
data[40] = 0xEB; // ECHONET プロパティ 0xEB :定時積算電力量計測値 逆方向
data[41] = 0x00; // プロパティデータカウンタ PDC
// send_dat_size のByte数として数えるのははここまで
for (cnt = 0; cnt < dat_length; cnt++)
{
dat_chksum += data[cnt];
}
msg_create(CMD_UDPSEND, msg_length, hdr_chksum, dat_chksum, data, send_data);
Serial2.write(send_data, msg_length + CMD_HDR_LEN);
#ifdef DEBUG
debugmsg(msg_length + CMD_HDR_LEN, send_data);
#endif
}
break;
default:
break;
}
return TRUE;
}
/********************************************************************************
Name : msg_create
Function : create Request command format
input : cmd - Request command
msg_length - message data length
hdr_chksum - header checksum
dat_chksum - data checksum
pdada - wireless data
psend_data- request command format data
return : -
*********************************************************************************/
void static BP35C0J11::msg_create(unsigned short cmd, unsigned short msg_length, unsigned short hdr_chksum, unsigned short dat_chksum, unsigned char *pdata, unsigned char *psend_data)
{
unsigned char cnt = 0;
for (cnt = 0; cnt < 4; cnt++)
{
psend_data[cnt] = uni_req[cnt];
}
psend_data[4] = (unsigned char)((cmd & 0xFF00) >> 8);
psend_data[5] = (unsigned char)(cmd & 0xFF);
psend_data[6] = (unsigned char)((msg_length & 0xFF00) >> 8);
psend_data[7] = (unsigned char)(msg_length & 0xFF);
psend_data[8] = (unsigned char)((hdr_chksum & 0xFF00) >> 8);
psend_data[9] = (unsigned char)(hdr_chksum & 0xFF);
psend_data[10] = (unsigned char)((dat_chksum & 0xFF00) >> 8);
psend_data[11] = (unsigned char)(dat_chksum & 0xFF);
if (msg_length > 4)
{
for (cnt = 0; cnt < msg_length - 4; cnt++)
{
psend_data[12 + cnt] = pdata[cnt];
}
}
}
/********************************************************************************
Name : debugmsg
Function : output serial console for debug
input : datalength - output data lengh
psend_data - output data pointer
return : -
*********************************************************************************/
void BP35C0J11::debugmsg(unsigned short datalength, unsigned char *psend_data)
{
unsigned char cnt = 0;
for (cnt = 0; cnt < datalength; cnt++)
{
Serial.print(psend_data[cnt], HEX);
Serial.print(" ");
}
Serial.println("");
}
bp35c0-j11.h
/*
bp35c0-j11.h
Copyright (c) 2019 ROHM Co.,Ltd.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
extern unsigned char state;
#include <Servo.h>
static Servo s_servo; /**< Servo object */
#ifndef _BP35C0J11_H_
#define _BP35C0J11_H_
// #define DEBUG
#define CMD_HDR_LEN ((unsigned char)8)
#define UNI_CODE_LEN ((unsigned char)4)
#define CMD_RESET (0x00D9)
#define CMD_INI (0x005F)
#define CMD_HAN (0x000A)
#define CMD_PANA (0x003A)
#define CMD_PANA_SET (0x002C)
#define CMD_CON_SET (0x0025)
#define CMD_UDPSEND (0x0008)
#define CMD_SCAN (0x0051)
#define CMD_PORTOPEN (0x0005)
#define NORT_WAKE (0x6019)
#define RES_INI (0x205F)
#define RES_HAN (0x200A)
#define RES_PANA (0x203A)
#define RES_PANA_SET (0x202C)
#define RES_CON_SET (0x2025)
#define RES_UDPSEND (0x2008)
#define RES_SCAN (0x2051)
#define NORT_SCAN (0x4051)
#define RES_PORTOPEN (0x2005)
#define NORT_PANA (0x6028)
#define RES_B_ROOT (0x2054) // Bルート認証情報設定の応答コマンド
#define RES_B_ROOT_Active (0x2053) // Bルート動作開始応答
#define RES_B_ROOT_PANA (0x2056) // PANA認証開始応答
#define RES_COM_STATUS (0x6018) // 接続状態変更通知応答
#define CMD_INI2 (0x1111) // 状態遷移用のダミー
#define CMD_B_ROOT_INI (0x0054) // Bルート認証情報設定
#define CMD_B_ROOT_ACTIVE (0x0053) // Bルート動作開始命令
#define CMD_B_ROOT_PANA (0x0056) // PANA認証開始要求
#define TIMEOUT ((unsigned short)90000)
#define PIN_ENABLE (PIN_D20) // level shifter enable pin
#define PIN_RESET (PIN_D21) // wisun module reset pin
#define TRUE 1
#define FALSE 0
typedef struct
{
unsigned char uni_code[4];
unsigned char cmd_code[2];
unsigned char msg_len[2];
unsigned char hdr_chksum[2];
unsigned char dat_chksum[2];
unsigned char data[128];
} CMD_FORMAT;
class BP35C0J11
{
public:
BP35C0J11(void);
void j11_init(void);
boolean wait_msg(void);
boolean cmd_send(unsigned short cmd);
void static msg_create(unsigned short cmd, unsigned short msg_length, unsigned short hdr_chksum, unsigned short dat_chksum, unsigned char *pdata, unsigned char *psend_data);
private:
void static debugmsg(unsigned short datalength, unsigned char *psend_data);
};
#endif //_BP35C0J11_H_
ソースコード (Processing)
import processing.serial.*;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
Serial myPort; // シリアルポート
String val; // ポートから読み込んだデータを格納する変数
float[] values; // 過去24時間のデータを格納する配列
String[] lastTenValues; // 最新の10個のデータ値
int dataIndex = 0; //
int graphShiftX = 0; // グラフの描画開始位置を制御する変数
BufferedWriter output;
void setup() {
fullScreen(); // フルスクリーン表示
println(Serial.list()); // 利用可能なシリアルポートをリストアップ
myPort = new Serial(this, Serial.list()[0], 115200); // COM6ポートを開く、ボーレートを115200に設定
myPort.bufferUntil('\n'); // 改行コードが来るまでバッファリング
values = new float[24 * 60]; // 24時間分のデータを格納する配列を初期化(1分ごとのデータと仮定)
lastTenValues = new String[10]; // 最新の10個のデータ値を格納する配列を初期化
try {
FileWriter fw = new FileWriter("data.csv", true); // 追記モードで開く
output = new BufferedWriter(fw);
} catch (IOException e) {
e.printStackTrace();
}
}
void draw() {
background(255); // 背景を白に設定
drawGraph(); // グラフを描画
displayLastTenValues(); // 最新の10個のデータ値を表示
}
void serialEvent(Serial p) {
val = p.readStringUntil('\n'); // 改行コードまでの文字列を読み込む
if (val != null) {
val = trim(val); // 余分な空白を削除
try {
float currentValue = float(val); // 文字列をfloatに変換
if (currentValue <= 5000) { // ここで5000Wh以上の値を無視
values[dataIndex] = currentValue; // 配列に値を格納
updateLastTenValues(currentValue); // 最新の10個の値を更新
dataIndex = (dataIndex + 1) % values.length; // インデックスを更新
graphShiftX += 10; // グラフを右に10px移動
if (graphShiftX > width - 200) {
graphShiftX = 0; // グラフが右端に達したら、左端から再開
// ここでvalues配列を初期化
for (int i = 0; i < values.length; i++) {
values[i] = 0;
}
dataIndex = 0; // dataIndexもリセット
}
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String timestamp = sdf.format(new Date());
try {
output.write(timestamp + ", " + currentValue + "\n"); // CSV形式でファイルに書き込む
output.flush(); // ファイルに書き込みを確実に行う
} catch (IOException e) {
e.printStackTrace();
}
} else {
println("Ignored value over 5000Wh: " + currentValue);
}
} catch (Exception e) {
println("Error converting data: " + val);
}
}
}
void drawGraph() {
float graphTop = 50; // グラフ上部の余白
float graphBottom = height - 50; // グラフ下部の余白
float graphMaxValue = 5000; // 表示する最大電力値
stroke(50);
noFill();
beginShape();
for (int i = 0; i < values.length; i++) {
float x = map(i, 0, values.length - 1, 50, width - 50) + ( i * 10 );
// 縦軸位置をマッピング(値が0の場合はgraphBottom、graphMaxValueの場合はgraphTop)
float y = map(values[i], 0, graphMaxValue, graphBottom, graphTop);
vertex(x, y);
}
endShape();
drawScale(); // スケールを描画
}
void drawScale() {
// スケールの線の色を灰色に設定
stroke(225, 225, 225);
float graphTop = 50; // グラフ上部の余白
float graphBottom = height - 50; // グラフ下部の余白
float graphMaxValue = 5000; // 表示する最大電力値
// 水平線と電力値テキストのための設定
stroke(150);
textAlign(RIGHT, CENTER); // テキストを右揃えで中央揃えに
for (int i = 0; i <= graphMaxValue; i += 500) {
float y = map(i, 0, graphMaxValue, graphBottom, graphTop); // 縦軸位置をマッピング
line(50, y, width - 50, y); // 水平線を描画
fill(0); // 文字色を黒に
text(i + " Wh", 45, y); // 電力値テキストを描画(横軸から少し左側に設定)
}
// 垂直線と時間テキストのための設定
textAlign(CENTER, BOTTOM); // テキストを中央揃えで下揃えに
for (int i = 0; i <= 24; i++) {
float x = map(i, 0, 24, 50, width - 50); // 横軸位置をマッピング
line(x, graphTop, x, graphBottom); // 垂直線を描画
}
}
void displayLastTenValues() {
float startY = height / 10.0; // 開始位置を画面の高さの1/10に設定
int padding = 4; // テキストの周りの余白
int lineHeight = 20; // 1行の高さ
for (int i = 0; i < lastTenValues.length; i++) {
if (lastTenValues[i] != null) {
float textWidth = textWidth(lastTenValues[i]); // テキストの幅を取得
fill(255); // 背景色を白に設定
// 四角形の左上角がテキストの左上に来るように調整
rect(130, startY + i * lineHeight, textWidth + (padding * 2), lineHeight);
fill(0); // 文字の色を黒に設定
// テキストを表示(テキストの位置は四角形の中央に来るように調整)
text(lastTenValues[i], 180 + padding, startY + 5 + i * lineHeight + (lineHeight / 2));
}
}
}
void updateLastTenValues(float newValue) {
SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss,");
String timestamp = sdf.format(new Date());
String newValueStr = timestamp + " " + newValue + " Wh";
for (int i = lastTenValues.length - 1; i > 0; i--) {
lastTenValues[i] = lastTenValues[i - 1]; // 値を1つずつシフト
}
lastTenValues[0] = newValueStr; // 最新の値を先頭に追加
}
float maxValue(float[] arr) {
float maxVal = arr[0];
for (int i = 1; i < arr.length; i++) {
if (arr[i] > maxVal) {
maxVal = arr[i];
}
}
return maxVal;
}
さいごに
モニターにてBLEも頂いていたのでBLE化も視野に入れていたのですが、業務的な都合で時間が取れず今回はここまでとしました。BLE化できれば常設で利用出来るので本格的に活用できたのですが・・・・残念!!
投稿者の人気記事
-
miso
さんが
2024/01/25
に
編集
をしました。
(メッセージ: 初版)
-
miso
さんが
2024/01/25
に
編集
をしました。
-
miso
さんが
2024/01/26
に
編集
をしました。
ログインしてコメントを投稿する