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:
A graphical representation of the intended application is shown below:
ArduinoRS485
, ArduinoModbus
and Scheduler
. You can install those libraries via the Library Manager of the Arduino IDE.The electrical connections of the intended application design are shown in the diagram below:
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.
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:
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.
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"3
4#include <ArduinoModbus.h>5#include <ArduinoRS485.h>6#include <Scheduler.h>7
8constexpr auto baudrate { 19200 };9
10// Calculate preDelay and postDelay in microseconds as per Modbus RTU Specification11// MODBUS over serial line specification and implementation guide V1.0212// Paragraph 2.5.1.1 MODBUS Message RTU Framing13// https://modbus.org/docs/Modbus_over_serial_line_V1_02.pdf14constexpr auto bitduration { 1.f / baudrate };15constexpr auto preDelayBR { bitduration * 9.6f * 3.5f * 1e6 };16constexpr auto postDelayBR { bitduration * 9.6f * 3.5f * 1e6 };17
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™.
The
relay_Trigger()
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.The
desired target
value is a user defined value that states the activation threshold desired for the relay activation.The
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 W_actual
that represents the instant total active power measured at the moment of request.The
user_profile.uV_code
defines the operating margin in percentage. For example, defining 10% as 1.1
inside the consumption_profile()
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.The
relayTarget
defines the output port prompted under certain activation conditions.1/**2 Control the relay output based on the user (consumption) profile input and configured power/energy target.3
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.8*/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 }22}23
24/**25 Initial user profile setup.26
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 none31*/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;35
36 uWatt = init_Watt;37 user_profile.uW_code = uWatt;38
39 uWhCon = init_WhCon;40 user_profile.uWh_code = uWhCon;41}
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.
The
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.The
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.1/**2 Monitors and uses the user defined profile and retrieved information from Energy meter to manage connected devices of interest.3
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.8*/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 }15
16 // Energy consumption conditionar with 10% safety margin17 if ((Wh_packet*user_profile.uV_code) <= user_profile.uWh_code){18 // Device #1 specific behaviors19 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 }30
31 // Device #2 specific behaviors32 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 }43
44 } else {45 digitalWrite(D0, LOW); 46 digitalWrite(D1, LOW); 47 Serial.println(F("Energy Manager: Energy consumption is high! - Warning"));48 }49 50 // Direct override request for Device #151 if (directOverride1 == true){52 // Override Device #153 Serial.println(F("Energy Manager: Direct Override Request Received"));54 digitalWrite(D0, HIGH); 55 }56
57 // Conditioner to notify users without cutting power to electronic devices58 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 }63}
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 V
, Active Power Total A
, Reactive Power Total W
, Apparent Power Total var
, Energy in two different units of VA
and Wh
. The data will be based on varh
actual
, average
, maximum
, and minimum
.1/**2 Requests and retrieves actual electrical, and power information from Energy meter over Modbus RTU protocol.3*/4void modbus_com_actual(){5 // Actual Measurements6 // Voltage (V)7 V_actual = readInputRegisterValues(F7M24, 0x6B, 2);8
9 // Current (A)10 A_actual = readInputRegisterValues(F7M24, 0x7E, 2);11
12 // Active Power Total - Pt (W)13 W_actual = readInputRegisterValues(F7M24, 0x8C, 2);14 15 // Reactive Power Total - Qt (var)16 Var_actual = readInputRegisterValues(F7M24, 0x94, 2);17
18 // Apparent Power Total - St (VA)19 Va_actual = readInputRegisterValues(F7M24, 0x9C, 2);20 delay(100);21}22
23...24
25/**26 Requests and retrieves energy information from Energy meter over Modbus RTU protocol.27*/28void modbus_com_energy(){29 // Energy30 // Energy (Wh) - n131 Wh_packet = readInputRegisterValues(F7M24, 0xAC0, 2);32
33 // Energy (varh) - n234 Varh_packet = readInputRegisterValues(F7M24, 0x2F2, 2);35 delay(100);36}
To enable the Modbus RTU on Opta™, the
RTU_Setup()
function is dedicated to initialize the protocol correctly.1/**2 Sets up Modbus RTU protocol configuration.3*/4void RTU_Setup(){5 Serial.println("Energy Management - Modbus RTU Client");6
7 RS485.setDelays(preDelayBR, postDelayBR);8
9 // start the Modbus RTU client10 // 7M.24 Energy meter specifies 19200 of default baudrate and 8N2 frame11 if (!ModbusRTUClient.begin(baudrate, SERIAL_8N2)) {12 Serial.println("Failed to start Modbus RTU Client!");13 while (1)14 ;15 }16}
The following functions allow to access the target's data by specifying the device and register address.
1/**2 Writes Coil values given argument inputs. 3
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.8*/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);12 13 if (!ModbusRTUClient.endTransmission()) {14 Serial.print("failed! ");15 Serial.println(ModbusRTUClient.lastError());16 } else {17 Serial.println("success");18 }19}20
21/**22 Reads Coil values given argument inputs. 23
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.28*/29void readCoilValues(int dev_address, uint8_t reg_address, int byte_count, int32_t packet){30 Serial.print("Reading Coil values ... ");31
32 // read 10 Coil values from (slave) id 42, address 0x0033 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");38
39 while (ModbusRTUClient.available()) {40 Serial.print(ModbusRTUClient.read());41 packet = ModbusRTUClient.read();42 Serial.print(' ');43 }44 45 Serial.println();46 }47}48
49/**50 Reads Discrete Input Register values given argument inputs. 51
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.56*/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");63
64 while (ModbusRTUClient.available()) {65 Serial.print(ModbusRTUClient.read());66 packet = ModbusRTUClient.read();67 Serial.print(' ');68 }69 Serial.println();70 }71}72
73/**74 Writes Holding Register values given argument inputs. 75
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.80*/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);84
85 if (!ModbusRTUClient.endTransmission()) {86 Serial.print("failed! ");87 Serial.println(ModbusRTUClient.lastError());88 } else {89 Serial.println("success");90 }91}92
93/**94 Reads Holding Register values given argument inputs. 95
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.100*/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");107
108 while (ModbusRTUClient.available()) {109 Serial.print(ModbusRTUClient.read());110 packet = ModbusRTUClient.read();111 Serial.print(' ');112 }113 Serial.println();114 }115}116
117/**118 Reads Input Register values given argument inputs. 119
120 @param dev_address Device address.121 @param reg_address Register address.122 @param byte_count Number of bytes.123*/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");132
133 while (ModbusRTUClient.available()) {134 packet = ModbusRTUClient.read();135 Serial.println(packet);136 }137 return packet;138 }139}
The bridge between the Arduino Cloud and Opta™ is defined using the
iot_cloud_setup()
function. The processes are grouped into a single task for easier code maintenance. The consumption_profile()
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.1/**2 Sets up configuration for Arduino Cloud3*/4void iot_cloud_setup(){5 // Defined in thingProperties.h6 initProperties();7
8 // Connect to Arduino IoT Cloud9 ArduinoCloud.begin(ArduinoIoTPreferredConnection);10 11 /*12 The following function allows you to obtain more information13 related to the state of network and IoT Cloud connection and errors14 the higher number the more granular information you’ll get.15 The default is 0 (only errors).16 Maximum is 417 */18 setDebugMessageLevel(2);19 ArduinoCloud.printDebugInfo();20
21 // Configure values at which the limit the user desires to operate within and share with Arduino Cloud22 consumption_profile(1.1, 120, 2880);23}
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;5
6 Serial.begin(9600);7 while (!Serial);8 9 delay(1000);10
11 // Analog/Digital IO Port Configuration12 analogIO_Setup();13 digitalIO_Setup();14
15 // Modbus RTU Configuration 16 RTU_Setup();17 18 // Status LED configuration;19 plc_led_Setup();20
21 // IoT Cloud Setup22 iot_cloud_setup();23
24 // Only for Device On State flag25 digitalWrite(LEDG, HIGH);26
27 // Scheduler -> ModBus28 Scheduler.startLoop(modbus_line);29}
The Opta™ will have the main
loop()
to prioritize tasks to communicate with Arduino Cloud and local processes. While the modbus_line()
will focus on handling Modbus RTU protocol-based communication over the RS-485 interface.1void loop() {2 // Cloud exchange3 consumption_var_container();4 ArduinoCloud.update();5
6 // Profile based energy management tasks7 energy_distro_ctrl();8
9 delay(1000);10}11
12/**13 Dedicated function for scheduler to retrieve Energy meter's information over Modbus RTU protocol14*/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();22}
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.2
3#include <ArduinoIoTCloud.h>4#include <Arduino_ConnectionHandler.h>5
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)8
9void onUOperMarginChange();10void onUWattChange();11void onUWhConChange();12void onDirectOverride1Change();13
14float uOperMargin;15float uWatt;16float uWhCon;17float uWhOpta;18bool directOverride1;19
20void initProperties(){21
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);27
28}29
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.
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.
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.
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.