Modbusデバイスを構築する上での考察

最終更新日 2023年2月16日

パソコン等でセンサデバイスからデータを取得したい場合、パソコンに直接センサデバイスを接続することは幾つか問題があるように思います。

このようなときフィールドネットワークを介したデータ取得をすることで全体構成がすっきりすると考えます。
既存のフィールドネットワークは幾つかあります。その一つとしてMODBUSがあります。

MODBUSとは

Modbus(モドバス)とは、米Modicon社が自社PLC用に1979年に開発した通信プロトコルで、PLCのみならず電子機器間においてもデータ受け渡しの目的で使用されています。 仕様がオープンであり、無償で利用できるなどの特徴から、産業分野のデファクトスタンダードとして広く採用されている通信プロトコルです。

Modbusはオープンな仕様で、無償で利用できるため、Arduino等のマイコンデバイスが扱う通信プロトコルとしては優位性があると思います。

Modbusは通信形態としてMaster/Slave型です。Master側からSlave側に【要求message】を送るとSlave側からMaster側に応答messageが返るプロトコルです。
これにより、

マイコン側がMasterデバイスになる事が出来ないわけでは無いと思いますが、基本はSlaveデバイスかと思います。ただ、Slaveデバイスで有ってもセンサ単体でModbusRS485仕様デバイスであるケースがありうるので、その場合SlaveデバイスでもMasterデバイスとして振る舞うこともあるのかと思います。

ここでは、Master/Slave間の接続はLANのみ検討します。つまりModbusTCPに絞ります。そして、マイコン側にてModbusTCPデバイスの構築することを検討します。
※Modbus485の場合データ転送速度は20kbps程度です。ModbusTCPの場合1Mbps程度は期待できるかと思います。

ArduinoIDE環境で用意されているサンプルスケッチの多くはModbus485のようですが、LANモジュールやWiFiモジュールライブラリを追加することで幾つかのModbusTCPのサンプルスケッチが見つかるかと思います。ただ、このスケッチをそのままビルドしてもModbusの雰囲気が掴めないかと思います。

とはいうものサンプルスケッチがビルド出来なかったり、ビルド出来ても、どのように確認したら良いのか判らないことも多いと思います。
ここではModbusTCPデバイスをどうやって構築するのか、その後の動作確認はどうするのかを紹介しておきます。

  1. ModbusTCPデバイスの構築
  2. ModbusTCPデバイスの動作確認

ModbusTCPデバイスの構築】

マイコンでModbusTCPSlaveデバイスを構築することは意味があると思います。

  • Modbusは汎用プロトコルであり、この部分は開発環境から提供されるライブラリを使えばほぼそのまま使えます。
  • 使いたいセンサデバイスが使える様にマイコンに登録/コーディングする際も、大体メーカがライブラリコードを提供してくれるために有り難く使う事が出来ます。
  • LAN接続するためのモジュールがマイコンボードに用意されていればそのまま使います。無ければSPI接続で使う事になります。※UART接続はパフォーマンスに問題があるので避けた方が良いと思います。
  • 複数のセンサデバイスを接続し、メモリマップに割り付けることが出来ますし、bitのON/OFF制御や、逆にボタンON/OFFのモニタが出来ます。マイコンボードの選択によって、この辺りの点数が決まるのかと思います。

一応推奨するデバイスを検討しました。

  • マイコンボードですが、32bitCPUでそれなりのクロック速度が期待できるモノがいいです。
    また、フラッシュはどこまで作り込むかによるのですが、64kBは最低でも必要ですね。
    メモリは20KBは必要です。大きければバッファを増やすことが出来、パフォーマンス向上が期待できます。
    EEPROMが用意されていたりフラッシュに割り付けしたり出来るといいです。
  • IOポート数ですが、I2Cでポートを増設できるchipを使う事をお勧めします。
    SPIは2系統あると、NICとSDを別々にするとか融通が利きます。
    UARTはハードウエア制御が出来る方がいいですね。
    アナログ入力は12bitは欲しいところですが、I2C経由でもいいのかと思います。
    アナログ出力ですが、ONボードの商品は少ないためMCP4725等の外付けchipの追加でも良いです。必要に応じて。
  • LANポートですが、WiznetのW5500chipがいいです。ライブラリもしっかりしています。
    WIFIにしたいのでしたらマイコンボードからESP32開発ボードにすべきですね。
    WizFi360ですが、シリアル接続というのがネックです。明らかにこの部分がボトルネックになっています。
  • マイコンボードですが、自分の推奨は、以下の通りです。ただ、STM32は入手困難かもしれません。潤沢と思われるRP2040で検討した方が良さそうです。
    • BluePil(STM32F103)
    • W5500-EVB-Pico(RP2040)

開発環境ですが、ArduinoIDEとします。最近ArduinoIDE2.0がリリースされていますが、幾つかのツールが使えないとか
アドオン開発環境の問題も残っており、FIX版の1.8.19がいいと思います。

RP2040をターゲットとするのでしたら Earle Philhowerコア を使用します。MBED版はダメですね。
ArduinoIDEの環境ファイルに、
https://github.com/earlephilhower/arduino-pico/releases/download/global/package_rp2040_index.json
を追加します。

STM32ならば、Roger Clark氏のコア Arduino_STM32 を使用します。STM32duino-bootloader も用意されているのとても敷居を下げて使う事が出来ます。
ArduinoIDEの環境ファイルに、
http://dan.drown.org/stm32duino/package_STM32duino_index.json
を追加します。

ESP32の場合は、ArduinoIDEの環境ファイルに、
https://dl.espressif.com/dl/package_esp32_index.json
を追加します。

Modbusのライブラリですが、デバイス環境を設定後、サンプルスケッチをビルドしてもコンパイルできないケースが多いです。ライブラリの中身を弄れば通るようにできますが、結構大変だと思っています。中にはModbusPortが“1502”に定義されているものがあったりめちゃくちゃです。
自分は、MgsModbusというライブラリをレタッチして使っています。レタッチしてしまっているため、スケッチと同じフォルダにライブラリも放り込んで使うことになります。

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"
#define         WIZFI360_EVB_PICO       1
#if     defined(WIZFI360_EVB_PICO)
        #include        "WizFi360.h"
#else
        #include        <SPI.h>
        #include        <Ethernet3.h>
#endif

#ifndef MgsModbus_h
#define MgsModbus_h
#define MbCoilDataLen           4       // length of the MbCoilDataLen array (Equivalent16bit*2)
#define MbDiscreteDataLen       4       // length of the MbDiscreteDataLen array(Equivalent16bit*2)
#define MbHoldDataLen           64      // length of the MbHoldDataLen array
#define MbInputDataLen          96      // 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"
//
#if     defined(WIZFI360_EVB_PICO)
        WiFiServer      MbServer(MB_PORT);
        WiFiClient      MbmClient;
#else
        EthernetServer  MbServer(MB_PORT);
        EthernetClient  MbmClient;
#endif
#define DEBUG      0                    // 1にするとRESPONS:00 00 00 00 00 00 00 d0 01 という出力がある
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();
#if     defined(WIZFI360_EVB_PICO)
        WiFiClient      client  =       MbServer.available();
        //Serial.print(F("\nWiFiClient"));
#else
        EthernetClient  client  =       MbServer.available();
        //Serial.print(F("\nEthernetClient"));
#endif
        delay(1);
        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;
}

ModbusTCPデバイスの動作確認】

汎用のModbusTCPマスターツールが幾つか存在します。

  1. Modbus Poll
    多分一番ポピュラーだと思います。ただ、有償です。お試し期間に動作確認する事になります。
  2. ModbusTool
    ソースコードが提供されています。https://github.com/ClassicDIY/ModbusTool
    このコードをビルドして実行ファイル形式で提供されている方も見えます。基本無償のようです。
  3. ModbusViewTCP
    登録しないといろいろ制限のあるツールです。
  4. LVModbusTCPMaster
    LabVIEWで構築された汎用ツールです。無償です。

動作確認方法

  1. 作成したModbusTCPデバイスのIPアドレスを確認しておきます。
    ※同じネットワークゾーンにあるPCからPINGが通るか確認しましょう。
    ※IPアドレスを後から確認するには コマンドプロンプトから arp -a あるいはAdvanced IP Scannerで調査しましょう。
  2. ModbusTCPデバイスと同じネットワークゾーンにあるPCにてModbusTCPマスターツールを起動します。
  3. ModbusTCPデバイスのIPアドレスを指定して接続します。◆接続出来ましたか?
  4. COILの入力確認します。COILの出力変更を実行します。◆期待通りの結果でしたか?
  5. 入力の確認をします。割り付けてあるbitのON/OFFを確認します。
    ModbusTCPデバイスで入力割り付けしたボタン等の変化が確認出来ますか?
  6. HOLD値を適当に書き込みます。読込直して期待通りの値か確認します。
  7. 入力データ値を確認します。ModbusTCPデバイス側でアナログ入力ポートを割り当てておき電圧を与えたときの値の変化で確認が出来ます。

汎用ツールで動作確認出来れば次のステップを検討してください。


戯言(nonsense)に戻る


問い合わせ頁の表示


免責事項

本ソフトウエアは、あなたに対して何も保証しません。本ソフトウエアの関係者(他の利用者も含む)は、あなたに対して一切責任を負いません。
あなたが、本ソフトウエアを利用(コンパイル後の再利用など全てを含む)する場合は、自己責任で行う必要があります。

本ソフトウエアの著作権は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社の登録商標です。
その他の企業名ならびに製品名は、それぞれの会社の商標もしくは登録商標です。
すべての商標および登録商標は、それぞれの所有者に帰属します。