最終更新日:2023/1/31
※W5500-EVB-Pico + MakerPiPico版を構築しました。
とりあえず動作するRP2040版ModbusTCPSlaveが出来ました。ただ、幾つか問題がわかったので新たなページを立てて改良してゆこうと思います。※ソースを置くことを考えるとページを替えた方が良いと考えました。
変更点は以下の通りです。
- 4桁の7セグLEDを接続し、時刻表示させます。TM1637搭載の表示器を検討します。
- GROVEコネクタを幾つか使える様にして置きたいです。
GROVE1=UART
GROVE2=禁止※NICで使用
GROVE3=禁止※NICで使用
GROVE4=I2C
GROVE5=DIO 表示器を接続
GROVE6=アナログ入力- NICのRESET及びINIT端子の接続先を変更します。GP18,19
- 今後W5500-EVB-Picoを導入する場合は改めて見直す必要があります。
- PicoのビルトインLEDの点滅をタイマ割込で制御するようにします。
実際の実行挙動ですが、
- 基本的に期待通りに動作しています。
- 電源が入るとNTPサーバ(ココでは192.168.0.199)にアクセスして時刻取得します。
- コロンが点滅しながら1秒毎に時刻更新します。また、GP25のLEDも同期して点滅します。
- 外部からModbusMasterツールから192.168.0.208:502 に接続します。※是非LVModbusTCPMasterをご利用ください。
- Coil[0]をONにするとGP16のLEDが消灯します。OFFに戻すと点灯します。
Coil[1]も同様な挙動です。- GP20/21/22の押しボタンスイッチを任意で押して、Readすると各ポートでのOn/Off状態を確認出来ます。
- GP26/27/28はアナログ入力です。GNDとの間に電圧を与えることで電圧変化をモニタすることが出来ます。※3.3V以上は掛けないでください。35pinがADC_Vrefです。ここに基準電圧を予め設定しておくと計測精度を少しでも高くすることが出来ます。
- WDTによるリスタ−トの確認方法が見いだせていません。方法論が確認出来次第チェックします。
/* * 2023/1/31 T.Wanibe * 1.0 で とりあえずModbusMasterとの通信が出来るSlaveが作成出来ました。 * これまでにArduino-Picoに実装してきたコードを移植してもう少し肉付けしたいと思う * Timer割込を追加してCP25のLEDを500msec毎の点滅にしたい。 * NTPクライアントによる時刻同期 及びTM1637に時刻表示 * 最大1044480バイトのフラッシュメモリのうち、スケッチが67348バイト(6%)を使っています。 * 最大262144バイトのRAMのうち、グローバル変数が8704バイト(3%)を使っていて、ローカル変数で253440バイト使うことができます。 */ #define TIMER_INTERRUPT_DEBUG 1 #define _TIMERINTERRUPT_LOGLEVEL_ 4 #include <TimeLib.h> #include "TM1637.h" #include "RPi_Pico_TimerInterrupt.h" #include <SPI.h> #include "MgsModbus.h" //このSketchと同じ階層に置く extern "C" { #include <hardware/watchdog.h> }; #define MbCoilDataLen 2 // length of the MbCoilDataLen array (Equivalent16bit*2) #define MbDiscreteDataLen 2 // length of the MbDiscreteDataLen array(Equivalent16bit*2) #define MbHoldDataLen 16 // length of the MbHoldDataLen array #define MbInputDataLen 48 // length of the MbInputDataLen array #define OK 0 #define NG 1 #define BAUDRATE 115200 #define CLK 9 #define DIO 8 #define NICReset 18 #define NICInit 19 #define SPI_SCK 2 #define SPI_TX 3 #define SPI_RX 4 #define SPI_CS 5 #define TIMER0_INTERVAL_MS 1000 uint32_t delay_ms = 8000; //8秒 最大8.3秒 //const char timeServer[] = "time.nist.gov"; // time.nist.gov NTP server char timeServer[] = "192.168.0.199"; const int NTPport = 123; const int NTP_PACKET_SIZE = 48; // NTP time stamp is in the first 48 bytes of the message byte packetBuffer[NTP_PACKET_SIZE]; //buffer to hold incoming and outgoing packets // Ethernet settings (depending on MAC and Local network) byte mac[] = {0x00,0x08,0xDC,0x54,0x4D,0xDD}; //WIZNET byte ip[] = {192, 168, 0, 208}; byte dns_server[] = {192, 168, 0, 1}; byte gateway[] = {192, 168, 0, 1}; byte subnet[] = {255, 255, 255, 0}; bool gMBusConnect = false; //Modbus接続中はWebServer接続を502で逃げる byte fTogle1 = LOW; bool gRTCOK; //内部カウンタ unsigned long gNTPqueryCount = 0; long gLoopCount = 3; //1時間毎に更新予定 最初は1分後に更新したくて初期値を調整 long gLoopTime = 0; //Loop()の実行時間を求める long gResetCount = 0; long gPreTime,gNowTime; unsigned long OperatingTime = 0; //稼働時間 Count2で1秒 int LinkStatus; int gHardwareStatus; float unit = 2.33E-7; unsigned long baseMilis = millis(); unsigned long miliTime; struct time_tm *tm; //STM32dinoとRP2040の構造体が異なる time_t currentTime,recieveTime; unsigned long currentHour,currentMin,currentSec; int8_t TimeDisp[] = {0x00,0x00,0x00,0x00}; uint32_t timestamp, tempval,microVal,fractions; float timezone = 9.0; time_t TimeZoneSec = long(timezone * 3600); const unsigned long seventyYears = 2208988800UL; char buff[80]; char buf1[32]; //物理接続要素 int dOsPins[] = {16, 17}; int dIsPins[] = {20, 21, 22}; int aIsPins[] = {26, 27, 28}; //物理接続配列要素数を求めておく int DObits = sizeof(dOsPins)/sizeof(int); int DIbits = sizeof(dIsPins)/sizeof(int); int AIbits = sizeof(aIsPins)/sizeof(int); //オブジェクトコンストラクタ MgsModbus Mb; TM1637 tm1637(CLK,DIO); EthernetUDP Udp; RPI_PICO_Timer ITimer0(0); //----------------------------MODBUS int GetDeviceInfo(){ for (int i=0;i<DIbits;i++){ Mb.SetBit(MB_FC_READ_DISCRETE_INPUT,i,!digitalRead(dIsPins[i])); //bit 論理反転 } for (int i=0;i<AIbits;i++){ Mb.MbInputData[i]=analogRead(aIsPins[i]); //U16 } return OK; } //----------------------------MODBUS int SetDeviceInfo(){ for (int i=0;i<DObits;i++){ digitalWrite(dOsPins[i],!Mb.GetBit(MB_FC_READ_COILS,i)); //LEDはLOWで点灯 } return OK; } //-------------タイマ割込で呼ばれるルーチン クロック表示 500msec毎 bool TimerHandler0(struct repeating_timer *t){ OperatingTime++; currentTime = now(); if(Serial) Serial.println(currentTime); currentTime %= 86400; currentHour = currentTime / 3600; currentMin = (currentTime % 3600)/60; TimeDisp[0] = currentHour/ 10; TimeDisp[1] = currentHour % 10; TimeDisp[2] = currentMin / 10; TimeDisp[3] = currentMin % 10; if(fTogle1) tm1637.point(POINT_OFF); else tm1637.point(POINT_ON); tm1637.display(TimeDisp); digitalWrite(LED_BUILTIN, fTogle1); fTogle1 = !fTogle1; return true; } //--------------------- bool processNTP() { //利用可能なデータがある場合は、パケットを読み取ります //https://blog.goo.ne.jp/hiro239415/e/c426a545863921a13a9d6a70b7ae4484 //NTP_Packet (48Byte) bool req = false; int packetSize = Udp.parsePacket(); if(packetSize){ digitalWrite(LED_BUILTIN,HIGH); //要求があったときにBuiltInLEDを点灯 miliTime = millis() - baseMilis; fractions = uint32_t(float(miliTime % 1000) / unit); sprintf(buf1,"■%d\t%d\n",packetSize,fractions); if(Serial) Serial.print(buf1); Udp.read(packetBuffer,NTP_PACKET_SIZE); if(Serial){ for (int i =0;i<48;i++){ Serial.print(packetBuffer[i],HEX); } Serial.println(); } IPAddress Remote = Udp.remoteIP(); int PortNum = Udp.remotePort(); //受信タイムスタンプ = サーバが受け取った時刻を予めセットしておく JSTなのでGMTに変換 timestamp = now() - TimeZoneSec; miliTime = millis() - baseMilis; fractions = uint32_t(float(miliTime % 1000) / unit); tempval = timestamp; packetBuffer[32] = (tempval >> 24) & 0XFF; tempval = timestamp; packetBuffer[33] = (tempval >> 16) & 0xFF; tempval = timestamp; packetBuffer[34] = (tempval >> 8) & 0xFF; tempval = timestamp; packetBuffer[35] = (tempval) & 0xFF; // microVal = fractions; packetBuffer[36] = (microVal >> 24) & 0xFF; microVal = fractions; packetBuffer[37] = (microVal >> 16) & 0xFF; microVal = fractions; packetBuffer[38] = (microVal >> 8) & 0xFF; microVal = fractions; packetBuffer[39] = microVal & 0xFF; //ヘッダーセット packetBuffer[0] = 0b00100100; // 閏秒警告なしLI, 4:SNTPサーバ(Version), 4:サーバ(Mode) packetBuffer[1] = 1 ; // 1:一次基準源(GPS等)(stratum packetBuffer[2] = 5 ; // ポーリング64秒デフォルト値 6(64秒)->5(32秒) packetBuffer[3] = 0xF6; // 精度 -3(0xFD)->-10(0xF6)に変更 msec packetBuffer[4] = 0; // ルート遅延 packetBuffer[5] = 0; packetBuffer[6] = 8; packetBuffer[7] = 0; packetBuffer[8] = 0; // ルート分散 packetBuffer[9] = 0; packetBuffer[10] = 0xC; packetBuffer[11] = 0; // packetBuffer[12] = 71; //"G"; ReferenceID packetBuffer[13] = 80; //"P"; ReferenceID packetBuffer[14] = 83; //"S"; ReferenceID packetBuffer[15] = 0; //"0"; ReferenceID // リファレンス時刻をセット(現在時間) timestamp = now() - TimeZoneSec; //UTC if(Serial){ Serial.print(timestamp);Serial.print(F(":Time synchronization Request received\n")); } tempval = timestamp; microVal = fractions; packetBuffer[16] = (tempval >> 24) & 0XFF; tempval = timestamp; packetBuffer[17] = (tempval >> 16) & 0xFF; tempval = timestamp; packetBuffer[18] = (tempval >> 8) & 0xFF; tempval = timestamp; packetBuffer[19] = (tempval) & 0xFF; packetBuffer[20] = (microVal >> 24) & 0xFF; microVal = fractions; packetBuffer[21] = (microVal >> 16) & 0xFF; microVal = fractions; packetBuffer[22] = (microVal >> 8) & 0xFF; microVal = fractions; packetBuffer[23] = microVal & 0xFF; //オリジナル時間をコピー(サーバに要求が届いた時間) packetBuffer[24] = packetBuffer[40]; packetBuffer[25] = packetBuffer[41]; packetBuffer[26] = packetBuffer[42]; packetBuffer[27] = packetBuffer[43]; packetBuffer[28] = packetBuffer[44]; packetBuffer[29] = packetBuffer[45]; packetBuffer[30] = packetBuffer[46]; packetBuffer[31] = packetBuffer[47]; //送信時刻をセット timestamp = now() - TimeZoneSec; //UTC miliTime = millis() - baseMilis; fractions = uint32_t(float(miliTime % 1000) / unit); tempval = timestamp; packetBuffer[40] = (tempval >> 24) & 0XFF; tempval = timestamp; packetBuffer[41] = (tempval >> 16) & 0xFF; tempval = timestamp; packetBuffer[42] = (tempval >> 8) & 0xFF; tempval = timestamp; packetBuffer[43] = (tempval) & 0xFF; //transmit_timestamp_fractions microVal = fractions; packetBuffer[44] = (microVal >> 24) & 0xFF; microVal = fractions; packetBuffer[45] = (microVal >> 16) & 0xFF; microVal = fractions; packetBuffer[46] = (microVal >> 8) & 0xFF; microVal = fractions; packetBuffer[47] = microVal & 0xFF; //NTP要求を送信したIPアドレスとポートに応答します Udp.beginPacket(Remote, PortNum); Udp.write(packetBuffer,NTP_PACKET_SIZE); Udp.endPacket(); digitalWrite(LED_BUILTIN,LOW); //処理完了で消灯 sprintf(buf1,"●%d\t%d\t%d\t%d\n",packetBuffer[20],packetBuffer[21],packetBuffer[22],packetBuffer[23]); if(Serial) Serial.print(buf1); req = true; gNTPqueryCount++; } return req; } //-----------------指定されたアドレスのタイムサーバーにNTP要求を送信します void sendNTPpacket(const char * address) { // バッファ内のすべてのバイトを0に設定します memset(packetBuffer, 0, NTP_PACKET_SIZE); // NTP要求を形成するために必要な値を初期化します(パケットの詳細については、上記のURLを参照してください) packetBuffer[0] = 0b11100011; // LI, Version, Mode packetBuffer[1] = 0; // Stratum, or type of clock packetBuffer[2] = 6; // ポーリング64秒デフォルト値 packetBuffer[3] = 0xEC; // 精度 // ルート遅延とルート分散の場合は8バイトのゼロ packetBuffer[12] = 0x31; // '1' packetBuffer[13] = 0x4E; // 'N' packetBuffer[14] = 0x31; // '1' packetBuffer[15] = 0x34; // '4' //すべてのNTPフィールドに値が指定されているため、タイムスタンプを要求するパケットを送信できます。 Udp.beginPacket(address, NTPport); // NTP要求はポート123に送信されます Udp.write(packetBuffer, NTP_PACKET_SIZE); Udp.endPacket(); } //-----------------受信したNTPパケットを解析してRTCにセットします bool parseNTPpacket(){ // 返信が利用可能かどうかを確認するのを待ちます bool result = false; if (Udp.parsePacket()) { // パケットを受信しました。そこからデータを読み取ります Udp.read(packetBuffer, NTP_PACKET_SIZE); // パケットをバッファに読み込みます // タイムスタンプは、受信したパケットのバイト40から始まり、4バイトです。 unsigned long secsSince1900; // 位置40から始まる4バイトを長整数に変換します secsSince1900 = (unsigned long)packetBuffer[40] << 24; secsSince1900 |= (unsigned long)packetBuffer[41] << 16; secsSince1900 |= (unsigned long)packetBuffer[42] << 8; secsSince1900 |= (unsigned long)packetBuffer[43]; Serial.print("Seconds since Jan 1 1900 = "); Serial.println(secsSince1900); // エピックタイムに変換します。(local) unsigned long epoch = secsSince1900 - seventyYears + TimeZoneSec; Serial.print("Unix time = "); Serial.println(epoch); setTime(epoch); //RTC登録(LocalTime) result = true; }else{ result = false; } return result; } //-------------- void setup() { Serial.begin(BAUDRATE); /* while (!Serial) { ; // シリアルポートが接続されるのを待ちます。シリアルモニタを寄贈するまで待機します。 } */ Serial.println("Start Ethernet Modbus TCP Example"); pinMode(LED_BUILTIN, OUTPUT); SPI.setSCK(SPI_SCK); SPI.setRX(SPI_RX); SPI.setTX(SPI_TX); SPI.setCS(SPI_CS); SPI.begin(); //Ethernet3で使用可能なAPI pinMode(SPI_CS, OUTPUT); //NIC_CS出力設定 Ethernet.setCsPin(SPI_CS); //NIC_CSアサイン Ethernet.setRstPin(NICReset); //NIC_RSTアサイン //NICリセット処理 pinMode(NICReset, OUTPUT); digitalWrite(NICReset, LOW); delay(10); //10msecパルス幅で初期化 digitalWrite(NICReset, HIGH); Ethernet.begin(mac, ip); delay(100); Serial.print(F("My IP address: "));Serial.println(Ethernet.localIP()); // print your local IP address: Udp.begin(NTPport); //最初にNTPサーバから時刻を取得する sendNTPpacket(timeServer); // NTPパケットをタイムサーバーに送信します delay(1000); gRTCOK = parseNTPpacket(); tm1637.init(); tm1637.set(BRIGHT_TYPICAL); //BRIGHT_TYPICAL = 2,BRIGHT_DARKEST = 0,BRIGHTEST = 7; //ModBusメモリ 初期化 for(int i = 0;i<MbCoilDataLen;i++) Mb.MbCoilData[i]=0x0000; //メモリ初期化 for(int i = 0;i<MbDiscreteDataLen;i++) Mb.MbDiscreteData[i]=0; //メモリ初期化 for(int i = 0;i<MbHoldDataLen;i++) Mb.MbHoldData[i]=0; //メモリ初期化 for(int i = 0;i<MbInputDataLen;i++) Mb.MbInputData[i]=0; //メモリ初期化 //Serial.println(F("ModBusメモリ 初期化")); //出力LED初期化 for (int i=0;i<DObits;i++){ Mb.SetBit(MB_FC_READ_COILS,i,false); //メモリマップはMb.MbData[0] pinMode(dOsPins[i],OUTPUT); digitalWrite(dOsPins[i], LOW); //LEDはHIGHで点灯 } //入力pin初期化 for (int i=0;i<DIbits;i++){ pinMode(dIsPins[i],INPUT); Mb.SetBit(MB_FC_READ_DISCRETE_INPUT,i,!digitalRead(dIsPins[i])); //bit メモリマップはMb.MbData[1] } for (int i=0;i<AIbits;i++){ Mb.MbInputData[i] = analogRead(aIsPins[i]); //U16 } //マイクロ秒単位の間隔 if (ITimer0.attachInterruptInterval(TIMER0_INTERVAL_MS * 500, TimerHandler0)){ if(Serial){ Serial.print(F("Starting ITimer0 OK, millis() = ")); Serial.println(millis()); } }else{ if(Serial){ Serial.println(F("Can't set ITimer0. Select another freq. or timer")); } } watchdog_reboot(0,0,delay_ms); gPreTime = millis(); if(Serial) Serial.println(F("EndSetup")); } //------------------- void loop() { //1秒のループ WDT8秒 if (watchdog_caused_reboot()){ if(Serial) Serial.print("Rebooted by Watchdog!\n"); }else{ if(Serial) Serial.print("Clean boot\n"); } watchdog_update(); // listen for incoming clients GetDeviceInfo(); // start the Modbus TCP server gMBusConnect = Mb.MbsRun(); //SlaveServer Serial.print("gMBusConnect=");Serial.println(gMBusConnect); SetDeviceInfo(); // configure the LED //digitalWrite(LED_BUILTIN, fTogle1); //fTogle1 = !fTogle1; //delay(100); //gHardwareStatus = Ethernet.hardwareStatus(); //if(gHardwareStatus == EthernetNoHardware) watchdog_reboot(0,0,1); gNowTime = millis(); gLoopTime = gNowTime - gPreTime; gPreTime = gNowTime; }
MgsModbus.h
/* * 2021/04/23 T.Wanibe Retouch * 20191029 T.Wanibe 保持メモリを個別に持たないとうまくゆかない * 個別に4つのメモリを保持するように変更 * 20191028 T.Wanibe 一般的なModbusデバイスとして構築出来るように * MgsModbusをレタッチすることにした。『ModbusPoll』とかLabVIEWの * Modbusライブラリを元に調査したところ、MgsModbusはスレーブサーバ * としてちゃんと機能するライブラリだと云う事が確認出来た。 * STM32duinoとしてコンパイル出来ないライブラリが殆ど。ライブラリ側で * Arduinoしか意識していない為と思われる。 * MgsModbusはシンプルであるが故にSTM32duinoでもコンパイルが通り実行 * 可能となる。 * が、アドレスマップがゼロベースであったり、4つのブロックの内Coilブロック * しかうまくアクセス出来ない点も確認出来た。 * 一般的なModbusデバイスとして認識出来るように大胆なデバッグを実施する * ことにした。 * まず、Localな扱いとした。inoと同じフォルダに配置すること。 * Slave機能のコードのみ残して(Masterのコードは削除) * * MgsModbus.h-Modbus TCPマスターおよびスレーブ用のArduinoライブラリ。 * V-0.1.1著作権(C)2013 Marco Gerritse * Arduino 1.0で作成およびテスト済み * * このライブラリは、 * MgsModbus.h-Modbus TCPマスターおよびスレーブ用のArduinoライブラリ。 * を原点としています。 * * テストに使用される外部ソフトウェア: * ★Modbus Poll * Modbus Poll x64 version 9.1.0 Build 1286, self-installing * ★LabVIEW Modbusライブラリ * NI Modbus Library v1.1.5.39 by National Instruments * ?? * このライブラリは、すべてのmodbusデータ(mbData []配列)に単一のメモリブロック * を使用します。 * 16ビットアクセスまたはアクセスビットを介して、いくつかのmodbus機能を介して同じ * データに到達できます。 MbDataの長さは少なくとも1でなければなりません。 * ●スレーブの場合、次のmodbus機能が実装されます:1、2、3、4、5、6、15、16 * ●内部および外部アドレスは1ベースに修正しています。 * ●EtherNetライブラリは『Ethernet3』を採用しています。W5500を安定して使用する為です。 */ #include "Arduino.h" #include <SPI.h> #include <Ethernet3.h> #ifndef MgsModbus_h #define MgsModbus_h #define MbCoilDataLen 2 // length of the MbCoilDataLen array (Equivalent16bit*2) #define MbDiscreteDataLen 2 // length of the MbDiscreteDataLen array(Equivalent16bit*2) #define MbHoldDataLen 64 // length of the MbHoldDataLen array #define MbInputDataLen 40 // length of the MbInputDataLen array #define MB_PORT 502 enum MB_FC { MB_FC_NONE = 0, MB_FC_READ_COILS = 1, MB_FC_READ_DISCRETE_INPUT = 2, MB_FC_READ_REGISTERS = 3, MB_FC_READ_INPUT_REGISTER = 4, MB_FC_WRITE_COIL = 5, MB_FC_WRITE_REGISTER = 6, MB_FC_WRITE_MULTIPLE_COILS = 15, MB_FC_WRITE_MULTIPLE_REGISTERS = 16 }; enum MB_XC { MB_XC_NONE = 0, MB_XC_Illegal_Function = 1, MB_XC_Illegal_Data_Address = 2, MB_XC_Illegal_Data_Value = 3, MB_XC_Slave_Device_Failure = 4, MB_XC_Acknowledge = 5, MB_XC_Slave_Device_Busy = 6, MB_XC_Negative_Acknowledge = 7, MB_XC_Memory_Parity_Error = 8, MB_XC_Gateway_Path_Unavailable = 10, MB_XC_Gateway_Target_Failed = 11 }; class MgsModbus { public: // general MgsModbus(); word MbCoilData[MbCoilDataLen]; // memory block that holds all the modbus user data word MbDiscreteData[MbDiscreteDataLen]; // memory block that holds all the modbus user data word MbHoldData[MbHoldDataLen]; // memory block that holds all the modbus user data word MbInputData[MbInputDataLen]; // memory block that holds all the modbus user data boolean GetBit(MB_FC fc,word Number); boolean SetBit(MB_FC fc,word Number,boolean Data); // returns true when the number is in the MbData range // modbus master //void Req(MB_FC FC, word Ref, word Count, word Pos); //void MbmRun(); //IPAddress remSlaveIP; // modbus slave bool MbsRun(); word GetDataLen(); private: // general MB_FC SetFC(int fc); // modbus master //uint8_t MbmByteArray[260]; // send and recieve buffer //MB_FC MbmFC; //int MbmCounter; //void MbmProcess(); //word MbmPos; //word MbmBitCount; //modbus slave uint8_t MbsByteArray[260]; // send and recieve buffer MB_FC MbsFC; MB_XC MbsXC; }; #endif
MgsModbus.cpp
/* * 2021/04/23 T.Wanibe Retouch */ #include "MgsModbus.h" // EthernetServer MbServer(MB_PORT); EthernetClient MbmClient; #define DEBUG 1 MgsModbus::MgsModbus() { } char buf[32]; //****************** Recieve data for ModBusSlave **************** // 2021/3/5 接続が有った場合はtrueをない場合はfalseを返すように変更 bool MgsModbus::MbsRun() { bool flagConnected = false; MbsXC = MB_XC_NONE; //****************** Read from socket **************** EthernetClient client = MbServer.available(); flagConnected = client.available(); if(flagConnected){ digitalWrite(LED_BUILTIN, LOW); delay(1); int i = 0; while(client.available()){ MbsByteArray[i] = client.read(); i++; } MbsFC = SetFC(MbsByteArray[7]); //Byte 7 of request is FC #if DEBUG Serial.print(F("\nREQUEST:")); for(int j = 0;j<i;j++){ sprintf(buf,"%02x ",MbsByteArray[j]); Serial.print(buf); } Serial.println(F("")); digitalWrite(LED_BUILTIN, HIGH); #endif } int Start, WordDataLength, ByteDataLength, CoilDataLength, MessageLength; //SwitchCaseに変更しようとしたがコンパイルエラーが発生するので諦めた if(MbsFC == MB_FC_READ_COILS) { //****************** Read Coils (1) ********************** Start = word(MbsByteArray[8],MbsByteArray[9]); //StartAddress確認 CoilDataLength = word(MbsByteArray[10],MbsByteArray[11]); //データ長の確認 #if DEBUG sprintf(buf,"01StartADRS=%d Length=%d",Start,CoilDataLength); Serial.println(buf); #endif if((Start<0) || ((CoilDataLength + Start) > (MbCoilDataLen * 16))){ //Exception MB_XC_Illegal_Data_Address MbsXC = MB_XC_Illegal_Data_Address; MbsByteArray[7] = 0x10 + MbsByteArray[7]; MbsByteArray[8] = MbsXC; MessageLength = 9; }else{ ByteDataLength = CoilDataLength / 8; if(ByteDataLength * 8 < CoilDataLength) ByteDataLength++; CoilDataLength = ByteDataLength * 8; MbsByteArray[5] = ByteDataLength + 3; //Number of bytes after this one. MbsByteArray[8] = ByteDataLength; //Number of bytes after this one (or number of bytes of data). for(int i = 0; i < ByteDataLength ; i++){ MbsByteArray[9 + i] = 0; // To get all remaining not written bits zero for(int j = 0; j < 8; j++){ bitWrite(MbsByteArray[9 + i], j, GetBit(MB_FC_READ_COILS,Start + i * 8 + j)); } } MessageLength = ByteDataLength + 9; } // }else if(MbsFC == MB_FC_READ_DISCRETE_INPUT) { //****************** Read Coils (2) ********************** Start = word(MbsByteArray[8],MbsByteArray[9]); //StartAddress確認 CoilDataLength = word(MbsByteArray[10],MbsByteArray[11]); //データ長の確認 #if DEBUG sprintf(buf,"02StartADRS=%d Length=%d",Start,CoilDataLength); Serial.println(buf); #endif if((Start<0) || ((CoilDataLength + Start) > (MbDiscreteDataLen * 16))){ //Exception MB_XC_Illegal_Data_Address MbsXC = MB_XC_Illegal_Data_Address; MbsByteArray[7] = 0x10 + MbsByteArray[7]; MbsByteArray[8] = MbsXC; MessageLength = 9; }else{ ByteDataLength = CoilDataLength / 8; if(ByteDataLength * 8 < CoilDataLength) ByteDataLength++; CoilDataLength = ByteDataLength * 8; MbsByteArray[5] = ByteDataLength + 3; //Number of bytes after this one. MbsByteArray[8] = ByteDataLength; //Number of bytes after this one (or number of bytes of data). for(int i = 0; i < ByteDataLength ; i++){ MbsByteArray[9 + i] = 0; // To get all remaining not written bits zero for(int j = 0; j < 8; j++){ bitWrite(MbsByteArray[9 + i], j, GetBit(MB_FC_READ_DISCRETE_INPUT,Start + i * 8 + j)); } } MessageLength = ByteDataLength + 9; } // }else if(MbsFC == MB_FC_READ_REGISTERS) { //****************** Read Registers (3) ****************** Start = word(MbsByteArray[8],MbsByteArray[9]); WordDataLength = word(MbsByteArray[10],MbsByteArray[11]); if((Start<0) || ((WordDataLength + Start) > MbInputDataLen)){ //Exception MB_XC_Illegal_Data_Address MbsXC = MB_XC_Illegal_Data_Address; MbsByteArray[7] = 0x10 + MbsByteArray[7]; MbsByteArray[8] = MbsXC; MessageLength = 9; }else{ ByteDataLength = WordDataLength * 2; MbsByteArray[5] = ByteDataLength + 3; //Number of bytes after this one. MbsByteArray[8] = ByteDataLength; //Number of bytes after this one (or number of bytes of data). for(int i = 0; i < WordDataLength; i++){ MbsByteArray[ 9 + i * 2] = highByte(MbHoldData[Start + i]); MbsByteArray[10 + i * 2] = lowByte(MbHoldData[Start + i]); } MessageLength = ByteDataLength + 9; } // }else if(MbsFC == MB_FC_READ_INPUT_REGISTER) { //****************** Read Registers (4) ****************** Start = word(MbsByteArray[8],MbsByteArray[9]); WordDataLength = word(MbsByteArray[10],MbsByteArray[11]); if((Start<0) || ((WordDataLength + Start) > MbHoldDataLen)){ //Exception MB_XC_Illegal_Data_Address MbsXC = MB_XC_Illegal_Data_Address; MbsByteArray[7] = 0x10 + MbsByteArray[7]; MbsByteArray[8] = MbsXC; MessageLength = 9; }else{ ByteDataLength = WordDataLength * 2; MbsByteArray[5] = ByteDataLength + 3; //Number of bytes after this one. MbsByteArray[8] = ByteDataLength; //Number of bytes after this one (or number of bytes of data). for(int i = 0; i < WordDataLength; i++){ MbsByteArray[ 9 + i * 2] = highByte(MbInputData[Start + i]); MbsByteArray[10 + i * 2] = lowByte(MbInputData[Start + i]); } MessageLength = ByteDataLength + 9; } // }else if(MbsFC == MB_FC_WRITE_COIL) { //****************** Write Coil (5) ********************** Start = word(MbsByteArray[8],MbsByteArray[9]); #if DEBUG sprintf(buf,"05StartADRS=%d",Start); Serial.println(buf); #endif if((Start<0) || (Start > (MbCoilDataLen * 16))){ //Exception MB_XC_Illegal_Data_Address MbsXC = MB_XC_Illegal_Data_Address; MbsByteArray[7] = 0x10 + MbsByteArray[7]; MbsByteArray[8] = MbsXC; MessageLength = 9; }else{ if (word(MbsByteArray[10],MbsByteArray[11]) == 0xFF00){SetBit(MB_FC_WRITE_COIL,Start,true);} if (word(MbsByteArray[10],MbsByteArray[11]) == 0x0000){SetBit(MB_FC_WRITE_COIL,Start,false);} MbsByteArray[5] = 2; //Number of bytes after this one. MessageLength = 8; } // }else if(MbsFC == MB_FC_WRITE_REGISTER) { //****************** Write Register (6) ****************** Start = word(MbsByteArray[8],MbsByteArray[9]); if((Start<0) || (Start > MbHoldDataLen)){ //Exception MB_XC_Illegal_Data_Address MbsXC = MB_XC_Illegal_Data_Address; MbsByteArray[7] = 0x10 + MbsByteArray[7]; MbsByteArray[8] = MbsXC; MessageLength = 9; }else{ MbHoldData[Start] = word(MbsByteArray[10],MbsByteArray[11]); MbsByteArray[5] = 6; //Number of bytes after this one. MessageLength = 12; } // }else if(MbsFC == MB_FC_WRITE_MULTIPLE_COILS) { //****************** Write Multiple Coils (15) ********************** Start = word(MbsByteArray[8],MbsByteArray[9]); CoilDataLength = word(MbsByteArray[10],MbsByteArray[11]); //データ長の確認 if((Start<0) || ((CoilDataLength + Start) > (MbCoilDataLen * 16))){ //Exception MB_XC_Illegal_Data_Address MbsXC = MB_XC_Illegal_Data_Address; MbsByteArray[7] = 0x10 + MbsByteArray[7]; MbsByteArray[8] = MbsXC; MessageLength = 9; }else{ MbsByteArray[5] = 6; for(int i = 0; i < CoilDataLength; i++){ SetBit(MB_FC_WRITE_MULTIPLE_COILS,Start + i,bitRead(MbsByteArray[13 + (i/8)],i-((i/8)*8))); } MessageLength = 12; } // }else if(MbsFC == MB_FC_WRITE_MULTIPLE_REGISTERS) { //****************** Write Multiple Registers (16) ****************** Start = word(MbsByteArray[8],MbsByteArray[9]); WordDataLength = word(MbsByteArray[10],MbsByteArray[11]); if((Start<0) || ((WordDataLength + Start) > MbHoldDataLen)){ //Exception MB_XC_Illegal_Data_Address MbsXC = MB_XC_Illegal_Data_Address; MbsByteArray[7] = 0x10 + MbsByteArray[7]; MbsByteArray[8] = MbsXC; MessageLength = 9; }else{ ByteDataLength = WordDataLength * 2; MbsByteArray[5] = 6; for(int i = 0; i < WordDataLength; i++){ MbHoldData[Start + i] = word(MbsByteArray[ 13 + i * 2],MbsByteArray[14 + i * 2]); } MessageLength = 12; } }else{ //Exception 01:IllegalFunction MbsXC = MB_XC_Illegal_Function; ByteDataLength = 0; MbsByteArray[7] = 0x10 + MbsByteArray[7]; MbsByteArray[8] = MbsXC; MessageLength = 9 + ByteDataLength; } // MbsFC = MB_FC_NONE; client.write(MbsByteArray, MessageLength); #if DEBUG Serial.print(F("\nRESPONS:")); for(int i = 0;i<MessageLength;i++){ sprintf(buf,"%02x ",MbsByteArray[i]); Serial.print(buf); } Serial.println(F("")); #endif return flagConnected; } //****************** ?? ****************** 20210423 retouch MB_FC MgsModbus::SetFC(int fc){ MB_FC FC; switch(fc){ case 1: FC = MB_FC_READ_COILS; break; case 2: FC = MB_FC_READ_DISCRETE_INPUT; break; case 3: FC = MB_FC_READ_REGISTERS; break; case 4: FC = MB_FC_READ_INPUT_REGISTER; break; case 5: FC = MB_FC_WRITE_COIL; break; case 6: FC = MB_FC_WRITE_REGISTER; break; case 15: FC = MB_FC_WRITE_MULTIPLE_COILS; break; case 16: FC = MB_FC_WRITE_MULTIPLE_REGISTERS; break; default: FC = MB_FC_NONE; } return FC; } //--------------------- word MgsModbus::GetDataLen(){ int MbDataLen; switch(MbsFC){ case MB_FC_READ_COILS: case MB_FC_WRITE_COIL: case MB_FC_WRITE_MULTIPLE_COILS: MbDataLen = MbCoilDataLen; break; case MB_FC_READ_DISCRETE_INPUT: MbDataLen = MbDiscreteDataLen; break; case MB_FC_READ_REGISTERS: case MB_FC_WRITE_REGISTER: case MB_FC_WRITE_MULTIPLE_REGISTERS: MbDataLen = MbHoldDataLen; break; case MB_FC_READ_INPUT_REGISTER: MbDataLen = MbInputDataLen; break; default: MbDataLen = 0; } return MbDataLen; } //--------------------- boolean MgsModbus::GetBit(MB_FC fc,word Number){ boolean Tmp; int ArrayPos = Number / 16; int BitPos = Number - ArrayPos * 16; if(fc == MB_FC_READ_DISCRETE_INPUT){ Tmp = bitRead(MbDiscreteData[ArrayPos],BitPos); }else{ Tmp = bitRead(MbCoilData[ArrayPos],BitPos); } return Tmp; } //--------------------- boolean MgsModbus::SetBit(MB_FC fc,word Number,boolean Data){ boolean Overrun; int ArrayPos = Number / 16; int BitPos = Number - ArrayPos * 16; if(fc == MB_FC_READ_DISCRETE_INPUT){ Overrun = ArrayPos > MbDiscreteDataLen * 16; // check for data overrun if (!Overrun){ bitWrite(MbDiscreteData[ArrayPos],BitPos,Data); } }else{ Overrun = ArrayPos > MbCoilDataLen * 16; // check for data overrun if (!Overrun){ bitWrite(MbCoilData[ArrayPos],BitPos,Data); } } return Overrun; }
免責事項
本ソフトウエアは、あなたに対して何も保証しません。本ソフトウエアの関係者(他の利用者も含む)は、あなたに対して一切責任を負いません。
あなたが、本ソフトウエアを利用(コンパイル後の再利用など全てを含む)する場合は、自己責任で行う必要があります。本ソフトウエアの著作権はToolsBoxに帰属します。
本ソフトウエアをご利用の結果生じた損害について、ToolsBoxは一切責任を負いません。
ToolsBoxはコンテンツとして提供する全ての文章、画像等について、内容の合法性・正確性・安全性等、において最善の注意をし、作成していますが、保証するものではありません。
ToolsBoxはリンクをしている外部サイトについては、何ら保証しません。
ToolsBoxは事前の予告無く、本ソフトウエアの開発・提供を中止する可能性があります。
商標・登録商標
Microsoft、Windows、WindowsNTは米国Microsoft Corporationの米国およびその他の国における登録商標です。
Windows Vista、Windows XPは、米国Microsoft Corporation.の商品名称です。
LabVIEW、National Instruments、NI、ni.comはNational Instrumentsの登録商標です。
I2Cは、NXP Semiconductors社の登録商標です。
その他の企業名ならびに製品名は、それぞれの会社の商標もしくは登録商標です。
すべての商標および登録商標は、それぞれの所有者に帰属します。