Skip to content
13 changes: 12 additions & 1 deletion examples/opcua_server/opcua_server.ino
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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",
Expand Down
5 changes: 5 additions & 0 deletions src/ArduinoOpta.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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.");
}
}

/**************************************************************************************
Expand Down
3 changes: 3 additions & 0 deletions src/ArduinoOpta.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

#include <memory>

#include "LedManager.h"
#include "RelayManager.h"
#include "AnalogInputManager.h"
#include "DigitalInputManager.h"
Expand Down Expand Up @@ -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:
Expand All @@ -53,6 +55,7 @@ class ArduinoOpta
AnalogInputManager::SharedPtr _analog_input_mgr;
DigitalInputManager::SharedPtr _digital_input_mgr;
RelayManager::SharedPtr _relay_mgr;
LedManager::SharedPtr _led_mgr;
};

/**************************************************************************************
Expand Down
69 changes: 69 additions & 0 deletions src/ArduinoOptaVariant.cpp
Original file line number Diff line number Diff line change
@@ -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 <cstdint>
# 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 */
46 changes: 46 additions & 0 deletions src/ArduinoOptaVariant.h
Original file line number Diff line number Diff line change
@@ -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 <string>

/**************************************************************************************
* 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 */
5 changes: 2 additions & 3 deletions src/Arduino_open62541.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
129 changes: 129 additions & 0 deletions src/Led.cpp
Original file line number Diff line number Diff line change
@@ -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<Led *>(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<Led>(node_id, on_set_led_state);

rc = UA_Server_setNodeContext(server, node_id, reinterpret_cast<void *>(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 */
57 changes: 57 additions & 0 deletions src/Led.h
Original file line number Diff line number Diff line change
@@ -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 <memory>
#include <functional>

/**************************************************************************************
* NAMESPACE
**************************************************************************************/

namespace opcua
{

/**************************************************************************************
* CLASS DECLARATION
**************************************************************************************/

class Led
{
public:
typedef std::shared_ptr<Led> SharedPtr;
typedef std::function<void(bool const)> 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 */
Loading