概要
SONYのSpresenseとROHMのWI-SUN拡張モジュールを使って家庭のスマートメータ―から現在の消費電力値を取得して、EVなどの大電力消費デバイスの使用可否を判断できるデバイスを作りました。
解決したいこと
最近アーク溶接機を購入しましたが、いざ家で溶接しようとすると気になるのが消費電力。
どれくらい電力を使っているか正直よくわかっていません。
いつも恐る恐る使っていますが、自宅で使用しているEVの充電タイミングとかち合ってブレーカー落すなんて事は回避したい。(家ではデスクトップで仕事もしていることですし)
欲しいもの
しかしながら 「今我が家はどれくら電力を消費しているのか」 を把握するのはかなりハードルが高い。
そしてブレーカーを落とす前に優先度の低い機器のスイッチを切ってほしい。←EVの充電とか。
それも自動で。
どうやって実現するか
Spresenseを基本として以下を追加して構成。
WI-sun拡張ボード (家庭用スマート電力メーターからの瞬間電力値取得用)
リレー (充電コードの根本を直接ON/OFFする)
これであれば既存のEV充電器にリレーをかまして、心置きなく溶接を楽しめるようなシステムが作れるはず!
以下がその構成図
が、実際に作成したものは大きく変更が入りました。
~背景(という名の言い訳)~
開発にあたり真っ先にWI-SUN経由でスマートメータから電力情報を取得するところから取り掛かりました。
が・・・想像の100倍大変でした。
基本的にはハードさえあれば、後は自宅の管轄の電力会社からもらえるメーター接続用のIDとパスワードがあれば必要なものは揃います。
これはスムーズに入手できました。
が、その後のソフト開発で難航‥‥。
ハードのWI-SUNボードの製造元(ROHM)のサンプルコードはあるのですが、それは拡張ボードを子機(スマートメータ側)として使用する場合のコードとなっていました。(←これに気づくのにも時間がかかった…)
拡張ボード”から”メーターにアクセスする際のサンプルコードを見つける事ができず、結局用途違いではありますが前述のROHMのサンプルコードをベースに組みなおしました。
スマートメータ接続用のプロトコルであるECHONET-Liteも馴染みが薄く、情報収集もなかなか進まない中で仕様書とにらめっこしながら実装を進める事10日間。
なんとか瞬時電力値の取得に成功しました!というのが締め切り2日前。。。
実際に作成した構成図は以下の通り。
動作
WI-sun拡張ボードがスマートメータ―から常時消費電力値を取得。
家全体の消費電力が一定の数値を超えるとサーボを駆動して車の模型からEV充電プラグを外して充電禁止をユーザーに通知。
一定以内であれば電力消費量に余裕ありとして車の模型に充電プラグを接続して、充電OKをユーザーに通知。
溶接を行なう時もEVの充電プラグが模型と同じ状態かを確認すれば、ブレーカーを飛ばす事も無い・・・はず。
参考URL
まさか情報集でもこんなに苦労するとは…という状況の中で最終的に輝いていた仕様書を記載します。
ECHONET規格団体
https://echonet.jp/spec_object_rq/
細かい通信仕様(ここの理解が超重要)
https://echonet.jp/spec_v113_lite/
ROHM(Wi-SUNのIC製造元)が出しているスマートメータと接続するためのコマンドやり取り。
これが神の様な役割を果たし、電力値の取得までいけました。感謝。
※これ見て実装するだけ…と言えばその通りなんですが、ハードもソフトもハマリポイント多し)
https://fscdn.rohm.com/jp/products/databook/applinote/module/wireless/bp35c0-j11_b-route_an-j.pdf
プログラム
最後にプログラムを記載しておきます。
※デバッグ分も多いため非常に長いプログラムとなってしまっていますがご勘弁ください
今回はArduino IDEで開発しました。
以下の3ファイルで構成されています。
・main.ino
シーケンス制御を主として行なう。
state=0から始まり、state=9で瞬時電力の取得を繰り返す
・SPRESENSE-WISUN-EVK-701_84mota-2.ino
メインの処理。
コマンドの送信、応答の処理など。
・bp35c0-j11.h
Wi-SUNやECHONET-Liteに関係するコマンドなどの定義。
#define DEBUG
のコメント/コメントアウトによりSerial上に各種状態などが大量に出力されます。
SPRESENSE-WISUN-EVK-701_84mota-2.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 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 "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 ;
}
//シリアルモニタ用
Serial.println("瞬時電力 [W]");
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;
}
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 DEBUG
}
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 DEBUG
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 DEBUG
state = 7;
}
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 DEBUG
state = 8;
} else {
#ifdef DEBUG
Serial.println("PANA認証開始応答を確認できませんでした。再起動します。");
#endif DEBUG
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 DEBUG
rc = bp35c0j11.wait_msg();
if (rc == TRUE) {
#ifdef DEBUG
Serial.println("statusを9に変更");
#endif DEBUG
state = 9;
} else
{
state = 0;
Serial.println("メーターからの応答がタイムアウトしたため再起動します");
}
} 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 DEBUG
rc = bp35c0j11.wait_msg();
if (rc == TRUE) {
#ifdef DEBUG
Serial.println("全シーケンス終了。再度計測します");
#endif DEBUG
state = 9;
} else {
Serial.println("メーターからの応答がタイムアウトしたため再要求を実施します");
}
} else {
Serial.println("データ送信要求 失敗");
}
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', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U',}; // //Bルート認証IDをフル桁
unsigned const char password[12] = { 'A' , 'B' , 'C' , 'D' , 'E', 'F' , 'G', 'H' , 'I' , 'J' , 'K' , 'L' }; // パスワード12桁
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) {
Serial.println("receive timeout -> hardware reest");
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) {
Serial.println("receive data over flow");
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 {
Serial.println("B-ROOT認証設定 Error");
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 {
Serial.println(" Bルート動作応答 失敗");
return false;
}
break;
case (RES_B_ROOT_PANA):
if (rcvdata[12] == 0x01) {
#ifdef DEBUG
Serial.println("PANA開始応答 成功");
#endif
} else {
Serial.println("PANA開始応答 失敗");
return FALSE;
}
break;
case (RES_HAN):
if (rcvdata[12] == 0x01) {
#ifdef DEBUG
Serial.println("Bルート応答 成功");
#endif
} else {
Serial.println("Bルート応答 失敗");
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 {
Serial.println("PANA 認証失敗");
return FALSE;
}
break;
case (RES_PORTOPEN):
if (rcvdata[12] == 0x01) {
#ifdef DEBUG
Serial.println("UDP port open 成功");
#endif
} else {
Serial.println("UDP port open 失敗");
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 {
Serial.println("UDP send 送信 失敗");
return FALSE;
}
break;
case (0x2FFF):
Serial.println("checksum error");
return FALSE;
break;
default:
Serial.println("uni code error");
return FALSE;
break;
}
} else {
Serial.println("recv data error");
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_
投稿者の人気記事
-
84mot
さんが
2022/09/25
に
編集
をしました。
(メッセージ: 初版)
-
84mot
さんが
2022/09/25
に
編集
をしました。
-
84mot
さんが
2022/09/26
に
編集
をしました。
ログインしてコメントを投稿する