diff --git a/examples/opcua_server/opcua_server.ino b/examples/opcua_server/opcua_server.ino index d78aecc..22bbb7e 100644 --- a/examples/opcua_server/opcua_server.ino +++ b/examples/opcua_server/opcua_server.ino @@ -197,7 +197,13 @@ void setup() UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "Arduino Opta IP: %s", Ethernet.localIP().toString().c_str()); - UA_StatusCode rc = UA_STATUSCODE_GOOD; + /* Determine the Arduino OPC/UA hardware variant. */ + opcua::ArduinoOptaVariant::Type opta_type; + if (!opcua::ArduinoOptaVariant::get_opta_variant(opta_type)) { + UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "opcua::ArduinoOptaVariant::get_opta_variant(...) failed"); + return; + } + UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "Arduino Opta Variant: %s", opcua::ArduinoOptaVariant::toString(opta_type).c_str()); /* Define the Arduino Opta as a OPC/UA object. */ arduino_opta_opcua = opcua::ArduinoOpta::create(opc_ua_server); @@ -234,6 +240,11 @@ void setup() arduino_opta_opcua->relay_mgr()->add_relay_output(opc_ua_server, "Relay 3", [](bool const value) { pinMode(RELAY3, OUTPUT); digitalWrite(RELAY3, value); pinMode(LED_D2, OUTPUT); digitalWrite(LED_D2, value);}); arduino_opta_opcua->relay_mgr()->add_relay_output(opc_ua_server, "Relay 4", [](bool const value) { pinMode(RELAY4, OUTPUT); digitalWrite(RELAY4, value); pinMode(LED_D3, OUTPUT); digitalWrite(LED_D3, value);}); + /* Add the various LED outputs. */ + if (opta_type == opcua::ArduinoOptaVariant::Type::WiFi) { + arduino_opta_opcua->led_mgr()->add_led_output(opc_ua_server, "User LED", [](bool const value) { pinMode(LEDB, OUTPUT); digitalWrite(LEDB, value); }); + } + /* Print some threading related message. */ UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "stack: size = %d | free = %d | used = %d | max = %d", diff --git a/src/ArduinoOpta.cpp b/src/ArduinoOpta.cpp index aea4bf1..10264fa 100644 --- a/src/ArduinoOpta.cpp +++ b/src/ArduinoOpta.cpp @@ -46,6 +46,11 @@ ArduinoOpta::ArduinoOpta(UA_Server * server, UA_NodeId const & node_id) if (!_relay_mgr) { UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "ArduinoOpta::Ctor: RelayManager::create(...) failed."); } + + _led_mgr = opcua::LedManager::create(server, _node_id); + if (!_led_mgr) { + UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "ArduinoOpta::Ctor: LedManager::create(...) failed."); + } } /************************************************************************************** diff --git a/src/ArduinoOpta.h b/src/ArduinoOpta.h index 6f3ab97..5f21076 100644 --- a/src/ArduinoOpta.h +++ b/src/ArduinoOpta.h @@ -17,6 +17,7 @@ #include +#include "LedManager.h" #include "RelayManager.h" #include "AnalogInputManager.h" #include "DigitalInputManager.h" @@ -45,6 +46,7 @@ class ArduinoOpta inline AnalogInputManager::SharedPtr analog_input_mgr() const { return _analog_input_mgr; } inline DigitalInputManager::SharedPtr digital_input_mgr() const { return _digital_input_mgr; } inline RelayManager::SharedPtr relay_mgr() const { return _relay_mgr; } + inline LedManager::SharedPtr led_mgr() const { return _led_mgr; } private: @@ -53,6 +55,7 @@ class ArduinoOpta AnalogInputManager::SharedPtr _analog_input_mgr; DigitalInputManager::SharedPtr _digital_input_mgr; RelayManager::SharedPtr _relay_mgr; + LedManager::SharedPtr _led_mgr; }; /************************************************************************************** diff --git a/src/ArduinoOptaVariant.cpp b/src/ArduinoOptaVariant.cpp new file mode 100644 index 0000000..fb1b503 --- /dev/null +++ b/src/ArduinoOptaVariant.cpp @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2024 Arduino + * + * SPDX-License-Identifier: MPL-2.0 + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +/************************************************************************************** + * INCLUDE + **************************************************************************************/ + +#include "ArduinoOptaVariant.h" + +#if __has_include("opta_info.h") +# include +# include "opta_info.h" +OptaBoardInfo * boardInfo(); +#else +# error "Can not find include file \"opta_info.h\"" +#endif + +/************************************************************************************** + * NAMESPACE + **************************************************************************************/ + +namespace opcua +{ + +/************************************************************************************** + * PUBLIC MEMBER FUNCTIONS + **************************************************************************************/ + +bool ArduinoOptaVariant::get_opta_variant(Type & type) +{ + OptaBoardInfo * info = boardInfo(); + + if (info->_board_functionalities.ethernet && info->_board_functionalities.wifi && info->_board_functionalities.rs485) { + type = ArduinoOptaVariant::Type::WiFi; + } + else if (info->_board_functionalities.ethernet && info->_board_functionalities.rs485) { + type = ArduinoOptaVariant::Type::RS485; + } + else if (info->_board_functionalities.ethernet) { + type = ArduinoOptaVariant::Type::Lite; + } + else + return false; + + return true; +} + +std::string ArduinoOptaVariant::toString(Type const type) +{ + switch(type) + { + case ArduinoOptaVariant::Type::WiFi: return std::string("Arduino Opta WiFi"); break; + case ArduinoOptaVariant::Type::RS485: return std::string("Arduino Opta RS485"); break; + case ArduinoOptaVariant::Type::Lite: return std::string("Arduino Opta Lite"); break; + default: __builtin_unreachable(); break; + } +} + +/************************************************************************************** + * NAMESPACE + **************************************************************************************/ + +} /* opcua */ diff --git a/src/ArduinoOptaVariant.h b/src/ArduinoOptaVariant.h new file mode 100644 index 0000000..e5d87c8 --- /dev/null +++ b/src/ArduinoOptaVariant.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2024 Arduino + * + * SPDX-License-Identifier: MPL-2.0 + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#pragma once + +/************************************************************************************** + * INCLUDE + **************************************************************************************/ + +#include + +/************************************************************************************** + * NAMESPACE + **************************************************************************************/ + +namespace opcua +{ + +/************************************************************************************** + * CLASS DECLARATION + **************************************************************************************/ + +class ArduinoOptaVariant +{ +public: + ArduinoOptaVariant() = delete; + ArduinoOptaVariant(ArduinoOptaVariant const &) = delete; + + enum class Type { Lite, RS485, WiFi }; + + static bool get_opta_variant(Type & type); + + static std::string toString(Type const type); +}; + +/************************************************************************************** + * NAMESPACE + **************************************************************************************/ + +} /* opcua */ diff --git a/src/Arduino_open62541.h b/src/Arduino_open62541.h index b29b627..4ca5ed8 100644 --- a/src/Arduino_open62541.h +++ b/src/Arduino_open62541.h @@ -15,10 +15,9 @@ #include "open62541.h" #include "o1heap/o1heap.h" -#include "Relay.h" + #include "ArduinoOpta.h" -#include "AnalogInputManager.h" -#include "DigitalInputManager.h" +#include "ArduinoOptaVariant.h" /************************************************************************************** * DEFINES diff --git a/src/Led.cpp b/src/Led.cpp new file mode 100644 index 0000000..a48be11 --- /dev/null +++ b/src/Led.cpp @@ -0,0 +1,129 @@ +/* + * Copyright (c) 2024 Arduino + * + * SPDX-License-Identifier: MPL-2.0 + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +/************************************************************************************** + * INCLUDE + **************************************************************************************/ + +#include "Led.h" + +/************************************************************************************** + * NAMESPACE + **************************************************************************************/ + +namespace opcua +{ + +/************************************************************************************** + * FUNCTION DEFINITION + **************************************************************************************/ + +static void led_on_write_request(UA_Server *server, + const UA_NodeId *sessionId, + void *sessionContext, + const UA_NodeId *nodeid, + void *nodeContext, + const UA_NumericRange *range, + const UA_DataValue *data) +{ + bool const value = *(UA_Boolean *)(data->value.data) == true; + Led * this_ptr = reinterpret_cast(nodeContext); + this_ptr->onWriteRequest(server, nodeid, value); +} + +/************************************************************************************** + * CTOR/DTOR + **************************************************************************************/ + +Led::Led(UA_NodeId const & node_id, OnSetLedStateFunc const on_set_led_state) + : _node_id{node_id} + , _on_set_led_state{on_set_led_state} +{ + +} + +/************************************************************************************** + * PUBLIC MEMBER FUNCTIONS + **************************************************************************************/ + +Led::SharedPtr Led::create(UA_Server *server, + UA_NodeId const &parent_node_id, + const char *display_name, + OnSetLedStateFunc const on_set_led_state) +{ + UA_StatusCode rc = UA_STATUSCODE_GOOD; + + UA_VariableAttributes led_value_attr = UA_VariableAttributes_default; + + UA_Boolean led_value = false; + UA_Variant_setScalar(&led_value_attr.value, &led_value, &UA_TYPES[UA_TYPES_BOOLEAN]); + + led_value_attr.displayName = UA_LOCALIZEDTEXT("en-US", (char *)display_name); + led_value_attr.dataType = UA_TYPES[UA_TYPES_BOOLEAN].typeId; + led_value_attr.accessLevel = + UA_ACCESSLEVELMASK_READ | + UA_ACCESSLEVELMASK_WRITE | UA_ACCESSLEVELMASK_STATUSWRITE | + UA_ACCESSLEVELMASK_TIMESTAMPWRITE; /* Status and timestamp write access necessary for opcua-client. */ + + UA_NodeId node_id; + rc = UA_Server_addVariableNode(server, + UA_NODEID_NULL, + parent_node_id, + UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT), + UA_QUALIFIEDNAME(1, "Value"), + UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE), + led_value_attr, + NULL, + &node_id); + if (UA_StatusCode_isBad(rc)) + { + UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, + "Led::create: UA_Server_addVariableNode(...) failed with %s", UA_StatusCode_name(rc)); + return nullptr; + } + + /* Create an instance of Led here. */ + auto const instance_ptr = std::make_shared(node_id, on_set_led_state); + + rc = UA_Server_setNodeContext(server, node_id, reinterpret_cast(instance_ptr.get())); + if (UA_StatusCode_isBad(rc)) + { + UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, + "Led::create: UA_Server_setNodeContext(...) failed with %s", + UA_StatusCode_name(rc)); + return nullptr; + } + + UA_ValueCallback callback; + callback.onRead = NULL; + callback.onWrite = led_on_write_request; + rc = UA_Server_setVariableNode_valueCallback(server, node_id, callback); + if (UA_StatusCode_isBad(rc)) + { + UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, + "Led::create: UA_Server_setVariableNode_valueCallback(...) failed with %s", + UA_StatusCode_name(rc)); + return nullptr; + } + + return instance_ptr; +} + +void Led::onWriteRequest(UA_Server * server, UA_NodeId const * node_id, bool const value) +{ + /* Some debug output. */ + UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "Led::onWriteRequest: value = %d", value); + _on_set_led_state(value); +} + +/************************************************************************************** + * NAMESPACE + **************************************************************************************/ + +} /* opcua */ diff --git a/src/Led.h b/src/Led.h new file mode 100644 index 0000000..f0b4185 --- /dev/null +++ b/src/Led.h @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2024 Arduino + * + * SPDX-License-Identifier: MPL-2.0 + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#pragma once + +/************************************************************************************** + * INCLUDE + **************************************************************************************/ + +#include "open62541.h" + +#include +#include + +/************************************************************************************** + * NAMESPACE + **************************************************************************************/ + +namespace opcua +{ + +/************************************************************************************** + * CLASS DECLARATION + **************************************************************************************/ + +class Led +{ +public: + typedef std::shared_ptr SharedPtr; + typedef std::function OnSetLedStateFunc; + + static SharedPtr create(UA_Server *server, + UA_NodeId const &parent_node_id, + const char *display_name, + OnSetLedStateFunc const on_set_led_state); + + Led(UA_NodeId const &node_id, OnSetLedStateFunc const on_set_led_state); + + void onWriteRequest(UA_Server * server, UA_NodeId const * node_id, bool const value); + + +private: + UA_NodeId _node_id; + OnSetLedStateFunc const _on_set_led_state; +}; + +/************************************************************************************** + * NAMESPACE + **************************************************************************************/ + +} /* opcua */ diff --git a/src/LedManager.cpp b/src/LedManager.cpp new file mode 100644 index 0000000..75e336f --- /dev/null +++ b/src/LedManager.cpp @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2024 Arduino + * + * SPDX-License-Identifier: MPL-2.0 + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +/************************************************************************************** + * INCLUDE + **************************************************************************************/ + +#include "LedManager.h" + +#include + +/************************************************************************************** + * NAMESPACE + **************************************************************************************/ + +namespace opcua +{ + +/************************************************************************************** + * CTOR/DTOR + **************************************************************************************/ + +LedManager::LedManager(UA_NodeId const & node_id) +: _node_id{node_id} +{ + /* Nothing happens here. */ +} + +/************************************************************************************** + * PUBLIC MEMBER FUNCTIONS + **************************************************************************************/ + +LedManager::SharedPtr LedManager::create(UA_Server * server, UA_NodeId const parent_node_id) +{ + UA_StatusCode rc = UA_STATUSCODE_GOOD; + + UA_ObjectAttributes oAttr = UA_ObjectAttributes_default; + oAttr.displayName = UA_LOCALIZEDTEXT("en-US", "LEDs"); + UA_NodeId node_id; + rc = UA_Server_addObjectNode(server, + UA_NODEID_NULL, + parent_node_id, + UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES), + UA_QUALIFIEDNAME(1, "LEDs"), + UA_NODEID_NUMERIC(0, UA_NS0ID_BASEOBJECTTYPE), + oAttr, + NULL, + &node_id); + if (UA_StatusCode_isBad(rc)) + { + UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, + "LedManager::create: UA_Server_addObjectNode(...) failed with %s", UA_StatusCode_name(rc)); + return nullptr; + } + + auto const instance_ptr = std::make_shared(node_id); + return instance_ptr; +} + +/************************************************************************************** + * PUBLIC MEMBER FUNCTIONS + **************************************************************************************/ + +void LedManager::add_led_output(UA_Server * server, + const char * display_name, + Led::OnSetLedStateFunc const on_set_led_state) +{ + auto const led = Led::create(server, _node_id, display_name, on_set_led_state); + if (!led) { + UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "LedManager::add_led_output: Led::create(...) failed: returned nullptr"); + return; + } + _led_list.push_back(led); +} + +/************************************************************************************** + * NAMESPACE + **************************************************************************************/ + +} /* opcua */ diff --git a/src/LedManager.h b/src/LedManager.h new file mode 100644 index 0000000..50b073b --- /dev/null +++ b/src/LedManager.h @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2024 Arduino + * + * SPDX-License-Identifier: MPL-2.0 + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#pragma once + +/************************************************************************************** + * INCLUDE + **************************************************************************************/ + +#include "open62541.h" + +#include +#include + +#include "Led.h" + +/************************************************************************************** + * NAMESPACE + **************************************************************************************/ + +namespace opcua +{ + +/************************************************************************************** + * CLASS DECLARATION + **************************************************************************************/ + +class LedManager +{ +public: + typedef std::shared_ptr SharedPtr; + + static SharedPtr create(UA_Server * server, UA_NodeId const parent_node_id); + + LedManager(UA_NodeId const & node_id); + + void add_led_output(UA_Server * server, const char * display_name, Led::OnSetLedStateFunc const on_set_led_state); + + +private: + UA_NodeId _node_id; + std::list _led_list; +}; + +/************************************************************************************** + * NAMESPACE + **************************************************************************************/ + +} /* opcua */ diff --git a/src/Relay.h b/src/Relay.h index 8beca5a..2e27176 100644 --- a/src/Relay.h +++ b/src/Relay.h @@ -55,4 +55,3 @@ class Relay **************************************************************************************/ } /* opcua */ -