ModbusTCPSlaveユニット

最終更新日:2023年6月27日
作成日:2023年6月5日

ここでは、W5500-EVB-PICO + Grove Shield for Pi Picoを使用した、ModbusTCPSlaveユニットを紹介します。
基本的な仕様はSTM32miniShield版に準拠しています。STM32Fのマイコンが本当に入手困難です。BluePillも一気に入手困難となりました。
そんなこともあり、比較的流通が安定してきたマイコンRP2040を積んだ開発ボードでModbusTCPSlaveユニットを構築し直すことを検討しました。

(01)Coil接点 16点 ※内部では32点(2word)分確保していますが、外部端子としては16点とします。
フォトカプラによる絶縁出力(オープンコレクタ) 24Vが掛かることを想定しており、16bit分のフォトカブラ入力ボードを用意する必要があります。I2CのIOエキスパンダ経由で接続します。

(02)Read接点 16点 ※内部では32点(2word)分確保していますが、外部端子としては16点とします。
入力レジスタ値に対するアラート表示もします。
フォトカプラ入力 24V入力を想定しており、22KΩの制限抵抗を入れることを検討ください。

(03)保持レジスタ 16ワード 
プログラムの追加で入力レジスタ値に対するアラート設定値やDAC出力値、カウンタ設定値等を入力設定出来ます。

(04)入力レジスタ 16ワード デバイスの情報や特定の入力値を表示します。
M5Stack社のENV3ユニットを接続し、気温、湿度、大気圧、及びCPUボードの温度を定期的に登録します。

Coil(01)
アドレス 機能 備考
00 外部出力OUTPUT_0 フォトカプラ出力0-0
01 外部出力OUTPUT_1 フォトカプラ出力0-1
02 外部出力OUTPUT_2 フォトカプラ出力0-2
03 外部出力OUTPUT_3 フォトカプラ出力0-3
04 外部出力OUTPUT_4 フォトカプラ出力0-4
05 外部出力OUTPUT_5 フォトカプラ出力0-5
06 外部出力OUTPUT_6 フォトカプラ出力0-6
07 外部出力OUTPUT_7 フォトカプラ出力0-7
08 外部出力OUTPUT_8 フォトカプラ出力1-0
09 外部出力OUTPUT_9 フォトカプラ出力1-1
10 外部出力OUTPUT_10 フォトカプラ出力1-2
11 外部出力OUTPUT_11 フォトカプラ出力1-3
12 外部出力OUTPUT_12 フォトカプラ出力1-4
13 外部出力OUTPUT_13 フォトカプラ出力1-5
14 外部出力OUTPUT_14 フォトカプラ出力1-6
15 外部出力OUTPUT_15 フォトカプラ出力1-7
16 内部出力OUTPUT_0  
17 内部出力OUTPUT_1  
18 内部出力OUTPUT_2  
19 内部出力OUTPUT_3  
20 内部出力OUTPUT_4  
21 内部出力OUTPUT_5  
22 内部出力OUTPUT_6  
23 内部出力OUTPUT_7  
24 内部出力OUTPUT_8  
25 内部出力OUTPUT_9  
26 内部出力OUTPUT_10  
27 内部出力OUTPUT_11  
28 内部出力OUTPUT_12  
29 内部出力OUTPUT_13  
30 内部出力OUTPUT_14  
31 内部出力OUTPUT_15  

Input(02)
アドレス 機能 備考
00 外部入力PUT_0 フォトカプラ入力0-0
01 外部入力PUT_1 フォトカプラ入力0-1
02 外部入力PUT_2 フォトカプラ入力0-2
03 外部入力PUT_3 フォトカプラ入力0-3
04 外部入力PUT_4 フォトカプラ入力0-4
05 外部入力PUT_5 フォトカプラ入力0-5
06 外部入力PUT_6 フォトカプラ入力0-6
07 外部入力PUT_7 フォトカプラ入力0-7
08 外部入力PUT_8 フォトカプラ入力1-0
09 外部入力PUT_9 フォトカプラ入力1-1
10 外部入力PUT_10 フォトカプラ入力1-2
11 外部入力PUT_11 フォトカプラ入力1-3
12 外部入力PUT_12 フォトカプラ入力1-4
13 外部入力PUT_13 フォトカプラ入力1-5
14 外部入力PUT_14 フォトカプラ入力1-6
15 外部入力PUT_15 フォトカプラ入力1-7
16 内部入力INPUT_0 入力値_0_アラート
17 内部入力INPUT_1 入力値_1_アラート
18 内部入力INPUT_2 入力値_2_アラート
19 内部入力INPUT_3 入力値_3_アラート
20 内部入力INPUT_4 入力値_4_アラート
21 内部入力INPUT_5 入力値_5_アラート
22 内部入力INPUT_6 入力値_6_アラート
23 内部入力INPUT_7 入力値_7_アラート
24 内部入力INPUT_8  
25 内部入力INPUT_9  
26 内部入力INPUT_10  
27 内部入力INPUT_11  
28 内部入力INPUT_12  
29 内部入力INPUT_13  
30 内部入力INPUT_14  
31 内部入力INPUT_15  

Hold(03)
アドレス 機能 ByteH ByteL 備考
00  

uint
 
01  

uint
 
02  

uint
 
03  

uint
 
04  

uint
 
05  

uint
 
06  

uint
 
07  

uint
 
08  

uint
 
09  

uint
 
10  

uint
 
11  

uint
 
12  

uint
 
13  

uint
 
14  

uint
 
15  

uint
 
16  

uint
 
17  

uint
 
18  

uint
 
19  

uint
 
20  

uint
 
21  

uint
 
22  

uint
 
23  

uint
 
24 入力値_0_上限値

I16
この値を入力値0と比較してアラートとして設定
上限>下限が不成立の場合は比較せずアラートFALSE
25 入力値_0_下限値

I16
26 入力値_1_上限値

I16
この値を入力値1と比較してアラートとして設定
上限>下限が不成立の場合は比較せずアラートFALSE
27 入力値_1_下限値

I16
28 入力値_2_上限値

I16
この値を入力値2と比較してアラートとして設定
上限>下限が不成立の場合は比較せずアラートFALSE
29 入力値_2_下限値

I16
30 入力値_3_上限値

I16
この値を入力値3と比較してアラートとして設定
上限>下限が不成立の場合は比較せずアラートFALSE
31 入力値_3_下限値

I16
32 入力値_4_上限値

I16
この値を入力値4と比較してアラートとして設定
上限>下限が不成立の場合は比較せずアラートFALSE
33 入力値_4_下限値

I16
34 入力値_5_上限値

I16
この値を入力値5と比較してアラートとして設定
上限>下限が不成立の場合は比較せずアラートFALSE
35 入力値_5_下限値

I16
36 入力値_6_上限値

I16
この値を入力値6と比較してアラートとして設定
上限>下限が不成立の場合は比較せずアラートFALSE
37 入力値_6_下限値

I16
38 入力値_7_上限値

I16
この値を入力値7と比較してアラートとして設定
上限>下限が不成立の場合は比較せずアラートFALSE
39 入力値_7_下限値

I16

Input(04)
アドレス 機能 ByteH ByteL 備考
00 入力データ_0

int
温度 25.45℃ =>  2545
01 入力データ_1

uint
湿度 50.89% =>  5089
02 入力データ_2

uint
気圧 1033.25hPa  =>  1033
03 入力データ_3

int
ボード温度  25.45℃ => 2545
04 入力データ_4

ADC_0
0-3.3V 10bit (0-1023) GPIO26を使用
05 入力データ_5

予約入力5
サンプルプログラムでは割付なし
06 入力データ_6

予約入力6
サンプルプログラムでは割付なし
07 入力データ_7

予約入力7
サンプルプログラムでは割付なし
08 入力データ_8   サンプルプログラムでは割付なし
09 入力データ_9   サンプルプログラムでは割付なし
10 入力データ_10   サンプルプログラムでは割付なし
11 入力データ_11   サンプルプログラムでは割付なし
12 入力データ_12

西暦
現在時刻
TZ:タイムゾーン 日本の場合+9=90ハワイ-8=-80
59分01秒=59×256+01=15105(0x5901)
13 入力データ_13

14 入力データ_14

TZ

15 入力データ_15

16 VenderString_0_1 ‘\0’ ‘’ ベンタ名31max文字+\0
17 VenderString_2_3 ‘’ ‘’
18 VenderString_4_5 ‘’ ‘’
19 VenderString_6_7 ‘’ ‘’
20 VenderString_8_9 ‘’ ‘’
21 VenderString_10_11 ‘’ ‘’
22 VenderString_12_13 ‘’ ‘’
23 VenderString_14_15 ‘’ ‘’
24 VenderString_16_17 ‘’ ‘’
25 VenderString_18_19 ‘’ ‘’
26 VenderString_20_21 ‘’ ‘’
27 VenderString_22_23 ‘’ ‘’
28 VenderString_24_25 ‘’ ‘’
29 VenderString_26_27 ‘’ ‘’
30 VenderString_28_29 ‘’ ‘’
31 VenderString_30_31 ‘’ ‘’
32 ProductString_0_1 ‘\0’ ‘’ 製品名31max文字+\0
33 ProductString_2_3 ‘’ ‘’
34 ProductString_4_5 ‘’ ‘’
35 ProductString_6_7 ‘’ ‘’
36 ProductString_8_9 ‘’ ‘’
37 ProductString_10_11 ‘’ ‘’
38 ProductString_12_13 ‘’ ‘’
39 ProductString_14_15 ‘’ ‘’
40 ProductString_16_17 ‘’ ‘’
41 ProductString_18_19 ‘’ ‘’
42 ProductString_20_21 ‘’ ‘’
43 ProductString_22_23 ‘’ ‘’
44 ProductString_24_25 ‘’ ‘’
45 ProductString_26_27 ‘’ ‘’
46 ProductString_28_29 ‘’ ‘’
47 ProductString_30_31 ‘’ ‘’
48 SystemVerValue0_1 ‘\1’ ‘\0’ システムバージョン表記
バイト単位で0/1/2/3
49 SystemVerValue2_3 ‘\0’ ‘\0’
50 FirmwareVerVarlue0_1 ‘\1’ ‘\0’ ファームウエアバージョン表記
バイト単位で0/1/2/3
51 FirmwareVerVarlue2_3 ‘\0’ ‘\0’
52 予約 ‘’ ‘’  
53 予約 ‘’ ‘’  
54 予約 ‘’ ‘’  
55 予約 ‘’ ‘’  
56 予約 ‘’ ‘’  
57 予約 ‘’ ‘’  
58 予約 ‘’ ‘’  
59 予約 ‘’ ‘’  
60 予約 ‘’ ‘’  
61 予約 ‘’ ‘’  
62 予約 ‘’ ‘’  
63 予約 ‘’ ‘’  


ソースコードも添えておきます。
このコードを実行するに当たり追加が必要なライブラリは、

W5500-EVB-PICO_ModbusSlave1_2_0.ino

/*
 * 2023/5/30 T.Wanibe
 * 1.2.1
 *      バグの修正版
 * 1.2.0
 *      ModbusSlaveを新規に構築することにした。
 *      WIZ5500-ENV-PICO+ENV3+TM1637
 *      IP=192.168.0.213  MAC:0xE3 ID = 0xA2
 * 1.1.4
 *      Operating time:の表示値にバグがあった。1/2にしなくてはいけない  
 *      Vstrのごみ対策
 *      内部時計が2か月で4分進んでしまった。1日一回の時刻同期が必要と感じ対策
 * 1.1.3
 *      メモリ内容の見直し
 * 1.1.2
 *      4-7SEGLEDのドット対策
 *      Pin割り当て再考
 * 1.1
 *      W5500-EVB-PICOを入手したのでPINアサインを見直してビルド
 * 1.03
 *      1.02のHTML未修正部分の手直し
 *      メモリもフラッシュも可成り余裕があるのでこちらも見直す
 * 1.02
 *      1.01がすんなり動いたので更に以下の機能を追加する
 *      EEPROMによるパラメータ登録
 *      WebServer機能の追加
 * 1.01
 *      1.0 で とりあえずModbusMasterとの通信が出来るSlaveが作成出来ました。
 *      これまでにArduino-Picoに実装してきたコードを移植してもう少し肉付けしたいと思う
 *      Timer割込を追加してCP25のLEDを500msec毎の点滅にしたい。
 *      NTPクライアントによる時刻同期 及びTM1637に時刻表示
 * 最大2093056バイトのフラッシュメモリのうち、スケッチが99432バイト(4%)を使っています。
 * 最大262144バイトのRAMのうち、グローバル変数が11080バイト(4%)を使っていて、ローカル変数で251064バイト使うことができます。
 */
#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と同じ階層に置く
#include        <EEPROM.h>
//#include        <Adafruit_NeoPixel.h>
#include        <Wire.h>
#include        "Adafruit_SHT31.h"
#include        "QMP6988.h"                                                     //UNIT_ENV3に含まれています。
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           32                                      // length of the MbHoldDataLen array
#define         MbInputDataLen          64                                      // length of the MbInputDataLen array
#define         OK                      0
#define         NG                      1
#define         BAUDRATE                115200
#define         CLK                     28                                       //A2ソケット
#define         DIO                     27                                       //A2ソケット
#define         NICReset                20
#define         NICInit                 21
#define         SPI_SCK                 18
#define         SPI_RX                  16
#define         SPI_TX                  19
#define         SPI_CS                  17
#define         I2C0_SCL                9
#define         I2C0_SDA                8
//#define         LED_PIN                 28                                      //NeoPixelsが接続されているPINを指定します。
#define         LED_COUNT               1                                       //NeoPixelsが連結されている数を登録します。
#define         BRIGHTNESS              50                                      // NeoPixelの明るさ設定です 0 (min) to 255 (max)
#define         TIMER0_INTERVAL_MS      1000
#define         HttpPort                80                                      //WebServer
#define         SEG_DP                  0x80
#define         vers                    "RPiPicoModbusTCP_Slave_1.0"
#define         webTitle                "RP2040版 ModbusTCP_Slave メニュー"
#define         topMenu1                "設定メニュー"
#define         topMenu2                "Modbus表示"
const char      titleStr[]      = "RPiPicoModbusTCP";
const char      VenderString[]  = "ToolsBox";
char            outputSTR[9];
const char      FVirsion[]      = "1.2.3";                                      //フォーマット厳守 x.x.x
const char      SVirsion[]      = "1.2.3";                                      //フォーマット厳守 x.x.x ソースコードのバージョン
//
uint32_t        delay_ms        = 8000;                                         //8秒 最大8.3秒
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,0xE3};              //WIZNET
byte            ip[]            = {192, 168, 0, 213};
byte            dns_server[]    = {192, 168, 0, 1};
byte            gateway[]       = {192, 168, 0, 1};
byte            subnet[]        = {255, 255, 255, 0};
byte            NTPip[]         = {192,168,0,199};                              //char*timeServerへの変換が必要内部管理は配列にする。
bool            gMBusConnect    = false;                                        //Modbus接続中はWebServer接続を502で逃げる
byte            fTogle1         = LOW;
bool            gRTCOK;
//WebServer
String          HTTP_req;
char            MacAddressStr[20];
char            IPAddressStr[20];
char            SubnetMaskStr[20];
char            GatewayStr[20];
char            NTPserverStr[20];
char            DateTimeStr[20];
//内部カウンタ
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            buf80[80];
char            buf32[32];
// これは、HTMLコードを「流す」ためのバッファーです。
// ””を含む最長の文字チェーン+1と同じ大きさでなければなりません。
char            buffer[256];
byte            preday;
byte            nowday;
static int8_t   tube_tab[] = {  0x3f, 0x06, 0x5b, 0x4f,
                                0x66, 0x6d, 0x7d, 0x07,
                                0x7f, 0x6f, 0x77, 0x7c,
                                0x39, 0x5e, 0x79, 0x71
                                }; //0~9,A,b,C,d,E,F
//物理接続要素
int             dOsPins[]       = {0,1};
int             dIsPins[]       = {22};
int             aIsPins[]       = {26};
byte            preData         = 255;
byte            preR            = 255;
byte            preG            = 255;
byte            preB            = 255;
bool            preShow         = true;
//物理接続配列要素数を求めておく
int             DObits          = sizeof(dOsPins)/sizeof(int);
int             DIbits          = sizeof(dIsPins)/sizeof(int);
int             AIbits          = sizeof(aIsPins)/sizeof(int);
//ENV3用パラメータ
float           ENV3_tmp        = 0.0;                                  //なぜかNaNが認められない
float           ENV3_hum        = 0.0;
float           ENV3_pressure   = 0.0;
float           tmpOnPico       = 0.0;
int             count           = 0;
//
const byte      ID = 0xA2;
//オブジェクトコンストラクタ
MgsModbus       Mb;
TM1637          tm1637(CLK,DIO);
EthernetUDP     Udp;
RPI_PICO_Timer  ITimer0(0);
EthernetServer  webServer(HttpPort);
EthernetClient  client;
Adafruit_SHT31  sht30(&Wire);
QMP6988         qmp6988;
//Adafruit_NeoPixel strip(LED_COUNT, LED_PIN, NEO_GRBW + NEO_KHZ800);
                                                                        // 引数 1 = NeoPixel ストリップのピクセル数
                                                                        // 引数 2 = Arduino のピン番号 (ほとんどが有効)
                                                                        // 引数 3 = ピクセル タイプ フラグ、必要に応じて追加:
                                                                        //   NEO_KHZ800  800 KHz ビットストリーム (ほとんどの NeoPixel 製品 w/WS2812 LED)
                                                                        //   NEO_KHZ400  400 KHz (従来の「v1」(v2 ではない) FLORA ピクセル、WS2811 ドライバー)
                                                                        //   NEO_GRB     ピクセルは GRB ビットストリーム用に配線されています (ほとんどの NeoPixel 製品)
                                                                        //   NEO_RGB     ピクセルは RGB ビットストリーム用に配線されています (v2 ではなく v1 FLORA ピクセル)
                                                                        //   NEO_RGBW    ピクセルは GRBW ビットストリーム用に配線されています (NeoPixel RGBW 製品)
//------------EEPROM.read
void ReadID(){
        for (int i = 0; i < 6; i++){
                //mac[i]        = EEPROM.read(i+1);
                mac[i]          = EEPROM[i+1];
                if(Serial)      Serial.print("R_mac=");Serial.println(mac[i]);
        }
        for (int i = 0; i < 4; i++){
                //ip[i]         = EEPROM.read(i+7);
                ip[i]           = EEPROM[i+7];
                if(Serial)      Serial.print("R_ip=");Serial.println(ip[i]);
        }
        for (int i = 0; i < 4; i++){
                //subnet[i]     = EEPROM.read(i+11);
                subnet[i]       = EEPROM[i+11];
                if(Serial)      Serial.print("R_subnet=");Serial.println(subnet[i]);
        }
        for (int i = 0; i < 4; i++){
                //gateway[i]    = EEPROM.read(i+15);
                gateway[i]      = EEPROM[i+15];
                if(Serial)      Serial.print("R_gateway=");Serial.println(gateway[i]);
        }
        for (int i = 0; i < 4; i++){
                //NTPip[i]      = EEPROM.read(i+19);
                NTPip[i]        = EEPROM[i+19];
                if(Serial)      Serial.print("R_NTPip=");Serial.println(NTPip[i]);
        }
        //timezone = float(EEPROM.read(23)) / 10.0;
        timezone = float(EEPROM[23]) / 10.0;
        if(Serial)      Serial.print("R_timezone=");Serial.println(timezone);
}
//------------EEPROM.write
void WriteID(){
        for (int i = 0 ; i < 6; i++){
                //EEPROM.write(i + 1,mac[i]);
                EEPROM[i + 1]   = mac[i];
                if(Serial)      Serial.print("W_mac=");Serial.println(mac[i]);
        }
        for (int i = 0 ; i < 4; i++){
                //EEPROM.write(i + 7, ip[i]);
                EEPROM[i + 7]   = ip[i];
                if(Serial)      Serial.print("W_ip=");Serial.println(ip[i]);
        }
        for (int i = 0 ; i < 4; i++){
                //EEPROM.write(i + 11, subnet[i]);
                EEPROM[i + 11]  = subnet[i];
                if(Serial)      Serial.print("W_subnet=");Serial.println(subnet[i]);
        }
        for (int i = 0 ; i < 4; i++){
                //EEPROM.write(i + 15, gateway[i]);
                EEPROM[i + 15]  = gateway[i];
                if(Serial)      Serial.print("W_gateway=");Serial.println(gateway[i]);
        }
        for (int i = 0; i < 4; i++){
                //EEPROM.write(i + 19, NTPip[i]);
                EEPROM[i + 19]  = NTPip[i];
                if(Serial)      Serial.print("W_NTPip=");Serial.println(NTPip[i]);
        }
        //EEPROM.write(23,char(timezone * 10));
        EEPROM[23]      = char(timezone * 10);
        if(Serial)      Serial.print("W_timezone=");Serial.println(timezone);
        // IDを既知のビットに設定します。したがって、Arduinoをリセットすると、EEPROM値が使用されます。
        //EEPROM.write(256, ID);
        EEPROM[256]     =       ID;
        EEPROM.commit();                        //2.5.5.2で追加
        
        if(Serial)      Serial.print("W_ID=");Serial.println(ID,HEX);
}
//----------------------------------
void LANSetup(){
        EEPROM.begin(512);
        //int idcheck   = EEPROM.read(256);
        int idcheck     =       EEPROM[256];
        if(Serial)      Serial.print("R_ID=");Serial.println(idcheck,HEX);
        Serial.print(F("LocalID = 0x"));Serial.println(idcheck,HEX);
        if (idcheck == ID){
                //idがIDと同じ値の場合、
                //これは、このスケッチがシールドを設定するために使用されたことを意味します。
                //EERPOMの値を読み取ってシールドを設定します。
                ReadID();
        }else{
                //idが一致しない場合、初期値を書き込む事にします。
                WriteID();
        }
        EEPROM.end();
        sprintf(MacAddressStr,"%02x.%02x.%02x.%02x.%02x.%02x",mac[0],mac[1],mac[2],mac[3],mac[4],mac[5]);
        sprintf(IPAddressStr,"%d.%d.%d.%d",ip[0],ip[1],ip[2],ip[3]);
        sprintf(SubnetMaskStr,"%d.%d.%d.%d",subnet[0],subnet[1],subnet[2],subnet[3]);
        sprintf(GatewayStr,"%d.%d.%d.%d",gateway[0],gateway[1],gateway[2],gateway[3]);
}
//----------------------------MODBUS
int     GetDeviceInfo(){
        for (int i=0;i<DIbits;i++){
                Mb.SetBit(MB_FC_READ_DISCRETE_INPUT,i,!digitalRead(dIsPins[i]));                //bit 論理反転
        }
        int j=0;
        for (j=0;j<AIbits;j++){
                Mb.MbInputData[j]=analogRead(aIsPins[j]);                                       //U16
        }
        Mb.MbInputData[j]       =       int(analogReadTemp()*100.0);
        ENV3_pressure           =       qmp6988.calcPressure();                                 //qmp6988のデータを取得します。
        Mb.MbInputData[j+1]     =       int(ENV3_pressure); 
        ENV3_tmp                =       sht30.readTemperature();                                //SHT30のデータを取得します。
        Mb.MbInputData[j+2]     =       int(ENV3_tmp*100.0); 
        ENV3_hum                =       sht30.readHumidity();                                   //SHT30から得られた湿度を代入します。
        Mb.MbInputData[j+3]     =       int(ENV3_hum*100.0); 
        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;
}
//--------------
void custom_display(uint8_t bit_addr, uint8_t seg_data) {
        tm1637.start();
        tm1637.writeByte( 0x44 );
        tm1637.stop();
        tm1637.start();
        tm1637.writeByte(bit_addr | 0xc0);
        tm1637.writeByte(seg_data);
        tm1637.stop();
        tm1637.start();
        tm1637.writeByte(tm1637.cmd_disp_ctrl);
        tm1637.stop();
}
//-------------タイマ割込で呼ばれるルーチン クロック表示 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);
        if(fTogle1){
                custom_display( 0, tube_tab[TimeDisp[0]]);
                custom_display( 1, tube_tab[TimeDisp[1]] | SEG_DP);
                custom_display( 2, tube_tab[TimeDisp[2]]);
                custom_display( 3, tube_tab[TimeDisp[3]]);               
        }else{
                custom_display( 0, tube_tab[TimeDisp[0]]);
                custom_display( 1, tube_tab[TimeDisp[1]]);
                custom_display( 2, tube_tab[TimeDisp[2]]);
                custom_display( 3, tube_tab[TimeDisp[3]]);           
        }
        Mb.MbInputData[12] = year(currentTime);
        nowday             = day(currentTime);
        Mb.MbInputData[13] = month(currentTime)*256 + nowday;
        Mb.MbInputData[14] = int(timezone * 2560) + currentHour;
        Mb.MbInputData[15] = currentMin * 256 + currentSec;
        digitalWrite(LED_BUILTIN, fTogle1);
        fTogle1         = !fTogle1;
        if (preday !=  nowday) {
                preday  =       nowday;
                sendNTPpacket(buf32);                                                   // NTPパケットをタイムサーバーに送信します
                delay(100); 
                gRTCOK = parseNTPpacket();
        }
        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(buf32,"■%d\t%d\n",packetSize,fractions);
                if(Serial)     Serial.print(buf32);
                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(buf32,"●%d\t%d\t%d\t%d\n",packetBuffer[20],packetBuffer[21],packetBuffer[22],packetBuffer[23]);
                if(Serial)      Serial.print(buf32);
                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;
}
//--------------------------Webserver
//--------OK_200
String OK_200           =
        "HTTP/1.1 200 OK\n"
        "Content-Type: text/html\n";
//--------NG_404
String NG_404           =
        "HTTP/1.1 404 Not Found\n"
        "Content-Type: text/html\n"
        "Connnection: close\n";
//--------LoadUtilsJS
String LoadUtilsJS1     =
        "'use strict';\n"
"window.chartColors = {\n"
        "\tred: 'rgb(255, 99, 132)',\n"
        "\torange: 'rgb(255, 159, 64)',\n"
        "\tyellow: 'rgb(255, 205, 86)',\n"
        "\tgreen: 'rgb(75, 192, 192)',\n"
        "\tblue: 'rgb(54, 162, 235)',\n"
        "\tpurple: 'rgb(153, 102, 255)',\n"
        "\tgrey: 'rgb(201, 203, 207)'\n"
"};\n"
"(function(global) {\n"
        "\tvar MONTHS = [\n"
                "\t\t'January',\n"
                "\t\t'February',\n"
                "\t\t'March',\n"
                "\t\t'April',\n"
                "\t\t'May',\n"
                "\t\t'June',\n"
                "\t\t'July',\n"
                "\t\t'August',\n"
                "\t\t'September',\n"
                "\t\t'October',\n"
                "\t\t'November',\n"
                "\t\t'December'\n"
        "\t];\n"
        "\tvar COLORS = [\n"
                "\t\t'#4dc9f6',\n"
                "\t\t'#f67019',\n"
                "\t\t'#f53794',\n"
                "\t\t'#537bc4',\n"
                "\t\t'#acc236',\n"
                "\t\t'#166a8f',\n"
                "\t\t'#00a950',\n"
                "\t\t'#58595b',\n"
                "\t\t'#8549ba'\n"
        "\t];\n"
        "\tvar Samples = global.Samples || (global.Samples = {});\n"
        "\tvar Color = Chart.helpers.color;\n"
        "\tfunction applyDefaultNumbers(config) {\n"
                "\t\tvar cfg = config || {};\n"
                "\t\tcfg.min = cfg.min || 0;\n"
                "\t\tcfg.max = cfg.max || 1;\n"
                "\t\tcfg.from = cfg.from || [];\n"
                "\t\tcfg.count = cfg.count || 8;\n"
                "\t\tcfg.decimals = cfg.decimals || 8;\n"
                "\t\tcfg.continuity = cfg.continuity || 1;\n"
                "\t\treturn cfg;\n"
        "\t}\n";
String LoadUtilsJS2     =
        "\tSamples.utils = {\n"
                "\t\t// Adapted from http://indiegamr.com/generate-repeatable-random-numbers-in-js/\n"
                "\t\tsrand: function(seed) {\n"
                "\t\t\tthis._seed = seed;\n"
                "\t\t},\n"
                "\t\trand: function(min, max) {\n"
                "\t\t\tvar seed = this._seed;\n"
                "\t\t\tmin = min === undefined ? 0 : min;\n"
                "\t\t\tmax = max === undefined ? 1 : max;\n"
                "\t\t\tthis._seed = (seed * 9301 + 49297) % 233280;\n"
                "\t\t\treturn min + (this._seed / 233280) * (max - min);\n"
                "\t\t},\n"
                "\t\tnumbers: function(config) {\n"
                        "\t\t\tvar cfg = applyDefaultNumbers(config);\n"
                        "\t\t\tvar dfactor = Math.pow(10, cfg.decimals) || 0;\n"
                        "\t\t\tvar data = [];\n"
                        "\t\t\tvar i, value;\n"
                        "\t\t\tfor (i = 0; i < cfg.count; ++i) {\n"
                                "\t\t\t\tvalue = (cfg.from[i] || 0) + this.rand(cfg.min, cfg.max);\n"
                                "\t\t\t\tif (this.rand() <= cfg.continuity) {\n"
                                "\t\t\t\t\tdata.push(Math.round(dfactor * value) / dfactor);\n"
                                "\t\t\t\t} else {\n"
                                "\t\t\t\t\tdata.push(null);\n"
                                "\t\t\t\t}\n"
                        "\t\t\t}\n"
                        "\t\t\treturn data;\n"
                "\t\t},\n"
                "\t\tlabels: function(config) {\n"
                        "\t\t\tvar cfg = config || {};\n"
                        "\t\t\tvar min = cfg.min || 0;\n"
                        "\t\t\tvar max = cfg.max || 100;\n"
                        "\t\t\tvar count = cfg.count || 8;\n"
                        "\t\t\tvar step = (max - min) / count;\n"
                        "\t\t\tvar decimals = cfg.decimals || 8;\n"
                        "\t\t\tvar dfactor = Math.pow(10, decimals) || 0;\n"
                        "\t\t\tvar prefix = cfg.prefix || '';\n"
                        "\t\t\tvar values = [];\n"
                        "\t\t\tvar i;\n"
                        "\t\t\tfor (i = min; i < max; i += step) {\n"
                        "\t\t\t\tvalues.push(prefix + Math.round(dfactor * i) / dfactor);\n"
                        "\t\t\t}\n"
                        "\t\t\treturn values;\n"
                "\t\t},\n"
                "\t\tmonths: function(config) {\n"
                        "\t\t\tvar cfg = config || {};\n"
                        "\t\t\tvar count = cfg.count || 12;\n"
                        "\t\t\tvar section = cfg.section;\n"
                        "\t\t\tvar values = [];\n"
                        "\t\t\tvar i, value;\n"
                        "\t\t\tfor (i = 0; i < count; ++i) {\n"
                                "\t\t\t\tvalue = MONTHS[Math.ceil(i) % 12];\n"
                                "\t\t\t\tvalues.push(value.substring(0, section));\n"
                        "\t\t\t}\n"
                        "\t\t\treturn values;\n"
                "\t\t},\n"
                "\t\tcolor: function(index) {\n"
                        "\t\t\treturn COLORS[index % COLORS.length];\n"
                "\t\t},\n"
                "\t\ttransparentize: function(color, opacity) {\n"
                        "\t\t\tvar alpha = opacity === undefined ? 0.5 : 1 - opacity;\n"
                        "\t\t\treturn Color(color).alpha(alpha).rgbString();\n"
                "\t\t}\n"
        "\t};\n";
String LoadUtilsJS3     =
        "\t// DEPRECATED\n"
        "\twindow.randomScalingFactor = function() {\n"
                "\t\treturn Math.round(Samples.utils.rand(-100, 100));\n"
        "\t};\n"
        "\t// INITIALIZATION\n"
        "\tSamples.utils.srand(Date.now());\n"
        "\t// Google Analytics\n"
        "\t/* eslint-disable */\n"
        "\tif (document.location.hostname.match(/^(www\\.)?chartjs\\.org$/)) {\n"
                "\t\t(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){\n"
                "\t\t(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),\n"
                "\t\tm=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)\n"
                "\t\t})(window,document,'script','//www.google-analytics.com/analytics.js','ga');\n"
                "\t\tga('create', 'UA-28909194-3', 'auto');\n"
                "\t\tga('send', 'pageview');\n"
        "\t}\n"
        "\t/* eslint-enable */\n"
"}(this));\n";
//--------WebTopPage
String WebTopPage1      =
"<HTML>\n"
"<HEAD>\n"
        "\t<META HTTP-EQUIV=\"Content-Type\" CONTENT=\"text/html;CHARSET=UTF-8\">\n"
        "\t<TITLE>";
String WebTopPage11      =
        "</TITLE>\n"
"</HEAD>\n"
"<BODY BGCOLOR='#ffffff'>\n"
"<H2><CENTER><FONT COLOR='#00ff00'>";
String WebTopPage12      =
"</FONT></CENTER></H2>\n"
"<P><CENTER><TABLE WIDTH='300' BORDER='0' CELLSPACING='0' CELLPADDING='0'>\n"
        "\t<TR>\n"
                "\t\t<TD WIDTH='100%'>\n"
                "\t\t<OL>\n"
                "\t\t\t<LI><FONT SIZE='+1'><A HREF=\"http://";
String WebTopPage2       =
                "/setup1\">";
String WebTopPage21       =
                "</A>\n"
                "\t\t\t<LI><A HREF=\"http://";
String WebTopPage3       =
                "/setup2\">";
String WebTopPage31       =
                "</A></FONT>\n"
                "\t\t</OL>\n"
                "\t\t</TD>\n"
        "\t</TR>\n"
"</TABLE></CENTER>\n"
"</BODY>\n"
"</HTML>\n";
//-------------SetPage
String SetPage1         =
        "<!DOCTYPE HTML PUBLIC \"\">\n<html>\n<HEAD>\n\t<META http-equiv=\"Content-Type\" charset=Shift_JIS\">\n"
        "\t<META http-equiv=\"Content-Style-Type\">\n\t<TITLE>";
String SetPage2         =
        " Setup Page</TITLE>\n"
        "\t<SCRIPT>\n\t\tfunction HP(w1){parent.window.open(w1,'_top')}\n\t</SCRIPT>\n</HEAD>\n"
        "<BODY marginwidth=\"0\" marginheight=\"0\" leftmargin=\"0\" style=\"margin: 0; padding: 0;\">\n<BLOCKQUOTE><BLOCKQUOTE>\n"
        "<table bgcolor=\"#17A1A5\" border=\"0\" width=\"100%\" cellpadding=\"1\" style=\"font-family:Verdana;color:#ffffff;font-size:12px;\">\n"
        "<tr><td>";
String SetPage3         =
        " Setup Page</td></tr></table><br>\n"
        "<script>\n\tfunction hex2num (s_hex) {\n\t\teval(\"var n_num=0X\" + s_hex);\n\t\treturn n_num;\n\t}\n</script>\n"
        "<tbody>\n<FORM><input type=\"hidden\" name=\"SBM\" value=\"1\">\n<table>\n<tr><td>MAC:</td><td>"
        "<input id=\"T1\" type=\"text\" size=\"3\" maxlength=\"2\" name=\"DT1\" value=\"";
String SetPage4         =
        "\">.<input id=\"T3\" type=\"text\" size=\"3\" maxlength=\"2\" name=\"DT2\" value=\"";
String SetPage5         =
        "\">.<input id=\"T5\" type=\"text\" size=\"3\" maxlength=\"2\" name=\"DT3\" value=\"";
String SetPage6         =
        "\">.<input id=\"T7\" type=\"text\" size=\"3\" maxlength=\"2\" name=\"DT4\" value=\"";
String SetPage7         =
        "\">.<input id=\"T9\" type=\"text\" size=\"3\" maxlength=\"2\" name=\"DT5\" value=\"";
String SetPage8         =
        "\">.<input id=\"T11\" type=\"text\" size=\"3\" maxlength=\"2\" name=\"DT6\" value=\"";
String SetPage9         =
        "\"><input id=\"T2\" type=\"hidden\" name=\"DT1\"><input id=\"T4\" type=\"hidden\" name=\"DT2\n"
        "\"><input id=\"T6\" type=\"hidden\" name=\"DT3\"><input id=\"T8\" type=\"hidden\" name=\"DT4\n"
        "\"><input id=\"T10\" type=\"hidden\" name=\"DT5\"><input id=\"T12\" type=\"hidden\" name=\"DT6\n"
        "\"></td></tr>\n<tr><td>IP:</td><td><input type=\"text\" size=\"3\" maxlength=\"3\" name=\"DT7\" value=\"";
String SetPage10        =
       "\">.<input type=\"text\" size=\"3\" maxlength=\"3\" name=\"DT8\" value=\"";
String SetPage11        =
        "\">.<input type=\"text\" size=\"3\" maxlength=\"3\" name=\"DT9\" value=\"";
String SetPage12        =
        "\">.<input type=\"text\" size=\"3\" maxlength=\"3\" name=\"DT10\" value=\"";
String SetPage13        =
        "\"></td></tr>\n<tr><td>MASK: </td><td><input type= \"text\" size=\"3\" maxlength=\"3\" name=\"DT11\" value=\"";
String SetPage14        =
        "\">.<input type=\"text\" size=\"3\" maxlength=\"3\" name=\"DT12\" value=\"";
String SetPage15        =
        "\">.<input type=\"text\" size=\"3\" maxlength=\"3\" name=\"DT13\" value=\"";
String SetPage16        =
        "\">.<input type=\"text\" size=\"3\" maxlength=\"3\" name=\"DT14\" value=\"";
String SetPage17        =
        "\"></td></tr>\n<tr><td>GW: </td><td><input type=\"text\" size=\"3\" maxlength=\"3\" name=\"DT15\" value=\"";
String SetPage18        =
        "\">.<input type=\"text\" size=\"3\" maxlength=\"3\" name=\"DT16\" value=\"";
String SetPage19        =
        "\">.<input type=\"text\" size=\"3\" maxlength=\"3\" name=\"DT17\" value=\"";
String SetPage20        =
        "\">.<input type=\"text\" size=\"3\" maxlength=\"3\" name=\"DT18\" value=\"";
String SetPage21        =
        "\"></td></tr><tr><td COLSPAN='2'>\n<HR ALIGN=CENTER></td></tr>\n"
        "<tr><td>NTPip: </td><td><input type=\"text\" size=\"3\" maxlength=\"3\" name=\"DT19\" value=\"";
String SetPage22        =
        "\">.<input type=\"text\" size=\"3\" maxlength=\"3\" name=\"DT20\" value=\"";
String SetPage23        =
        "\">.<input type=\"text\" size=\"3\" maxlength=\"3\" name=\"DT21\" value=\"";
String SetPage24        =
        "\">.<input type=\"text\" size=\"3\" maxlength=\"3\" name=\"DT22\" value=\"";
String SetPage25        =
        "\"></td></tr>\n"
        "<tr><td>TimeZone:</td><td><input type=\"text\" size=\"3\" maxlength=\"5\" name=\"DT23\" value=\"";
String SetPage26        =
        "\"></td></tr>\n<tr><td><br></td></tr><tr><td COLSPAN='2'><P ALIGN=RIGHT>"
        "<INPUT TYPE=\"button\" VALUE=\"RETURN\" onClick=\"HP('http://";
String SetPage27        =      
        "/index')\"/><input id=\"button1\"type=\"submit\" value=\"SUBMIT\" "
        "Onclick=\""
        "document.getElementById('T2').value = hex2num(document.getElementById('T1').value);\n"
        "\t\tdocument.getElementById('T4').value = hex2num(document.getElementById('T3').value);\n"
        "\t\tdocument.getElementById('T6').value = hex2num(document.getElementById('T5').value);\n"
        "\t\tdocument.getElementById('T8').value = hex2num(document.getElementById('T7').value);\n"
        "\t\tdocument.getElementById('T10').value = hex2num(document.getElementById('T9').value);\n"
        "\t\tdocument.getElementById('T12').value = hex2num(document.getElementById('T11').value);\""
        "></td></tr></tbody>\n</table>\n</BLOCKQUOTE></BLOCKQUOTE>\n</form>\n</BODY>\n</html>\n";
//-------------UtilPage
String SetUtil1         =
        "<!DOCTYPE HTML PUBLIC \"\">\n<html>\n<HEAD>\n\t<META http-equiv=\"Content-Type\" charset=UTF-8\">\n"
        "\t<META http-equiv=\"Content-Style-Type\">\n\t<TITLE>Modbus ";
String SetUtil2         =
        " Utility Page</TITLE>\n"
        "\t<SCRIPT>\n\t\tfunction HP(w1){parent.window.open(w1,'_top')}\n\t</SCRIPT>\n</HEAD>\n"
        "<BODY marginwidth=\"0\" marginheight=\"0\" leftmargin=\"0\" style=\"margin: 0; padding: 0;\" BGCOLOR=\"#ffffff\">\n"
        "<FORM>\n<BLOCKQUOTE><BLOCKQUOTE>\n"
        "<P><table bgcolor=\"#17A1A5\" border=\"0\" width=\"100%\" cellpadding=\"1\" style=\"font-family:Verdana;color:#ffffff;font-size:12px;\" CELLSPACING=\"2\">\n"
        "<tr><td>Modbus ";
String SetUtil3         =
        " Utility Page</td></tr></table><br>\n"
         "<TABLE WIDTH=\"100%\" BORDER=\"0\" CELLSPACING=\"0\" CELLPADDING=\"0\" style=\"table-layout: fixed\">"
        "<TR>\n\t<TD WIDTH=\"25%\">&nbsp;</TD><TD WIDTH=\"20%\">&nbsp;</TD><TD WIDTH=\"55%\"><P ALIGN=CENTER><INPUT TYPE=\"button\" VALUE=\"RETURN\" onClick=\"HP('http://";
String SetUtil4         =       
        "/setup9')\"/></TD>\n" 
        "<TR>\n\t<TD WIDTH=\"25%\">FirmwareVirsion:</TD>\n\t<TD WIDTH=\"11%\"></TD>\n\t<TD WIDTH=\"64%\">";
String SetUtil5         = 
        "</TD>\n</TR>"
        "<TR>\n\t<TD WIDTH=\"25%\">SourceVirsion:</TD>\n\t<TD WIDTH=\"11%\"></TD>\n\t<TD WIDTH=\"64%\">";
String SetUtil6         = 
        "</TD>\n</TR>"        
        "<TR>\n\t<TD WIDTH=\"25%\">MacAddress:</TD>\n\t<TD WIDTH=\"11%\"></TD>\n\t<TD WIDTH=\"64%\">";
String SetUtil7         =
        "</TD>\n</TR>"
        "<TR>\n\t<TD WIDTH=\"25%\">IPAddress:</TD>\n\t<TD WIDTH=\"11%\"></TD>\n\t<TD WIDTH=\"64%\">";
String SetUtil8         =
        "</TD>\n</TR>"
        "<TR>\n\t<TD WIDTH=\"25%\">SubnetMask:</TD>\n\t<TD WIDTH=\"11%\"></TD>\n\t<TD WIDTH=\"64%\">";
String SetUtil9         =
        "</TD>\n</TR>"
        "<TR>\n\t<TD WIDTH=\"25%\">Data acquisition time:</TD>\n\t<TD WIDTH=\"11%\"></TD>\n\t<TD WIDTH=\"64%\">";
String SetUtil10         =
        "</TD>\n</TR>"
        "<TR>\n\t<TD WIDTH=\"25%\">Operating time:</TD>\n\t<TD WIDTH=\"11%\"></TD>\n\t<TD WIDTH=\"64%\">";
String SetUtil11         =
        "</TD>\n</TR></TABLE></P>\n"
        "<P><HR ALIGN=LEFT></P>\n<P><TABLE WIDTH=\"100%\" BORDER=\"0\" CELLSPACING=\"0\" CELLPADDING=\"0\" HEIGHT=\"800\" style=\"table-layout: fixed\">"
        "\t<tbody>\n\t<TR>\n\t\t<TD ROWSPAN=\"2\"  WIDTH=\"25%\">DI(02)InputStatus</TD>\n"
        "\t\t<TD COLSPAN=\"2\" HEIGHT=\"23\"></TD>\n"
        "\t\t<TD WIDTH=\"4%\" HEIGHT=\"23\" NOWRAP ALIGN=\"CENTER\">";
String SetUtil12         =
        "\t<TR>\n\t\t<TD WIDTH=\"4%\" HEIGHT=\"23\"><P ALIGN=RIGHT>0</TD>\n\t\t<TD WIDTH=\"7%\" HEIGHT=\"23\"></TD>\n"
        "\t\t<TD WIDTH=\"4%\" HEIGHT=\"23\" NOWRAP ALIGN=\"CENTER\">\n";
String SetUtil13         =
        "\t\t<INPUT TYPE=\"checkbox\" NAME=\"DI_15\" disabled=\"disabled\" ";
String SetUtil14         =
        "></TD>\n\t</TR>\n\t<TR>\n\t\t<TD COLSPAN=\"19\" HEIGHT=\"5\"><HR ALIGN=LEFT></TD>\n\t</TR>\n"
        "\t<TR>\n\t\t<TD ROWSPAN=\"3\"  WIDTH=\"25%\">Coil(01)CoilStatus</TD>\n"
        "\t\t<TD COLSPAN=\"2\" HEIGHT=\"23\"></TD>\n\t\t<TD WIDTH=\"4%\" HEIGHT=\"23\" NOWRAP ALIGN=\"CENTER\">";
String SetUtil15         =
        "\t\t<TD ROWSPAN=\"2\"  WIDTH=\"4%\"><P ALIGN=RIGHT>0</TD>\n"
        "\t\t<TD WIDTH=\"7%\" HEIGHT=\"23\"><P ALIGN=RIGHT>T</TD>\n\t\t<TD WIDTH=\"4%\" HEIGHT=\"23\" NOWRAP ALIGN=\"CENTER\">\n";
String SetUtil16         =
        "\t</TR>\n\t<TR>\n\t\t<TD WIDTH=\"7%\" HEIGHT=\"23\" ALIGN=\"RIGHT\">F</TD>\n"
        "\t\t<TD WIDTH=\"4%\" HEIGHT=\"23\" NOWRAP ALIGN=\"CENTER\">\n";
String SetUtil17         =
        "\t</TR>\n\t<TR>\n\t\t<TD COLSPAN=\"2\" HEIGHT=\"23\"></TD><TD COLSPAN=\"16\" HEIGHT=\"23\"></TD>\n"
        "\t</TR>\n\t<TR>\n\t\t<TD COLSPAN=\"19\" HEIGHT=\"30\"><HR ALIGN=LEFT></TD>\n\t</TR>\n"
        "\t<TR>\n\t\t<TD ROWSPAN=\"12\"  WIDTH=\"25%\">IR(04)InputRegistor</TD>\n";
String SetUtil18         =
        "\t\t<TD COLSPAN=\"2\" ALIGN=\"CENTER\">\n\t\tVstr</TD><TD COLSPAN=\"16\">";
String SetUtil19         =
        "</TD>\n\t\t</TR>\n\t\t<TR>\n"
        "\t\t<TD COLSPAN=\"2\" ALIGN=\"CENTER\">\n\t\tPstr</TD><TD COLSPAN=\"16\">";
String SetUtil20        =
        "</TD>\n\t\t</TR>\n\t\t<TR>\n"
        "\t\t<TD COLSPAN=\"2\" ALIGN=\"CENTER\">\n\t\tSver</TD><TD WIDTH=\"4%\">";
String SetUtil21        =
        "</TD>\n\t</TR></tbody>\n</TABLE>\n"
        "</BLOCKQUOTE></BLOCKQUOTE>\n</form>\n</BODY></html>";
//-----------------------
void paramParse(String reqStr){
        int Code;
        int len         = reqStr.length();
        char* HTTP_c    = (char*)malloc(len+1);
        reqStr.toCharArray(HTTP_c, len);
        char bufff[10];
        //DT1 mac[0]            HEX
        int indexS      = reqStr.indexOf("DT1=");
        int indexE      = reqStr.indexOf("&",indexS);
        int k = 0;
        char c;
        for(int j = indexS;j<=indexE;j++){
                c = reqStr.charAt(j);
                if (c == '='){
                        int m = indexE - indexS;
                        for (int l = 1; l < m;l++){
                                c = reqStr.charAt(j+l);
                                if(c == '&'){
                                        bufff[k]='\0';                          //Serial.println(bufff);
                                        mac[0]=int(strtol(bufff, NULL, 16));    //Serial.print(bufff);Serial.println(mac[0]);
                                        break; 
                                }else{
                                        bufff[k++] = c;
                                }
                        }
                        break;
                }
        }
        //DT2 mac[1]            HEX
        indexS = reqStr.indexOf("DT2=");
        indexE = reqStr.indexOf("&",indexS);
        k = 0;
        for(int j = indexS;j<=indexE;j++){
                c = reqStr.charAt(j);
                 if (c == '='){
                        int m = indexE - indexS;
                        for (int l = 1; l < m;l++){
                                c = reqStr.charAt(j+l);
                                if(c == '&'){
                                        bufff[k]='\0';                          //Serial.println(bufff);
                                        mac[1]=int(strtol(bufff, NULL, 16));    //Serial.print(bufff);Serial.println(mac[1]);
                                        break; 
                                }else{
                                        bufff[k++] = c;
                                }
                        }
                        break;
                }        
        }
        //DT3 mac[2]            HEX
        indexS = reqStr.indexOf("DT3=");                                        //Serial.print("Pos=");Serial.println(indexS);
        indexE = reqStr.indexOf("&",indexS);                                    //Serial.print("Pos=");Serial.println(indexE);
        k = 0;
        for(int j = indexS;j<=indexE;j++){
                c = reqStr.charAt(j);                                           //Serial.println(c);
                if (c == '='){
                        int m = indexE - indexS;                                //Serial.print("m=");Serial.println(m);
                        for (int l = 1; l < m;l++){
                                c = reqStr.charAt(j+l);
                                if(c == '&'){
                                        bufff[k]='\0';                          //Serial.println(bufff);
                                        mac[2]=int(strtol(bufff, NULL, 16));    //Serial.print(bufff);Serial.println(mac[2]);
                                        break; 
                                }else{
                                        bufff[k++] = c;
                                }
                        }
                        break;
                 }
        }
        //DT4 mac[3]            HEX
        indexS = reqStr.indexOf("DT4=");
        indexE = reqStr.indexOf("&",indexS);
        k = 0;
        for(int j = indexS;j<=indexE;j++){
                c = reqStr.charAt(j);
                 if (c == '='){
                        int m = indexE - indexS;
                        for (int l = 1; l < m;l++){
                                c = reqStr.charAt(j+l);
                                if(c == '&'){
                                        bufff[k]='\0';                          //Serial.println(bufff);
                                        mac[3]=int(strtol(bufff, NULL, 16));    //Serial.print(bufff);Serial.println(mac[3]);
                                        break; 
                                }else{
                                        bufff[k++] = c;
                                }
                        }
                        break;
                }
        }
        //DT5 mac[4]            HEX
        indexS = reqStr.indexOf("DT5=");
        indexE = reqStr.indexOf("&",indexS);
        k = 0;
        for(int j = indexS;j<=indexE;j++){
                c = reqStr.charAt(j);
                if (c == '='){
                        int m = indexE - indexS;
                        for (int l = 1; l < m;l++){
                                c = reqStr.charAt(j+l);
                                if(c == '&'){
                                        bufff[k]='\0';                          //Serial.println(bufff);
                                        mac[4]=int(strtol(bufff, NULL, 16));    //Serial.print(bufff);Serial.println(mac[4]);
                                        break; 
                                }else{
                                        bufff[k++] = c;
                                }
                        }
                        break;
                }        
        }
        //DT6 mac[5]            HEX
        indexS = reqStr.indexOf("DT6=");
        indexE = reqStr.indexOf("&",indexS);
        k = 0;
        for(int j = indexS;j<=indexE;j++){
                c = reqStr.charAt(j);
                if (c == '='){
                        int m = indexE - indexS;
                        for (int l = 1; l < m;l++){
                                c = reqStr.charAt(j+l);
                                if(c == '&'){
                                        bufff[k]='\0';                          //Serial.println(bufff);
                                        mac[5]=int(strtol(bufff, NULL, 16));    //Serial.print(bufff);Serial.println(mac[5]);
                                        break; 
                                }else{
                                        bufff[k++] = c;
                                }
                        }
                        break;
                }        
        }
        //DT7 ip[0]             DEC
        indexS = reqStr.indexOf("DT7=");
        indexE = reqStr.indexOf("&",indexS);
        k = 0;
        for(int j = indexS;j<=indexE;j++){
                c = reqStr.charAt(j);
                if (c == '='){
                        int m = indexE - indexS;
                        for (int l = 1; l < m;l++){
                                c = reqStr.charAt(j+l);
                                if(c == '&'){
                                        bufff[k]='\0';                          //Serial.println(bufff);
                                        ip[0]=int(strtol(bufff, NULL, 10));     //Serial.print(bufff);Serial.println(ip[0]);
                                        break; 
                                }else{
                                        bufff[k++] = c;
                                }
                        }
                        break;
                }
        }
        //DT8 ip[1]             DEC
        indexS = reqStr.indexOf("DT8=");
        indexE = reqStr.indexOf("&",indexS);
        k = 0;
        for(int j = indexS;j<=indexE;j++){
                c = reqStr.charAt(j);
                if (c == '='){
                        int m = indexE - indexS;
                        for (int l = 1; l < m;l++){
                                c = reqStr.charAt(j+l);
                                if(c == '&'){
                                        bufff[k]='\0';                          //Serial.println(bufff);
                                        ip[1]=int(strtol(bufff, NULL, 10));     //Serial.print(bufff);Serial.println(ip[1]);
                                        break; 
                                }else{
                                        bufff[k++] = c;
                                }
                        }
                        break;
                }
        }
        //DT9 ip[2]             DEC
        indexS = reqStr.indexOf("DT9=");
        indexE = reqStr.indexOf("&",indexS);
        k = 0;
        for(int j = indexS;j<=indexE;j++){
                c = reqStr.charAt(j);
                if (c == '='){
                        int m = indexE - indexS;
                        for (int l = 1; l < m;l++){
                                c = reqStr.charAt(j+l);
                                if(c == '&'){
                                        bufff[k]='\0';                          //Serial.println(bufff);
                                        ip[2]=int(strtol(bufff, NULL, 10));     //Serial.print(bufff);Serial.println(ip[2]);
                                        break; 
                                }else{
                                        bufff[k++] = c;
                                }
                        }
                        break;
                }        
        }
        //DT10 ip[3]            DEC
        indexS = reqStr.indexOf("DT10=");
        indexE = reqStr.indexOf("&",indexS);
        k = 0;
        for(int j = indexS;j<=indexE;j++){
                c = reqStr.charAt(j);
                if (c == '='){
                        int m = indexE - indexS;
                        for (int l = 1; l < m;l++){
                                c = reqStr.charAt(j+l);
                                if(c == '&'){
                                        bufff[k]='\0';                          //Serial.println(bufff);
                                        ip[3]=int(strtol(bufff, NULL, 10));     //Serial.print(bufff);Serial.println(ip[3]);
                                        break; 
                                }else{
                                        bufff[k++] = c;
                                }
                        }
                        break;
                }
        }
        //DT11 subnet[0]        DEC
        indexS = reqStr.indexOf("DT11=");
        indexE = reqStr.indexOf("&",indexS);
        k = 0;
        for(int j = indexS;j<=indexE;j++){
                c = reqStr.charAt(j);
                 if (c == '='){
                        int m = indexE - indexS;
                        for (int l = 1; l < m;l++){
                                c = reqStr.charAt(j+l);
                                if(c == '&'){
                                        bufff[k]='\0';                          //Serial.println(bufff);
                                        subnet[0]=int(strtol(bufff, NULL, 10)); //Serial.print(bufff);Serial.println(subnet[0]);
                                        break; 
                                }else{
                                        bufff[k++] = c;
                                }
                        }
                        break;
                }        
        }
        //DT12 subnet[1]        DEC
        indexS = reqStr.indexOf("DT12=");
        indexE = reqStr.indexOf("&",indexS);
        k = 0;
        for(int j = indexS;j<=indexE;j++){
                c = reqStr.charAt(j);
                if (c == '='){
                        int m = indexE - indexS;
                        for (int l = 1; l < m;l++){
                                c = reqStr.charAt(j+l);
                                if(c == '&'){
                                        bufff[k]='\0';                          //Serial.println(bufff);
                                        subnet[1]=int(strtol(bufff, NULL, 10)); //Serial.print(bufff);Serial.println(subnet[1]);
                                        break; 
                                }else{
                                        bufff[k++] = c;
                                }
                        }
                        break;
                }        
        }
        //DT13 subnet[2]        DEC
        indexS = reqStr.indexOf("DT13=");
        indexE = reqStr.indexOf("&",indexS);
        k = 0;
        for(int j = indexS;j<=indexE;j++){
                c = reqStr.charAt(j); //Serial.println(bufff);
                if (c == '='){
                        int m = indexE - indexS;
                        for (int l = 1; l < m;l++){
                                c = reqStr.charAt(j+l);
                                if(c == '&'){
                                        bufff[k]='\0';                          //Serial.println(bufff);
                                        subnet[2]=int(strtol(bufff, NULL, 10)); //Serial.print(bufff);Serial.println(subnet[2]);
                                        break; 
                                }else{
                                        bufff[k++] = c;
                                }
                        }
                        break;
                }        
        }
        //DT14 subnet[3]        DEC
        indexS = reqStr.indexOf("DT14=");
        indexE = reqStr.indexOf("&",indexS);
        k = 0;
        for(int j = indexS;j<=indexE;j++){
                c = HTTP_req.charAt(j);
                 if (c == '='){
                        int m = indexE - indexS;
                        for (int l = 1; l < m;l++){
                                c = reqStr.charAt(j+l);
                                if(c == '&'){
                                        bufff[k]='\0';                          //Serial.println(bufff);
                                        subnet[3]=int(strtol(bufff, NULL, 10)); //Serial.print(bufff);Serial.println(subnet[3]);
                                        break; 
                                }else{
                                        bufff[k++] = c;
                                }
                        }
                        break;
                }        
        }
        //DT15 gateway[0]       DEC
        indexS = reqStr.indexOf("DT15=");
        indexE = reqStr.indexOf("&",indexS);
        k = 0;
        for(int j = indexS;j<=indexE;j++){
                c = reqStr.charAt(j);
                if (c == '='){
                        int m = indexE - indexS;
                        for (int l = 1; l < m;l++){
                                c = reqStr.charAt(j+l);
                                if(c == '&'){
                                        bufff[k]='\0';                          //Serial.println(bufff);
                                        gateway[0]=int(strtol(bufff, NULL, 10));//Serial.print(bufff);Serial.println(gateway[0]);
                                        break; 
                                }else{
                                        bufff[k++] = c;
                                }
                        }
                        break;
                }        
        }
        //DT16 gateway[1]       DEC
        indexS = reqStr.indexOf("DT16=");
        indexE = reqStr.indexOf("&",indexS);
        k = 0;
        for(int j = indexS;j<=indexE;j++){
                c = reqStr.charAt(j);
                if (c == '='){
                        int m = indexE - indexS;
                        for (int l = 1; l < m;l++){
                                c = reqStr.charAt(j+l);
                                if(c == '&'){
                                        bufff[k]='\0';                          //Serial.println(bufff);
                                        gateway[1]=int(strtol(bufff, NULL, 10));//Serial.print(bufff);Serial.println(gateway[1]);
                                        break; 
                                }else{
                                        bufff[k++] = c;
                                }
                        }
                        break;
                }        
        }
        //DT17 gateway[2]       DEC
        indexS = reqStr.indexOf("DT17=");
        indexE = reqStr.indexOf("&",indexS);
        k = 0;
        for(int j = indexS;j<=indexE;j++){
                c = reqStr.charAt(j);
                if (c == '='){
                        int m = indexE - indexS;
                        for (int l = 1; l < m;l++){
                                c = reqStr.charAt(j+l);
                                if(c == '&'){
                                        bufff[k]='\0';                          //Serial.println(bufff);
                                        gateway[2]=int(strtol(bufff, NULL, 10));//Serial.print(bufff);Serial.println(gateway[2]);
                                        break; 
                                }else{
                                        bufff[k++] = c;
                                }
                        }
                        break;
                }        
        }
        //DT18 gateway[3]       DEC
        indexS = reqStr.indexOf("DT18=");
        indexE = reqStr.indexOf("&",indexS);
        k = 0;
        for(int j = indexS;j<=indexE;j++){
                c = reqStr.charAt(j);
                if (c == '='){
                        int m = indexE - indexS;
                        for (int l = 1; l < m;l++){
                                c = reqStr.charAt(j+l);
                                if(c == '&'){
                                        bufff[k]='\0';                          //Serial.println(bufff);
                                        gateway[3]=int(strtol(bufff, NULL, 10));//Serial.print(bufff);Serial.println(gateway[3]);
                                        break; 
                                }else{
                                        bufff[k++] = c;
                                }
                        }
                        break;
                }
        }
        //DT19 NTPip[0]       DEC
        indexS = reqStr.indexOf("DT19=");
        indexE = reqStr.indexOf("&",indexS);
        k = 0;
        for(int j = indexS;j<=indexE;j++){
                c = reqStr.charAt(j);
                if (c == '='){
                        int m = indexE - indexS;
                        for (int l = 1; l < m;l++){
                                c = reqStr.charAt(j+l);
                                if(c == '&'){
                                        bufff[k]='\0';                          //Serial.println(bufff);
                                        NTPip[0]=int(strtol(bufff, NULL, 10));  //Serial.print(bufff);Serial.println(NTPip[0]);
                                        break; 
                                }else{
                                        bufff[k++] = c;
                                }
                        }
                        break;
                }        
        }
        //DT120 NTPip[1]       DEC
        indexS = reqStr.indexOf("DT20=");
        indexE = reqStr.indexOf("&",indexS);
        k = 0;
        for(int j = indexS;j<=indexE;j++){
                c = reqStr.charAt(j);
                if (c == '='){
                        int m = indexE - indexS;
                        for (int l = 1; l < m;l++){
                                c = reqStr.charAt(j+l);
                                if(c == '&'){
                                        bufff[k]='\0';                          //Serial.println(bufff);
                                        NTPip[1]=int(strtol(bufff, NULL, 10));  //Serial.print(bufff);Serial.println(NTPip[1]);
                                        break; 
                                }else{
                                        bufff[k++] = c;
                                }
                        }
                        break;
                }        
        }
        //DT21 NTPip[2]       DEC
        indexS = reqStr.indexOf("DT21=");
        indexE = reqStr.indexOf("&",indexS);
        k = 0;
        for(int j = indexS;j<=indexE;j++){
                c = reqStr.charAt(j);
                if (c == '='){
                        int m = indexE - indexS;
                        for (int l = 1; l < m;l++){
                                c = reqStr.charAt(j+l);
                                if(c == '&'){
                                        bufff[k]='\0';                          //Serial.println(bufff);
                                        NTPip[2]=int(strtol(bufff, NULL, 10));  //Serial.print(bufff);Serial.println(NTPip[2]);
                                        break; 
                                }else{
                                        bufff[k++] = c;
                                }
                        }
                        break;
                }        
        }
        //DT22 NTPip[3]       DEC
        indexS = reqStr.indexOf("DT22=");
        indexE = reqStr.indexOf("&",indexS);
        k = 0;
        for(int j = indexS;j<=indexE;j++){
                c = reqStr.charAt(j);
                if (c == '='){
                        int m = indexE - indexS;
                        for (int l = 1; l < m;l++){
                                c = reqStr.charAt(j+l);
                                if(c == '&'){
                                        bufff[k]='\0';                          //Serial.println(bufff);
                                        NTPip[3]=int(strtol(bufff, NULL, 10));  //Serial.print(bufff);Serial.println(NTPip[3]);
                                        break; 
                                }else{
                                        bufff[k++] = c;
                                }
                        }
                        break;
                }
        }
        //DT23 timezone       float
        indexS = reqStr.indexOf("DT23=");
        indexE = reqStr.indexOf("&",indexS);
        k = 0;
        for(int j = indexS;j<=indexE;j++){
                c = reqStr.charAt(j);
                if (c == '='){
                        int m = indexE - indexS;
                        for (int l = 1; l < m;l++){
                                c = reqStr.charAt(j+l);
                                if(c == '&'){
                                        bufff[k]='\0';                          //Serial.println(bufff);
                                        timezone =int((strtof(bufff, NULL)*10));
                                        break; 
                                }else{
                                        bufff[k++] = c;
                                }
                        }
                        break;
                }
        }
}
//------------------PrintSetUpPage
void PrintSetUpPage( EthernetClient client){
        client.println(OK_200);
        client.print(SetPage1);
        client.print(vers);
        client.print(SetPage2);
        client.print(vers);
        client.print(SetPage3);
        client.print(mac[0],HEX);
        client.print(SetPage4);
        client.print(mac[1],HEX);
        client.print(SetPage5);
        client.print(mac[2],HEX);
        client.print(SetPage6);
        client.print(mac[3],HEX);
        client.print(SetPage7);
        client.print(mac[4],HEX);
        client.print(SetPage8);
        client.print(mac[5],HEX);
        client.print(SetPage9);
        client.print(ip[0],DEC);
        client.print(SetPage10);
        client.print(ip[1],DEC);
        client.print(SetPage11);
        client.print(ip[2],DEC);
        client.print(SetPage12);
        client.print(ip[3],DEC);
        client.print(SetPage13);
        client.print(subnet[0],DEC);
        client.print(SetPage14);
        client.print(subnet[1],DEC);
        client.print(SetPage15);
        client.print(subnet[2],DEC);
        client.print(SetPage16);
        client.print(subnet[3],DEC);
        client.print(SetPage17);
        client.print(gateway[0],DEC);
        client.print(SetPage18);
        client.print(gateway[1],DEC);
        client.print(SetPage19);
        client.print(gateway[2],DEC);
        client.print(SetPage20);
        client.print(gateway[3],DEC);
        client.print(SetPage21);
        client.print(NTPip[0],DEC);
        client.print(SetPage22);
        client.print(NTPip[1],DEC);
        client.print(SetPage23);
        client.print(NTPip[2],DEC);
        client.print(SetPage24);
        client.print(NTPip[3],DEC);
        client.print(SetPage25);
        client.print(timezone,2);
        client.print(SetPage26);
        client.print(IPAddressStr);
        client.print(SetPage27);
}
//-------------PrintUtilPage
void PrintUtilPage( EthernetClient client){
        //sprintf(DateTimeStr,"%04u/%02u/%02u %02u:%02u:%02u",year(), month(), day()-1,hour(), minute(), second()); 
        sprintf(DateTimeStr,"%04u/%02u/%02u %02u:%02u:%02u",year(), month(), day(),hour(), minute(), second()); 
        client.println(OK_200);
        client.print(SetUtil1);
        client.print(titleStr);
        client.print(SetUtil2);
        client.print(titleStr);
        client.print(SetUtil3);
        client.print(IPAddressStr);
        client.print(SetUtil4);
        client.print(FVirsion);
        client.print(SetUtil5);
        client.print(SVirsion);
        client.print(SetUtil6);
        client.print(MacAddressStr);
        client.print(SetUtil7);
        client.print(IPAddressStr);
        client.print(SetUtil8);
        client.print(SubnetMaskStr);
        client.print(SetUtil9);
        client.print(DateTimeStr);
        client.print(SetUtil10);
        sprintf(buffer,"%d[sec]",int(OperatingTime / 2));
        client.print(buffer);
        client.print(SetUtil11);
        for (int i = 0;i<15;i++){
                sprintf(buffer,"%x</TD>\n\t\t<TD WIDTH=\"4%%\" HEIGHT=\"23\" ALIGN=\"CENTER\">",i);
                client.print(buffer);
        }
        sprintf(buffer,"%x</TD>\n\t</TR>\n",15);
        client.print(buffer);
        client.print(SetUtil12);
        for (int i = 0;i<15;i++){
                sprintf(buffer,"\t\t<INPUT TYPE=\"checkbox\" NAME=\"DI_%d\" disabled=\"disabled\" ",i);
                client.print(buffer);
                if(Mb.GetBit(MB_FC_READ_DISCRETE_INPUT,i) == true) client.print(F("CHECKED"));
                client.print(F("></TD>\n\t\t<TD WIDTH=\"4%\" HEIGHT=\"23\" ALIGN=\"CENTER\">\n"));
        }
        client.print(SetUtil13);
        if(Mb.GetBit(MB_FC_READ_DISCRETE_INPUT,15) == true) client.print(F("CHECKED"));
        client.print(SetUtil14);
        for (int i = 0;i<15;i++){
                sprintf(buffer,"%x</TD>\n\t\t<TD WIDTH=\"4%%\" HEIGHT=\"23\" ALIGN=\"CENTER\">",i);
                client.print(buffer);
        }
        sprintf(buffer,"%x</TD>\n\t</TR>\n",15);
        client.print(buffer);
        client.print(SetUtil15);
        for (int i = 0;i<16;i++){
                sprintf(buffer,"\t\t<INPUT TYPE=\"radio\"  NAME=\"CL_%02x\" disabled=\"disabled\" ",i);
                client.print(buffer);
                if(Mb.GetBit(MB_FC_READ_COILS,i) == true) client.print(F("CHECKED"));
                client.print(F("></TD>\n"));
                if(i<15) client.print(F("\t\t<TD WIDTH=\"4%\" HEIGHT=\"23\" ALIGN=\"CENTER\">\n"));
        }
        client.print(SetUtil16);
        for (int i = 0;i<16;i++){
                sprintf(buffer,"\t\t<INPUT TYPE=\"radio\"  NAME=\"CL_%02x\" disabled=\"disabled\" ",i);
                client.print(buffer);
                if(Mb.GetBit(MB_FC_READ_COILS,i) == false) client.print(F("CHECKED"));
                client.print(F("></TD>\n"));
                if(i<15) client.print(F("\t\t<TD WIDTH=\"4%\" HEIGHT=\"23\" ALIGN=\"CENTER\">\n"));
        }
        client.print(SetUtil17);
        for (int i = 0;i<8;i++){
                sprintf(buffer,"\t\t<TD WIDTH=\"4%%\" ALIGN=\"RIGHT\"  HEIGHT=\"23\">%d</TD>\n\t\t<TD WIDTH=\"4%%\" HEIGHT=\"23\"></TD><TD COLSPAN=\"16\" HEIGHT=\"23\">%d</TD>\n\t</TR>\n\t<TR>\n",i,Mb.MbInputData[i]);
                client.print(buffer);
        }
        client.print(SetUtil18);
        for (int i = 0;i<16;i++){
                word q  = Mb.MbInputData[i+16];
                client.print(char(q/256));
                client.print(char(q%256));
        }
        client.print(SetUtil19);
        for (int i = 0;i<16;i++){
                word q  = Mb.MbInputData[i+32];
                client.print(char(q/256));
                client.print(char(q%256));
        }
        client.print(SetUtil20);
        char    mainVer,minorVer,Rev;
        //sscanf(SVirsion,"%1s.%1s.%1s",&mainVer,&minorVer,&Rev);
        client.print(char(Mb.MbInputData[48] / 256));
        client.print(F("</TD><TD WIDTH=\"4%\">.</TD><TD WIDTH=\"4%\">"));
        client.print(char(Mb.MbInputData[48] % 256));
        client.print(F("</TD><TD WIDTH=\"4%\">.</TD><TD WIDTH=\"4%\">"));
        client.print(char(Mb.MbInputData[49] / 256));
        client.print(F("</TD><TD COLSPAN=\"11\"></TD>\n\t\t</TR>\n\t\t<TR>\n"));
        client.print(F("\t\t<TD COLSPAN=\"2\" ALIGN=\"CENTER\">\n\t\tFver</TD><TD WIDTH=\"4%\">"));
        //sscanf(FVirsion,"%1s.%1s.%1s",&mainVer,&minorVer,&Rev);
        client.print(char(Mb.MbInputData[50] / 256));
        client.print(F("</TD><TD WIDTH=\"4%\">.</TD><TD WIDTH=\"4%\">"));
        client.print(char(Mb.MbInputData[50] % 256));
        client.print(F("</TD><TD WIDTH=\"4%\">.</TD><TD WIDTH=\"4%\">"));
        client.print(char(Mb.MbInputData[51] / 256));
        client.print(F("</TD><TD COLSPAN=\"11\"></TD>\n\t\t</TR>\n\t\t<TR>\n"));
        client.print(F("\t\t<TD COLSPAN=\"19\" HEIGHT=\"23\"><HR ALIGN=LEFT></TD>\n\t</TR>\n"));
        //HR
        client.print(F("\t<TR>\n\t<TD ROWSPAN=\"16\"  WIDTH=\"25%\">HR(03)HolingRegistor</TD>\n"));
        for (int i = 0;i<16;i++){
                sprintf(buffer,"\t\t<TD WIDTH=\"4%%\" ALIGN=\"RIGHT\"  HEIGHT=\"23\">%d</TD>\n\t\t<TD WIDTH=\"4%%\" HEIGHT=\"23\"></TD>\n\t\t<TD COLSPAN=\"16\" HEIGHT=\"23\">%d",i,Mb.MbHoldData[i]);
                client.print(buffer);
                if(i<15) client.print(F("</TD>\n\t</TR>\n\t<TR>\n"));
        }
        client.print(SetUtil21);
}
//-------------WebPageTop
void pageIntroduction( EthernetClient client){
        client.println(OK_200);
        client.print(WebTopPage1);
        client.print(webTitle);
        client.print(WebTopPage11);
        client.print(webTitle);
        client.print(WebTopPage12);
        client.print(IPAddressStr);
        client.print(WebTopPage2);
        client.print(topMenu1);
        client.print(WebTopPage21);
        client.print(IPAddressStr);
        client.println(WebTopPage3);
        client.print(topMenu2);
        client.println(WebTopPage31);
        client.stop();
}
//-------------LoadUtilsJS
void pageLoadUtilsJS( EthernetClient client){
        client.println(OK_200);
        client.println(LoadUtilsJS1);                                     //バッファサイズを考慮して3分割しています。
        client.println(LoadUtilsJS2);
        client.println(LoadUtilsJS3);
        client.stop();
}
//------------PrintResponse404
void PrintResponse404( EthernetClient client ){
        client.println(NG_404);
        client.stop();
}
//--------------------------
void checkWebPage( EthernetClient client){
        if(Serial)      Serial.println(F("new webClient"));
        boolean restart = false;
        if (client) {
                // HTTPリクエスト空行(\r\n\r\n)で終わる。ので、空行を探す。
                boolean currentLineIsBlank = true;
                while (client.connected()) {
                        if( !client.available() ){
                                if(Serial)    Serial.print(F("."));
                                continue;
                        }
                        char c = client.read();
                        HTTP_req += c;
                        if(Serial)    Serial.print(c);
                        if( c == '\n'  && currentLineIsBlank ){                 // 空行発見。HTTPリクエスト終了。レスポンスを返す。
                                //Serial.print("RecieveLength=");Serial.println(HTTP_req.length());
                                if (HTTP_req.indexOf("/?")>0) {                 //setup?と/?を区別する必要がある
                                        if(Serial)    Serial.print(F("CASE1:"));Serial.print(gLoopCount);
                                        /*if(gLoopCount % 2){
                                                digitalWrite(LED_BUILTIN,HIGH); //Serial.println(F(" H"));
                                        }else{
                                                digitalWrite(LED_BUILTIN,LOW);  //Serial.println(F(" L"));
                                        }*/
                                        delay(100);
                                        HTTP_req = "";
                                        break;
                                }else if(HTTP_req.indexOf("setup")>0){
                                        //setup頁の場合 SBMが含まれていればEEPROMに書込、そうで無ければ表示のみ
                                        //setup?SBM=1&DT1=0&DT2=8&DT3=DC&DT4=54&DT5=4D&DT6=D0&DT1=0&DT2=8&DT3=220&DT4=84&DT5=77&DT6=208&DT7=192&DT8=168&DT9=0&DT10=200&DT11=255&DT12=255&DT13=255&DT14=0&DT15=192&DT16=168&DT17=0&DT18=1
                                        //TextFinderを使うとHTMLの切り出しは比較的容易これをSTRING型のままどう扱うか?
                                        if(Serial)    Serial.print(F("CASE2:"));Serial.println(gLoopCount);
                                        if (HTTP_req.indexOf("SBM")>0){
                                                //Serial.println(F("Found_SBM"));
                                                if(Serial)    Serial.print(F("CASE3:"));Serial.println(gLoopCount);
                                                paramParse(HTTP_req);
                                                // すべてのデータを取得したので、EEPROMに保存します。
                                                EEPROM.begin(512);
                                                WriteID();
                                                //データがEEPROMに書き込まれている場合は、arduinoをリセットする必要があります。
                                                //読込直す
                                                //int idcheck = EEPROM.read(256);
                                                int idcheck     =       EEPROM[256];
                                                if(Serial)    Serial.print(F("R_ID="));Serial.println(idcheck,HEX);
                                                if (idcheck == ID){
                                                        //idがIDと同じ値の場合、
                                                        //これは、このスケッチがシールドを設定するために使用されたことを意味します。
                                                        //EERPOMの値を読み取ってシールドを設定します。
                                                        ReadID();
                                                }
                                                EEPROM.end();
                                                //ハードウェアリセットボタンを使用する必要があります
                                                HTTP_req = "";
                                                client.stop();
                                                delay(1000);
                                                watchdog_reboot(0,0,1);
                                                break;
                                        }else if(HTTP_req.indexOf("setup1")>0){
                                                if(Serial)      Serial.println(F("setup1"));
                                                PrintSetUpPage(client);
                                                HTTP_req = "";
                                                break;
                                        }else if(HTTP_req.indexOf("setup2")>0){
                                                if(Serial)      Serial.println(F("setup2"));
                                                PrintUtilPage(client);
                                                HTTP_req = "";
                                                break;
                                        }else if(HTTP_req.indexOf("setup3")>0){
                                                if(Serial)      Serial.println(F("setup3"));
                                                //pageLoadUtilsJS(client);
                                                HTTP_req = "";
                                                break;
                                        }else if(HTTP_req.indexOf("setup9")>0){
                                                if(Serial)      Serial.println(F("setup9"));
                                                pageIntroduction(client);
                                                HTTP_req = "";
                                                break;
                                        }else{
                                                if(Serial)      Serial.print(F("CASE404-1"));
                                                pageIntroduction(client);
                                                HTTP_req = "";
                                                break;
                                        }
                                }else if(HTTP_req.indexOf("\n")>0) {
                                        if(Serial)      Serial.println(F("CASE4"));
                                        pageIntroduction(client);
                                        HTTP_req = "";
                                        break;
                                }else if(HTTP_req.indexOf("/index")>0) {
                                        if(Serial)      Serial.println(F("CASE5"));
                                        pageIntroduction(client);
                                        HTTP_req = "";
                                        break;
                                }else{
                                        if(Serial)      Serial.print(F("CASE404-2"));
                                        PrintResponse404(client);
                                        HTTP_req = "";
                                        break;
                                }
                                HTTP_req = "";
                                break;                          // ループを抜ける
                        }
                        if(c ==  '\n'){                         // 新しい行の始まり。
                                currentLineIsBlank = true;
                        } else if(c !=  '\r' ){                 // この行は空行ではなかった。
                                currentLineIsBlank = false;
                        }
                }
                delay(1);
                client.stop();
        }     
}
//--------------
void setup() {
        Serial.begin(BAUDRATE);
        /*
        while (!Serial) {
                ; // シリアルポートが接続されるのを待ちます。シリアルモニタを寄贈するまで待機します。
        }
        
        Serial.println("Start Ethernet Modbus TCP Example");
        */
        pinMode(LED_BUILTIN, OUTPUT);
        digitalWrite(LED_BUILTIN, HIGH);
        Wire.setSDA(I2C0_SDA);                                  //RP2040にはI2C0の組み合わせが6通りあるので設定しておくのが無難です。
        Wire.setSCL(I2C0_SCL);
        Wire.begin();                                           //ENV IIのgroveコネクタ接続pin設定
        sht30.begin(0x44);                                      // sht30 I2C address: 0x44 or 0x45
        qmp6988.init(0x70,&Wire);
        qmp6988.setpPowermode(QMP6988_NORMAL_MODE);
        qmp6988.setFilter(QMP6988_FILTERCOEFF_OFF);
        Serial.println(qmp6988.deviceCheck());
        Serial.println(F("ENV Unit III test"));
        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);
        // start Ethernet and UDP
        LANSetup();       
        Ethernet.begin(mac, ip);
        delay(100);
        Serial.print(F("My IP address: "));Serial.println(Ethernet.localIP());  // print your local IP address:
        Udp.begin(NTPport);
        webServer.begin();
        //最初にNTPサーバから時刻を取得する
        sprintf(buf32,"%d.%d.%d.%d",NTPip[0],NTPip[1],NTPip[2],NTPip[3]);
        sendNTPpacket(buf32);                                                   // 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
        }
        preShow = Mb.GetBit(MB_FC_READ_COILS,2);
        for (int i = 0;i<16;i++){ buf32[i] = 0;}
        int VstrLen     = strlen(VenderString);
        strcpy(buf32,VenderString);
        for (int i = 0;i<16;i++){
                Mb.MbInputData[i+16]     = char(buf32[2 * i]) * 256 + char(buf32[2 * i + 1]);
        }
        for (int i = 0;i<16;i++){ buf32[i] = 0;}
        strcpy(buf32,titleStr);
        for (int i = 0;i<16;i++){
                Mb.MbInputData[i+32]    = char(buf32[2 * i]) * 256 + char(buf32[2 * i + 1]);
        }
        char    mainVer,minorVer,Rev;
        sscanf(SVirsion,"%1s.%1s.%1s",&mainVer,&minorVer,&Rev);
        Mb.MbInputData[48] = mainVer * 256 + minorVer;
        Mb.MbInputData[49] = Rev * 256;
        sscanf(FVirsion,"%1s.%1s.%1s",&mainVer,&minorVer,&Rev);
        Mb.MbInputData[50] = mainVer * 256 + minorVer;
        Mb.MbInputData[51] = Rev * 256;
        //マイクロ秒単位の間隔
        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();
        client          = webServer.available();                                //HTTP要求確認
        if(client)      checkWebPage(client);
        client.stop();
        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;
}


製作物に戻る


問い合わせ頁の表示


オブジェクト自体はベクター殿のストレージをお借りしています。

https://www.vector.co.jp/vpack/browse/person/an051501.html


免責事項

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

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