Skip to content

Commit 62bbe2d

Browse files
authored
Merge pull request #17 from bcmi-labs/feature/analog-expansion-analog-inputs
Feature: implement suppport for Arduino Opta Analog expansion module
2 parents 694f6b4 + 7f1877a commit 62bbe2d

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+1865
-371
lines changed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66

77
This library provides an implementation of [OPC/UA](https://en.wikipedia.org/wiki/OPC_Unified_Architecture) by porting the Fraunhofer [`open62541`](https://github.com/open62541/open62541) for the Arduino [Opta](https://www.arduino.cc/pro/hardware-arduino-opta/) `microPLC` family.
88

9+
Furthermore, the library supports automatic detection, configuration and exposure of up to two Arduino Opta Expansion Boards (i.e. Digital Expansion w/ mechanical relays [`D1608E`](https://store.arduino.cc/products/opta-ext-d1608e), Digital Expansion w/ solid-state relays [`D1608S`](https://store.arduino.cc/products/opta-ext-d1608e), Analog Expansion [`A0602`](https://store.arduino.cc/products/opta-ext-a0602)) via OPC UA.
10+
911
### How-to-OPC/UA
1012
* Compile and upload [`examples/opcua_server`](examples/opcua_server/opcua_server.ino)
1113
```bash

examples/opcua_server/opcua_server.ino

Lines changed: 161 additions & 30 deletions
Large diffs are not rendered by default.

extras/precompile/README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
:floppy_disk: Precompile `open62541` library for `cortex-m7`
22
============================================================
33
The following instructions can be used to pre-compile the `open62541` library for `cortex-m7`.
4+
5+
**Note**: these steps are only relevant for the maintainers of this library.
6+
47
```bash
58
./docker-build.sh
69
./docker-run.sh

src/Arduino_open62541.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,3 +32,9 @@
3232
#if !defined(ARDUINO_OPTA)
3333
# error "This library does only support Arduino Opta"
3434
#endif
35+
36+
/* Note: exposing properties via OPC UA is extremely
37+
* RAM hungry. We therefore need to limit the number
38+
* of supported Opta expansion modules via OPC UA.
39+
*/
40+
#define OPCUA_MAX_OPTA_EXPANSION_NUM (2u)

src/Opta.cpp

Lines changed: 52 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -24,24 +24,38 @@ namespace opcua
2424
* CTOR/DTOR
2525
**************************************************************************************/
2626

27-
Opta::Opta(UA_Server * server, UA_NodeId const & node_id)
27+
Opta::Opta(
28+
UA_Server * server,
29+
UA_NodeId const & node_id,
30+
OptaVariant::Type const opta_type)
2831
: _server{server}
2932
, _node_id{node_id}
30-
, _analog_input_mgr{nullptr}
31-
, _digital_input_mgr{nullptr}
32-
, _relay_mgr{nullptr}
33-
, _led_mgr{nullptr}
33+
, _usr_button{opcua::UserButton::create(_server, _node_id)}
34+
, _analog_input_mgr{opcua::AnalogInputManager::create(_server, _node_id)}
35+
, _digital_input_mgr{opcua::DigitalInputManager::create(_server, _node_id)}
36+
, _relay_mgr{opcua::RelayManager::create(_server, _node_id)}
37+
, _led_mgr{(opta_type == OptaVariant::Type::WiFi) ? opcua::LedManager::create(_server, _node_id) : nullptr}
3438
{
35-
_usr_button = opcua::UserButton::create(_server, _node_id);
3639
if (!_usr_button)
37-
UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "Opta::Ctor: UserButton::create(...) failed.");
40+
UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "%s: UserButton::create(...) failed.", __PRETTY_FUNCTION__);
41+
if (!_analog_input_mgr)
42+
UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "%s: AnalogInputManager::create(...) failed.", __PRETTY_FUNCTION__);
43+
if (!_digital_input_mgr)
44+
UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "%s: DigitalInputManager::create(...) failed.", __PRETTY_FUNCTION__);
45+
if (!_relay_mgr)
46+
UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "%s: RelayManager::create(...) failed.", __PRETTY_FUNCTION__);
47+
if ((opta_type == OptaVariant::Type::WiFi) && !_led_mgr)
48+
UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "%s: LedManager::create(...) failed.", __PRETTY_FUNCTION__);
3849
}
3950

4051
/**************************************************************************************
4152
* PUBLIC MEMBER FUNCTIONS
4253
**************************************************************************************/
4354

44-
Opta::SharedPtr Opta::create(UA_Server * server, OptaVariant::Type const opta_type)
55+
Opta::SharedPtr
56+
Opta::create(
57+
UA_Server * server,
58+
OptaVariant::Type const opta_type)
4559
{
4660
UA_StatusCode rc = UA_STATUSCODE_GOOD;
4761

@@ -60,8 +74,7 @@ Opta::SharedPtr Opta::create(UA_Server * server, OptaVariant::Type const opta_ty
6074
if (UA_StatusCode_isBad(rc))
6175
{
6276
UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_SERVER,
63-
"Opta::create: UA_Server_addObjectNode(...) failed with %s",
64-
UA_StatusCode_name(rc));
77+
"%s: UA_Server_addObjectNode(...) failed with %s", __PRETTY_FUNCTION__, UA_StatusCode_name(rc));
6578
return nullptr;
6679
}
6780

@@ -81,8 +94,7 @@ Opta::SharedPtr Opta::create(UA_Server * server, OptaVariant::Type const opta_ty
8194
if (UA_StatusCode_isBad(rc))
8295
{
8396
UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_SERVER,
84-
"Opta::create: UA_Server_addVariableNode(..., \"ManufacturerName\", ...) failed with %s",
85-
UA_StatusCode_name(rc));
97+
"%s: UA_Server_addVariableNode(..., \"ManufacturerName\", ...) failed with %s", __PRETTY_FUNCTION__, UA_StatusCode_name(rc));
8698
return nullptr;
8799
}
88100

@@ -102,8 +114,7 @@ Opta::SharedPtr Opta::create(UA_Server * server, OptaVariant::Type const opta_ty
102114
if (UA_StatusCode_isBad(rc))
103115
{
104116
UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_SERVER,
105-
"Opta::create: UA_Server_addVariableNode(..., \"ModelName\", ...) failed with %s",
106-
UA_StatusCode_name(rc));
117+
"%s: UA_Server_addVariableNode(..., \"ModelName\", ...) failed with %s", __PRETTY_FUNCTION__, UA_StatusCode_name(rc));
107118
return nullptr;
108119
}
109120

@@ -123,61 +134,49 @@ Opta::SharedPtr Opta::create(UA_Server * server, OptaVariant::Type const opta_ty
123134
if (UA_StatusCode_isBad(rc))
124135
{
125136
UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_SERVER,
126-
"Opta::create: UA_Server_addVariableNode(..., \"Status\", ...) failed with %s",
127-
UA_StatusCode_name(rc));
137+
"%s: UA_Server_addVariableNode(..., \"Status\", ...) failed with %s", __PRETTY_FUNCTION__, UA_StatusCode_name(rc));
128138
return nullptr;
129139
}
130140

131-
auto const instance_ptr = std::make_shared<Opta>(server, node_id);
141+
auto const instance_ptr = std::make_shared<Opta>(server, node_id, opta_type);
132142
return instance_ptr;
133143
}
134144

135-
AnalogInputManager::SharedPtr Opta::analog_input_mgr()
145+
void
146+
Opta::add_analog_input(
147+
UA_Server * server,
148+
const char * display_name,
149+
AnalogInput::OnReadRequestFunc const on_read_request_func)
136150
{
137-
if (!_analog_input_mgr)
138-
{
139-
_analog_input_mgr = opcua::AnalogInputManager::create(_server, _node_id);
140-
if (!_analog_input_mgr)
141-
UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "Opta::analog_input_mgr: AnalogInputManager::create(...) failed.");
142-
}
143-
144-
return _analog_input_mgr;
151+
_analog_input_mgr->add_analog_input(server, display_name, on_read_request_func);
145152
}
146153

147-
DigitalInputManager::SharedPtr Opta::digital_input_mgr()
154+
void
155+
Opta::add_digital_input(
156+
UA_Server * server,
157+
const char * display_name,
158+
DigitalInput::OnReadRequestFunc const on_read_request_func)
148159
{
149-
if (!_digital_input_mgr)
150-
{
151-
_digital_input_mgr = opcua::DigitalInputManager::create(_server, _node_id);
152-
if (!_digital_input_mgr)
153-
UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "Opta::digital_input_mgr: DigitalInputManager::create(...) failed.");
154-
}
155-
156-
return _digital_input_mgr;
160+
_digital_input_mgr->add_digital_input(server, display_name, on_read_request_func);
157161
}
158162

159-
RelayManager::SharedPtr Opta::relay_mgr()
163+
void
164+
Opta::add_relay_output(
165+
UA_Server * server,
166+
const char * display_name,
167+
Relay::OnSetRelayStateFunc const on_set_relay_state)
160168
{
161-
if (!_relay_mgr)
162-
{
163-
_relay_mgr = opcua::RelayManager::create(_server, _node_id);
164-
if (!_relay_mgr)
165-
UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "Opta::relay_mgr: RelayManager::create(...) failed.");
166-
}
167-
168-
return _relay_mgr;
169+
_relay_mgr->add_relay_output(server, display_name, on_set_relay_state);
169170
}
170171

171-
LedManager::SharedPtr Opta::led_mgr()
172+
void
173+
Opta::add_led_output(
174+
UA_Server * server,
175+
const char * display_name,
176+
Led::OnSetLedStateFunc const on_set_led_state)
172177
{
173-
if (!_led_mgr)
174-
{
175-
_led_mgr = opcua::LedManager::create(_server, _node_id);
176-
if (!_led_mgr)
177-
UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "Opta::led_mgr: LedManager::create(...) failed.");
178-
}
179-
180-
return _led_mgr;
178+
if (_led_mgr) /* Only available for Arduino Opta WiFi. */
179+
_led_mgr->add_led_output(server, display_name, on_set_led_state);
181180
}
182181

183182
/**************************************************************************************

src/Opta.h

Lines changed: 35 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -42,19 +42,41 @@ class Opta
4242
typedef std::shared_ptr<Opta> SharedPtr;
4343

4444

45-
static SharedPtr create(UA_Server * server, OptaVariant::Type const opta_type);
46-
47-
48-
Opta(UA_Server * server, UA_NodeId const & node_id);
49-
50-
51-
AnalogInputManager::SharedPtr analog_input_mgr();
52-
DigitalInputManager::SharedPtr digital_input_mgr();
53-
RelayManager::SharedPtr relay_mgr();
54-
LedManager::SharedPtr led_mgr();
55-
56-
57-
[[nodiscard]] UA_NodeId node_id() const { return _node_id; }
45+
static SharedPtr
46+
create(
47+
UA_Server * server,
48+
OptaVariant::Type const opta_type);
49+
50+
51+
Opta(
52+
UA_Server * server,
53+
UA_NodeId const & node_id,
54+
OptaVariant::Type const opta_type);
55+
56+
57+
void
58+
add_analog_input(
59+
UA_Server * server,
60+
const char * display_name,
61+
AnalogInput::OnReadRequestFunc const on_read_request_func);
62+
63+
void
64+
add_digital_input(
65+
UA_Server * server,
66+
const char * display_name,
67+
DigitalInput::OnReadRequestFunc const on_read_request_func);
68+
69+
void
70+
add_relay_output(
71+
UA_Server * server,
72+
const char * display_name,
73+
Relay::OnSetRelayStateFunc const on_set_relay_state);
74+
75+
void
76+
add_led_output(
77+
UA_Server * server,
78+
const char * display_name,
79+
Led::OnSetLedStateFunc const on_set_led_state);
5880

5981

6082
private:

src/OptaExpansionManager.cpp

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

27-
DigitalMechExpansion::SharedPtr OptaExpansionManager::create_digital_mechanical_expansion(uint8_t const exp_num)
27+
DigitalMechExpansion::SharedPtr
28+
OptaExpansionManager::create_digital_mechanical_expansion(
29+
uint8_t const exp_num)
2830
{
2931
auto const exp_mech_opcua = opcua::DigitalMechExpansion::create(
3032
_server,
@@ -35,7 +37,9 @@ DigitalMechExpansion::SharedPtr OptaExpansionManager::create_digital_mechanical_
3537
return exp_mech_opcua;
3638
}
3739

38-
DigitalStSolidExpansion::SharedPtr OptaExpansionManager::create_digital_solid_state_expansion(uint8_t const exp_num)
40+
DigitalStSolidExpansion::SharedPtr
41+
OptaExpansionManager::create_digital_solid_state_expansion(
42+
uint8_t const exp_num)
3943
{
4044
auto const exp_solid_state_opcua = opcua::DigitalStSolidExpansion::create(
4145
_server,
@@ -46,7 +50,9 @@ DigitalStSolidExpansion::SharedPtr OptaExpansionManager::create_digital_solid_st
4650
return exp_solid_state_opcua;
4751
}
4852

49-
AnalogExpansion::SharedPtr OptaExpansionManager::create_analog_expansion(uint8_t const exp_num)
53+
AnalogExpansion::SharedPtr
54+
OptaExpansionManager::create_analog_expansion(
55+
uint8_t const exp_num)
5056
{
5157
auto const exp_analog_opcua = opcua::AnalogExpansion::create(
5258
_server,

src/OptaExpansionManager.h

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,12 +38,15 @@ class OptaExpansionManager
3838
typedef std::shared_ptr<OptaExpansionManager> SharedPtr;
3939

4040

41-
static SharedPtr create(UA_Server * server) {
41+
static SharedPtr
42+
create(
43+
UA_Server * server) {
4244
return std::make_shared<OptaExpansionManager>(server);
4345
}
4446

4547

46-
OptaExpansionManager(UA_Server * server)
48+
OptaExpansionManager(
49+
UA_Server * server)
4750
: _server{server}
4851
{ }
4952

src/OptaVariant.cpp

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,9 @@ namespace opcua
3232
* PUBLIC MEMBER FUNCTIONS
3333
**************************************************************************************/
3434

35-
bool OptaVariant::get_opta_variant(Type & type)
35+
bool
36+
OptaVariant::get_opta_variant(
37+
Type & type)
3638
{
3739
OptaBoardInfo * info = boardInfo();
3840

@@ -51,7 +53,9 @@ bool OptaVariant::get_opta_variant(Type & type)
5153
return true;
5254
}
5355

54-
std::string OptaVariant::toString(Type const type)
56+
std::string
57+
OptaVariant::toString(
58+
Type const type)
5559
{
5660
switch(type)
5761
{
@@ -62,13 +66,15 @@ std::string OptaVariant::toString(Type const type)
6266
}
6367
}
6468

65-
std::string OptaVariant::toSKUString(Type const type)
69+
std::string
70+
OptaVariant::toSKUString(
71+
Type const type)
6672
{
6773
switch(type)
6874
{
69-
case OptaVariant::Type::WiFi: return std::string("AFX00002"); break;
75+
case OptaVariant::Type::WiFi: return std::string("AFX00002"); break;
7076
case OptaVariant::Type::RS485: return std::string("AFX00001"); break;
71-
case OptaVariant::Type::Lite: return std::string("AFX00003"); break;
77+
case OptaVariant::Type::Lite: return std::string("AFX00003"); break;
7278
default: __builtin_unreachable(); break;
7379
}
7480
}

src/OptaVariant.h

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,12 +32,20 @@ class OptaVariant
3232
OptaVariant() = delete;
3333
OptaVariant(OptaVariant const &) = delete;
3434

35+
3536
enum class Type { Lite, RS485, WiFi };
3637

37-
static bool get_opta_variant(Type & type);
3838

39-
static std::string toString(Type const type);
40-
static std::string toSKUString(Type const type);
39+
static bool
40+
get_opta_variant(
41+
Type & type);
42+
43+
static std::string
44+
toString(
45+
Type const type);
46+
static std::string
47+
toSKUString(
48+
Type const type);
4149
};
4250

4351
/**************************************************************************************

0 commit comments

Comments
 (0)