Skip to content

Commit f317b84

Browse files
committed
First draft on exposing PWM channels for control.
1 parent 6f03ad0 commit f317b84

File tree

7 files changed

+452
-12
lines changed

7 files changed

+452
-12
lines changed

examples/opcua_server/opcua_server.ino

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -312,6 +312,21 @@ void setup()
312312
output_num++;
313313
}
314314

315+
/* Configure PWM outputs. */
316+
int pwm_output_num = 1;
317+
for (int p = OA_PWM_CH_FIRST; p <= OA_PWM_CH_LAST; p++)
318+
{
319+
char pwm_out_name[32] = {0};
320+
snprintf(pwm_out_name, sizeof(pwm_out_name), "PWM%d", pwm_output_num);
321+
exp_analog->pwm_output_mgr()->add_pwm_output(opc_ua_server,
322+
pwm_out_name,
323+
[i, p](uint32_t const pwm_period_ms, uint32_t const pwm_pulse_width_ms)
324+
{
325+
reinterpret_cast<AnalogExpansion *>(OptaController.getExpansionPtr(i))->setPwm(p, pwm_period_ms, pwm_pulse_width_ms);
326+
});
327+
pwm_output_num++;
328+
}
329+
315330
/* Configure controllable LEDs of analog expansion module. */
316331
for (int l = 0; l < OA_LED_NUM; l++)
317332
{

src/expansion/AnalogExpansion.cpp

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,37 +24,55 @@ namespace opcua
2424
* PUBLIC MEMBER FUNCTIONS
2525
**************************************************************************************/
2626

27-
AnalogInputManager::SharedPtr AnalogExpansion::analog_input_mgr()
27+
AnalogInputManager::SharedPtr
28+
AnalogExpansion::analog_input_mgr()
2829
{
2930
if (!_analog_input_mgr)
3031
{
3132
_analog_input_mgr = opcua::AnalogInputManager::create(_server, _node_id);
3233
if (!_analog_input_mgr)
33-
UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "AnalogExpansion::analog_input_mgr: AnalogInputManager::create(...) failed.");
34+
UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_SERVER,
35+
"%s: AnalogInputManager::create(...) failed.", __PRETTY_FUNCTION__);
3436
}
3537

3638
return _analog_input_mgr;
3739
}
3840

39-
AnalogOutputManager::SharedPtr AnalogExpansion::analog_output_mgr()
41+
AnalogOutputManager::SharedPtr
42+
AnalogExpansion::analog_output_mgr()
4043
{
4144
if (!_analog_output_mgr)
4245
{
4346
_analog_output_mgr = opcua::AnalogOutputManager::create(_server, _node_id);
4447
if (!_analog_output_mgr)
45-
UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "AnalogExpansion::analog_output_mgr: AnalogOutputManager::create(...) failed.");
48+
UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "%s: AnalogOutputManager::create(...) failed.", __PRETTY_FUNCTION__);
4649
}
4750

4851
return _analog_output_mgr;
4952
}
5053

51-
LedManager::SharedPtr AnalogExpansion::led_mgr()
54+
PwmOutputManager::SharedPtr
55+
AnalogExpansion::pwm_output_mgr()
56+
{
57+
if (!_pwm_output_mgr)
58+
{
59+
_pwm_output_mgr = opcua::PwmOutputManager::create(_server, _node_id);
60+
if (!_pwm_output_mgr)
61+
UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "%s: PwmOutputManager::create(...) failed.", __PRETTY_FUNCTION__);
62+
}
63+
64+
return _pwm_output_mgr;
65+
}
66+
67+
LedManager::SharedPtr
68+
AnalogExpansion::led_mgr()
5269
{
5370
if (!_led_mgr)
5471
{
5572
_led_mgr = opcua::LedManager::create(_server, _node_id);
5673
if (!_led_mgr)
57-
UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "AnalogExpansion::led_mgr: LedManager::create(...) failed.");
74+
UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_SERVER,
75+
"%s: LedManager::create(...) failed.", __PRETTY_FUNCTION__);
5876
}
5977

6078
return _led_mgr;

src/expansion/AnalogExpansion.h

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include <memory>
1919

2020
#include "../io/led/LedManager.h"
21+
#include "../io/pwm/PwmOutputManager.h"
2122
#include "../io/analog/AnalogInputManager.h"
2223
#include "../io/analog/AnalogOutputManager.h"
2324

@@ -38,7 +39,11 @@ class AnalogExpansion : public Expansion
3839
typedef std::shared_ptr<AnalogExpansion> SharedPtr;
3940

4041

41-
static SharedPtr create(UA_Server *server, UA_NodeId const parent_node_id, uint8_t const exp_num)
42+
static SharedPtr
43+
create(
44+
UA_Server *server,
45+
UA_NodeId const parent_node_id,
46+
uint8_t const exp_num)
4247
{
4348
char display_name[64] = {0};
4449
snprintf(display_name, sizeof(display_name), "Arduino Opta Expansion %d: Analog", exp_num);
@@ -53,24 +58,27 @@ class AnalogExpansion : public Expansion
5358
}
5459

5560

56-
AnalogExpansion(UA_Server * server,
57-
UA_NodeId const parent_node_id,
58-
char * display_name,
59-
char * node_name,
60-
char * model_name)
61+
AnalogExpansion(
62+
UA_Server * server,
63+
UA_NodeId const parent_node_id,
64+
char * display_name,
65+
char * node_name,
66+
char * model_name)
6167
: Expansion(server, parent_node_id, display_name, node_name, model_name)
6268
{ }
6369

6470

6571
AnalogInputManager::SharedPtr analog_input_mgr();
6672
AnalogOutputManager::SharedPtr analog_output_mgr();
73+
PwmOutputManager::SharedPtr pwm_output_mgr();
6774
LedManager::SharedPtr led_mgr();
6875

6976

7077

7178
private:
7279
AnalogInputManager::SharedPtr _analog_input_mgr;
7380
AnalogOutputManager::SharedPtr _analog_output_mgr;
81+
PwmOutputManager::SharedPtr _pwm_output_mgr;
7482
LedManager::SharedPtr _led_mgr;
7583
};
7684

src/io/pwm/PwmOutput.cpp

Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
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 "PwmOutput.h"
15+
16+
/**************************************************************************************
17+
* NAMESPACE
18+
**************************************************************************************/
19+
20+
namespace opcua
21+
{
22+
23+
/**************************************************************************************
24+
* FUNCTION DEFINITION
25+
**************************************************************************************/
26+
27+
static void
28+
pwm_output_on_write_request_pwm_period(
29+
UA_Server *server,
30+
const UA_NodeId *sessionId,
31+
void *sessionContext,
32+
const UA_NodeId *nodeid,
33+
void *nodeContext,
34+
const UA_NumericRange *range,
35+
const UA_DataValue *data)
36+
{
37+
PwmOutput * this_ptr = reinterpret_cast<PwmOutput *>(nodeContext);
38+
uint32_t const pwm_period_ms = *(UA_UInt32 *)(data->value.data);
39+
this_ptr->onWriteRequestPwmPeriod(server, nodeid, pwm_period_ms);
40+
}
41+
42+
static void
43+
pwm_output_on_write_request_pwm_pulse_width(
44+
UA_Server *server,
45+
const UA_NodeId *sessionId,
46+
void *sessionContext,
47+
const UA_NodeId *nodeid,
48+
void *nodeContext,
49+
const UA_NumericRange *range,
50+
const UA_DataValue *data)
51+
{
52+
PwmOutput * this_ptr = reinterpret_cast<PwmOutput *>(nodeContext);
53+
uint32_t const pwm_pulse_width_ms = *(UA_UInt32 *)(data->value.data);
54+
this_ptr->onWriteRequestPwmPulseWidth(server, nodeid, pwm_pulse_width_ms);
55+
}
56+
57+
/**************************************************************************************
58+
* CTOR/DTOR
59+
**************************************************************************************/
60+
61+
PwmOutput::PwmOutput(
62+
UA_NodeId const & node_id,
63+
SetPwmFunc const set_pwm_func)
64+
: _node_id{node_id}
65+
, _set_pwm_func{set_pwm_func}
66+
, _pwm_period_ms{0}
67+
, _pwm_pulse_width_ms{0}
68+
{
69+
70+
}
71+
72+
/**************************************************************************************
73+
* PUBLIC MEMBER FUNCTIONS
74+
**************************************************************************************/
75+
76+
PwmOutput::SharedPtr
77+
PwmOutput::create(
78+
UA_Server * server,
79+
UA_NodeId const & parent_node_id,
80+
const char * display_name,
81+
SetPwmFunc const set_pwm_func)
82+
{
83+
UA_StatusCode rc = UA_STATUSCODE_GOOD;
84+
85+
UA_VariableAttributes pwm_out_period_value_attr = UA_VariableAttributes_default;
86+
87+
UA_Boolean pwm_output_period_value = 0;
88+
UA_Variant_setScalar(&pwm_out_period_value_attr.value, &pwm_output_period_value, &UA_TYPES[UA_TYPES_UINT32]);
89+
90+
pwm_out_period_value_attr.displayName = UA_LOCALIZEDTEXT("en-US", (char *)display_name);
91+
pwm_out_period_value_attr.dataType = UA_TYPES[UA_TYPES_UINT32].typeId;
92+
pwm_out_period_value_attr.accessLevel =
93+
UA_ACCESSLEVELMASK_READ |
94+
UA_ACCESSLEVELMASK_WRITE | UA_ACCESSLEVELMASK_STATUSWRITE |
95+
UA_ACCESSLEVELMASK_TIMESTAMPWRITE; /* Status and timestamp write access necessary for opcua-client. */
96+
97+
UA_NodeId node_id;
98+
rc = UA_Server_addVariableNode(server,
99+
UA_NODEID_NULL,
100+
parent_node_id,
101+
UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
102+
UA_QUALIFIEDNAME(1, "Value"),
103+
UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE),
104+
pwm_out_period_value_attr,
105+
NULL,
106+
&node_id);
107+
if (UA_StatusCode_isBad(rc))
108+
{
109+
UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_SERVER,
110+
"%s: failed with %s", __PRETTY_FUNCTION__, UA_StatusCode_name(rc));
111+
return nullptr;
112+
}
113+
114+
/* Create an instance of AnalogOutput here. */
115+
auto const instance_ptr = std::make_shared<PwmOutput>(node_id, set_pwm_func);
116+
117+
rc = UA_Server_setNodeContext(server, node_id, reinterpret_cast<void *>(instance_ptr.get()));
118+
if (UA_StatusCode_isBad(rc))
119+
{
120+
UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_SERVER,
121+
"%s: UA_Server_setNodeContext(...) failed with %s", __PRETTY_FUNCTION__, UA_StatusCode_name(rc));
122+
return nullptr;
123+
}
124+
125+
UA_ValueCallback callback;
126+
callback.onRead = NULL;
127+
callback.onWrite = pwm_output_on_write_request_pwm_period;
128+
rc = UA_Server_setVariableNode_valueCallback(server, node_id, callback);
129+
if (UA_StatusCode_isBad(rc))
130+
{
131+
UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_SERVER,
132+
"%s: UA_Server_setVariableNode_valueCallback(...) failed with %s", __PRETTY_FUNCTION__, UA_StatusCode_name(rc));
133+
return nullptr;
134+
}
135+
136+
return instance_ptr;
137+
}
138+
139+
void
140+
PwmOutput::onWriteRequestPwmPeriod(
141+
UA_Server * server,
142+
UA_NodeId const * node_id,
143+
uint32_t const pwm_period_ms)
144+
{
145+
_pwm_period_ms = pwm_period_ms;
146+
UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "%s: pwm period = %d ms", __PRETTY_FUNCTION__, _pwm_period_ms);
147+
_set_pwm_func(_pwm_period_ms, _pwm_pulse_width_ms);
148+
}
149+
150+
void
151+
PwmOutput::onWriteRequestPwmPulseWidth(
152+
UA_Server * server,
153+
UA_NodeId const * node_id,
154+
uint32_t const pwm_pulse_width_ms)
155+
{
156+
_pwm_pulse_width_ms = pwm_pulse_width_ms;
157+
UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "%s: pwm pulse width = %d ms", __PRETTY_FUNCTION__, _pwm_pulse_width_ms);
158+
_set_pwm_func(_pwm_period_ms, _pwm_pulse_width_ms);
159+
}
160+
161+
/**************************************************************************************
162+
* NAMESPACE
163+
**************************************************************************************/
164+
165+
} /* opcua */

src/io/pwm/PwmOutput.h

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
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 PwmOutput
35+
{
36+
public:
37+
typedef std::shared_ptr<PwmOutput> SharedPtr;
38+
typedef std::function<void(uint32_t const, uint32_t const)> SetPwmFunc;
39+
40+
41+
static SharedPtr
42+
create(
43+
UA_Server * server,
44+
UA_NodeId const & parent_node_id,
45+
const char * display_name,
46+
SetPwmFunc const set_pwm_func);
47+
48+
49+
PwmOutput(
50+
UA_NodeId const & node_id,
51+
SetPwmFunc const set_pwm_func);
52+
53+
54+
void
55+
onWriteRequestPwmPeriod(
56+
UA_Server * server,
57+
UA_NodeId const * node_id,
58+
uint32_t const pwm_period_ms);
59+
60+
void
61+
onWriteRequestPwmPulseWidth(
62+
UA_Server * server,
63+
UA_NodeId const * node_id,
64+
uint32_t const pwm_pulse_width_ms);
65+
66+
67+
private:
68+
UA_NodeId _node_id;
69+
SetPwmFunc const _set_pwm_func;
70+
uint32_t _pwm_period_ms, _pwm_pulse_width_ms;
71+
};
72+
73+
/**************************************************************************************
74+
* NAMESPACE
75+
**************************************************************************************/
76+
77+
} /* opcua */

0 commit comments

Comments
 (0)