From c2c3d81805bf0a339c0763239e93c2a140b19437 Mon Sep 17 00:00:00 2001 From: Alexander Entinger Date: Mon, 15 Jul 2024 08:27:57 +0200 Subject: [PATCH 1/3] Vanilla integration of Arduino Opta Modbus control into OPC/UA server example sketch. --- examples/opcua_server/opcua_server.ino | 60 ++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/examples/opcua_server/opcua_server.ino b/examples/opcua_server/opcua_server.ino index c2ad08d..75579b6 100644 --- a/examples/opcua_server/opcua_server.ino +++ b/examples/opcua_server/opcua_server.ino @@ -13,6 +13,30 @@ #include "mbed_mem_trace.h" #endif +#ifndef USE_MODBUS_SENSOR_MD02 +# define USE_MODBUS_SENSOR_MD02 (1) +#endif + +#if USE_MODBUS_SENSOR_MD02 +# include +# include +#endif + +/************************************************************************************** + * CONSTANTS + **************************************************************************************/ + +#if USE_MODBUS_SENSOR_MD02 +static unsigned int const MODBUS_BAUDRATE = 9600; +static float const MODBUS_BIT_DURATION = 1.f / MODBUS_BAUDRATE; +static float const MODBUS_PRE_DELAY_BR = MODBUS_BIT_DURATION * 9.6f * 3.5f * 1e6; +static float const MODBUS_POST_DELAY_BR = MODBUS_BIT_DURATION * 9.6f * 3.5f * 1e6; + +static int const MODBUS_DEVICE_ID = 1; +static int const MODBUS_DEVICE_TEMPERATURE_REGISTER = 0x0001; +static int const MODBUS_DEVICE_HUMIDITY_REGISTER = 0x0002; +#endif + /************************************************************************************** * GLUE CODE **************************************************************************************/ @@ -186,6 +210,16 @@ void setup() //for (; !Serial && (millis() - start) < 1000; ) { } while (!Serial) { } +#if USE_MODBUS_SENSOR_MD02 + RS485.setDelays(MODBUS_PRE_DELAY_BR, MODBUS_POST_DELAY_BR); + if (!ModbusRTUClient.begin(MODBUS_BAUDRATE, SERIAL_8N1)) + { + Serial.println("Failed to start Modbus RTU Client!"); + for (;;) { } + } + ModbusRTUClient.setTimeout(2 * 1000UL); /* 2 seconds. */ +#endif + /* Initialize Ethernet interface and print obtained IP to Serial. */ if (!Ethernet.begin()) { Serial.println("\"Ethernet.begin()\" failed."); @@ -314,4 +348,30 @@ void loop() { digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN)); delay(500); + +#if USE_MODBUS_SENSOR_MD02 + if (!ModbusRTUClient.requestFrom(MODBUS_DEVICE_ID, INPUT_REGISTERS, MODBUS_DEVICE_TEMPERATURE_REGISTER, 1)) { + Serial.print("failed to read temperature register! "); + Serial.println(ModbusRTUClient.lastError()); + return; + } + if (ModbusRTUClient.available()) + { + int16_t const temperature_raw = ModbusRTUClient.read(); + float const temperature_deg = temperature_raw / 100.f; + Serial.println(temperature_deg); + } + + if (!ModbusRTUClient.requestFrom(MODBUS_DEVICE_ID, INPUT_REGISTERS, MODBUS_DEVICE_HUMIDITY_REGISTER, 1)) { + Serial.print("failed to read humidity register! "); + Serial.println(ModbusRTUClient.lastError()); + return; + } + if (ModbusRTUClient.available()) + { + int16_t const humidity_raw = ModbusRTUClient.read(); + float const humidity_per_cent = humidity_raw / 100.f; + Serial.println(humidity_per_cent); + } +#endif } From 1f805312f65f5321f266446aec3bafaf872717c2 Mon Sep 17 00:00:00 2001 From: Alexander Entinger Date: Tue, 16 Jul 2024 14:45:19 +0200 Subject: [PATCH 2/3] Expose temperature obtained via ModBus by OPC/UA. --- examples/opcua_server/opcua_server.ino | 67 +++++++++++++++++++++----- src/ArduinoOpta.h | 1 + 2 files changed, 57 insertions(+), 11 deletions(-) diff --git a/examples/opcua_server/opcua_server.ino b/examples/opcua_server/opcua_server.ino index 75579b6..ecd5e87 100644 --- a/examples/opcua_server/opcua_server.ino +++ b/examples/opcua_server/opcua_server.ino @@ -101,6 +101,9 @@ O1HeapInstance * o1heap_ins = nullptr; rtos::Thread opc_ua_server_thread(osPriorityNormal, OPC_UA_SERVER_THREAD_STACK.size(), OPC_UA_SERVER_THREAD_STACK.data()); opcua::ArduinoOpta::SharedPtr arduino_opta_opcua; +#if USE_MODBUS_SENSOR_MD02 +UA_NodeId modbus_md02_temperature_node_id; +#endif /************************************************************************************** * DEFINES @@ -300,6 +303,54 @@ void setup() arduino_opta_opcua->led_mgr()->add_led_output(opc_ua_server, "User LED", [](bool const value) { pinMode(LEDB, OUTPUT); digitalWrite(LEDB, value); }); } +#if USE_MODBUS_SENSOR_MD02 + { + UA_StatusCode rc = UA_STATUSCODE_GOOD; + UA_ObjectAttributes oAttr = UA_ObjectAttributes_default; + oAttr.displayName = UA_LOCALIZEDTEXT("en-US", "Modbus RS485 MD02 Sensor"); + UA_NodeId modbus_md02_node_id; + rc = UA_Server_addObjectNode(opc_ua_server, + UA_NODEID_NULL, + arduino_opta_opcua->node_id(), + UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES), + UA_QUALIFIEDNAME(1, "ModbusRs485Md02"), + UA_NODEID_NUMERIC(0, UA_NS0ID_BASEOBJECTTYPE), + oAttr, + NULL, + &modbus_md02_node_id); + if (UA_StatusCode_isBad(rc)) { + UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "Modbus MD02 Sensor: UA_Server_addObjectNode(...) failed with %s", UA_StatusCode_name(rc)); + return; + } + + UA_VariableAttributes temperature_value_attr = UA_VariableAttributes_default; + + /* Obtain the current value of the input pin. */ + UA_Float temperature_value = 0.f; + UA_Variant_setScalar(&temperature_value_attr.value, &temperature_value, &UA_TYPES[UA_TYPES_FLOAT]); + + temperature_value_attr.displayName = UA_LOCALIZEDTEXT("en-US", "Temperature / °C"); + temperature_value_attr.dataType = UA_TYPES[UA_TYPES_FLOAT].typeId; + temperature_value_attr.accessLevel = UA_ACCESSLEVELMASK_READ; + + /* Add the variable node. */ + rc = UA_Server_addVariableNode(opc_ua_server, + UA_NODEID_NULL, + modbus_md02_node_id, + UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT), + UA_QUALIFIEDNAME(1, "md02_temperature_deg"), + UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE), + temperature_value_attr, + NULL, + &modbus_md02_temperature_node_id); + if (UA_StatusCode_isBad(rc)) + { + UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "Modbus MD02 Sensor: UA_Server_addVariableNode(...) failed with %s", UA_StatusCode_name(rc)); + return; + } + } +#endif + /* Print some threading related message. */ UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "stack: size = %d | free = %d | used = %d | max = %d", @@ -360,18 +411,12 @@ void loop() int16_t const temperature_raw = ModbusRTUClient.read(); float const temperature_deg = temperature_raw / 100.f; Serial.println(temperature_deg); - } - if (!ModbusRTUClient.requestFrom(MODBUS_DEVICE_ID, INPUT_REGISTERS, MODBUS_DEVICE_HUMIDITY_REGISTER, 1)) { - Serial.print("failed to read humidity register! "); - Serial.println(ModbusRTUClient.lastError()); - return; - } - if (ModbusRTUClient.available()) - { - int16_t const humidity_raw = ModbusRTUClient.read(); - float const humidity_per_cent = humidity_raw / 100.f; - Serial.println(humidity_per_cent); + UA_Float temperature_deg_opcua_value = temperature_deg; + UA_Variant temperature_deg_opcua_variant; + UA_Variant_init(&temperature_deg_opcua_variant); + UA_Variant_setScalar(&temperature_deg_opcua_variant, &temperature_deg_opcua_value, &UA_TYPES[UA_TYPES_FLOAT]); + UA_Server_writeValue(opc_ua_server, modbus_md02_temperature_node_id, temperature_deg_opcua_variant); } #endif } diff --git a/src/ArduinoOpta.h b/src/ArduinoOpta.h index 21710c8..364cd52 100644 --- a/src/ArduinoOpta.h +++ b/src/ArduinoOpta.h @@ -49,6 +49,7 @@ class ArduinoOpta RelayManager::SharedPtr relay_mgr(); LedManager::SharedPtr led_mgr(); + [[nodiscard]] UA_NodeId node_id() const { return _node_id; } private: UA_Server * _server; From afd9b889d8292b2f569edfd8f5b87473447f6550 Mon Sep 17 00:00:00 2001 From: Alexander Entinger Date: Wed, 17 Jul 2024 06:04:30 +0200 Subject: [PATCH 3/3] Minimal documentation on how to build Modbus demo. --- examples/opcua_server/README.md | 7 +++++++ examples/opcua_server/opcua_server.ino | 4 ---- 2 files changed, 7 insertions(+), 4 deletions(-) create mode 100644 examples/opcua_server/README.md diff --git a/examples/opcua_server/README.md b/examples/opcua_server/README.md new file mode 100644 index 0000000..003e523 --- /dev/null +++ b/examples/opcua_server/README.md @@ -0,0 +1,7 @@ +:floppy_disk: `opcua_server.ino` +================================ + +### How-to-build Modbus MD02 demo +```bash +arduino-cli compile --fqbn arduino:mbed_opta:opta examples/opcua_server -v --build-property compiler.cpp.extra_flags="-DUSE_MODBUS_SENSOR_MD02=1" +``` diff --git a/examples/opcua_server/opcua_server.ino b/examples/opcua_server/opcua_server.ino index ecd5e87..2b0cc45 100644 --- a/examples/opcua_server/opcua_server.ino +++ b/examples/opcua_server/opcua_server.ino @@ -13,10 +13,6 @@ #include "mbed_mem_trace.h" #endif -#ifndef USE_MODBUS_SENSOR_MD02 -# define USE_MODBUS_SENSOR_MD02 (1) -#endif - #if USE_MODBUS_SENSOR_MD02 # include # include