diff --git a/include/localmodbus.h b/include/localmodbus.h index 994ab84..24ac499 100644 --- a/include/localmodbus.h +++ b/include/localmodbus.h @@ -3,13 +3,20 @@ #include #include + #include #include #include "config.h" enum SubFunctionCode : uint16_t { - RETURN_QUERY_DATA = 0x00, - RESTART_COMMUNICATION_OPTION = 0x01, + RETURN_QUERY_DATA = 0x00, + RESTART_COMMUNICATION_OPTION = 0x01, + RETURN_DIAGNOSTIC_REGISTER = 0x02, + CLEAR_COUNTERS_AND_DIAGNOSTIC_REGISTER = 0x0a, + RETURN_BUS_MESSAGE_COUNT = 0x0b, + RETURN_BUS_COMMUNICATION_ERROR_COUNT = 0x0c, + RETURN_SLAVE_MESSAGE_COUNT = 0x0e, + }; - void setupLocalModbus(uint8_t serverID, ModbusBridgeWiFi *bridge, Config *config, WiFiManager *wm); +void setupLocalModbus(uint8_t serverID, ModbusClientRTU *rtu, ModbusBridgeWiFi *bridge, Config *config, WiFiManager *wm); #endif /* LOCALMODBUS_H */ \ No newline at end of file diff --git a/include/pages.h b/include/pages.h index 3fe2a58..38b6a76 100644 --- a/include/pages.h +++ b/include/pages.h @@ -15,7 +15,8 @@ void sendButton(AsyncResponseStream *response, const char *title, const char *action, const char *css = ""); void sendTableRow(AsyncResponseStream *response, const char *name, uint32_t value); void sendTableRow(AsyncResponseStream *response, const char *name, String value); - void sendDebugForm(AsyncResponseStream *response, String slaveId, String reg, String function, String count); + void sendDebugForm_read(AsyncResponseStream *response, String slaveId, String reg, String function, String count); + void sendDebugForm_diagnosticSerial(AsyncResponseStream *response, String slaveId, String subFunction, String data); void sendMinCss(AsyncResponseStream *response); const String ErrorName(Modbus::Error code); const String WiFiQuality(int rssiValue); diff --git a/src/localmodbus.cpp b/src/localmodbus.cpp index 5c948ad..d5c6820 100644 --- a/src/localmodbus.cpp +++ b/src/localmodbus.cpp @@ -1,6 +1,10 @@ #include "localmodbus.h" #define ETAG "\"" __DATE__ "" __TIME__ "\"" +static ModbusClientRTU* _rtu = NULL; +static ModbusBridgeWiFi* _bridge = NULL; + + // FC03: worker do serve Modbus function code 0x03 (READ_HOLD_REGISTER) ModbusMessage FC03(ModbusMessage request) { uint16_t address; // requested register address @@ -32,32 +36,66 @@ ModbusMessage FC03(ModbusMessage request) { ModbusMessage FC08(ModbusMessage request) { uint16_t subFunctionCode; // Sub-function code ModbusMessage response; // response message to be sent back + uint16_t resultWord = 0; - LOG_D("WORKER CALLED FC08"); + LOG_D("WORKER CALLED FC08\n"); // get request values request.get(2, subFunctionCode); switch(subFunctionCode) { case RETURN_QUERY_DATA: - LOG_D("Return Query Data"); + LOG_D("Return Query Data\n"); response = request; break; case RESTART_COMMUNICATION_OPTION: - LOG_D("Restart Communications Option"); + LOG_D("Restart Communications Option\n"); + response = request; + break; + case CLEAR_COUNTERS_AND_DIAGNOSTIC_REGISTER: + LOG_D("Restart Communications Option\n"); + if(_rtu != NULL) { + _rtu->resetCounts(); + } + if(_bridge != NULL) { + _bridge->resetCounts(); + } response = request; break; + case RETURN_BUS_MESSAGE_COUNT: + LOG_D("Return Bus Message Count\n"); + if(_bridge != NULL) { + resultWord = _bridge->getMessageCount(); + } + response = ModbusMessage(request.getServerID(), DIAGNOSTICS_SERIAL, RETURN_BUS_MESSAGE_COUNT, resultWord); + break; + case RETURN_BUS_COMMUNICATION_ERROR_COUNT: + LOG_D("Return Bus Communication Error Count\n"); + if(_bridge != NULL) { + resultWord = _bridge->getErrorCount(); + } + response = ModbusMessage(request.getServerID(), DIAGNOSTICS_SERIAL, RETURN_BUS_COMMUNICATION_ERROR_COUNT, resultWord); + break; + case RETURN_SLAVE_MESSAGE_COUNT: + LOG_D("Return Slave Message Count\n"); + if(_rtu != NULL) { + resultWord = _rtu->getMessageCount(); + } + response = ModbusMessage(request.getServerID(), DIAGNOSTICS_SERIAL, RETURN_SLAVE_MESSAGE_COUNT, resultWord); + break; default: - LOG_D("default: ILLEGAL_FUNCTION"); + LOG_D("default: ILLEGAL_FUNCTION\n"); response.setError(request.getServerID(), request.getFunctionCode(), ILLEGAL_FUNCTION); } return response; } -void setupLocalModbus(uint8_t serverID, ModbusBridgeWiFi *bridge, Config *config, WiFiManager *wm) { +void setupLocalModbus(uint8_t serverID, ModbusClientRTU *rtu, ModbusBridgeWiFi *bridge, Config *config, WiFiManager *wm) { String EfuseMac = String(ESP.getEfuseMac(), 16); dbgln(EfuseMac); + _rtu = rtu; + _bridge = bridge; bridge->registerWorker(serverID, READ_HOLD_REGISTER, &FC03); bridge->registerWorker(serverID, DIAGNOSTICS_SERIAL, &FC08); //bridge->registerWorker(serverID, REPORT_SERVER_ID_SERIAL, &FC08); diff --git a/src/main.cpp b/src/main.cpp index 8a6abb7..b6b7492 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -59,20 +59,16 @@ boolean WiFiConnect() { // Callback for reconnecting void WiFiLostIP(WiFiEvent_t event, WiFiEventInfo_t info) { dbgln("[WiFi] (possibly) disconnected"); + wm.disconnect(); + dbgln("[WiFi] trying to reconnect"); + MDNS.end(); - if(WiFi.status() != WL_CONNECTED) { - dbgln("[WiFi] trying to reconnect"); - MDNS.end(); - - isConnected = false; - isConnected = WiFiConnect(); + isConnected = false; + isConnected = WiFiConnect(); - // Start mDNS - if(isConnected) { - startMDNS(80, config.getTcpPort()); - } - } else { - dbgln("[WiFi] actually is connected"); + // Start mDNS + if(isConnected) { + startMDNS(80, config.getTcpPort()); } } @@ -113,7 +109,7 @@ void setup() { // register worker for local Modbus function if(skipAddress) { - setupLocalModbus(config.getLocalModbusAddress(), &MBbridge, &config, &wm); + setupLocalModbus(config.getLocalModbusAddress(), MBclient, &MBbridge, &config, &wm); } // Start Modbus Bridge diff --git a/src/pages.cpp b/src/pages.cpp index 054e375..59bdcd9 100644 --- a/src/pages.cpp +++ b/src/pages.cpp @@ -2,8 +2,13 @@ #include "localmodbus.h" #define ETAG "\"" __DATE__ "" __TIME__ "\"" +// Remember last slave-ID +static uint8_t _lastSlaveID; + void setupPages(AsyncWebServer *server, ModbusClientRTU *rtu, ModbusBridgeWiFi *bridge, Config *config, WiFiManager *wm){ + _lastSlaveID = 1; + server->on("/", HTTP_GET, [](AsyncWebServerRequest *request){ dbgln("[webserver] GET /"); auto *response = request->beginResponseStream("text/html"); @@ -26,7 +31,7 @@ void setupPages(AsyncWebServer *server, ModbusClientRTU *rtu, ModbusBridgeWiFi * // show ESP infos... sendTableRow(response, "WiFi TX Power (dBm)", String(((float)WiFi.getTxPower())/4, 2)); - sendTableRow(response, "ESP Temperature (C)", String(temperatureRead(), 2)); + sendTableRow(response, "ESP Temperature (°C)", String(temperatureRead(), 2)); sendTableRow(response, "ESP Uptime (sec)", esp_timer_get_time() / 1000000); sendTableRow(response, "ESP SSID", WiFi.SSID()); sendTableRow(response, "ESP RSSI", WiFi.RSSI()); @@ -369,29 +374,40 @@ void setupPages(AsyncWebServer *server, ModbusClientRTU *rtu, ModbusBridgeWiFi * server->on("/debug", HTTP_GET, [](AsyncWebServerRequest *request){ dbgln("[webserver] GET /debug"); auto *response = request->beginResponseStream("text/html"); - sendResponseHeader(response, "Debug - Read from Device"); - sendDebugForm(response, "1", "1", "3", "1"); - //sendButton(response, "Read Device", "/read"); + sendResponseHeader(response, "Modbus Debug"); + sendButton(response, "Read (FC01 - FC04)", "debug_read"); + sendButton(response, "Diagnostic Serial (FC08)", "debug_diagnosticSerial"); sendButton(response, "Back", "/"); sendResponseTrailer(response); request->send(response); }); - // server->on("read", HTTP_GET, [](AsyncWebServerRequest *request){ - // dbgln("[webserver] GET /read"); - // auto *response = request->beginResponseStream("text/html"); - // sendResponseHeader(response, "Debug - Read from Device"); - // sendDebugForm(response, "1", "1", "3", "1"); - // sendButton(response, "Back", "/debug"); - // sendResponseTrailer(response); - // request->send(response); - // }); - - server->on("/debug", HTTP_POST, [config, rtu, bridge](AsyncWebServerRequest *request){ - dbgln("[webserver] POST /debug"); + server->on("/debug_read", HTTP_GET, [](AsyncWebServerRequest *request){ + dbgln("[webserver] GET /debug_read"); + auto *response = request->beginResponseStream("text/html"); + sendResponseHeader(response, "Modbus Debug - Read from Device"); + sendDebugForm_read(response, String(_lastSlaveID), "1", "3", "1"); + sendButton(response, "Back", "debug"); + sendResponseTrailer(response); + request->send(response); + }); + + server->on("/debug_diagnosticSerial", HTTP_GET, [](AsyncWebServerRequest *request){ + dbgln("[webserver] GET /debug_diagnosticSerial"); + auto *response = request->beginResponseStream("text/html"); + sendResponseHeader(response, "Modbus Debug - Diagnostic Serial"); + sendDebugForm_diagnosticSerial(response, String(_lastSlaveID), "0", "0000"); + sendButton(response, "Back", "debug"); + sendResponseTrailer(response); + request->send(response); + }); + + server->on("/debug_read", HTTP_POST, [config, rtu, bridge](AsyncWebServerRequest *request){ + dbgln("[webserver] POST /debug_read"); String slaveId = "1"; if (request->hasParam("slave", true)){ slaveId = request->getParam("slave", true)->value(); + _lastSlaveID = (uint8_t) slaveId.toInt(); } String reg = "1"; if (request->hasParam("reg", true)){ @@ -406,7 +422,7 @@ void setupPages(AsyncWebServer *server, ModbusClientRTU *rtu, ModbusBridgeWiFi * count = request->getParam("count", true)->value(); } auto *response = request->beginResponseStream("text/html"); - sendResponseHeader(response, "Debug - Read from Device"); + sendResponseHeader(response, "Modbus Debug - Read from Device"); response->print("
");
     auto previous = LOGDEVICE;
     auto previousLevel = MBUlogLvl;
@@ -444,8 +460,77 @@ void setupPages(AsyncWebServer *server, ModbusClientRTU *rtu, ModbusBridgeWiFi *
     else{
       response->printf("Error: %#02x (%s)", error, ErrorName(error).c_str());
     }
-    sendDebugForm(response, slaveId, reg, func, count);
-    sendButton(response, "Back", "/");
+    sendDebugForm_read(response, slaveId, reg, func, count);
+    sendButton(response, "Back", "debug");
+    sendResponseTrailer(response);
+    request->send(response);
+  });
+
+  server->on("/debug_diagnosticSerial", HTTP_POST, [config, rtu, bridge](AsyncWebServerRequest *request){
+    dbgln("[webserver] POST /debug_diagnosticSerial");
+    String slaveId = "1";
+    if (request->hasParam("slave", true)){
+      slaveId = request->getParam("slave", true)->value();
+      _lastSlaveID = (uint8_t) slaveId.toInt();
+    }
+    String subFunction = "0";
+    if (request->hasParam("sf", true)){
+      subFunction = request->getParam("sf", true)->value();
+    }
+    String data = "0000";
+    if (request->hasParam("dt", true)){
+      data = request->getParam("dt", true)->value();
+    }
+
+    // convert data string to WORD
+    uint16_t dataWord = 0;
+    if(data.length() != 0) {
+      dataWord = (uint16_t) strtol(data.c_str(), NULL, 16);
+    } 
+    
+    auto *response = request->beginResponseStream("text/html");
+    sendResponseHeader(response, "Modbus Debug - Diagnostic Serial");
+    response->print("
");
+    auto previous = LOGDEVICE;
+    auto previousLevel = MBUlogLvl;
+    auto debug = WebPrint(previous, response);
+    LOGDEVICE = &debug;
+    MBUlogLvl = LOG_LEVEL_DEBUG;
+    ModbusMessage answer;
+    // Call local worker (when enabled)
+    if(slaveId.toInt() == config->getLocalModbusAddress() && config->getLocalModbusEnable()) {
+      MBSworker lclWrkr = bridge->getWorker(slaveId.toInt(), DIAGNOSTICS_SERIAL);
+      if(lclWrkr != NULL) {
+        LOG_D("found local worker... calling for response\n");
+        answer = lclWrkr(ModbusMessage(slaveId.toInt(), DIAGNOSTICS_SERIAL, subFunction.toInt(), dataWord));        
+      } else {
+        LOG_D("no local worker found... answering with ILLEGAL_FUNCTION\n");
+        answer.setError(slaveId.toInt(), DIAGNOSTICS_SERIAL, ILLEGAL_FUNCTION);
+      }
+    } else {
+      // Call via RTU
+      answer = rtu->syncRequest(0xdeadbeef, slaveId.toInt(), DIAGNOSTICS_SERIAL, subFunction.toInt(), dataWord);   
+    }    
+    MBUlogLvl = previousLevel;
+    LOGDEVICE = previous;
+    response->print("
"); + auto error = answer.getError(); + if (error == SUCCESS){ + auto count = answer.size() - 2; + if(count < 0) { + count = 0; + } + response->print("Answer: 0x"); + for (size_t i = 0; i < count; i++) { + response->printf("%02x", answer[i + 2]); + } + response->print(""); + } + else { + response->printf("Error: %#02x (%s)", error, ErrorName(error).c_str()); + } + sendDebugForm_diagnosticSerial(response, slaveId, subFunction, data); + sendButton(response, "Back", "debug"); sendResponseTrailer(response); request->send(response); @@ -540,6 +625,7 @@ void setupPages(AsyncWebServer *server, ModbusClientRTU *rtu, ModbusBridgeWiFi * sendResponseTrailer(response); request->send(response); }); + server->on("/wifi", HTTP_POST, [wm](AsyncWebServerRequest *request){ dbgln("[webserver] POST /wifi"); request->redirect("/"); @@ -549,10 +635,12 @@ void setupPages(AsyncWebServer *server, ModbusClientRTU *rtu, ModbusBridgeWiFi * ESP.restart(); dbgln("[webserver] rebooted..."); }); + server->on("/favicon.ico", [](AsyncWebServerRequest *request){ dbgln("[webserver] GET /favicon.ico"); request->send(204);//TODO add favicon }); + server->on("/style.css", [](AsyncWebServerRequest *request){ if (request->hasHeader("If-None-Match")){ auto header = request->getHeader("If-None-Match"); @@ -588,6 +676,7 @@ void setupPages(AsyncWebServer *server, ModbusClientRTU *rtu, ModbusBridgeWiFi * response->addHeader("ETag", ETAG); request->send(response); }); + server->onNotFound([](AsyncWebServerRequest *request){ dbg("[webserver] request to "); dbg(request->url()); dbgln("not found"); request->send(404, "text/plain", "404"); @@ -672,7 +761,55 @@ void sendTableRow(AsyncResponseStream *response, const char *name, uint32_t valu "", name, value); } -void sendDebugForm(AsyncResponseStream *response, String slaveId, String reg, String function, String count){ +void sendDebugForm_diagnosticSerial(AsyncResponseStream *response, String slaveId, String subFunction, String data) { + response->print("
"); + response->print("" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "
" + "" + ""); + response->printf("", slaveId.c_str()); + response->print("
" + "" + ""); + response->printf("" + "
" + "" + ""); + response->printf("", data.c_str()); + response->print("
"); + response->print("" + "
" + "

"); + response->print(""); +} + +void sendDebugForm_read(AsyncResponseStream *response, String slaveId, String reg, String function, String count) { response->print("
"); response->print("" ""