Skip to content

Commit 9715161

Browse files
committed
Basic implementation for controlling the analog output.
1 parent 4033b0b commit 9715161

File tree

7 files changed

+364
-3
lines changed

7 files changed

+364
-3
lines changed

examples/opcua_server/opcua_server.ino

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -269,7 +269,7 @@ void setup()
269269
{
270270
auto const exp_analog = opta_expansion_manager_opcua->create_analog_expansion(i);
271271

272-
for(int a = 0; a < OA_AN_CHANNELS_NUM; a++)
272+
for(int a = OA_CH_0; a <= OA_CH_5; a++)
273273
{
274274
/* Configure analog expansion module analog channels as analog inputs. */
275275
AnalogExpansion::beginChannelAsAdc(OptaController,
@@ -287,6 +287,23 @@ void setup()
287287
exp_analog->analog_input_mgr()->add_analog_input(opc_ua_server, analog_in_name, [i, a]() { return reinterpret_cast<AnalogExpansion *>(OptaController.getExpansionPtr(i))->pinVoltage(a); });
288288
}
289289

290+
for(int a = OA_CH_6; a <= OA_CH_7; a++)
291+
{
292+
/* Configure analog expansion module analog channels as analog outputs. */
293+
AnalogExpansion::beginChannelAsDac(OptaController,
294+
i, /* expansion module number */
295+
a, /* analog channel of expansion module */
296+
OA_VOLTAGE_DAC, /* DAC type */
297+
true, /* limit current */
298+
false, /* disable slew rate */
299+
OA_SLEW_RATE_0);
300+
301+
/* Expose analog inputs as readable OPC UA properties. */
302+
char analog_out_name[32] = {0};
303+
snprintf(analog_out_name, sizeof(analog_out_name), "Analog Output I%d", a + 1);
304+
exp_analog->analog_output_mgr()->add_analog_output(opc_ua_server, analog_out_name, [i, a](float const voltage) { return reinterpret_cast<AnalogExpansion *>(OptaController.getExpansionPtr(i))->pinVoltage(a, voltage); });
305+
}
306+
290307
/* Configure controllable LEDs of analog expansion module. */
291308
for (int l = 0; l < OA_LED_NUM; l++)
292309
{

src/expansion/AnalogExpansion.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,18 @@ AnalogInputManager::SharedPtr AnalogExpansion::analog_input_mgr()
3636
return _analog_input_mgr;
3737
}
3838

39+
AnalogOutputManager::SharedPtr AnalogExpansion::analog_output_mgr()
40+
{
41+
if (!_analog_output_mgr)
42+
{
43+
_analog_output_mgr = opcua::AnalogOutputManager::create(_server, _node_id);
44+
if (!_analog_output_mgr)
45+
UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "AnalogExpansion::analog_output_mgr: AnalogOutputManager::create(...) failed.");
46+
}
47+
48+
return _analog_output_mgr;
49+
}
50+
3951
LedManager::SharedPtr AnalogExpansion::led_mgr()
4052
{
4153
if (!_led_mgr)

src/expansion/AnalogExpansion.h

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919

2020
#include "../io/led/LedManager.h"
2121
#include "../io/analog/AnalogInputManager.h"
22+
#include "../io/analog/AnalogOutputManager.h"
2223

2324
/**************************************************************************************
2425
* NAMESPACE
@@ -61,13 +62,15 @@ class AnalogExpansion : public Expansion
6162
{ }
6263

6364

64-
AnalogInputManager::SharedPtr analog_input_mgr();
65-
LedManager::SharedPtr led_mgr();
65+
AnalogInputManager::SharedPtr analog_input_mgr();
66+
AnalogOutputManager::SharedPtr analog_output_mgr();
67+
LedManager::SharedPtr led_mgr();
6668

6769

6870

6971
private:
7072
AnalogInputManager::SharedPtr _analog_input_mgr;
73+
AnalogOutputManager::SharedPtr _analog_output_mgr;
7174
LedManager::SharedPtr _led_mgr;
7275
};
7376

src/io/analog/AnalogOutput.cpp

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
/*
2+
* Copyright (c) 2024 Arduino
3+
*
4+
* SPDX-License-Identifier: MPL-2.0
5+
* This Source Code Form is subject to the terms of the Mozilla Public
6+
* License, v. 2.0. If a copy of the MPL was not distributed with this
7+
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
8+
*/
9+
10+
/**************************************************************************************
11+
* INCLUDE
12+
**************************************************************************************/
13+
14+
#include "AnalogOutput.h"
15+
16+
/**************************************************************************************
17+
* NAMESPACE
18+
**************************************************************************************/
19+
20+
namespace opcua
21+
{
22+
23+
/**************************************************************************************
24+
* FUNCTION DEFINITION
25+
**************************************************************************************/
26+
27+
static void analog_output_on_write_request(UA_Server *server,
28+
const UA_NodeId *sessionId,
29+
void *sessionContext,
30+
const UA_NodeId *nodeid,
31+
void *nodeContext,
32+
const UA_NumericRange *range,
33+
const UA_DataValue *data)
34+
{
35+
AnalogOutput * this_ptr = reinterpret_cast<AnalogOutput *>(nodeContext);
36+
bool const voltage = *(UA_Float *)(data->value.data);
37+
this_ptr->onWriteRequest(server, nodeid, voltage);
38+
}
39+
40+
/**************************************************************************************
41+
* CTOR/DTOR
42+
**************************************************************************************/
43+
44+
AnalogOutput::AnalogOutput(UA_NodeId const & node_id, OnWriteRequestFunc const on_write_request)
45+
: _node_id{node_id}
46+
, _on_write_request{on_write_request}
47+
{
48+
49+
}
50+
51+
/**************************************************************************************
52+
* PUBLIC MEMBER FUNCTIONS
53+
**************************************************************************************/
54+
55+
AnalogOutput::SharedPtr AnalogOutput::create(UA_Server * server,
56+
UA_NodeId const & parent_node_id,
57+
const char * display_name,
58+
OnWriteRequestFunc const on_write_request)
59+
{
60+
UA_StatusCode rc = UA_STATUSCODE_GOOD;
61+
62+
UA_VariableAttributes analog_output_value_attr = UA_VariableAttributes_default;
63+
64+
UA_Boolean analog_output_value = 0.;
65+
UA_Variant_setScalar(&analog_output_value_attr.value, &analog_output_value, &UA_TYPES[UA_TYPES_FLOAT]);
66+
67+
analog_output_value_attr.displayName = UA_LOCALIZEDTEXT("en-US", (char *)display_name);
68+
analog_output_value_attr.dataType = UA_TYPES[UA_TYPES_FLOAT].typeId;
69+
analog_output_value_attr.accessLevel =
70+
UA_ACCESSLEVELMASK_READ |
71+
UA_ACCESSLEVELMASK_WRITE | UA_ACCESSLEVELMASK_STATUSWRITE |
72+
UA_ACCESSLEVELMASK_TIMESTAMPWRITE; /* Status and timestamp write access necessary for opcua-client. */
73+
74+
UA_NodeId node_id;
75+
rc = UA_Server_addVariableNode(server,
76+
UA_NODEID_NULL,
77+
parent_node_id,
78+
UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
79+
UA_QUALIFIEDNAME(1, "Value"),
80+
UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE),
81+
analog_output_value_attr,
82+
NULL,
83+
&node_id);
84+
if (UA_StatusCode_isBad(rc))
85+
{
86+
UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_SERVER,
87+
"AnalogOutput::create: UA_Server_addVariableNode(...) failed with %s", UA_StatusCode_name(rc));
88+
return nullptr;
89+
}
90+
91+
/* Create an instance of AnalogOutput here. */
92+
auto const instance_ptr = std::make_shared<AnalogOutput>(node_id, on_write_request);
93+
94+
rc = UA_Server_setNodeContext(server, node_id, reinterpret_cast<void *>(instance_ptr.get()));
95+
if (UA_StatusCode_isBad(rc))
96+
{
97+
UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_SERVER,
98+
"AnalogOutput::create: UA_Server_setNodeContext(...) failed with %s",
99+
UA_StatusCode_name(rc));
100+
return nullptr;
101+
}
102+
103+
UA_ValueCallback callback;
104+
callback.onRead = NULL;
105+
callback.onWrite = analog_output_on_write_request;
106+
rc = UA_Server_setVariableNode_valueCallback(server, node_id, callback);
107+
if (UA_StatusCode_isBad(rc))
108+
{
109+
UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_SERVER,
110+
"AnalogOutput::create: UA_Server_setVariableNode_valueCallback(...) failed with %s",
111+
UA_StatusCode_name(rc));
112+
return nullptr;
113+
}
114+
115+
return instance_ptr;
116+
}
117+
118+
void AnalogOutput::onWriteRequest(UA_Server * server, UA_NodeId const * node_id, float const voltage)
119+
{
120+
/* Some debug output. */
121+
UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "AnalogOutput::onWriteRequest: voltage = %0.2f", voltage);
122+
_on_write_request(voltage);
123+
}
124+
125+
/**************************************************************************************
126+
* NAMESPACE
127+
**************************************************************************************/
128+
129+
} /* opcua */

src/io/analog/AnalogOutput.h

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
/*
2+
* Copyright (c) 2024 Arduino
3+
*
4+
* SPDX-License-Identifier: MPL-2.0
5+
* This Source Code Form is subject to the terms of the Mozilla Public
6+
* License, v. 2.0. If a copy of the MPL was not distributed with this
7+
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
8+
*/
9+
10+
#pragma once
11+
12+
/**************************************************************************************
13+
* INCLUDE
14+
**************************************************************************************/
15+
16+
#include "../../open62541.h"
17+
18+
#include <memory>
19+
#include <functional>
20+
21+
#include <Arduino.h>
22+
23+
/**************************************************************************************
24+
* NAMESPACE
25+
**************************************************************************************/
26+
27+
namespace opcua
28+
{
29+
30+
/**************************************************************************************
31+
* CLASS DECLARATION
32+
**************************************************************************************/
33+
34+
class AnalogOutput
35+
{
36+
public:
37+
typedef std::shared_ptr<AnalogOutput> SharedPtr;
38+
typedef std::function<void(float)> OnWriteRequestFunc;
39+
40+
static SharedPtr create(UA_Server * server,
41+
UA_NodeId const & parent_node_id,
42+
const char * display_name,
43+
OnWriteRequestFunc const on_write_request);
44+
45+
AnalogOutput(UA_NodeId const & node_id, OnWriteRequestFunc const on_write_request);
46+
47+
void onWriteRequest(UA_Server * server, UA_NodeId const * node_id, float const voltage);
48+
49+
50+
private:
51+
UA_NodeId _node_id;
52+
OnWriteRequestFunc _on_write_request;
53+
};
54+
55+
/**************************************************************************************
56+
* NAMESPACE
57+
**************************************************************************************/
58+
59+
} /* opcua */
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
/*
2+
* Copyright (c) 2024 Arduino
3+
*
4+
* SPDX-License-Identifier: MPL-2.0
5+
* This Source Code Form is subject to the terms of the Mozilla Public
6+
* License, v. 2.0. If a copy of the MPL was not distributed with this
7+
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
8+
*/
9+
10+
/**************************************************************************************
11+
* INCLUDE
12+
**************************************************************************************/
13+
14+
#include "AnalogOutputManager.h"
15+
16+
/**************************************************************************************
17+
* NAMESPACE
18+
**************************************************************************************/
19+
20+
namespace opcua
21+
{
22+
23+
/**************************************************************************************
24+
* CTOR/DTOR
25+
**************************************************************************************/
26+
27+
AnalogOutputManager::AnalogOutputManager(UA_NodeId const & node_id)
28+
: _node_id{node_id}
29+
{
30+
/* Nothing happens here. */
31+
}
32+
33+
/**************************************************************************************
34+
* PUBLIC MEMBER FUNCTIONS
35+
**************************************************************************************/
36+
37+
AnalogOutputManager::SharedPtr AnalogOutputManager::create(UA_Server * server, UA_NodeId const parent_node_id)
38+
{
39+
UA_StatusCode rc = UA_STATUSCODE_GOOD;
40+
41+
UA_ObjectAttributes oAttr = UA_ObjectAttributes_default;
42+
oAttr.displayName = UA_LOCALIZEDTEXT("en-US", "Analog Outputs");
43+
UA_NodeId node_id;
44+
rc = UA_Server_addObjectNode(server,
45+
UA_NODEID_NULL,
46+
parent_node_id,
47+
UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES),
48+
UA_QUALIFIEDNAME(1, "AnalogOutputs"),
49+
UA_NODEID_NUMERIC(0, UA_NS0ID_BASEOBJECTTYPE),
50+
oAttr,
51+
NULL,
52+
&node_id);
53+
if (UA_StatusCode_isBad(rc))
54+
{
55+
UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_SERVER,
56+
"AnalogOutputManager::create: UA_Server_addObjectNode(...) failed with %s", UA_StatusCode_name(rc));
57+
return nullptr;
58+
}
59+
60+
auto const instance_ptr = std::make_shared<AnalogOutputManager>(node_id);
61+
return instance_ptr;
62+
}
63+
64+
/**************************************************************************************
65+
* PUBLIC MEMBER FUNCTIONS
66+
**************************************************************************************/
67+
68+
void AnalogOutputManager::add_analog_output(UA_Server * server,
69+
const char * display_name,
70+
AnalogOutput::OnWriteRequestFunc const on_write_request_func)
71+
{
72+
auto const analog_output = AnalogOutput::create(server, _node_id, display_name, on_write_request_func);
73+
if (!analog_output) {
74+
UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "AnalogOutputManager::add_analog_output: AnalogOutput::create(...) failed: returned nullptr");
75+
return;
76+
}
77+
_analog_output_list.push_back(analog_output);
78+
}
79+
80+
/**************************************************************************************
81+
* NAMESPACE
82+
**************************************************************************************/
83+
84+
} /* opcua */

0 commit comments

Comments
 (0)