最終更新日: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 | ゲートウェイターゲット失敗 | 未対応 |
名称 | アドレス | 説明 | |
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) |
名称 | アドレス | 説明 | |
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) |
名称 | アドレス | 説明 | |
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 |
名称 | アドレス | 説明 | |
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=\"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のディスプレーで確認出来る情報があります。
- 温度(BME280)
- 湿度(BME280)
- 気圧(BME280)
- 現在時刻(SNTP+内部RTC)
- ファームバージョン表記
- LoopTime[msec]
- 日付
S8ボタンの設定或いは0x10007の状態で逐次更新が可能
【ブラウザ操作】
設定されたIPアドレスにブラウザからアクセスするとメニュー頁が表示されリンク先を表示します。
設定メニューでSlaveデバイスの固定IPアドレスの変更が可能です。
また、SNTP時刻設定に利用するサーバの設定が出来ます。
現在値表示ではSlaveデバイスの現在の状況が確認出来ます。BluePIllにはRTC機能は搭載されているのですが、初期値をどのように設定するか?精度が良くない点をどう対処するか等々課題はあります。
本基では起動時にSNTPで時刻同期します。
精度が良くない点は実感しています。定期的にSNTP同期掛ける方法もありますが、可能ならDS3231のようなRTCに置き換える方が良さそうです。
免責事項
本ソフトウエアは、あなたに対して何も保証しません。本ソフトウエアの関係者(他の利用者も含む)は、あなたに対して一切責任を負いません。
あなたが、本ソフトウエアを利用(コンパイル後の再利用など全てを含む)する場合は、自己責任で行う必要があります。本ソフトウエアの著作権は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社の登録商標です。
その他の企業名ならびに製品名は、それぞれの会社の商標もしくは登録商標です。
すべての商標および登録商標は、それぞれの所有者に帰属します。