Energy Management with Opta™

This application note describes how to implement Opta™ for a domestic energy management system.


The Opta™ can be an irreplaceable support for home energy management. Getting information on instantaneous electrical consumption and interacting with the customer’s consumption plan, daily usage statistics, and seasonal forecasts can help in planning and managing electrical devices to optimize energetical efficiency. Always be connected and informed by integrating the Arduino IoT cloud, and add self-adjustment capability by monitoring and logging electrical statistics along with the option to operate the connected devices on-demand based on pre-set triggers.

The industry is transitioning towards Industry 4.0, going by the name of the Industrial Internet of Things (IIoT). Industrial energy management and operation of connected devices on-demand within a power grid, will bring vast cost benefits and production performance uplift. To guarantee protection from unauthorized access and service interruptions, Opta™ integrates a secure element that offers the data integrity, encryption, and certificate storage capabilities needed to provide an IoT node for the deployment of a private and safe Industrial Internet of Things network.


This application note shows an example of an energy management system, leveraging Opta™ and Arduino IoT Cloud capabilities to perform the following operations:

  • The Opta™ will receive remote actuation commands triggered on demand via Arduino Cloud
  • The Opta™ will operate devices, based on the user's consumption profile and on the available power provided by solar panels
  • The domestic appliances actuation will be automated considering both the operational contexts above: upon user's demand and triggered by power availability and consumption efficiency

A graphical representation of the intended application is shown below:

Graphical representation of the energy management application
Graphical representation of the energy management application

Hardware and Software Requirements

Hardware Requirements

  • Opta™ PLC with RS-485 support (x1)
  • Solar panel with respective system (Controller, battery, and inverter) or similar power system
  • USB-C® cable (x1)
  • 7M.24 Energy meter (x1)
  • Domestic appliance or devices of interest
  • RS-485 connection wire as recommended by the standard specification (x3):
  • STP/UTP 24-18AWG (Unterminated) 100-130Ω rated
  • STP/UTP 22-16AWG (Terminated) 100-130Ω rated

Software Requirements

Hardware Setup Overview

The electrical connections of the intended application design are shown in the diagram below:

Electrical connections of the application
Electrical connections of the application

The Opta™ will receive instant consumption information from the energy meter's thresholds via Modbus RTU through the RS-485 interface. The power delivered by the solar panels will go through a series of processes to get to the energy meter. The domestic appliances will be controlled using the built-in relay outputs of Opta™. Other types of power sources can be used instead of solar panels.

Opta™ Energy Management Model Description

The Opta™ will manage the available power, based on the data reported by the energy meter connected to the power source: the solar panel. It will request and receive data from the energy meter and will estimate the instant consumption given the energy meter thresholds and actual power delivered from the solar panel.

The energy meter model used in this application note is 7M.24 from Finder, the datasheet can be found here, and communicates using Modbus RTU over the RS-485 interface. The Opta™ relays will be used to actuate the domestic appliances of interest. To compile data and manage the power distribution, Opta™ will perform the following actions:

  • Request voltage and current data measured from the energy meter.
  • Obtain three sets of Power data from the energy meter displayed as: Active Power Total - Pt (W), Reactive Power Total - Qt (var), and Apparent Power Total - St (VA).
  • Categorize the data acquired regarding Voltage, Current, Active Power Total - Pt (W), Reactive Power Total - Qt (var), and Apparent Power Total - St (VA) in multiple variables to report the attributes of each value by Actual, Average, Maximum, and Minimum.
  • Retrieve the Energy Counter value in Wh and varh.
  • Make an adequate power distribution to control the desired domestic appliances, based on the user's energy profile.

While all these processes are handled by Opta™ locally, the device will also be connected to the Arduino Cloud via Wi-Fi®. Through the Arduino Cloud, it will be possible to visualize the energy consumption and actuate remotely on-demand the connected devices.

Opta™ Energy Management Example Code

The code exemplifies how Opta™ can achieve the functionalities described previously. Note that some of the functions in the code are generated by Arduino Cloud during the dashboard configuration.

The following headers are required to enable the RS-485 interface, Modbus RTU protocol, Arduino Cloud connection, and the scheduler. The scheduler supervises the data exchange over the RS-485 interface using the Modbus RTU protocol. In addition to this, it contains the parameters needed for a stable communication as per Modbus RTU specifications.

1#include "stm32h7xx_ll_gpio.h"
2#include "thingProperties.h"
4#include <ArduinoModbus.h>
5#include <ArduinoRS485.h>
6#include <Scheduler.h>
8constexpr auto baudrate { 19200 };
10// Calculate preDelay and postDelay in microseconds as per Modbus RTU Specification
11// MODBUS over serial line specification and implementation guide V1.02
12// Paragraph MODBUS Message RTU Framing
13// https://modbus.org/docs/Modbus_over_serial_line_V1_02.pdf
14constexpr auto bitduration { 1.f / baudrate };
15constexpr auto preDelayBR { bitduration * 9.6f * 3.5f * 1e6 };
16constexpr auto postDelayBR { bitduration * 9.6f * 3.5f * 1e6 };
18#define F7M24 0x21

Getting Started with Modbus RTU on Opta™ tutorial will be able to give a deeper understanding of implementings Modbus RTU with Opta™.


method uses a simple comparison between a
desired target
value and a
required target
value and generates, as an output, the trigger for a relay activation.


desired target
value is a user defined value that states the activation threshold desired for the relay activation.


required target
value is a variable stating the minimum power required to operate a relay in stable manner and is calculated based on readings gathered from the energy meter, such as
that represents the instant total active power measured at the moment of request.


defines the operating margin in percentage. For example, defining 10% as
inside the
method, provides a safety overhead headroom. The arguments in this method are defined as default configurations and can be later modified from the Arduino Cloud dashboard.


defines the output port prompted under certain activation conditions.

2 Control the relay output based on the user (consumption) profile input and configured power/energy target.
4 @param desired_target Desired resource required to run the connected device on the relay.
5 @param req_target Minimum resource required to run the connected device on the relay.
6 @param relayTarget Relay to activate or deactivate.
7 @return Returns 0 or 1, representing HIGH state for 1 and LOW state for 0.
9uint8_t relay_Trigger(int desired_target, int req_target, pin_size_t relayTarget){
10 if ((desired_target >= req_target) && (desired_target < (req_target*user_profile.uV_code))){
11 digitalWrite(relayTarget, HIGH);
12 Serial.println(F("Energy Manager: Stable operation margin - Turning ON: "));
13 Serial.print(relayTarget);
14 Serial.println(F(""));
15 } else {
16 digitalWrite(relayTarget, LOW);
17 Serial.println(F("Energy Manager: Unstable / possible overload - Turning OFF: "));
18 Serial.print(relayTarget);
19 Serial.println(F(""));
20 return 0;
21 }
25 Initial user profile setup.
27 @param init_OperMargin System operation margin for power budget represented in percentage.
28 @param init_Watt User defined Wattage limit for the system.
29 @param init_WhCon User defined Energy consumption limit for the system.
30 @return none
32void consumption_profile(uint32_t init_OperMargin, uint32_t init_Watt, uint32_t init_WhCon){
33 uOperMargin = init_OperMargin;
34 user_profile.uV_code = uOperMargin;
36 uWatt = init_Watt;
37 user_profile.uW_code = uWatt;
39 uWhCon = init_WhCon;
40 user_profile.uWh_code = uWhCon;

The following function uses the information obtained by the energy meter and the inputs from the user provided through the Arduino Cloud, to control the connected electronic devices. It has two main conditions based on energy and power, to determine the system's capacity to activate the devices connected to it, which will produce an action only within an energy consumption margin below a 10% safety operation margin.

However, the system will warn the user if the average power required is above the defined profile limit. Specific data have been considered for this application note based on the devices used to realize the proof of concept for this example.


Device #1
is configured for low-power devices seeking for nominal current feed or actual available power to turn on safely. It can be also actuated remotely by the user.


Device #2
is configured for devices requiring more power. It will trigger the actuation if the average or the actual available power is enough to satisfy the defined power condition.

2 Monitors and uses the user defined profile and retrieved information from Energy meter to manage connected devices of interest.
4 @param Wh_packet Retrieved energy information from Energy meter in unit of [Wh].
5 @param Device_#_f Device flag controlled by relay_Trigger() function. # specifies device number or designation.
6 @param directOverride1 Direct override flag for Device #1 controlled via Cloud.
7 @param W_avg Average power information retrieved from Energy meter.
9void energy_distro_ctrl(){
10 if (Wh_packet != 0){
11 uWhOpta = Wh_packet;
12 } else {
13 Serial.println(F("Energy Manager: Energy information recollection stand-by"));
14 }
16 // Energy consumption conditionar with 10% safety margin
17 if ((Wh_packet*user_profile.uV_code) <= user_profile.uWh_code){
18 // Device #1 specific behaviors
19 Device_1_f = relay_Trigger(1, A_actual, D0);
20 if (!Device_1_f){
21 if (relay_Trigger(3, W_actual, D0)){
22 Serial.println(F("Energy Manager: Secondary condition pass, may be unstable"));
23 } else {
24 Serial.println(F("Energy Manager: Insufficient resource"));
25 digitalWrite(D0, LOW);
26 }
27 } else{
28 Serial.println(F("Energy Manager: Conditions green"));
29 }
31 // Device #2 specific behaviors
32 Device_2_f = relay_Trigger(12, W_avg, D1);
33 if (!Device_2_f){
34 if (relay_Trigger(12, W_actual, D1)){
35 Serial.println(F("Energy Manager: Secondary Power condition pass"));
36 } else {
37 Serial.println(F("Energy Manager: Insufficient resource"));
38 digitalWrite(D0, LOW);
39 }
40 } else{
41 Serial.println(F("Energy Manager: Conditions green"));
42 }
44 } else {
45 digitalWrite(D0, LOW);
46 digitalWrite(D1, LOW);
47 Serial.println(F("Energy Manager: Energy consumption is high! - Warning"));
48 }
50 // Direct override request for Device #1
51 if (directOverride1 == true){
52 // Override Device #1
53 Serial.println(F("Energy Manager: Direct Override Request Received"));
54 digitalWrite(D0, HIGH);
55 }
57 // Conditioner to notify users without cutting power to electronic devices
58 if ((W_avg*user_profile.uV_code) > user_profile.uW_code){
59 Serial.println(F("Energy Manager: Average Power is above profile limit! - Warning"));
60 Serial.print(W_avg*user_profile.uV_code);
61 Serial.println(F(""));
62 }

The energy meter has several registry addresses specifying a wide range of electrical information. For this application, we will prioritize 7 elements. These 7 elements are Voltage

, Current
, Active Power Total
, Reactive Power Total
, Apparent Power Total
, Energy in two different units of
. The data will be based on
, and

2 Requests and retrieves actual electrical, and power information from Energy meter over Modbus RTU protocol.
4void modbus_com_actual(){
5 // Actual Measurements
6 // Voltage (V)
7 V_actual = readInputRegisterValues(F7M24, 0x6B, 2);
9 // Current (A)
10 A_actual = readInputRegisterValues(F7M24, 0x7E, 2);
12 // Active Power Total - Pt (W)
13 W_actual = readInputRegisterValues(F7M24, 0x8C, 2);
15 // Reactive Power Total - Qt (var)
16 Var_actual = readInputRegisterValues(F7M24, 0x94, 2);
18 // Apparent Power Total - St (VA)
19 Va_actual = readInputRegisterValues(F7M24, 0x9C, 2);
20 delay(100);
26 Requests and retrieves energy information from Energy meter over Modbus RTU protocol.
28void modbus_com_energy(){
29 // Energy
30 // Energy (Wh) - n1
31 Wh_packet = readInputRegisterValues(F7M24, 0xAC0, 2);
33 // Energy (varh) - n2
34 Varh_packet = readInputRegisterValues(F7M24, 0x2F2, 2);
35 delay(100);

To enable the Modbus RTU on Opta™, the

function is dedicated to initialize the protocol correctly.

2 Sets up Modbus RTU protocol configuration.
4void RTU_Setup(){
5 Serial.println("Energy Management - Modbus RTU Client");
7 RS485.setDelays(preDelayBR, postDelayBR);
9 // start the Modbus RTU client
10 // 7M.24 Energy meter specifies 19200 of default baudrate and 8N2 frame
11 if (!ModbusRTUClient.begin(baudrate, SERIAL_8N2)) {
12 Serial.println("Failed to start Modbus RTU Client!");
13 while (1)
14 ;
15 }

The following functions allow to access the target's data by specifying the device and register address.

2 Writes Coil values given argument inputs.
4 @param dev_address Device address.
5 @param reg_address Register address.
6 @param coil_write Data to write.
7 @param byte_count Number of bytes.
9void writeCoilValues(int dev_address, uint8_t reg_address, uint8_t coil_write, int byte_count){
10 ModbusRTUClient.beginTransmission(dev_address, COILS, reg_address, byte_count);
11 ModbusRTUClient.write(coil_write);
13 if (!ModbusRTUClient.endTransmission()) {
14 Serial.print("failed! ");
15 Serial.println(ModbusRTUClient.lastError());
16 } else {
17 Serial.println("success");
18 }
22 Reads Coil values given argument inputs.
24 @param dev_address Device address.
25 @param reg_address Register address.
26 @param byte_count Number of bytes.
27 @param packet Holding register value reading.
29void readCoilValues(int dev_address, uint8_t reg_address, int byte_count, int32_t packet){
30 Serial.print("Reading Coil values ... ");
32 // read 10 Coil values from (slave) id 42, address 0x00
33 if (!ModbusRTUClient.requestFrom(dev_address, COILS, reg_address, byte_count)) {
34 Serial.print("failed! ");
35 Serial.println(ModbusRTUClient.lastError());
36 } else {
37 Serial.println("success");
39 while (ModbusRTUClient.available()) {
40 Serial.print(ModbusRTUClient.read());
41 packet = ModbusRTUClient.read();
42 Serial.print(' ');
43 }
45 Serial.println();
46 }
50 Reads Discrete Input Register values given argument inputs.
52 @param dev_address Device address.
53 @param reg_address Register address.
54 @param byte_count Number of bytes.
55 @param packet Holding register value reading.
57void readDiscreteInputValues(int dev_address, uint8_t reg_address, int byte_count, int32_t packet){
58 if (!ModbusRTUClient.requestFrom(dev_address, DISCRETE_INPUTS, reg_address, byte_count)) {
59 Serial.print("failed! ");
60 Serial.println(ModbusRTUClient.lastError());
61 } else {
62 Serial.println("success");
64 while (ModbusRTUClient.available()) {
65 Serial.print(ModbusRTUClient.read());
66 packet = ModbusRTUClient.read();
67 Serial.print(' ');
68 }
69 Serial.println();
70 }
74 Writes Holding Register values given argument inputs.
76 @param dev_address Device address.
77 @param reg_address Register address.
78 @param holding_write Data to write.
79 @param byte_count Number of bytes.
81void writeHoldingRegisterValues(int dev_address, uint8_t reg_address, uint8_t holding_write, int byte_count){
82 ModbusRTUClient.beginTransmission(dev_address, HOLDING_REGISTERS, reg_address, byte_count);
83 ModbusRTUClient.write(holding_write);
85 if (!ModbusRTUClient.endTransmission()) {
86 Serial.print("failed! ");
87 Serial.println(ModbusRTUClient.lastError());
88 } else {
89 Serial.println("success");
90 }
94 Reads Holding Register values given argument inputs.
96 @param dev_address Device address.
97 @param reg_address Register address.
98 @param byte_count Number of bytes.
99 @param packet Holding register value reading.
101void readHoldingRegisterValues(int dev_address, uint8_t reg_address, int byte_count, int32_t packet){
102 if (!ModbusRTUClient.requestFrom(dev_address, HOLDING_REGISTERS, reg_address, byte_count)) {
103 Serial.print("failed! ");
104 Serial.println(ModbusRTUClient.lastError());
105 } else {
106 Serial.println("success");
108 while (ModbusRTUClient.available()) {
109 Serial.print(ModbusRTUClient.read());
110 packet = ModbusRTUClient.read();
111 Serial.print(' ');
112 }
113 Serial.println();
114 }
118 Reads Input Register values given argument inputs.
120 @param dev_address Device address.
121 @param reg_address Register address.
122 @param byte_count Number of bytes.
124uint8_t readInputRegisterValues(int dev_address, uint8_t reg_address, int byte_count){
125 uint8_t packet;
126 if (!ModbusRTUClient.requestFrom(dev_address, INPUT_REGISTERS, reg_address, byte_count)) {
127 Serial.print("failed! ");
128 Serial.println(ModbusRTUClient.lastError());
129 return 0;
130 } else {
131 Serial.println("success");
133 while (ModbusRTUClient.available()) {
134 packet = ModbusRTUClient.read();
135 Serial.println(packet);
136 }
137 return packet;
138 }

The bridge between the Arduino Cloud and Opta™ is defined using the

function. The processes are grouped into a single task for easier code maintenance. The
characterizes the operating headroom margin, power, and energy limit. These parameters will be used as the default local configuration and available for access via the Arduino Cloud dashboard.

2 Sets up configuration for Arduino Cloud
4void iot_cloud_setup(){
5 // Defined in thingProperties.h
6 initProperties();
8 // Connect to Arduino IoT Cloud
9 ArduinoCloud.begin(ArduinoIoTPreferredConnection);
11 /*
12 The following function allows you to obtain more information
13 related to the state of network and IoT Cloud connection and errors
14 the higher number the more granular information you’ll get.
15 The default is 0 (only errors).
16 Maximum is 4
17 */
18 setDebugMessageLevel(2);
19 ArduinoCloud.printDebugInfo();
21 // Configure values at which the limit the user desires to operate within and share with Arduino Cloud
22 consumption_profile(1.1, 120, 2880);

The Opta™ will initialize the RS-485 interface and Modbus RTU protocol, the Arduino Cloud connection, and the scheduler to handle Modbus RTU protocol based communication with the 7M.24 energy meter. Analog and Digital I/Os are configured here as well.

1void setup() {
2 // Initial Parameter
3 directOverride1 = false;
4 uWhOpta = 0;
6 Serial.begin(9600);
7 while (!Serial);
9 delay(1000);
11 // Analog/Digital IO Port Configuration
12 analogIO_Setup();
13 digitalIO_Setup();
15 // Modbus RTU Configuration
16 RTU_Setup();
18 // Status LED configuration;
19 plc_led_Setup();
21 // IoT Cloud Setup
22 iot_cloud_setup();
24 // Only for Device On State flag
25 digitalWrite(LEDG, HIGH);
27 // Scheduler -> ModBus
28 Scheduler.startLoop(modbus_line);

The Opta™ will have the main

to prioritize tasks to communicate with Arduino Cloud and local processes. While the
will focus on handling Modbus RTU protocol-based communication over the RS-485 interface.

1void loop() {
2 // Cloud exchange
3 consumption_var_container();
4 ArduinoCloud.update();
6 // Profile based energy management tasks
7 energy_distro_ctrl();
9 delay(1000);
13 Dedicated function for scheduler to retrieve Energy meter's information over Modbus RTU protocol
15void modbus_line(){
16 modbus_com_actual();
17 modbus_com_avg();
18 modbus_com_max();
19 modbus_com_min();
20 modbus_com_energy();
21 modbus_com_monitor();

The header containing Arduino Cloud properties is as follows. Please note that this is a file generated within the Arduino Cloud platform inside the sketch's working space, requiring Opta™ to be configured beforehand. Thus, given project requirements, the properties are subject to changes.

1// Code generated by Arduino IoT Cloud, DO NOT EDIT.
3#include <ArduinoIoTCloud.h>
4#include <Arduino_ConnectionHandler.h>
6const char SSID[] = SECRET_SSID; // Network SSID (name)
7const char PASS[] = SECRET_OPTIONAL_PASS; // Network password (use for WPA, or use as key for WEP)
9void onUOperMarginChange();
10void onUWattChange();
11void onUWhConChange();
12void onDirectOverride1Change();
14float uOperMargin;
15float uWatt;
16float uWhCon;
17float uWhOpta;
18bool directOverride1;
20void initProperties(){
22 ArduinoCloud.addProperty(uOperMargin, READWRITE, ON_CHANGE, onUOperMarginChange);
23 ArduinoCloud.addProperty(uWatt, READWRITE, ON_CHANGE, onUWattChange);
24 ArduinoCloud.addProperty(uWhCon, READWRITE, ON_CHANGE, onUWhConChange);
25 ArduinoCloud.addProperty(uWhOpta, READ, ON_CHANGE, NULL);
26 ArduinoCloud.addProperty(directOverride1, READWRITE, ON_CHANGE, onDirectOverride1Change);
30WiFiConnectionHandler ArduinoIoTPreferredConnection(SSID, PASS);

As the header is generated by Arduino Cloud as a function of the variables created, preferably it should not be edited. Further changes to add or remove variables, should be managed within the Cloud environment. The header above is an example that couples with the specific script running based on the demonstration specification. The same script can be used or modified to scale and adapt to the desired system specification.

Connecting Opta™ with Arduino Cloud

To set up Opta™ with the cloud platform go to the Arduino Cloud. You can go to our Getting started with the cloud tutorial to learn on how to get started with the Arduino Cloud. Follow the Arduino Cloud link to get access to more helpful and interesting tutorials.

You will be able to have an interface configured as the following preview to control and monitor parameters of interest as a dashboard example.

Opta™ Energy Management Interactive Dashboard on Arduino Cloud
Opta™ Energy Management Interactive Dashboard on Arduino Cloud

Complete Opta™ Energy Management Sketch

The complete sketch used for the development of energy management on Opta™ with Arduino Cloud can be accessed here.


You have built an Opta™ energy manager capable of monitoring an electrical system for its power availability and consumption to control power or energy-prioritized devices, with a remote actuation feature available thanks to the Arduino Cloud.

Opta™ can help manage the energetical balance in industrial environments: in this example, we have considered a context where machines can be operated opportunistically, based on power availability, over a 24/7 time range, to improve the overall power efficiency.

By slightly modifying this project, for example, by connecting different types of power sources, stating different conditions for the machine's operations, and modifying parameters linked to the power requirements, it will be easy to create new projects to address many cases, to reach the best solution to manage the energy distribution effectively.

Next Steps

Implementing an energy management system at home or in a workspace environment can be very useful to reduce unnecessary power consumption. Explore the possibilities of energy management powered by Arduino Cloud to build an energy-efficient environment.

Tutorial Toolbox

Contribute to Arduino

Join the community and suggest improvements to this article via GitHub. Make sure to read out contribution policy before making your pull request.

Missing something?

Check out our store and get what you need to follow this tutorial.

Suggest Changes

The content on docs.arduino.cc is facilitated through a public GitHub repository. You can read more on how to contribute in the contribution policy.