Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature: implement suppport for Arduino Opta Analog expansion module #17

Merged
merged 29 commits into from
Sep 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
326af6a
Expose analog inputs of analog expansion modules via OPC UA.
aentinger Aug 26, 2024
f9d3c75
Limit support for expansion modules to 2 (can't afford more due to RAM).
aentinger Aug 26, 2024
5023c47
Transfer constant from sketch to library.
aentinger Aug 26, 2024
1a17e0e
Documenting source of limitation of supported expansion modules.
aentinger Aug 26, 2024
00e1ec9
Dedicating 320 kBytes for OPC UA heap allows for two expansion module…
aentinger Aug 26, 2024
30701ae
Fix: periodically call "updateAnalogInputs" in order to obtain the la…
aentinger Aug 26, 2024
5f461f7
There is no need to update connected expansion modules which we are c…
aentinger Aug 26, 2024
d5a2ff4
Fix: do not use raw ADC reading but already converted pin voltage.
aentinger Aug 26, 2024
4033b0b
Enable control of exposed LEDs on analog expansion module.
aentinger Aug 26, 2024
9715161
Basic implementation for controlling the analog output.
aentinger Aug 28, 2024
f5a74e3
Fix erroneous case to boolean (most be float).
aentinger Aug 28, 2024
f2c6062
Fix numbering of analog outputs to be consistent with printed marking…
aentinger Aug 28, 2024
6f03ad0
Fix: channel ordering to match markings on case.
aentinger Aug 28, 2024
f317b84
First draft on exposing PWM channels for control.
aentinger Sep 3, 2024
7d21101
Move AnalogExpansion::create implementation into .cpp file.
aentinger Sep 3, 2024
14934f6
Hide AnalogInput/AnalogOutput/PwmOutput/LedOutput-Manager classes so …
aentinger Sep 3, 2024
994fd4d
Add note concering content of extras/precompile (only relevant for ma…
aentinger Sep 3, 2024
28b71e4
Unify format of function definition across all files.
aentinger Sep 3, 2024
fa8a277
Simplify API surface of DigitalExpansion classes.
aentinger Sep 3, 2024
f11a82b
Reformatting general Opta public API access to match overall library …
aentinger Sep 3, 2024
40b7787
Read back configured DAC value when queried via OPC/UA.
aentinger Sep 3, 2024
096b4a5
Introduce high level PWM1-n object node to group period and pulse wid…
aentinger Sep 4, 2024
d54d655
Add function to obtain the current value of the PWM period.
aentinger Sep 4, 2024
0f8ef0f
Fix: we need to supply the direct channel number as a parameter (1, 2…
aentinger Sep 4, 2024
3ee5179
Implement reading of PWM pulse width from Arduino_Opta_Blueprint.
aentinger Sep 4, 2024
ff9e303
Fix: also update state of internal member variables when reading back.
aentinger Sep 4, 2024
f647894
Eliminate "node_id" API - as it only makes sense for the ModBus demon…
aentinger Sep 5, 2024
c726189
Document support for various Arduino Opta Expansion modules as well a…
aentinger Sep 5, 2024
7f1877a
Use polymorphism to obtain the correct SKU number for OPC UA.
aentinger Sep 5, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@

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.

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.

### How-to-OPC/UA
* Compile and upload [`examples/opcua_server`](examples/opcua_server/opcua_server.ino)
```bash
Expand Down
191 changes: 161 additions & 30 deletions examples/opcua_server/opcua_server.ino

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions extras/precompile/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
:floppy_disk: Precompile `open62541` library for `cortex-m7`
============================================================
The following instructions can be used to pre-compile the `open62541` library for `cortex-m7`.

**Note**: these steps are only relevant for the maintainers of this library.

```bash
./docker-build.sh
./docker-run.sh
Expand Down
6 changes: 6 additions & 0 deletions src/Arduino_open62541.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,9 @@
#if !defined(ARDUINO_OPTA)
# error "This library does only support Arduino Opta"
#endif

/* Note: exposing properties via OPC UA is extremely
* RAM hungry. We therefore need to limit the number
* of supported Opta expansion modules via OPC UA.
*/
#define OPCUA_MAX_OPTA_EXPANSION_NUM (2u)
105 changes: 52 additions & 53 deletions src/Opta.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,24 +24,38 @@ namespace opcua
* CTOR/DTOR
**************************************************************************************/

Opta::Opta(UA_Server * server, UA_NodeId const & node_id)
Opta::Opta(
UA_Server * server,
UA_NodeId const & node_id,
OptaVariant::Type const opta_type)
: _server{server}
, _node_id{node_id}
, _analog_input_mgr{nullptr}
, _digital_input_mgr{nullptr}
, _relay_mgr{nullptr}
, _led_mgr{nullptr}
, _usr_button{opcua::UserButton::create(_server, _node_id)}
, _analog_input_mgr{opcua::AnalogInputManager::create(_server, _node_id)}
, _digital_input_mgr{opcua::DigitalInputManager::create(_server, _node_id)}
, _relay_mgr{opcua::RelayManager::create(_server, _node_id)}
, _led_mgr{(opta_type == OptaVariant::Type::WiFi) ? opcua::LedManager::create(_server, _node_id) : nullptr}
{
_usr_button = opcua::UserButton::create(_server, _node_id);
if (!_usr_button)
UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "Opta::Ctor: UserButton::create(...) failed.");
UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "%s: UserButton::create(...) failed.", __PRETTY_FUNCTION__);
if (!_analog_input_mgr)
UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "%s: AnalogInputManager::create(...) failed.", __PRETTY_FUNCTION__);
if (!_digital_input_mgr)
UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "%s: DigitalInputManager::create(...) failed.", __PRETTY_FUNCTION__);
if (!_relay_mgr)
UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "%s: RelayManager::create(...) failed.", __PRETTY_FUNCTION__);
if ((opta_type == OptaVariant::Type::WiFi) && !_led_mgr)
UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "%s: LedManager::create(...) failed.", __PRETTY_FUNCTION__);
}

/**************************************************************************************
* PUBLIC MEMBER FUNCTIONS
**************************************************************************************/

Opta::SharedPtr Opta::create(UA_Server * server, OptaVariant::Type const opta_type)
Opta::SharedPtr
Opta::create(
UA_Server * server,
OptaVariant::Type const opta_type)
{
UA_StatusCode rc = UA_STATUSCODE_GOOD;

Expand All @@ -60,8 +74,7 @@ Opta::SharedPtr Opta::create(UA_Server * server, OptaVariant::Type const opta_ty
if (UA_StatusCode_isBad(rc))
{
UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_SERVER,
"Opta::create: UA_Server_addObjectNode(...) failed with %s",
UA_StatusCode_name(rc));
"%s: UA_Server_addObjectNode(...) failed with %s", __PRETTY_FUNCTION__, UA_StatusCode_name(rc));
return nullptr;
}

Expand All @@ -81,8 +94,7 @@ Opta::SharedPtr Opta::create(UA_Server * server, OptaVariant::Type const opta_ty
if (UA_StatusCode_isBad(rc))
{
UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_SERVER,
"Opta::create: UA_Server_addVariableNode(..., \"ManufacturerName\", ...) failed with %s",
UA_StatusCode_name(rc));
"%s: UA_Server_addVariableNode(..., \"ManufacturerName\", ...) failed with %s", __PRETTY_FUNCTION__, UA_StatusCode_name(rc));
return nullptr;
}

Expand All @@ -102,8 +114,7 @@ Opta::SharedPtr Opta::create(UA_Server * server, OptaVariant::Type const opta_ty
if (UA_StatusCode_isBad(rc))
{
UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_SERVER,
"Opta::create: UA_Server_addVariableNode(..., \"ModelName\", ...) failed with %s",
UA_StatusCode_name(rc));
"%s: UA_Server_addVariableNode(..., \"ModelName\", ...) failed with %s", __PRETTY_FUNCTION__, UA_StatusCode_name(rc));
return nullptr;
}

Expand All @@ -123,61 +134,49 @@ Opta::SharedPtr Opta::create(UA_Server * server, OptaVariant::Type const opta_ty
if (UA_StatusCode_isBad(rc))
{
UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_SERVER,
"Opta::create: UA_Server_addVariableNode(..., \"Status\", ...) failed with %s",
UA_StatusCode_name(rc));
"%s: UA_Server_addVariableNode(..., \"Status\", ...) failed with %s", __PRETTY_FUNCTION__, UA_StatusCode_name(rc));
return nullptr;
}

auto const instance_ptr = std::make_shared<Opta>(server, node_id);
auto const instance_ptr = std::make_shared<Opta>(server, node_id, opta_type);
return instance_ptr;
}

AnalogInputManager::SharedPtr Opta::analog_input_mgr()
void
Opta::add_analog_input(
UA_Server * server,
const char * display_name,
AnalogInput::OnReadRequestFunc const on_read_request_func)
{
if (!_analog_input_mgr)
{
_analog_input_mgr = opcua::AnalogInputManager::create(_server, _node_id);
if (!_analog_input_mgr)
UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "Opta::analog_input_mgr: AnalogInputManager::create(...) failed.");
}

return _analog_input_mgr;
_analog_input_mgr->add_analog_input(server, display_name, on_read_request_func);
}

DigitalInputManager::SharedPtr Opta::digital_input_mgr()
void
Opta::add_digital_input(
UA_Server * server,
const char * display_name,
DigitalInput::OnReadRequestFunc const on_read_request_func)
{
if (!_digital_input_mgr)
{
_digital_input_mgr = opcua::DigitalInputManager::create(_server, _node_id);
if (!_digital_input_mgr)
UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "Opta::digital_input_mgr: DigitalInputManager::create(...) failed.");
}

return _digital_input_mgr;
_digital_input_mgr->add_digital_input(server, display_name, on_read_request_func);
}

RelayManager::SharedPtr Opta::relay_mgr()
void
Opta::add_relay_output(
UA_Server * server,
const char * display_name,
Relay::OnSetRelayStateFunc const on_set_relay_state)
{
if (!_relay_mgr)
{
_relay_mgr = opcua::RelayManager::create(_server, _node_id);
if (!_relay_mgr)
UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "Opta::relay_mgr: RelayManager::create(...) failed.");
}

return _relay_mgr;
_relay_mgr->add_relay_output(server, display_name, on_set_relay_state);
}

LedManager::SharedPtr Opta::led_mgr()
void
Opta::add_led_output(
UA_Server * server,
const char * display_name,
Led::OnSetLedStateFunc const on_set_led_state)
{
if (!_led_mgr)
{
_led_mgr = opcua::LedManager::create(_server, _node_id);
if (!_led_mgr)
UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "Opta::led_mgr: LedManager::create(...) failed.");
}

return _led_mgr;
if (_led_mgr) /* Only available for Arduino Opta WiFi. */
_led_mgr->add_led_output(server, display_name, on_set_led_state);
}

/**************************************************************************************
Expand Down
48 changes: 35 additions & 13 deletions src/Opta.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,19 +42,41 @@ class Opta
typedef std::shared_ptr<Opta> SharedPtr;


static SharedPtr create(UA_Server * server, OptaVariant::Type const opta_type);


Opta(UA_Server * server, UA_NodeId const & node_id);


AnalogInputManager::SharedPtr analog_input_mgr();
DigitalInputManager::SharedPtr digital_input_mgr();
RelayManager::SharedPtr relay_mgr();
LedManager::SharedPtr led_mgr();


[[nodiscard]] UA_NodeId node_id() const { return _node_id; }
static SharedPtr
create(
UA_Server * server,
OptaVariant::Type const opta_type);


Opta(
UA_Server * server,
UA_NodeId const & node_id,
OptaVariant::Type const opta_type);


void
add_analog_input(
UA_Server * server,
const char * display_name,
AnalogInput::OnReadRequestFunc const on_read_request_func);

void
add_digital_input(
UA_Server * server,
const char * display_name,
DigitalInput::OnReadRequestFunc const on_read_request_func);

void
add_relay_output(
UA_Server * server,
const char * display_name,
Relay::OnSetRelayStateFunc const on_set_relay_state);

void
add_led_output(
UA_Server * server,
const char * display_name,
Led::OnSetLedStateFunc const on_set_led_state);


private:
Expand Down
12 changes: 9 additions & 3 deletions src/OptaExpansionManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,9 @@ namespace opcua
* PUBLIC MEMBER FUNCTIONS
**************************************************************************************/

DigitalMechExpansion::SharedPtr OptaExpansionManager::create_digital_mechanical_expansion(uint8_t const exp_num)
DigitalMechExpansion::SharedPtr
OptaExpansionManager::create_digital_mechanical_expansion(
uint8_t const exp_num)
{
auto const exp_mech_opcua = opcua::DigitalMechExpansion::create(
_server,
Expand All @@ -35,7 +37,9 @@ DigitalMechExpansion::SharedPtr OptaExpansionManager::create_digital_mechanical_
return exp_mech_opcua;
}

DigitalStSolidExpansion::SharedPtr OptaExpansionManager::create_digital_solid_state_expansion(uint8_t const exp_num)
DigitalStSolidExpansion::SharedPtr
OptaExpansionManager::create_digital_solid_state_expansion(
uint8_t const exp_num)
{
auto const exp_solid_state_opcua = opcua::DigitalStSolidExpansion::create(
_server,
Expand All @@ -46,7 +50,9 @@ DigitalStSolidExpansion::SharedPtr OptaExpansionManager::create_digital_solid_st
return exp_solid_state_opcua;
}

AnalogExpansion::SharedPtr OptaExpansionManager::create_analog_expansion(uint8_t const exp_num)
AnalogExpansion::SharedPtr
OptaExpansionManager::create_analog_expansion(
uint8_t const exp_num)
{
auto const exp_analog_opcua = opcua::AnalogExpansion::create(
_server,
Expand Down
7 changes: 5 additions & 2 deletions src/OptaExpansionManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,15 @@ class OptaExpansionManager
typedef std::shared_ptr<OptaExpansionManager> SharedPtr;


static SharedPtr create(UA_Server * server) {
static SharedPtr
create(
UA_Server * server) {
return std::make_shared<OptaExpansionManager>(server);
}


OptaExpansionManager(UA_Server * server)
OptaExpansionManager(
UA_Server * server)
: _server{server}
{ }

Expand Down
16 changes: 11 additions & 5 deletions src/OptaVariant.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,9 @@ namespace opcua
* PUBLIC MEMBER FUNCTIONS
**************************************************************************************/

bool OptaVariant::get_opta_variant(Type & type)
bool
OptaVariant::get_opta_variant(
Type & type)
{
OptaBoardInfo * info = boardInfo();

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

std::string OptaVariant::toString(Type const type)
std::string
OptaVariant::toString(
Type const type)
{
switch(type)
{
Expand All @@ -62,13 +66,15 @@ std::string OptaVariant::toString(Type const type)
}
}

std::string OptaVariant::toSKUString(Type const type)
std::string
OptaVariant::toSKUString(
Type const type)
{
switch(type)
{
case OptaVariant::Type::WiFi: return std::string("AFX00002"); break;
case OptaVariant::Type::WiFi: return std::string("AFX00002"); break;
case OptaVariant::Type::RS485: return std::string("AFX00001"); break;
case OptaVariant::Type::Lite: return std::string("AFX00003"); break;
case OptaVariant::Type::Lite: return std::string("AFX00003"); break;
default: __builtin_unreachable(); break;
}
}
Expand Down
14 changes: 11 additions & 3 deletions src/OptaVariant.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,20 @@ class OptaVariant
OptaVariant() = delete;
OptaVariant(OptaVariant const &) = delete;


enum class Type { Lite, RS485, WiFi };

static bool get_opta_variant(Type & type);

static std::string toString(Type const type);
static std::string toSKUString(Type const type);
static bool
get_opta_variant(
Type & type);

static std::string
toString(
Type const type);
static std::string
toSKUString(
Type const type);
};

/**************************************************************************************
Expand Down
Loading
Loading