手元で動作中のModbusSlaveデバイス

最終更新日:2021年5月17日

手元で動作中のModbusSlaveデバイスを紹介させて戴きます。STM32miniShieldをベースにしています。

安定してModbusSlaveデバイスが動作するか、手元で運用して確認/検証います。
問題が発生した場合はもちろん、新しい機能を思いついた場合にもソースコードを修正し、すぐに動作再開を試みています。
ここでは、最新ソースの提供と紹介をしています。

ネットワーク越しにiPhoneやWindowsPCから既製のModbusTCPツールからアクセスして問題点の洗い出しをしています。
LVModbusMasterからアクセスする分には殆ど問題は出ていません。
しかし、市販ツールからアクセスすると、未対応機能のFunctionコードによるアクセスとかが発生し、エラー処理が出来ずに止まってしまうとか、諸問題が顕在化するため、新しいツールを入手したらすぐに試してみる等の作業をしている次第です。

まず、現状のModbusSlaveデバイスの仕様を示しておきます。

■電源仕様

項目 仕様
電源電圧 DC5V(USBから供給)
消費電流 220mA(実測値)

■環境仕様

項目 仕様
使用周囲温度 ※検証方法を検討する必要あり
使用周囲湿度 ※検証方法を検討する必要あり

■外形仕様

項目 仕様
外形寸法 OP-125(H45 D125 W100)
質量 ※未確認

■通信仕様

項目 仕様
通信方式 MODBUSTCP(ポーリング)
HTTP
通信速度 100Mbps(実質4Mbps(SPI))
通信距離 100m それ以上の場合はリピータ経由
応答時間 20mse以下(ModbusTCP)
1msec以下(ping)
セッション数 1※要求毎に処理終了後にCloseしているため、外観上 複数のデバイスが同時にアクセス可能なように見えます。
ポート番号 502(変更するにはソースコードの修正が必要)

■端子仕様

項目 仕様
PLS(4P) PA0、PA1、PA2、PA3
DIR(4P) PB12、PB13、PB14、PB15
RST/GO(4P) 3.3V、PA9、PA10、GND UARTに利用可能
ALM(6P) 3.3V、PB11、PB10、PB1、PB0、GND
I2C1(4P) 3.3V、GND、PB6、PB7
I2C2(4P) 3.3V、GND、PB6、PB7
TM1638(5P) 3.3V、GND、PA15、PB3、PB4
LED1 PB8
LED2 PB9

■ファンクション

ファンクション 機能 説明
01 コイル読み込み ビット単位の読込(連続した複数ビット可)
02 入力ステータス読み込み ビット単位の読込(連続した複数ビット可)
03 保持レジスタ読み込み ワード単位の読込(連続した複数ワード可)
04 入力レジスタ読み込み ワード単位の読込(連続した複数ワード可)
05 単一コイル書き込み ビット単位の書込
06 単一保持レジスタ書き込み ワード単位の書込
15 複数コイル書き込み 連続した複数ビット単位の書込
16 複数レジスタ書き込み 連続した複数ワード単位の書込

■リファレンスとファンクションコードの対応

デバイス設定 ファンクション 要素数 説明
リファレンス アドレス例 コード アドレス
00 0001 01 0000 16 コイル読み込み
0001 05 0000 コイル書込
0001 15 0000 コイル複数書込
01 0001 02 0000 16 入力ステータス読込
03 0001 04 0000 48 入力レジスタ読込
04 0001 03 0000 16 保持レジスタ読込
0001 06 0000 保持レジスタ書込
0001 16 0000 保持レジスタ複数書込

■異常応答

エラー
コード
名称 説明
00 エラー無し
01 不正ファンクション ファンクションコード不良(実在しないあるいは未対応)
02 不正アドレス コイル、入力、レジスタのアドレス不良(範囲外)
03 不正データ コイル、入力、レジスタの個数不良(範囲外)
04 スレーブ デバイス エラー 未対応
05 肯定応答(ACK) 未対応
06 スレーブ デバイス 処理中 未対応
07 否定応答(NAK) 未対応
08 メモリパリティエラー 未対応
10 ゲートウェイパス使用不可 未対応
11 ゲートウェイターゲット失敗 未対応

■アドレス表 コイル 16

  名称 アドレス 説明
01 LED1 0000(0) 0で点灯 1で消灯
02 LED2 0000(1) 0で点灯 1で消灯
03 予約 0000(2)  
04 予約 0000(3)  
05 予約 0000(4)  
06 予約 0000(5)  
07 優先制御 0000(6) 1の場合リモート 0の場合LED&KEY
08 連続更新 0000(7) 1の場合連続更新 0の場合、その瞬時
09 予約 0000(8)  
10 予約 0000(9)  
11 予約 0000(A)  
12 予約 0000(B)  
13 予約 0000(C)  
14 予約 0000(D)  
15 予約 0000(E)  
16 予約 0000(F)  

■アドレス表 入力 16

  名称 アドレス 説明
01 DipSW0 0000(0) ON=1 OFF=0
02 DipSW1 0000(1) ON=1 OFF=0
03 DipSW2 0000(2) ON=1 OFF=0
04 DipSW3 0000(3) ON=1 OFF=0
05 予約 0000(4)  
06 予約 0000(5)  
07 予約 0000(6)  
08 予約 0000(7)  
09 入力値_0_上限値越 0000(8) 条件合致でON=1 通常OFF=0
10 入力値_0_下限値欠 0000(9)
11 入力値_1_上限値越 0000(A)
12 入力値_1_下限値欠 0000(B)
13 入力値_2_上限値越 0000(C)
14 入力値_2_下限値欠 0000(D)
15 入力値_3_上限値越 0000(E)
16 入力値_3_下限値欠 0000(F)

■アドレス表 保持レジスタ 16

  名称 アドレス 説明
01 DAC設定値 0000  
02 予約 0001  
03 予約 0002  
04 予約 0003  
05 予約 0004  
06 予約 0005  
07 予約 0006  
08 LED表示Index 0007 0..6
09 入力値_0_上限値 0008 この値を入力値0と比較してアラートとして設定
上限>下限が不成立の場合は比較せずアラートFALSE
10 入力値_0_下限値 0009
11 入力値_1_上限値 000A この値を入力値1と比較してアラートとして設定
上限>下限が不成立の場合は比較せずアラートFALSE
12 入力値_1_下限値 000B
13 入力値_2_上限値 000C この値を入力値2と比較してアラートとして設定
上限>下限が不成立の場合は比較せずアラートFALSE
14 入力値_2_下限値 000D
15 入力値_3_上限値 000E この値を入力値3と比較してアラートとして設定
上限>下限が不成立の場合は比較せずアラートFALSE
16 入力値_3_下限値 000F

■アドレス表 入力レジスタ 48

  名称 アドレス 説明
01 AI0(PA0) 0000 12bit(0-4095) 入力値0 上下限対象
02 温度データ 0001 温度データ x100 (WORD化)入力値1 上下限対象
03 湿度データ 0002 湿度データ x100 (WORD化)入力値2 上下限対象
04 大気圧データ 0003 大気圧データ ÷100 [hPa]入力値3 上下限対象
05 西暦 0004 現在時刻
TZ:タイムゾーン 日本の場合+9=90ハワイ-8=-80
59分01秒=59×256+01=15105(0x5901)
06 月/日 0005
07 TZ/時 0006
08 分/秒 0007
09 VenderString_0_1 0008 ベンタ名31max文字+\0
10 VenderString_2_3 0009
11 VenderString_4_5 000A
12 VenderString_6_7 000B
13 VenderString_8_9 000C
14 VenderString_10_11 000D
15 VenderString_12_13 000E
16 VenderString_14_15 000F
17 VenderString_16_17 0010
18 VenderString_18_19 0011
19 VenderString_20_21 0012
20 VenderString_22_23 0013
21 VenderString_24_25 0014
22 VenderString_26_27 0015
23 VenderString_28_29 0016
24 VenderString_30_31 0017
25 ProductString_0_1 0018 製品名31max文字+\0
26 ProductString_2_3 0019
27 ProductString_4_5 001A
28 ProductString_6_7 001B
29 ProductString_8_9 001C
30 ProductString_10_11 001D
31 ProductString_12_13 001E
32 ProductString_14_15 001F
33 ProductString_16_17 0020
34 ProductString_18_19 0021
35 ProductString_20_21 0022
36 ProductString_22_23 0023
37 ProductString_24_25 0024
38 ProductString_26_27 0025
39 ProductString_28_29 0026
40 ProductString_30_31 0027
41 SystemVerValue0_1 0028 システムバージョン表記
バイト単位で0/1/2/3
42 SystemVerValue2_3 0029
43 FirmwareVerVarlue0_1 002A ファームウエアバージョン表記
バイト単位で0/1/2/3
44 FirmwareVerVarlue2_3 002B
45 予約 002C  
46 予約 002D  
47 予約 002E  
48 予約 002F  

上記仕様に伴うソースコードは以下の通りです。

STM32duinoソースコード(Sketch)は以下の通りです。必ず、“MgsModbus.h”“MgsModbus.cpp”をinoと同じフォルダに入れてください。
“MgsModbus.h”“MgsModbus.cpp”はオリジナルコードをレタッチしています。オリジナルコードの作者はMarco Gerritse氏[V-0.1.1著作権(C)2013]です。断りを入れているわけではないです。オリジナルのコードのままではどうしてもうまく実現できませんでした。

このコードを実行するに当たり追加が必要なライブラリは、

STM32MINIShieldEvaluation_ModbusSlave8.1.2.ino

/*
 * 2021/05/12 T.Wanibe
 * v1.2.2:C_
 *      Modbus STM32MINIShield Utility Page \e
 * v1.2.1:C_
 *      }bv
 * v1.2.0:C_
 *      HTTPCj[\LX@NX
 *      LED&KEY{^L@BA\j[LED1..8\B
 * v1.1.3:C_
 *      1.1.1
*      {^S6MainLoopScanTimeB
*      obt@TCY128->256\XBsB
 * v1.1.2:C_
 *      setup2htmlR[h^b` \Table
 *      {^S6MainLoopScanTimeB 
* v1.1.1:C_
 *      ExceptionCode
 *      OANZX
 *      {OR[hoBmFB
 *      MgsModbusCuvCuKvB
 *      _AWDT timeup30bX(Max31.982b)
 * v1.0.15:C_
 *      WDT timeup15bX@ v1.0.12ql
 * v1.0.14:C_
 *      HTTP status 
 * v1.0.13:C_
 *      setup2htmlR[h^b`@Table\xP@ @B
 *      ModbusPollingWebAccess~BtKvB
 *      @ModbusM@@500 Internal Server Error@B
 * v1.0.12:C_ 
 *      setup2htmlR[h^b`
 * v1.0.11:sp
 * v1.0.10:X_
 *      html(setup2)\eX@Coil32bitX
 * v1.0.9:X_
 *      html(setup2)R[h@isnan()g
 * v1.0.8:X_
 *      WDTB
 * softresetBA[eBeBB
 * xxB]T
 * MCP23017CoilyInputStatusB
 * 10mocLoB
 * RTC@NTP@NANZXZbg
 * LED&KEY@\BAJE^L[I[o[tB
 * STM32MINIShieldEvaluationpB
 * CoilData:16bit 4bit
 * DiscreteDataF16bit@4bit
 * InputDataF8[h@AhX1FMCP4735l@Address2:MCP280 x@Address3Frerserved@Address4FMCP280@C
 * HoldDataF16[h@AhX1lMCP4725EEPROM
 * I2CfoCXlAKPulUpRB
 * CvB
 * NCAgRlNgBuiltINLED]|
 * LED&KEYLEDt
 * LED&KEY\XVoBDO7bittOB
 * iPhoneModbusAvAANZXARlNgoBAMomFoMasterBcOB
 * STM32MINIShieldEvaluationR8/R910kB
 * 
 * RpCYB
 * 131072oCgtbVAXPb`84760oCgi64%jgB
 * 20480oCgRAMAO[o6696oCgi32%jgA[J13784oCggB
 */
#include <libmaple/iwdg.h>
#include <SPI.h>
#include <Ethernet3.h>                  //WizNetCu
#include <EthernetUdp3.h>
#include "MgsModbus.h"                  //SketchKwu
#include <EEPROM.h>
#include <avr/pgmspace.h>
#include <TextFinder.h>                 //WebSetting
#include <Wire.h>
#include <MCP4725.h>                    //DAC
#include <BME280I2C.h>                  //x@CZT
#include <RTClock.h>
#include <Adafruit_MCP23017.h>
#include <TM1638.h>
#define DEBUG                   1
#define OK                      0
#define NG                      1
//PinAssign
#define dataPin                 PB4                                     //STM32MINIShieldEvaluation
#define clockPin                PB3                                     //STM32MINIShieldEvaluation
#define strobePin               PA15                                    //STM32MINIShieldEvaluation
#define W550io_Rst              PA8                                     //STM32MINIShieldEvaluation
#define SPI1_NSS_PIN            PA4                                     //SPI_1
#define LED                     PC13
#define LED1                    PB8                                     //LEDo1
#define LED2                    PB9                                     //LEDo2
#define ModBusPort              502                                     //
#define MbCoilDataLen           2                                       // length of the MbCoilDataLen array (Equivalent16bit*2)
                                                                        //4bitB
#define MbDiscreteDataLen       2                                       // length of the MbDiscreteDataLen array(Equivalent16bit*2)
                                                                        //4bitB
#define MbHoldDataLen           16                                      // length of the MbHoldDataLen array
#define MbInputDataLen          48                                       // length of the MbInputDataLen array
                                                                        //7BAI~4Ax(x100[])x(x100[%])x(x1[lux])
#define HttpPort                80
#define DAC_REF_VOLTAGE         3.3                                     //dac supply-reference voltage
#define I2C_BUS_SPEED           400000                                  //i2c bus speed, 100 000Hz or 400 000Hz
#define ID1                     0x20    
#define ID2                     0x21    
#define iwdg_init_ms(N)         iwdg_init(IWDG_PRE_256,((N)/8))         //msec}NpB(N)/6l12bit(4095)
#define NTP_PACKET_SIZE         48                                      // NTP time is in the first 48 bytes of message
int     inByte          =       0;                                      // incoming serial byte
// Ethernet settings (depending on MAC and Local network)
byte mac[]              = {0x00,0x08,0xDC,0x54,0x4D,0xD2};              //WIZNET
byte ip[]               = {192, 168, 0, 202};
byte dns_server[]       = {192, 168, 0, 1};
byte gateway[]          = {192, 168, 0, 1};
byte subnet[]           = {255, 255, 255, 0};
int dOsPins[]           = {LED1,LED2};
int dIsPins[]           = {PB11, PB10, PB1, PB0};
//int aOsPins[]         = {};
int aIsPins[]           = {PA0};
//byte timeServer[]     = {130, 69, 251, 23};                   //w  ntp.nc.u-tokyo.ac.jp 2020/08/10 ping
byte timeServer[]       = {133, 31, 180, 6};                    //w ntp.nc.u-tokyo.ac.jp   2020/08/10@9msec
//byte timeServer[]     = {130, 34, 11,  117};                  //kw  ntp1.tohoku.ac.jp       2020/08/10@14msec
//byte timeServer[]     = {130, 34, 48,  32};                   //kw  ntp2.tohoku.ac.jp       2020/08/10@ping
//zvf
int DObits              = sizeof(dOsPins)/sizeof(int);
int DIbits              = sizeof(dIsPins)/sizeof(int);
int AIbits              = sizeof(aIsPins)/sizeof(int);
char buf1[32];
byte    ErrorTMP        = false;
byte    ErrorHUM        = false;
byte    ErrorPRS        = false;
uint16_t value          = 0;
float   voltage         = 0;
float   temp(NAN), hum(NAN), pres(NAN);
int     cntTemp,cntHum,cntPres;
float   sumTemp,sumHum,sumPres;
int     movingAverageLength     = 10;
float   tempAve[10];
float   humAve[10];
float   presAve[10];
byte    packetBuffer[NTP_PACKET_SIZE];                          //buffer to hold incoming & outgoing packets
char    s[16];                                                  // for sprintf
EthernetServer  webServer(HttpPort);
EthernetClient  client;
TM1638          LEDandKEY(dataPin, clockPin, strobePin);
BME280I2C       bme;                                            // Default : forced mode, standby time = 1000 ms
MCP4725         dac(MCP4725A0_ADDR_A00, DAC_REF_VOLTAGE);
String          HTTP_req;                                       // stores the HTTP request
MgsModbus       Mb;
word            preData = Mb.MbHoldData[0];                     //lmF
word            preTime = -1;
byte            lastKey;
Adafruit_MCP23017 mcpIn;                                        //ID=0x20
Adafruit_MCP23017 mcpOut;                                       //ID=0x21
// AHTMLR[huvobt@[B
// hh`F[+1B
char    buffer[256];
// lHTMLR[hB GfB^[HTMLR[hAobt@B
// HTMLA\ "BHTMLsimple"\ "
// gpKvB@\B
//
//RTC
RTClock rtclock (RTCSEL_LSE);                                   // initialise
const int       timeZone        = 9;                            // change to your timezone
time_t          tt, tt1;
tm_t            mtt;
uint8_t         dateread[11];
EthernetUDP     Udp;
unsigned int    localPort       = 8888;                         // local port to listen for UDP packets

const byte      ID              = 0x92;
/*
        EEPROMLf[^umvrbggpB
        EEPROMAXPb`Os
        gpAXPb`sALlgpB 
        f[^EEPROMAhXgp`
 */
const char      titleStr[]      = "STM32MINIShield";
const char      VenderString[]  = "ToolsBox";
char            outputSTR[9];
const char      FVirsion[]      = "1.2.2";                      //tH[}bg@x.x.x
const char      SVirsion[]      = "1.0.0";                      //tH[}bg@x.x.x
char            MacAddressStr[20];
char            IPAddressStr[20];
char            SubnetMaskStr[20];
char            GatewayStr[20];
char            NTPserverStr[20];
char            DateTimeStr[20];
bool            gMBusConnect    = false;                        //ModbusWebServer502
//----------------------------------
void LANSetup(){
        int idcheck = EEPROM.read(0);
        if (idcheck != ID){
                //idIDlA
                //XPb`V[hOgp
                //XPb`nlgp
        }
        if (idcheck == ID){
                //idIDlA
                //AXPb`V[hgpB
                //EERPOMlV[hB
                for (int i = 0; i < 6; i++){
                        mac[i] = EEPROM.read(i+1);
                }
                for (int i = 0; i < 4; i++){
                        ip[i] = EEPROM.read(i+7);
                }
                for (int i = 0; i < 4; i++){
                        subnet[i] = EEPROM.read(i+11);
                }
                for (int i = 0; i < 4; i++){
                        gateway[i] = EEPROM.read(i+15);
                }
		//ANZXNTPT[o[
                for (int i = 0; i < 4; i++){
                        timeServer[i] = EEPROM.read(i+19);
                }
        }
        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]);
        sprintf(NTPserverStr,"%d.%d.%d.%d",timeServer[0],timeServer[1],timeServer[2],timeServer[3]);
        Ethernet.begin(mac, ip);
//      Ethernet.begin(mac);                            //DHCP
//      Ethernet.begin(mac, ip, subnet);                //SubnetMaskB
//      Ethernet.begin(mac, ip, subnet, gateway);       //gatewayB
}
//--------------------------
void SetWebPage( EthernetClient client){
        client.println(F("HTTP/1.1 200 OK"));
        client.println(F("Content-Type: text/html"));
        client.println();
        //
        client.print(F("<!DOCTYPE HTML PUBLIC \"\">\n<html>\n<HEAD>\n\t<META http-equiv=\"Content-Type\" charset=UTF-8\">\n"));
        client.print(F("\t<META http-equiv=\"Content-Style-Type\">\n\t<TITLE>"));
        client.print(titleStr);
        client.print(F(" Setup Page</TITLE>\n</HEAD>\n"));
        client.print(F("<BODY marginwidth=\"0\" marginheight=\"0\" leftmargin=\"0\" style=\"margin: 0; padding: 0;\">\n<BLOCKQUOTE><BLOCKQUOTE>\n"));
        client.print(F("<table bgcolor=\"#17A1A5\" border=\"0\" width=\"100%\" cellpadding=\"1\" style=\"font-family:Verdana;color:#ffffff;font-size:12px;\">\n"));
        client.print(F("<tr><td>"));
        client.print(titleStr);
        client.print(F(" Setup Page</td></tr></table><br>\n"));
        //
        client.print(F("<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"));
        client.print(F("<tbody>\n<FORM><input type=\"hidden\" name=\"SBM\" value=\"1\">\n<table>\n<tr><td>MAC:</td><td>"));
        client.print(F("<input id=\"T1\" type=\"text\" size=\"3\" maxlength=\"2\" name=\"DT1\" value=\""));
        client.print(mac[0],HEX);
        client.print(F("\">.<input id=\"T3\" type=\"text\" size=\"3\" maxlength=\"2\" name=\"DT2\" value=\""));
        client.print(mac[1],HEX);
        client.print(F("\">.<input id=\"T5\" type=\"text\" size=\"3\" maxlength=\"2\" name=\"DT3\" value=\""));
        client.print(mac[2],HEX);
        client.print(F("\">.<input id=\"T7\" type=\"text\" size=\"3\" maxlength=\"2\" name=\"DT4\" value=\""));
        client.print(mac[3],HEX);
        client.print(F("\">.<input id=\"T9\" type=\"text\" size=\"3\" maxlength=\"2\" name=\"DT5\" value=\""));
        client.print(mac[4],HEX);
        client.print(F("\">.<input id=\"T11\" type=\"text\" size=\"3\" maxlength=\"2\" name=\"DT6\" value=\""));
        client.print(mac[5],HEX);
        //
        client.print(F("\"><input id=\"T2\" type=\"hidden\" name=\"DT1\"><input id=\"T4\" type=\"hidden\" name=\"DT2"));
        client.print(F("\"><input id=\"T6\" type=\"hidden\" name=\"DT3\"><input id=\"T8\" type=\"hidden\" name=\"DT4"));
        client.print(F("\"><input id=\"T10\" type=\"hidden\" name=\"DT5\"><input id=\"T12\" type=\"hidden\" name=\"DT6"));
        client.print(F("\"></td></tr>\n<tr><td>IP:</td><td><input type=\"text\" size=\"3\" maxlength=\"3\" name=\"DT7\" value=\""));
        client.print(ip[0],DEC);
        client.print(F("\">.<input type=\"text\" size=\"3\" maxlength=\"3\" name=\"DT8\" value=\""));
        client.print(ip[1],DEC);
        client.print(F("\">.<input type=\"text\" size=\"3\" maxlength=\"3\" name=\"DT9\" value=\""));
        client.print(ip[2],DEC);
        client.print(F("\">.<input type=\"text\" size=\"3\" maxlength=\"3\" name=\"DT10\" value=\""));
        client.print(ip[3],DEC);
        //
        client.print(F("\"></td></tr>\n<tr><td>MASK: </td><td><input type= \"text\" size=\"3\" maxlength=\"3\" name=\"DT11\" value=\""));
        client.print(subnet[0],DEC);
        client.print(F("\">.<input type=\"text\" size=\"3\" maxlength=\"3\" name=\"DT12\" value=\""));
        client.print(subnet[1],DEC);
        client.print(F("\">.<input type=\"text\" size=\"3\" maxlength=\"3\" name=\"DT13\" value=\""));
        client.print(subnet[2],DEC);
        client.print(F("\">.<input type=\"text\" size=\"3\" maxlength=\"3\" name=\"DT14\" value=\""));
        client.print(subnet[3],DEC);
        //
        client.print(F("\"></td></tr>\n<tr><td>GW: </td><td><input type=\"text\" size=\"3\" maxlength=\"3\" name=\"DT15\" value=\""));
        client.print(gateway[0],DEC);
        client.print(F("\">.<input type=\"text\" size=\"3\" maxlength=\"3\" name=\"DT16\" value=\""));
        client.print(gateway[1],DEC);
        client.print(F("\">.<input type=\"text\" size=\"3\" maxlength=\"3\" name=\"DT17\" value=\""));
        client.print(gateway[2],DEC);
        client.print(F("\">.<input type=\"text\" size=\"3\" maxlength=\"3\" name=\"DT18\" value=\""));
        client.print(gateway[3],DEC);
        //
        //client.print(F("\"></td></tr>\n<tr><td><br></td COLSPAN='2'>\n<HR ALIGN=LEFT>"));
        client.print(F("\"></td></tr><tr><td COLSPAN='2'>\n<HR ALIGN=CENTER></td><td>"));                      //1s
	//
        client.print(F("</td></tr><tr><td>NTPd: </td><td><input type=\"text\" size=\"3\" maxlength=\"3\" name=\"DT19\" value=\""));
        client.print(timeServer[0],DEC);
        client.print(F("\">.<input type=\"text\" size=\"3\" maxlength=\"3\" name=\"DT20\" value=\""));
        client.print(timeServer[1],DEC);
        client.print(F("\">.<input type=\"text\" size=\"3\" maxlength=\"3\" name=\"DT21\" value=\""));
        client.print(timeServer[2],DEC);
        client.print(F("\">.<input type=\"text\" size=\"3\" maxlength=\"3\" name=\"DT22\" value=\""));
        client.print(timeServer[3],DEC);
        client.print(F("\"></td></tr>\n<tr><td><br></td></tr><tr><td COLSPAN='2'><P ALIGN=RIGHT><input id=\"button1\"type=\"submit\" value=\"SUBMIT\" "));
        //
        client.print(F("Onclick=\""));
        client.print(F("document.getElementById('T2').value      = hex2num(document.getElementById('T1').value);\n"));
        client.print(F("\t\tdocument.getElementById('T4').value  = hex2num(document.getElementById('T3').value);\n"));
        client.print(F("\t\tdocument.getElementById('T6').value  = hex2num(document.getElementById('T5').value);\n"));
        client.print(F("\t\tdocument.getElementById('T8').value  = hex2num(document.getElementById('T7').value);\n"));
        client.print(F("\t\tdocument.getElementById('T10').value = hex2num(document.getElementById('T9').value);\n"));
        client.print(F("\t\tdocument.getElementById('T12').value = hex2num(document.getElementById('T11').value);\""));
        //
        client.print(F("></td></tr></tbody>\n</table>\n</BLOCKQUOTE></BLOCKQUOTE>\n</form>\n</BODY>\n</html>\n"));
}
//--------------------------@2021/05/12@retouch
void SetUtilPage( EthernetClient client){
        client.println(F("HTTP/1.1 200 OK"));
        client.println(F("Content-Type: text/html"));
        client.println();
        //
        client.print(F("<!DOCTYPE HTML PUBLIC \"\">\n<html>\n<HEAD>\n\t<META http-equiv=\"Content-Type\" charset=UTF-8\">\n"));
        client.print(F("\t<META http-equiv=\"Content-Style-Type\">\n\t<TITLE>Modbus "));
        client.print(titleStr);
        client.print(F(" Utility Page</TITLE>\n</HEAD>\n"));
        client.print(F("<BODY marginwidth=\"0\" marginheight=\"0\" leftmargin=\"0\" style=\"margin: 0; padding: 0;\" BGCOLOR=\"#ffffff\">\n"));
        client.print(F("<FORM>\n<BLOCKQUOTE><BLOCKQUOTE>\n"));
        client.print(F("<P><table bgcolor=\"#17A1A5\" border=\"0\" width=\"100%\" cellpadding=\"1\" style=\"font-family:Verdana;color:#ffffff;font-size:12px;\" CELLSPACING=\"2\">\n"));
        client.print(F("<tr><td>Modbus "));
        client.print(titleStr);
        client.print(F(" Utility Page</td></tr></table><br>\n"));
        //
        client.print(F("<TABLE WIDTH=\"100%\" BORDER=\"0\" CELLSPACING=\"0\" CELLPADDING=\"0\" style=\"table-layout: fixed\">"));
        client.print(F("<TR>\n\t<TD WIDTH=\"25%\">FirmwareVirsion:</TD>\n\t<TD WIDTH=\"11%\"></TD>\n\t<TD WIDTH=\"64%\">"));
        client.print(FVirsion);
        client.print(F("</TD>\n</TR>"));
        client.print(F("<TR>\n\t<TD WIDTH=\"25%\">MacAddress:</TD>\n\t<TD WIDTH=\"11%\"></TD>\n\t<TD WIDTH=\"64%\">"));
        client.print(MacAddressStr);
        client.print(F("</TD>\n</TR>"));
        client.print(F("<TR>\n\t<TD WIDTH=\"25%\">IPAddress:</TD>\n\t<TD WIDTH=\"11%\"></TD>\n\t<TD WIDTH=\"64%\">"));
        client.print(IPAddressStr);
        client.print(F("</TD>\n</TR>"));
        client.print(F("<TR>\n\t<TD WIDTH=\"25%\">SubnetMask:</TD>\n\t<TD WIDTH=\"11%\"></TD>\n\t<TD WIDTH=\"64%\">"));
        client.print(SubnetMaskStr);
        client.print(F("</TD>\n</TR>"));
        client.print(F("<TR>\n\t<TD WIDTH=\"25%\">Data acquisition time:</TD>\n\t<TD WIDTH=\"11%\"></TD>\n\t<TD WIDTH=\"64%\">"));
        client.print(DateTimeStr);
        client.print(F("</TD>\n</TR></TABLE></P>\n"));
        //DI
        client.print(F("<P><HR ALIGN=LEFT></P>\n<P><TABLE WIDTH=\"100%\" BORDER=\"0\" CELLSPACING=\"0\" CELLPADDING=\"0\" HEIGHT=\"800\" style=\"table-layout: fixed\">"));
        client.print(F("\t<tbody>\n\t<TR>\n\t\t<TD ROWSPAN=\"2\"  WIDTH=\"25%\">DI(02)InputStatus</TD>\n"));
        client.print(F("\t\t<TD COLSPAN=\"2\" HEIGHT=\"23\"></TD>\n"));
        client.print(F("\t\t<TD WIDTH=\"4%\" HEIGHT=\"23\" NOWRAP ALIGN=\"CENTER\">"));
        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(F("\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"));
        client.print(F("\t\t<TD WIDTH=\"4%\" HEIGHT=\"23\" NOWRAP ALIGN=\"CENTER\">\n"));
        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(F("\t\t<INPUT TYPE=\"checkbox\" NAME=\"DI_15\" disabled=\"disabled\" "));
        if(Mb.GetBit(MB_FC_READ_DISCRETE_INPUT,15) == true) client.print(F("CHECKED"));
        /*
        client.print(F("></TD>\n\t</TR>\n\t<TR>\n\t\t<TD WIDTH=\"4%\"  HEIGHT=\"23\"><P ALIGN=RIGHT>1</TD>\n"));
        client.print(F("\t\t<TD WIDTH=\"7%\" HEIGHT=\"23\"></TD>\n\t\t<TD WIDTH=\"4%\" HEIGHT=\"23\" NOWRAP ALIGN=\"CENTER\">\n"));
        for (int i = 16;i<31;i++){
                sprintf(buffer,"\t\t<INPUT TYPE=\"checkbox\" NAME=\"DI_%d\" disabled=\"disabled\" ",i);
                client.print(buffer);
                //client.print(F("\t\t<INPUT TYPE=\"checkbox\" NAME=\"DI_"));
                //client.print(i);
                //client.print(F("\" disabled=\"disabled\" "));
                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(F("\t\t<INPUT TYPE=\"checkbox\" NAME=\"DI_31\" disabled=\"disabled\" "));
        if(Mb.GetBit(MB_FC_READ_DISCRETE_INPUT,31) == true) client.print(F("CHECKED"));
        */
        client.print(F("></TD>\n\t</TR>\n\t<TR>\n\t\t<TD COLSPAN=\"19\" HEIGHT=\"5\"><HR ALIGN=LEFT></TD>\n\t</TR>\n"));
        //Coil
        client.print(F("\t<TR>\n\t\t<TD ROWSPAN=\"3\"  WIDTH=\"25%\">Coil(01)CoilStatus</TD>\n"));
        client.print(F("\t\t<TD COLSPAN=\"2\" HEIGHT=\"23\"></TD>\n\t\t<TD WIDTH=\"4%\" HEIGHT=\"23\" NOWRAP ALIGN=\"CENTER\">"));
        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\t<TR>\n\t\t<TD ROWSPAN=\"2\"  WIDTH=\"4%%\"><P ALIGN=RIGHT>0</TD>\n",15);
        client.print(buffer);
        client.print(F("\t\t<TD WIDTH=\"7%\" HEIGHT=\"23\"><P ALIGN=RIGHT>T</TD>\n\t\t<TD WIDTH=\"4%\" HEIGHT=\"23\" NOWRAP ALIGN=\"CENTER\">\n"));
        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(F("\t</TR>\n\t<TR>\n\t\t<TD WIDTH=\"7%\" HEIGHT=\"23\" ALIGN=\"RIGHT\">F</TD>\n"));
        client.print(F("\t\t<TD WIDTH=\"4%\" HEIGHT=\"23\" NOWRAP ALIGN=\"CENTER\">\n"));
        for (int i = 0;i<16;i++){
                sprintf(buffer,"\t\t<INPUT TYPE=\"radio\"  NAME=\"CL_%02x\" disabled=\"disabled\" ",i);
                client.print(buffer);
                //client.print(F("\t\t<INPUT TYPE=\"radio\"  NAME=\"CL_"));
                //sprintf(buf1,"%02x",i);
                //client.print(buf1);
                //client.print(F("\" disabled=\"disabled\" "));
                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(F("\t</TR>\n\t<TR>\n\t\t<TD COLSPAN=\"2\" HEIGHT=\"23\"></TD><TD COLSPAN=\"16\" HEIGHT=\"23\"></TD>\n"));
        /*
        client.print(F("\t</TR>\n\t<TR>\n\t\t<TD ROWSPAN=\"2\"  WIDTH=\"4%\"><P ALIGN=RIGHT>1</TD>\n"));
        client.print(F("\t\t<TD WIDTH=\"7%\" HEIGHT=\"23\" ALIGN=\"RIGHT\">T</TD>\n"));
        client.print(F("\t\t<TD WIDTH=\"4%\" HEIGHT=\"23\" NOWRAP ALIGN=\"CENTER\">\n"));
        for (int i = 16;i<32;i++){
                sprintf(buffer,"\t\t<INPUT TYPE=\"radio\"  NAME=\"CL_%02x\" disabled=\"disabled\" ",i);
                client.print(buffer);
                //client.print(F("\t\t<INPUT TYPE=\"radio\"  NAME=\"CL_"));
                //sprintf(buf1,"%02x",i);
                //client.print(buf1);
                //client.print(F("\" disabled=\"disabled\" "));
                if(Mb.GetBit(MB_FC_READ_COILS,i) == true) client.print(F("CHECKED"));
                client.print(F("></TD>\n"));
                if(i<31) client.print(F("\t\t<TD WIDTH=\"4%\" HEIGHT=\"23\" ALIGN=\"CENTER\">\n"));
        }
        client.print(F("\t</TR>\n\t<TR>\n\t\t<TD WIDTH=\"7%\" HEIGHT=\"23\" ALIGN=\"RIGHT\">F</TD>\n"));
        client.print(F("\t\t<TD WIDTH=\"4%\" HEIGHT=\"23\" NOWRAP ALIGN=\"CENTER\">\n"));
        for (int i = 16;i<32;i++){
                sprintf(buffer,"\t\t<INPUT TYPE=\"radio\"  NAME=\"CL_%02x\" disabled=\"disabled\" ",i);
                client.print(buffer);
                //client.print(F("\t\t<INPUT TYPE=\"radio\"  NAME=\"CL_"));
                //sprintf(buf1,"%02x",i);
                //client.print(buf1);
                //client.print(F("\" disabled=\"disabled\" "));
                if(Mb.GetBit(MB_FC_READ_COILS,i) == false) client.print(F("CHECKED"));
                client.print(F("></TD>\n"));
                if(i<31) client.print(F("\t\t<TD WIDTH=\"4%\" HEIGHT=\"23\" ALIGN=\"CENTER\">\n"));
        }
        */
        client.print(F("\t</TR>\n\t<TR>\n\t\t<TD COLSPAN=\"19\" HEIGHT=\"30\"><HR ALIGN=LEFT></TD>\n\t</TR>\n"));
        //IR
        client.print(F("\t<TR>\n\t\t<TD ROWSPAN=\"12\"  WIDTH=\"25%\">IR(04)InputRegistor</TD>\n"));
        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(F("\t\t<TD COLSPAN=\"2\" ALIGN=\"CENTER\">\n\t\tVstr</TD><TD COLSPAN=\"16\">"));
        for (int i = 0;i<16;i++){
                word q  = Mb.MbInputData[i+8];
                client.print(char(q/256));
                client.print(char(q%256));
        }
        client.print(F("</TD>\n\t\t</TR>\n\t\t<TR>\n"));
        client.print(F("\t\t<TD COLSPAN=\"2\" ALIGN=\"CENTER\">\n\t\tPstr</TD><TD COLSPAN=\"16\">"));
        for (int i = 0;i<16;i++){
                word q  = Mb.MbInputData[i+24];
                client.print(char(q/256));
                client.print(char(q%256));
        }
        client.print(F("</TD>\n\t\t</TR>\n\t\t<TR>\n"));
        client.print(F("\t\t<TD COLSPAN=\"2\" ALIGN=\"CENTER\">\n\t\tSver</TD><TD WIDTH=\"4%\">"));
        char    mainVer,minorVer,Rev;
        sscanf(SVirsion,"%1s.%1s.%1s",&mainVer,&minorVer,&Rev);
        client.print(mainVer);
        client.print(F("</TD><TD WIDTH=\"4%\">.</TD><TD WIDTH=\"4%\">"));
        client.print(minorVer);
        client.print(F("</TD><TD WIDTH=\"4%\">.</TD><TD WIDTH=\"4%\">"));
        client.print(Rev);
        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(mainVer);
        client.print(F("</TD><TD WIDTH=\"4%\">.</TD><TD WIDTH=\"4%\">"));
        client.print(minorVer);
        client.print(F("</TD><TD WIDTH=\"4%\">.</TD><TD WIDTH=\"4%\">"));
        client.print(Rev);
        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(F("</TD>\n\t</TR></tbody>\n</TABLE>\n"));
        //
        client.print(F("</BLOCKQUOTE></BLOCKQUOTE>\n</form>\n</BODY></html>"));
}
//--------------------------
void checkWebPage( EthernetClient client)
{
        Serial.println(F("new webClient"));
        boolean restart = false;
        if(gMBusConnect == false){        
                if (client) {
                        TextFinder  finder(client);
                        //TextFindergrBmFgB
                        //indexOfrBAlString^R[hXebvAB
                        //
                        while (client.connected()) {
                                //digitalWrite(LED, HIGH);
                                if (client.available()) {
                                        //eLXgsB|C^~B
                                        if( finder.find("GET /") == true ) {
                                                Serial.println(F("GETIn"));
                                                // usetupvATB
                                                // PA~iB
                                                // AXPb`Weby[WzuB
                                                if (finder.findUntil("setup", "\n\r")){
                                                        long cord = finder.getValue();
                                                        sprintf(buf1,"setupIn=%d",cord);
                                                        Serial.println(buf1);
                                                        switch(cord){
                                                                case 1:
                                                                        // uSBMvPATB
                                                                        // tAT~B
                                                                        // SUBMIT{^A
                                                                        // ZbgAbvy[W\zANCAguEU\B
                                                                        Serial.print(F("SETUP"));
                                                                        if (finder.findUntil("SBM", "\n\r")){
                                                                                Serial.println(F(" SBM"));
                                                                                //submitB
                                                                                byte SET = finder.getValue();
                                                                                // AuDTvTAuDTvoA
                                                                                // lvAmacAipAsubnetAgatewayKvB
                                                                                while(finder.findUntil("DT", "\n\r")){
                                                                                        int val = finder.getValue();
                                                                                        // uDTvval1?6AlMAClB
                                                                                        if(val >= 1 && val <= 6) {
                                                                                                mac[val - 1] = finder.getValue();
                                                                                        }
                                                                                        // uDTvval7?10AlIPlB
                                                                                        if(val >= 7 && val <= 10) {
                                                                                                ip[val - 7] = finder.getValue();
                                                                                        }
                                                                                        // uDTvval11?14AlMASKlB
                                                                                        if(val >= 11 && val <= 14) {
                                                                                                subnet[val - 11] = finder.getValue();
                                                                                        }
                                                                                        // uDTvval15?18AlGWlB
                                                                                        if(val >= 15 && val <= 18) {
                                                                                                gateway[val - 15] = finder.getValue();
                                                                                        }
                                                                                        // uDTvval19?22AlTimeServerlB
                                                                                        if(val >= 19 && val <= 22) {
                                                                                                timeServer[val - 19] = finder.getValue();
                                                                                        }                                                        }
                                                                                // f[^AEEPROM
                                                                                for (int i = 0 ; i < 6; i++){
                                                                                        EEPROM.write(i + 1,mac[i]);
                                                                                }
                                                                                for (int i = 0 ; i < 4; i++){
                                                                                        EEPROM.write(i + 7, ip[i]);
                                                                                }
                                                                                for (int i = 0 ; i < 4; i++){
                                                                                        EEPROM.write(i + 11, subnet[i]);
                                                                                }
                                                                                for (int i = 0 ; i < 4; i++){
                                                                                        EEPROM.write(i + 15, gateway[i]);
                                                                                }
                                                                                for (int i = 0 ; i < 4; i++){
                                                                                        EEPROM.write(i + 19, timeServer[i]);
                                                                                }                                                        // IDmrbgBAArduinoZbgAEEPROMlgpB
                                                                                EEPROM.write(0, ID); 
                                                                                // f[^EEPROMAarduinoZbgKvB
                                                                                //n[hEFAZbg{^gpKvB
                                                                                restart = true;
                                                                        }else{
                                                                                Serial.print(F("\n"));
                                                                        }
                                                                        // _AZbgAbvy[W\zJnANCAguEU[\B
                                                                        SetWebPage(client);
                                                                        break;
                                                                case 2:
                                                                        // VK[eBeBB
                                                                        Serial.println(F("utilIn"));
                                                                        rtclock.getTime(mtt);
                                                                        sprintf(DateTimeStr,"%04u/%02u/%02u %02u:%02u:%02u",mtt.year+1970, mtt.month, mtt.day,mtt.hour, mtt.minute, mtt.second);
                                                                        SetUtilPage(client);
                                                                        break;
                                                                default:
                                                                        Serial.println(F("None1"));
                                                                        PrintResponseStatus(client,404);
                                                                        break;                
                                                        }
                                                }else{
                                                        Serial.println(F("None2"));
                                                        pageIntroduction(client);
                                                }       
                                        }
                                        break;
                                }
                                PrintResponseStatus(client,404);
                        }
                        //digitalWrite(LED, LOW);
                        
                        delay(1);
                        client.stop();
                        if(restart == true)     nvic_sys_reset();
                }
        }else{
                PrintResponseStatus(client,503);
                delay(1);
                client.stop();                        
        }
}
//----------------------------2021^5^10@j[\X
void pageIntroduction( EthernetClient client){
        client.println(F("HTTP/1.1 200 OK"));
        client.println(F("Content-Type: text/html"));
        client.println();
        // put your own html from here on
        sprintf(buf1,"%d.%d.%d.%d",ip[0],ip[1],ip[2],ip[3]);
        client.println(F("<HTML>\n<HEAD>\n\t<META HTTP-EQUIV=\"Content-Type\" CONTENT=\"text/html;CHARSET=UTF-8\">\n</HEAD>\n"));
        client.println(F("<BODY BGCOLOR='#ffffff'>\n<H2><CENTER><FONT COLOR='#00ff00'>Modbus j[</FONT></CENTER></H2>\n"));
        client.println(F("<P><CENTER><TABLE WIDTH='200' BORDER='0' CELLSPACING='0' CELLPADDING='0'>\n\t<TR>\n\t\t<TD WIDTH='100%'>\n\t\t<OL>\n"));
        sprintf(buffer,"\t\t\t<LI><FONT SIZE='+2'><A HREF=\"http://%s/setup1\">j[</A>\n",buf1);
        client.println(buffer);
        sprintf(buffer,"\t\t\t<LI><A HREF=\&quot;http://%s/setup2\">l\</A></FONT>\n",buf1);
        client.println(buffer);
        client.print(F("\t\t</OL>\n\t\t</TD>\n\t</TR>\n</TABLE></CENTER>\n</BODY>\n</HTML>\n"));
        client.stop();
}
//--------------------------HTTP X|XXe[^XR[h
void PrintResponseStatus( EthernetClient client,int code){
        //pxvmD
        switch(code){
                case 404:
                        client.println(F("HTTP/1.1 404 Not Found"));
                        break;
                case 500:
                        client.println(F("HTTP/1.1 500 Internal Server Error"));
                        break;
                case 503:
                        client.println(F("HTTP/1.1 503 Service Unavailable"));
                        break;
                case 100:
                        client.println(F("HTTP/1.1 100 Continue"));
                        break;
                case 101:
                        client.println(F("HTTP/1.1 101 Switching Protocol"));
                        break;
                case 102:
                        client.println(F("HTTP/1.1 102 Processing (WebDAV)"));
                        break;
                case 103:
                        client.println(F("HTTP/1.1 103 Early Hints"));
                        break;
                case 201:
                        client.println(F("HTTP/1.1 201 Created"));
                        break;
                case 202:
                        client.println(F("HTTP/1.1 202 Accepted"));
                        break;
                case 203:
                        client.println(F("HTTP/1.1 203 Non-Authoritative Information"));
                        break;
                case 204:
                        client.println(F("HTTP/1.1 204 No Content"));
                        break;
                case 205:
                        client.println(F("HTTP/1.1 205 Reset Content"));
                        break;
                case 206:
                        client.println(F("HTTP/1.1 206 Partial Content"));
                        break;
                case 207:
                        client.println(F("HTTP/1.1 207 Multi-Status (WebDAV)"));
                        break;
                case 208:
                        client.println(F("HTTP/1.1 208 Already Reported (WebDAV)"));
                        break;
                case 226:
                        client.println(F("HTTP/1.1 226 IM Used (HTTP Delta encoding)"));
                        break;
                case 300:
                        client.println(F("HTTP/1.1 300 Multiple Choice"));
                        break;
                case 301:
                        client.println(F("HTTP/1.1 301 Moved Permanently"));
                        break;
                case 302:
                        client.println(F("HTTP/1.1 302 Found"));
                        break;
                case 303:
                        client.println(F("HTTP/1.1 303 See Other"));
                        break;
                case 304:
                        client.println(F("HTTP/1.1 304 Not Modified"));
                        break;
                case 305:
                        client.println(F("HTTP/1.1 305 Use Proxy"));
                        break;
                case 306:
                        client.println(F("HTTP/1.1 306 unused"));
                        break;
                case 307:
                        client.println(F("HTTP/1.1 307 Temporary Redirect"));
                        break;
                case 308:
                        client.println(F("HTTP/1.1 308 Permanent Redirect"));
                        break;
                case 400:
                        client.println(F("HTTP/1.1 400 Bad Request"));
                        break;
                case 401:
                        client.println(F("HTTP/1.1 401 Unauthorized"));
                        break;
                case 402:
                        client.println(F("HTTP/1.1 402 Payment Required"));
                        break;
                case 403:
                        client.println(F("HTTP/1.1 403 Forbidden"));
                        break;
                case 405:
                        client.println(F("HTTP/1.1 405 Method Not Allowed"));
                        break;
                case 406:
                        client.println(F("HTTP/1.1 406 Not Acceptable"));
                        break;
                case 407:
                        client.println(F("HTTP/1.1 407 Proxy Authentication Required"));
                        break;
                case 408:
                        client.println(F("HTTP/1.1 408 Request Timeout"));
                        break;
                case 409:
                        client.println(F("HTTP/1.1 409 Conflict"));
                        break;
                case 410:
                        client.println(F("HTTP/1.1 410 Gone"));
                        break;
                case 411:
                        client.println(F("HTTP/1.1 411 Length Required"));
                        break;
                case 412:
                        client.println(F("HTTP/1.1 412 Precondition Failed"));
                        break;
                case 413:
                        client.println(F("HTTP/1.1 413 Payload Too Large"));
                        break;
                case 414:
                        client.println(F("HTTP/1.1 414 URI Too Long"));
                        break;
                case 415:
                        client.println(F("HTTP/1.1 415 Unsupported Media Type"));
                        break;
                case 416:
                        client.println(F("HTTP/1.1 416 Range Not Satisfiable"));
                        break;
                case 417:
                        client.println(F("HTTP/1.1 417 Expectation Failed"));
                        break;
                case 418:
                        client.println(F("HTTP/1.1 418 I'm a teapot"));
                        break;
                case 421:
                        client.println(F("HTTP/1.1 421 Misdirected Request"));
                        break;
                case 422:
                        client.println(F("HTTP/1.1 422 Unprocessable Entity (WebDAV)"));
                        break;                        
                case 423:
                        client.println(F("HTTP/1.1 423 Locked (WebDAV)"));
                        break;
                case 424:
                        client.println(F("HTTP/1.1 424 Failed Dependency (WebDAV)"));
                        break;
                case 425:
                        client.println(F("HTTP/1.1 425 Too Early"));
                        break;
                case 426:
                        client.println(F("HTTP/1.1 426 Upgrade Required"));
                        break;
                case 428:
                        client.println(F("HTTP/1.1 428 Precondition Required"));
                        break;
                case 429:
                        client.println(F("HTTP/1.1 429 Too Many Requests"));
                        break;
                case 431:
                        client.println(F("HTTP/1.1 431 Request Header Fields Too Large"));
                        break;
                case 451:
                        client.println(F("HTTP/1.1 451 Unavailable For Legal Reasons"));
                        break;
                case 501:
                        client.println(F("HTTP/1.1 501 Not Implemented"));
                        break;
                case 502:
                        client.println(F("HTTP/1.1 502 Bad Gateway"));
                        break;
                case 504:
                        client.println(F("HTTP/1.1 504 Gateway Timeout"));
                        break;
                case 505:
                        client.println(F("HTTP/1.1 505 HTTP Version Not Supported"));
                        break;
                case 506:
                        client.println(F("HTTP/1.1 506 Variant Also Negotiates"));
                        break;
                case 507:
                        client.println(F("HTTP/1.1 507 Insufficient Storage (WebDAV)"));
                        break;
                case 508:
                        client.println(F("HTTP/1.1 508 Loop Detected (WebDAV)"));
                        break;
                case 510:
                        client.println(F("HTTP/1.1 510 Not Extended"));
                        break;
                case 511:
                        client.println(F("HTTP/1.1 511 Network Authentication Required"));
                        break;
                case 200:
                default:
                        client.println(F("HTTP/1.1 200 OK"));
                        break;
        }
        client.println(F("Content-Type: text/html"));
        client.println(F("Connnection: close")); 
        client.println();
        client.stop();
}
//--------------------------404 Not Found
void PrintResponse404( EthernetClient client ){
        //client.println(F("HTTP/1.1 404 Not Found\nContent-Type: text/html\nConnnection: close\n"));
        client.println(F("HTTP/1.1 404 Not Found"));
        client.println(F("Content-Type: text/html"));
        client.println(F("Connnection: close")); 
        client.println();
        client.stop();
}
//----------------------------500 Internal Server Error
void PrintResponse500( EthernetClient client ){
        client.println(F("HTTP/1.1 500 Internal Server Error"));
        client.println(F("Content-Type: text/html"));
        client.println(F("Connnection: close")); 
        client.println();
        client.stop();
}
//----------------------------
int     GetDeviceInfo(){
        for (int i=0;i<DIbits;i++){
                Mb.SetBit(MB_FC_READ_DISCRETE_INPUT,i,!digitalRead(dIsPins[i]));        //bit _]
        }
        for (int i=0;i<AIbits;i++){
                Mb.MbInputData[i]=analogRead(aIsPins[i]);                               //U16
        }
        //ModbusSlave616BIT@16..31
        for (int i=0;i<16;i++){
                Mb.SetBit(MB_FC_READ_DISCRETE_INPUT,i+16,mcpIn.digitalRead(i));         //
        }        
        return OK;            
}
//----------------------------
int     SetDeviceInfo(){
        for (int i=0;i<DObits;i++){          
                digitalWrite(dOsPins[i],!Mb.GetBit(MB_FC_READ_COILS,i));                 //LEDLOW_                            
        }
        //ModbusSlave616BIT@16..31
        for (int i=0;i<16;i++){
                mcpOut.digitalWrite(i,Mb.GetBit(MB_FC_READ_COILS,i+16));                //
        }        
        if(Mb.MbHoldData[0] != preData){
                preData =       Mb.MbHoldData[0];
                dac.setValue(preData, MCP4725_FAST_MODE, MCP4725_POWER_DOWN_OFF);
                Serial.print(F("SetDAC = "));Serial.println(preData);
        }
        rtclock.getTime(mtt);
        if(preTime != (mtt.minute * 256  + mtt.second)){
                Mb.MbInputData[4]       =       mtt.year+1970;
                Mb.MbInputData[5]       =       mtt.month  * 256  + mtt.day;
                Mb.MbInputData[6]       =       timeZone   * 2560 + mtt.hour;
                Mb.MbInputData[7]       =       mtt.minute * 256  + mtt.second;
                preTime                 =       Mb.MbInputData[7]; 
        }
        return OK;
}
//----------------------------
int     SetI2CDeviceInfo(bool init = false){
        //BME280v
        double rf;
        BME280::TempUnit tempUnit(BME280::TempUnit_Celsius);
        BME280::PresUnit presUnit(BME280::PresUnit_Pa);
        bme.read(pres, temp, hum, tempUnit, presUnit);
        if(init == true){
                for (int i = 0;i<movingAverageLength;i++){
                        tempAve[i] = temp;
                        sumTemp    += tempAve[i];
                        humAve[i]  = hum;
                        sumHum     += humAve[i];
                        presAve[i] = pres;
                        sumPres    += presAve[i];
                }              
        }
        //InputDataIndex=0xZbg@INTKv100{@25.65->2565
        if(ErrorTMP == false)   Mb.MbInputData[1]        = int(getTempAve(temp)*100);
        //InputDataIndex=1xZbg@INTKv100{@57.88->5788
        if(ErrorHUM == false)   Mb.MbInputData[2]        = int(getHumAve(hum)*100);
        //InputDataIndex=1xZbg@INTKv[mbar]101300->1013 i16 
        if(ErrorPRS == false)   Mb.MbInputData[3]        = int(getPresAve(pres)/100);
}
//--------------------------
void printError(byte error){
        // If there's an I2C error, this function will
        // print out an explanation.
        Serial.print(F("I2C error: "));
        Serial.print(error,DEC);
        Serial.print(F(", "));
  
        switch(error){
                case 0:
                        Serial.println(F("success"));
                        break;
                case 1:
                        Serial.println(F("data too long for transmit buffer"));
                        break;
                case 2:
                        Serial.println(F("received NACK on address (disconnected?)"));
                        break;
                case 3:
                        Serial.println(F("received NACK on data"));
                        break;
                case 4:
                        Serial.println(F("other error"));
                        break;
                default:
                        Serial.println(F("unknown error"));
        }
}
//----------------------------
time_t getNtpTime(){
        while (Udp.parsePacket() > 0) ;                                 // discard any previously received packets
        Serial.println(F("Transmit NTP Request"));
        sendNTPpacket(timeServer);
        uint32_t beginWait = millis();
        while (millis() - beginWait < 1500) {
                int size = Udp.parsePacket();
                if (size >= NTP_PACKET_SIZE) {
                        Serial.println(F("Receive NTP Response"));
                        Udp.read(packetBuffer, NTP_PACKET_SIZE);        // read packet into the buffer
                        unsigned long secsSince1900;
                        // convert four bytes starting at location 40 to a long integer
                        secsSince1900 =  (unsigned long)packetBuffer[40] << 24;
                        secsSince1900 |= (unsigned long)packetBuffer[41] << 16;
                        secsSince1900 |= (unsigned long)packetBuffer[42] << 8;
                        secsSince1900 |= (unsigned long)packetBuffer[43];
                        return secsSince1900 - 2208988800UL + timeZone * SECS_PER_HOUR;
                }
        }
        Serial.println(F("No NTP Response :-("));
        return 0;                                                       // return 0 if unable to get the time
}
//----------------------------send an NTP request to the time server at the given address
void sendNTPpacket(byte address[]){
        // set all bytes in the buffer to 0
        memset(packetBuffer, 0, NTP_PACKET_SIZE);
        // Initialize values needed to form NTP request(see URL above for details on the packets)
        packetBuffer[0]         = 0b11100011;                           // LI, Version, Mode
        packetBuffer[1]         = 0;                                    // Stratum, or type of clock
        packetBuffer[2]         = 6;                                    // |[O64bftHgl
        packetBuffer[3]         = 0xEC;                                 // x
        // 8 bytes of zero for Root Delay & Root Dispersion
        packetBuffer[12]        = 49;                                   // '1'
        packetBuffer[13]        = 0x4E;                                 // 'N'
        packetBuffer[14]        = 49;                                   // '1'
        packetBuffer[15]        = 52;                                   // '4'
        // all NTP fields have been given values, now@you can send a packet requesting a timestamp:                 
        Udp.beginPacket(address, 123);                                  //NTP requests are to port 123
        Udp.write(packetBuffer, NTP_PACKET_SIZE);
        Udp.endPacket();
}
//----------------------------
float   getTempAve(float value){
        if(cntTemp == movingAverageLength)  cntTemp = 0;
        sumTemp         -= tempAve[cntTemp];                            //f[^
        tempAve[cntTemp] = value;                                       //Vf[^o^
        sumTemp         += tempAve[cntTemp];
        cntTemp++;
        return sumTemp/movingAverageLength;
}
//----------------------------
float   getHumAve(float value){
        if(cntHum == movingAverageLength)  cntHum = 0;
        sumHum         -= humAve[cntHum];                               //f[^
        humAve[cntHum] = value;                                         //Vf[^o^
        sumHum         += humAve[cntHum];
        cntHum++;
        return sumHum/movingAverageLength;
}
//----------------------------
float   getPresAve(float value){
        if(cntPres == movingAverageLength)  cntPres = 0;
        sumPres         -= presAve[cntPres];                            //f[^
        presAve[cntPres] = value;                                       //Vf[^o^
        sumPres         += presAve[cntPres];
        cntPres++;
        return sumPres/movingAverageLength;
}
//----------------------------
void setup()
{
        // serial setup
        Serial.begin(115200);
        delay(1000);
        Serial.println(F("Serial interface started"));
        pinMode(LED_BUILTIN, OUTPUT);digitalWrite(LED_BUILTIN, HIGH);
        SPI.begin();                                                    //Initialize the SPI_1 port.
        SPI.setBitOrder(MSBFIRST);                                      // Set the SPI_1 bit order
        SPI.setClockDivider(SPI_CLOCK_DIV4);                            //SPI_CLOCK_DIV16 (72 / 16 = 4.5 MHz SPI_1 speed)
                                                                        //SPI_CLOCK_DIV4 (72 / 4 = 18 MHz SPI_1 speed)
        //Ethernet3gp\API
        pinMode(SPI1_NSS_PIN, OUTPUT);                                  //NIC_CSo
        Ethernet.setCsPin(SPI1_NSS_PIN);                                //NIC_CSATC
        Ethernet.setRstPin(W550io_Rst);                                 //NIC_RSTATC
        //NICZbg
        pinMode(W550io_Rst, OUTPUT);
        digitalWrite(W550io_Rst, LOW);
        delay(10);                                                      //10msecpX
        digitalWrite(W550io_Rst, HIGH);
        LANSetup();
        Serial.println(F("Ethernet interface started")); 
        Serial.print(F("My IP address: "));Serial.println(Ethernet.localIP());// print your local IP address:
        webServer.begin();
        //I2C`FbNB
        Wire.begin();
        Serial.print(F("I2C_Start\n"));
        //BME280@BMP280
        while(!bme.begin())
        {
                Serial.println(F("Could not find BME280 sensor!"));
                delay(1000);
        }
        // bme.chipID(); // Deprecated. See chipModel().
        switch(bme.chipModel()){
                case BME280::ChipModel_BME280:
                        Serial.println(F("Found BME280 sensor! Success."));
                        break;
                case BME280::ChipModel_BMP280:
                        Serial.println(F("Found BMP280 sensor! No Humidity available."));
                        ErrorHUM = true;
                        break;
                default:
                        Serial.println(F("Found UNKNOWN sensor! Error!"));
                        ErrorTMP = true;
                        ErrorHUM = true;
                        ErrorPRS = true;
        }
        SetI2CDeviceInfo(true);
        mcpIn.begin(ID1);
        mcpOut.begin(ID2);
        for(int i = 0;i<16;i++){
                mcpIn.pinMode(i, INPUT);
                mcpIn.pullUp(i, HIGH);                              // turn on a 100K pullup internally
                mcpOut.pinMode(i, OUTPUT);
        }
        
        //DAC(MCP4725)
        while (dac.begin() != true){
                Serial.println(F("MCP4725 is not connected"));          //iFijjtbVAI
                delay(5000);
        }
        Serial.println(F("MCP4725 is OK"));
        Wire.setClock(I2C_BUS_SPEED);                                   //i2c bus speed 100kBp^o400K        
        //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 "));
        //ModBus l
        //VenderString
        int strLength   =       1 +(strlen(VenderString) -1)/2;
        for(int i = 0;i<strLength;i++){
               Mb.MbInputData[i+8]= VenderString[2*i]*256 + VenderString[2*i+1];
        }
        //Serial.println(F("VenderString"));
        //ProductName
        strLength       =       1 +(strlen(titleStr) -1)/2;
        for(int i = 0;i<strLength;i++){
               Mb.MbInputData[i+24]= titleStr[2*i]*256 + titleStr[2*i+1];
        }
        Serial.println(F("ProductName"));
        //SystemVerValue
        Mb.MbInputData[40]= 1*256 + 0;
        Mb.MbInputData[41]= 0*256 + 0;
        //FirmwareVer
        char    mainVer,minorVer,Rev;
        sscanf(FVirsion,"%d.%d.%d",&mainVer,&minorVer,&Rev);
        Mb.MbInputData[42]= mainVer*256 + minorVer;
        Mb.MbInputData[43]= Rev*256 + 0;
        //oLED
        for (int i=0;i<DObits;i++){          
                Mb.SetBit(MB_FC_READ_COILS,i,false);                    //}bvMb.MbData[0]
                pinMode(dOsPins[i],OUTPUT);
                digitalWrite(dOsPins[i], LOW);                         //LEDHIGH_                            
        }
        //pin
        for (int i=0;i<DIbits;i++){
                pinMode(dIsPins[i],INPUT);
                Mb.SetBit(MB_FC_READ_DISCRETE_INPUT,i,!digitalRead(dIsPins[i]));        //bit }bvMb.MbData[1]
        }
        for (int i=0;i<AIbits;i++){
                Mb.MbInputData[i] = analogRead(aIsPins[i]);             //U16
        }
        //NTPT[oANZXZbg
        Udp.begin(localPort);
        rtclock.setTime(getNtpTime());
        //
        Serial.println(F("EndSetUp"));
        rtclock.getTime(mtt);
        sprintf(s, "%04u%02u%02u",mtt.year+1970, mtt.month, mtt.day);
        LEDandKEY.setDisplayToString(s);
        //EHb`hbOIAI[o[t[B50005b@m5.3b
        //1.1.130bX 30000/8 ~@7D8@EEEE@29.3b
        iwdg_init_ms(30000);
}
//----------------------------
long            LoopCount       = 0;
unsigned long   LoopTime        = 0;
unsigned long   PreTime         = micros();
unsigned long   SetTime         = 0;
void loop()
{
        //Loopscantime=sB5boZbgB
        iwdg_feed();                                    //EHb`hbO^C}[ZbgAhbOtB[h        
        SetTime         =       micros();
        LoopTime        =       SetTime - PreTime;
        PreTime         =       SetTime;
        if(LoopCount++ != 0)            Serial.print(F("LoopStart"));Serial.println(LoopCount);
        if(LoopCount > 0x7FFFFFF0)      LoopCount = 0;
        client = webServer.available();
        if(client){
                //digitalWrite(LED, LOW);
                checkWebPage(client);
        }else{
                //digitalWrite(LED, HIGH);           
        }
        client.stop();                                                  // RlNVB
        GetDeviceInfo();
        gMBusConnect = Mb.MbsRun();                                      //SlaveServer
        SetDeviceInfo();
        SetI2CDeviceInfo();
        if((LoopCount%1000)  != 0)      Serial.print(F("."));
        if((LoopCount%40000) != 0)      Serial.println(F(""));
        byte keys       = LEDandKEY.getButtons();
        if(keys != 0){
                switch(keys){
                        case 0x01:
                                LEDandKEY.setDisplayToDecNumber(int(Mb.MbInputData[1]),4,false);
                                lastKey = keys;
                                Serial.print(F("keys=0x"));Serial.println(lastKey,HEX);
                                break;
                        case 0x02:
                                LEDandKEY.setDisplayToDecNumber(int(Mb.MbInputData[2]),4,false);
                                lastKey = keys;
                                Serial.print(F("keys=0x"));Serial.println(lastKey,HEX);
                                break;
                        case 0x04:
                                LEDandKEY.setDisplayToDecNumber(int(pres),0,false);
                                lastKey = keys;
                                Serial.print(F("keys=0x"));Serial.println(lastKey,HEX);
                                break;
                        case 0x08:                                                              //20200810@
                                rtclock.getTime(mtt);
                                sprintf(s,"  %02u%02u%02u",mtt.hour, mtt.minute, mtt.second);
                                LEDandKEY.setDisplayToString(s);
                                lastKey = keys;
                                Serial.print(F("keys=0x"));Serial.println(lastKey,HEX);
                                break;
                        case 0x10:
                                sprintf(s, "   %5s",FVirsion);
                                LEDandKEY.setDisplayToString(s);
                                lastKey = keys;
                                Serial.print(F("keys=0x"));Serial.println(lastKey,HEX);
                                break;
                        case 0x20:
                                LEDandKEY.setDisplayToDecNumber(LoopTime,0,false);
                                lastKey = keys;
                                Serial.print(F("keys=0x"));Serial.println(lastKey,HEX);
                                break;
                        case 0x40:                                              //20210428 C
                                rtclock.getTime(mtt);
                                sprintf(s, "%04u%02u%02u",mtt.year+1970, mtt.month, mtt.day);
                                LEDandKEY.setDisplayToString(s);
                                lastKey = keys;
                                Serial.print(F("keys=0x"));Serial.println(lastKey,HEX);
                                break;
                        case 0x80:
                                //{^LI^l[g
                                if(Mb.GetBit(MB_FC_READ_COILS,6) == true)       Mb.SetBit(MB_FC_READ_COILS,6,false);
                                //AXVI^l[g
                                if(Mb.GetBit(MB_FC_READ_COILS,7) == true)       Mb.SetBit(MB_FC_READ_COILS,7,false);
                                else                                            Mb.SetBit(MB_FC_READ_COILS,7,true);
                                //
                                Serial.print(F("keys=0x"));Serial.println(lastKey,HEX);
                                //`^O100msecdelay
                                delay(500);                                     //20201221@X100->500
                                break;                        
                } 
        }else{
                if(Mb.GetBit(MB_FC_READ_COILS,6) == true)       lastKey = 0x01 << byte(Mb.MbHoldData[7] % 8);
        }
        //LEDandKEY.setLEDs(Mb.MbCoilData[0]>>8);                       //LED&KEYLED_  dlX
        //2021/5/10 T.Wanibe LED\lastKeyX
        for(int i = 0;i < 4;i++){
                word H_Limit = Mb.MbHoldData[2*i+8];
                word L_Limit = Mb.MbHoldData[2*i+9];
                word ActualV = Mb.MbInputData[i];
                Serial.print(F("L_Limit = "));          Serial.print(L_Limit);
                Serial.print(F("\tH_Limit = "));        Serial.print(H_Limit);
                Serial.print(F("\tActualV = "));        Serial.println(ActualV);
                if(H_Limit > L_Limit){
                        if(ActualV < L_Limit){
                                Mb.SetBit(MB_FC_READ_DISCRETE_INPUT,2*i+9,true);
                        }else{
                                Mb.SetBit(MB_FC_READ_DISCRETE_INPUT,2*i+9,false);
                        }
                        if(ActualV > H_Limit){
                                Mb.SetBit(MB_FC_READ_DISCRETE_INPUT,2*i+8,true);
                        }else{
                                Mb.SetBit(MB_FC_READ_DISCRETE_INPUT,2*i+8,false);
                        }
                }
                //Serial.print(F("\tLEDPTN = 0x"));Serial.println(LEDPTN,HEX);
        }
        LEDandKEY.setLEDs(lastKey);
        if((Mb.GetBit(MB_FC_READ_COILS,7) == true) && (lastKey != 0)){
                switch(lastKey){
                        case 0x01:
                                LEDandKEY.setDisplayToDecNumber(int(Mb.MbInputData[1]),4,false);
                                break;
                        case 0x02:
                                LEDandKEY.setDisplayToDecNumber(int(Mb.MbInputData[2]),4,false);
                                break;
                        case 0x04:
                                LEDandKEY.setDisplayToDecNumber(int(pres),0,false);
                                break;
                        case 0x08:
                                rtclock.getTime(mtt);
                                sprintf(s,"  %02u%02u%02u",mtt.hour, mtt.minute, mtt.second);
                                LEDandKEY.setDisplayToString(s);
                                break;        
                        case 0x10:
                                sprintf(s, "   %5s",FVirsion);
                                LEDandKEY.setDisplayToString(s);
                                break;
                        case 0x20:
                                LEDandKEY.setDisplayToDecNumber(LoopTime,0,false);
                                break;
                        case 0x40:
                                rtclock.getTime(mtt);
                                sprintf(s, "%04u%02u%02u",mtt.year+1970, mtt.month, mtt.day);
                                LEDandKEY.setDisplayToString(s);
                                break;
                }
        }        
}
//----------------------------

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;
}

上記ファームコードを実行するとLED&KEYは日付表示をして待機状態になります。

ModbusTCPアクセス】

標準的なModbusMasterツールで、設定されたIPアドレス及びポート番号502を使用して接続可能です。

LED&KEY操作

Slaveデバイス単体でLED&Keyのディスプレーで確認出来る情報があります。

S8ボタンの設定或いは0x10007の状態で逐次更新が可能

ブラウザ操作】

設定されたIPアドレスにブラウザからアクセスするとメニュー頁が表示されリンク先を表示します。

設定メニューでSlaveデバイスの固定IPアドレスの変更が可能です。
また、SNTP時刻設定に利用するサーバの設定が出来ます。
現在値表示ではSlaveデバイスの現在の状況が確認出来ます。

BluePIllにはRTC機能は搭載されているのですが、初期値をどのように設定するか?精度が良くない点をどう対処するか等々課題はあります。
本基では起動時にSNTPで時刻同期します。
精度が良くない点は実感しています。定期的にSNTP同期掛ける方法もありますが、可能ならDS3231のようなRTCに置き換える方が良さそうです。

戯言(nonsense)に戻る


免責事項

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

本ソフトウエアの著作権はToolsBoxに帰属します。
本ソフトウエアをご利用の結果生じた損害について、ToolsBoxは一切責任を負いません。
ToolsBoxはコンテンツとして提供する全ての文章、画像等について、内容の合法性・正確性・安全性等、において最善の注意をし、作成していますが、保証するものではありません。
ToolsBoxはリンクをしている外部サイトについては、何ら保証しません。
ToolsBoxは事前の予告無く、本ソフトウエアの開発・提供を中止する可能性があります。

商標・登録商標

Microsoft、Windows、WindowsNTは米国Microsoft Corporationの米国およびその他の国における登録商標です。
Windows Vista、Windows XPは、米国Microsoft Corporation.の商品名称です。
LabVIEW、National Instruments、NI、ni.comはNational Instrumentsの登録商標です。
I2Cは、NXP Semiconductors社の登録商標です。
その他の企業名ならびに製品名は、それぞれの会社の商標もしくは登録商標です。
すべての商標および登録商標は、それぞれの所有者に帰属します。