|
||||
|
||||
KWS-AC301(modbus) 掲載2024年4月16日 | ||||
|
|
目 次 |
|
1.KWS-AC301 2.ModoBusのフレーム構造 3.プログラム例 |
本文以外 ・KWS-AC301 ・KWS-AC301(RS232C絶縁) ・ModBus |
|
1.KWS-AC301 (1)計測項目
KWS-AC301の内部基板にTxとRx端子がある。 この端子信号電圧が0-5(V)でその他はある程度RS232C(ANSI/EIA-232-E)の規格になっているようです。 通信速度は9600bps スタートビット 1ビット固定 ストップビット 1ビット 1 , 1.5 , 2 ビットから選択 データビット長 8ビット 8 , 7 bitから選択 パリティチェック NONE パリティチェック無し::NINE、偶数パリティチェック:EVEN、奇数パリティチェック:0DDから選択 KWS-AC301Lは上記端子に子基板が付加されており、この基板はRS485(EIA-485)のインターフェース基板になっている。 2.ModoBusのフレーム構造: [ 先頭に戻る ] 通常のModoBus構造は下記の構造となる。
(1)KWS-AC301マスター・スレーブフレーム構造(start and end 除く) a.マスターフレーム構造 電圧値の要求の場合
b.スレーブフレーム構造 電圧値を返答
※1 アドレスフィールド 0x02は固定 ※2 ファンクションフィールド 0x03はRead Holding Register その他(3)参照
※3 要求するデータアドレスとその内容
※5 CRCチェック CRCは16bit=2Byte(2バイト)のバイナリ値で構成されています。 CRCは送信側が計算し、受信側が受信データのCRCを計算し受信データのCRCと比較して違っていればエラーになります。 3.プログラム例 [ 先頭に戻る ] 参考 GitHub GitHub - SensorsIot/KWS-AC301 (1)プログラムj //KWS-AC301 100A power meter read database write program. // by ESP8266 E12F //Created by T.Hirabayashi version 0.1 #include <SoftwareSerial.h> SoftwareSerial Serial_AC301(4,5); //rxPin=D2, txPin=D1 別名設定 const byte flame_vr = 20;//受信するフレームの最大数の定義 uint8_t flame_cr[flame_vr]; // 受信フレームデータを格納する配列 uint8_t Txflame_dt[8] = {0x02, 0x03, 0x00, 0x0E, 0x00, 0x01}; //transmit data to meter, element 3 is swapped with get_data[z].command_HEX byte result[8]; //結果はこの配列に格納されます uint8_t size;//size of Txflame_dt to meter //受信データ const uint8_t gdv=12; //受信データの数 1 4 1 4 3 0 0 2 1 uint8_t gdchh[gdv] ={0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00}; //アドレスHi uint8_t gdchl[gdv] ={0x0E ,0x0F ,0x10 ,0x11 ,0x12 ,0x15 ,0x16 ,0x17 ,0x18 ,0x1A ,0x1D ,0x1E}; //アドレスLo uint8_t gdd10[gdv] ={1 ,3 ,0 ,1 ,0 ,1 ,0 ,3 ,0 ,0 ,2 ,1}; //小数点桁 String gdct[gdv] ={"電圧 ","電流Lo","電流Hi","有効Lo","有効Hi","皮相Lo","皮相Hi","電量Lo","電量Hi","外温 ","力率 ","周波数"};//計測名 String gdu[gdv] ={"V" ,"A" ,"A" ,"W" ,"W" ,"VA" ,"VA" ,"kWH" ,"KWH" ,"℃" ,"PF" ,"Hz"}; //計測単位 float gdrd[gdv] ={0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0}; //計測値 uint32_t gdrt[gdv]; //値の表示 const uint8_t dav=8; //受信数値の数 float da[dav] ={0 ,0 ,0 ,0 ,0 ,0 ,0 , 0} ;//データベース送信用配列 String dat[dav] ={"電圧","電流","有効","皮相","電量","外温","力率","周波"}; String dau[dav] ={"V" ,"A" ,"W" ,"VA" ,"KWH" ,"℃" ,"PF" ,"Hz"}; uint8_t da10[dav]={1 ,3 ,1 ,1 ,3 ,0 ,2 ,1}; //小数点桁 boolean data_found;//looking for reply to a Txflame_dt boolean newData;//looking for reply to a Txflame_dt uint32_t time_to_reply;//what is Txflame_dt to reply time? void setup() { Serial.begin(115200); //ESP8266 Serial TX=GPIO 1 RX=GPIO 3 Serial_AC301.begin(9600); //RS485 to AC301L-100A power meter on MOD BUS type interface Serial.println("KWS-AC301 100A power meter read database write program"); } int j; void loop() { for (j = 0; j < gdv; j++) { Txflame_dt[2] =gdchh[j];//メッセージは送信データの配列で 2,3番目にはコマンド番号が入る Txflame_dt[3] =gdchl[j]; size = 8; ////メッセージは送信データの配列数 crc_Master_ModRTU(Txflame_dt, size); //マスター ModBus データ に CRC 追加 delay(85);//send new request every 85ms as response time is ~80ms from request to send serial_flush_buffer();//clear out buffer //■ CRC_check_ModRTU(Txflame_dt,result); //returns byte array Serial_AC301.write(Txflame_dt, sizeof(Txflame_dt)); //送信データの配列分を送信 receive_Data(0); //受信データ記録およびシリアル表示 // 送信データ、受信データ、変数データの表示 //0:表示なし 1:表示 //void showNewData(); //delay(650);//send new request every half second minimum is ~85ms as response time is ~80ms from request to send } //データベースデータの計算 da[0]=gdrd[0];//電圧 da[1]=gdrd[1]+gdrd[2];//電流 da[2]=gdrd[3]+gdrd[4];//有効電力 da[3]=gdrd[5]+gdrd[6];//皮相電力 da[4]=gdrd[7]+gdrd[8];//エネルギー da[5]=gdrd[9];//外温 da[6]=gdrd[10];//力率 da[7]=gdrd[11];//周波数 for (int i = 0; i < dav; i++) { Serial.print(dat[i]); Serial.print(" = "); Serial.print(da[i],da10[i]); Serial.print("("); Serial.print(dau[i]); Serial.println(")"); } Serial.print("計算有効= "); Serial.print(da[3]*da[6],1); Serial.print("W 電流から計算= "); Serial.print(da[0]*da[1]*da[6],1); Serial.println("W"); Serial.print("計算皮相= "); Serial.print(da[0]*da[1],1); Serial.println("VA"); Serial.println(""); delay(5000);//delay 5 seconds between sending the requests } //######### サブルーチン ########### //Function for CRC insertion //byte ModRTU_CRC(uint8_t Txflame_dt[], uint8_t size) //, uint8_t result[]) //returns byte array void crc_Master_ModRTU(uint8_t Txflame_dt[], uint8_t size) { uint16_t crc = 0xFFFF;//initialize for (int pos = 0; pos < size - 2; pos++) //size is data array length so pos ranges 0 to size-2 as array index { //result[pos] = Txflame_dt[pos]; crc ^= (uint16_t)Txflame_dt[pos]; // XOR byte into least sig. byte of crc for (int i = 8; i != 0; i--) { // Loop over each bit if ((crc & 0x0001) != 0) // If the LSB is set { crc >>= 1; // Shift right and XOR 0xA001 crc ^= 0xA001; // this is reflection of 0x8005 } else // Else LSB is not set crc >>= 1; // Just shift right } } Txflame_dt[size - 2] = lowByte(crc); Txflame_dt[size - 1] = highByte(crc); } void serial_flush_buffer() { while (Serial.read() >= 0) ; // do nothing } void receive_Data(int Dsp) { //need to time out after some 8 bit no response after bytes start coming in data_found = false;//メッセージへの返信を探しています uint32_t zero_timer = millis();//want to know how long from send Txflame_dt to receive boolean no_reply_timeout = false; boolean no_data = false; //受信データが無いとき true uint8_t rc; uint32_t last_rx; float Data=0; //受信値 uint8_t k = 0;//array index for read characters //Serial.println("in read data routine"); while (no_reply_timeout == false && data_found == false) //( )内がfalseになるまで繰り返される。 { if (Serial_AC301.available()) //ソフトウェアシリアルポートのバッファに何バイトのデータが到着しているかを返します。 { time_to_reply = millis() - zero_timer;//メッセージの返信時間 data_found = true; last_rx = millis();//read the timer to see when the meter stops sending } else { uint32_t time_since_Txflame_dt = millis() - zero_timer; if (time_since_Txflame_dt > 1450) { no_reply_timeout = true; } } } //Serial.print("no reply timeout="); //Serial.print(no_reply_timeout, DEC); //Serial.print(" data found="); //Serial.println(data_found, DEC); //■以下表示プログラムの改造箇所 if (data_found == true) { //read in response while (millis() - last_rx < 4) //the bytes are close together, bit time of 9600 baud is .104 millsecond { if (Serial_AC301.available()) { // rc = Serial1.read(); rc = Serial_AC301.read(); //受信データを変数へ flame_cr[k] = rc; k++; last_rx = millis();//read the timer to see when the meter stops sending } } int A = (flame_cr[3] << 8 | flame_cr[4]); //受信データ 値 Data = (float ) A / pow(10, gdd10[j]); //桁計算 gdrd[j]=Data; //受信データの値を配列へ記録 } else { no_data = 1; gdrd[j] = 0; //受信データが無いので 前の値も含め初期化 } //get_data[j].receive_time = millis() - zero_timer; //ここまでの処理を配列へ記録 gdrt[j] = millis() - zero_timer; //ここまでの処理を配列へ記録; // // USBモニター // if(Dsp==1) //データモニター { print_Master_ModRTU(0); //チェックモニタ マスター ModoBus RTU データ // 常時コメントアウト // Serial //0:0x付き //1:0xなし if(no_data == 0 ) { Serial.print("got data RX t="); Serial.print(time_to_reply); //受信までの時間 Serial.print(" arrayV="); Serial.print(k); //受信データ配列数 Serial.print(" data=0x"); for (uint8_t i = 0; i < k; i++)// { if (flame_cr[i] < 16 ) Serial.print("0"); Serial.print(flame_cr[i], HEX); if (i != k - 1 ) Serial.print(", 0x"); } Serial.print(" data="); Serial.print(gdct[j]); Serial.print(" time="); Serial.print(gdrt[j]); Serial.print(" No="); Serial.print(j); Serial.print(" 桁="); Serial.print(gdd10[j]); Serial.print(" Data="); Serial.print(gdrd[j],gdd10[j]); Serial.print("("); Serial.print(gdu[j]); Serial.print(")"); for (uint8_t i = 0; i < sizeof(flame_cr); i++)// { flame_cr[i] = 0x00; } }else{ Serial.print("No data"); } Serial.println(""); }else { Serial.print("Rxdata="); Serial.print(gdct[j]); Serial.print(" time="); Serial.print(gdrt[j]); Serial.print(" No="); Serial.print(j); Serial.print(" Data="); Serial.print(gdrd[j],gdd10[j]); Serial.print("("); Serial.print(gdu[j]); Serial.println(")"); } } void print_Master_ModRTU(int Dsp) //Hx=1 0x表示追加 { Serial.print("Data sent ->");//print the bytes of the data sent if (Dsp==0) Serial.print("0x"); for (uint8_t i = 0; i < sizeof(Txflame_dt); i++)// { if (Txflame_dt[i] < 16) Serial.print("0"); Serial.print(Txflame_dt[i], HEX); if (Dsp==0) { if (i != size - 1) Serial.print(", 0x"); } else Serial.print(" ");//space between hex bytes } Serial.print(" -->"); } void showNewData() { if (newData == true) { Serial.print("data "); for (uint8_t i = 0; i < sizeof(flame_cr); i++)//Is there a CR-LF sent, I hope not. { if (flame_cr[i] < 16) Serial.print("0"); Serial.print(flame_cr[i], HEX); } Serial.println(""); } } //Function for CRC byte CRC_check_ModRTU(byte Txflame_dt[], byte result[]) //returns byte array { uint16_t crc = 0xFFFF;//initialize for (int pos = 0; pos < 8; pos++) { //8 is data array length so pos ranges 0 to 7 as array index result[pos] = Txflame_dt[pos]; crc ^= (uint16_t)Txflame_dt[pos]; // XOR byte into least sig. byte of crc for (int i = 8; i != 0; i--) { // Loop over each bit if ((crc & 0x0001) != 0) { // If the LSB is set crc >>= 1; // Shift right and XOR 0xA001 crc ^= 0xA001; // this is reflection of 0x8005 } else // Else LSB is not set crc >>= 1; // Just shift right } } Serial.print("CRC-"); Serial.print(lowByte(crc), HEX); Serial.println(highByte(crc), HEX); //エラーメッセージが表示されたので、下記を追加した return result[8]; } (2)結果表示 パソコンのシリアルモニター ■receive_Data(0);の場合 [ 先頭に戻る ] 19:55:11.652 -> Rxdata=電圧 time=90 No=0 Data=107.7(V) 19:55:11.833 -> Rxdata=電流Lo time=87 No=1 Data=2.739(A) 19:55:12.054 -> Rxdata=電流Hi time=86 No=2 Data=0(A) 19:55:12.224 -> Rxdata=有効Lo time=86 No=3 Data=227.0(W) 19:55:12.396 -> Rxdata=有効Hi time=86 No=4 Data=0(W) 19:55:12.568 -> Rxdata=皮相Lo time=86 No=5 Data=301.6(VA) 19:55:12.754 -> Rxdata=皮相Hi time=87 No=6 Data=0(VA) 19:55:12.941 -> Rxdata=電量Lo time=86 No=7 Data=2.286(kWH) 19:55:13.126 -> Rxdata=電量Hi time=86 No=8 Data=0(KWH) 19:55:13.265 -> Rxdata=外温 time=86 No=9 Data=25(℃) 19:55:13.444 -> Rxdata=力率 time=86 No=10 Data=0.73(PF) 19:55:13.625 -> Rxdata=周波数 time=87 No=11 Data=60.5(Hz) 19:55:13.672 -> 電圧 = 107.7(V) 19:55:13.672 -> 電流 = 2.739(A) 19:55:13.672 -> 有効 = 227.0(W) 19:55:13.672 -> 皮相 = 301.6(VA) 19:55:13.672 -> 電量 = 2.286(KWH) 19:55:13.672 -> 外温 = 25(℃) 19:55:13.672 -> 力率 = 0.73(PF) 19:55:13.672 -> 周波 = 60.5(Hz) 19:55:13.672 -> 計算有効= 220.2W 電流から計算= 215.3W 19:55:13.672 -> 計算皮相= 295.0VA 19:55:13.672 -> ■receive_Data(1);の場合 [ 先頭に戻る ] 20:04:50.296 -> Data sent ->0x02, 0x03, 0x00, 0x0E, 0x00, 0x01, 0xE5, 0xFA -->got data RX t=79 arrayV=7 data=0x02, 0x03, 0x02, 0x04, 0x2B, 0xBE, 0x9B data=電圧 time=90 No=0 桁=1 Data=106.7(V) 20:04:50.473 -> Data sent ->0x02, 0x03, 0x00, 0x0F, 0x00, 0x01, 0xB4, 0x3A -->got data RX t=82 arrayV=7 data=0x02, 0x03, 0x02, 0x06, 0xFB, 0xBE, 0x67 data=電流Lo time=92 No=1 桁=3 Data=1.787(A) 20:04:50.698 -> Data sent ->0x02, 0x03, 0x00, 0x10, 0x00, 0x01, 0x85, 0xFC -->got data RX t=82 arrayV=7 data=0x02, 0x03, 0x02, 0x00, 0x00, 0xFC, 0x44 data=電流Hi time=92 No=2 桁=0 Data=0(A) 20:04:50.883 -> Data sent ->0x02, 0x03, 0x00, 0x11, 0x00, 0x01, 0xD4, 0x3C -->got data RX t=72 arrayV=7 data=0x02, 0x03, 0x02, 0x05, 0xED, 0x3F, 0x59 data=有効Lo time=83 No=3 桁=1 Data=151.7(W) 20:04:51.071 -> Data sent ->0x02, 0x03, 0x00, 0x12, 0x00, 0x01, 0x24, 0x3C -->got data RX t=82 arrayV=7 data=0x02, 0x03, 0x02, 0x00, 0x00, 0xFC, 0x44 data=有効Hi time=92 No=4 桁=0 Data=0(W) 20:04:51.257 -> Data sent ->0x02, 0x03, 0x00, 0x15, 0x00, 0x01, 0x95, 0xFD -->got data RX t=72 arrayV=7 data=0x02, 0x03, 0x02, 0x07, 0x98, 0xFF, 0xDE data=皮相Lo time=82 No=5 桁=1 Data=194.4(VA) 20:04:51.391 -> Data sent ->0x02, 0x03, 0x00, 0x16, 0x00, 0x01, 0x65, 0xFD -->got data RX t=71 arrayV=7 data=0x02, 0x03, 0x02, 0x00, 0x00, 0xFC, 0x44 data=皮相Hi time=81 No=6 桁=0 Data=0(VA) 20:04:51.613 -> Data sent ->0x02, 0x03, 0x00, 0x17, 0x00, 0x01, 0x34, 0x3D -->got data RX t=72 arrayV=7 data=0x02, 0x03, 0x02, 0x09, 0x07, 0xBB, 0xD6 data=電量Lo time=83 No=7 桁=3 Data=2.311(kWH) 20:04:51.799 -> Data sent ->0x02, 0x03, 0x00, 0x18, 0x00, 0x01, 0x04, 0x3E -->got data RX t=82 arrayV=7 data=0x02, 0x03, 0x02, 0x00, 0x00, 0xFC, 0x44 data=電量Hi time=92 No=8 桁=0 Data=0(KWH) 20:04:51.972 -> Data sent ->0x02, 0x03, 0x00, 0x1A, 0x00, 0x01, 0xA5, 0xFE -->got data RX t=72 arrayV=7 data=0x02, 0x03, 0x02, 0x00, 0x19, 0x3D, 0x8E data=外温 time=82 No=9 桁=0 Data=25(℃) 20:04:52.158 -> Data sent ->0x02, 0x03, 0x00, 0x1D, 0x00, 0x01, 0x14, 0x3F -->got data RX t=71 arrayV=7 data=0x02, 0x03, 0x02, 0x00, 0x4B, 0xBC, 0x73 data=力率 time=81 No=10 桁=2 Data=0.75(PF) 20:04:52.297 -> Data sent ->0x02, 0x03, 0x00, 0x1E, 0x00, 0x01, 0xE4, 0x3F -->got data RX t=71 arrayV=7 data=0x02, 0x03, 0x02, 0x02, 0x5D, 0x3C, 0xDD data=周波数 time=81 No=11 桁=1 Data=60.5(Hz) 20:04:52.329 -> 電圧 = 106.7(V) 20:04:52.329 -> 電流 = 1.787(A) 20:04:52.329 -> 有効 = 151.7(W) 20:04:52.329 -> 皮相 = 194.4(VA) 20:04:52.329 -> 電量 = 2.311(KWH) 20:04:52.329 -> 外温 = 25(℃) 20:04:52.329 -> 力率 = 0.75(PF) 20:04:52.329 -> 周波 = 60.5(Hz) 20:04:52.329 -> 計算有効= 145.8W 電流から計算= 143.0W 20:04:52.361 -> 計算皮相= 190.7VA 20:04:52.361 -> [ 先頭に戻る ] |