Monitor Your Energy Bill with Modbus and the Arduino Cloud

Connect a Modbus energy meter to an Arduino® MKR WiFi 1010 board and a MKR 485 Shield and monitor the power consumption of your home via an Arduino Cloud IoT dashboard.

Introduction

If you really want to make your home smarter, you'll probably want start from your monthly bills (for example, energy, gas, etc...). As some say: good for the planet, the wallet and the bottom line. In this tutorial, we are going to learn how to connect a Modbus energy meter to the Arduino Cloud IoT using an Arduino® MKR WiFi 1010 board and an Arduino® MKR 485 Shield.

This tutorial assumes you know the basics of the Arduino Cloud. If you are new check out our Getting Started Guide.

Goals

The goals with this tutorial are:

Hardware and Software Needed

The hardware and software used in this tutorial:

Electric Meters a.k.a Energy Meters

Energy consumption awareness is a key factor to reduce energy costs and improve energy efficiency; we can measure energy consuption using electric meters, a.k.a energy meters.

An analog energy meter (source: General Electric)
An analog energy meter (source: General Electric)

Electric meters, also known as energy meters, are electronic devices that can measure the amount of energy consumed by an electrically powered equipment such as a refrigerator or a lamp. Energy meters can be use also to measure the energy consuption of houses and buildings. While different types of energy meters exist, in this tutorial we choose a Finder Type 7E.64 Energy Meter. This energy meter is designed for DIN rail use and fits perfectly in the main cabinet of our house. Also, this energy meter has a RS-485 Modbus interface, this is an industrial communication protocol that can be decoded in Arduino boards using an Arduino MKR 485 Shield and the Arduino Modbus library.

Finder Type 73.64 Energy Meter (source: Finder)
Finder Type 73.64 Energy Meter (source: Finder)

Setting Up the Finder Type 7E.64 Energy Meter

First, you must install the energy meter in your electrical cabinet. To ensure you are working in a safe environment, turn off the power from the electrical terminal ahead of your system and double check with a multimeter that there is no voltage between the terminals.

Warning! Check your country regulations about dealing with your house electrical system and be extremely careful because it can be deadly! If you don't know how, call an electrician.

Place the energy meter inside your cabinet and connect the live and neutral wires from the main breaker to the input of the meter, remember to use the standard color convention (blue for neutral and brown/black/grey for live in EU. The output has to be connected to the rest of the system.

Main voltage connections. Wires above are inputs, wires below are outputs
Main voltage connections. Wires above are inputs, wires below are outputs

Now it is time make the connection between the energy meter and our MKR WiFi 1010 board! For this, we will use twisted single pair cable with ground. This type of cable is typically used for phone lines, so it can be used to transmit electrical signals over long distances (up to 1.2 km). However, we are going to use a cable long enough to exit the cabinet and place our MKR WiFi 1010 board in an accessible place.

Energy meter RS-485 interface connections
Energy meter RS-485 interface connections

The RS-485 standard names its terminals A, B and COM. A common de-facto standard is the use of TX+/RX+ (or D+) as an alternative for B (high for mark i.e. idle) and TX-/RX- (or D-) as an alternative for A (low for mark i.e. idle). As shown in the image above, we connected the red cable to the D+ terminal, the white cable to the D- terminal and the brown cable to the COM terminal of the energy meter. You can read more about the RS-485 standard here.

The Finder energy meter supports half-duplex communication, this means that data can move in two directions, but not at the same time. The MKR 485 Shield supports both half and full-duplex communication (this means data moving in two directions simultaneously), so we need to set up the shield for half-duplex communication. In the MKR 485 Shield, half-duplex communication uses Y and Z terminals, Y terminal is B or D+ and Z terminal y A or D-, this means that the red cable must be connected to Y terminal and the white cable to Z terminal; the brown cable (COM) must be connected to ISOGND terminal. Also, we need to set the second switch to HALF (2 to OFF) and the third switch to Y-Z (3 to ON): the first switch is not used in half-duplex communication. The third switch is used for setting up the termination, this is a resistor connecting the two data terminals that is used for dampening interferences. The complete shield setup is shown in the image below:

MKR 485 Shield setup and connections
MKR 485 Shield setup and connections

Now, we can connect the MKR 485 Shield and the MKR WiFi 1010 board:

MKR 485 Shield setup and connections
MKR 485 Shield setup and connections

Now that we have finished setting up the hardware, it is time to connect our energy meter to the Arduino Cloud IoT

Setting Up the Arduino Cloud IoT

  • Create a Thing with the following variables:
VariableTypePermissionUpdate Policy
voltageFloating Point NumberRead OnlyOn change
currentFloating Point NumberRead OnlyOn change
powerFloating Point NumberRead OnlyOn change
frequencyFloating Point NumberRead OnlyOn change
energyFloating Point NumberRead OnlyOn change

Thing Set up
Thing Set up

  • Set up your MKR WiFi 1010 and configure your network credentials.

Creating a Sketch for a "Thing" in the Arduino Cloud IoT

Once we are finished with all the configurations of the "Energy Thing", we can move on to creating the sketch that we are going to upload to our MKR WiFi 1010 board. To do so, we first need to go to the "Sketch" tab. But before, let's talk about Modbus.

Sketch tab in the Arduino Cloud IoT
Sketch tab in the Arduino Cloud IoT

Modbus is an open source communication protocol designed specifically for industrial sensors and machines. In simple terms, it is a method used for transmitting information over serial lines between electronic devices. Our MKR WiFi 1010 board can talk Modbus using the Arduino Modbus library. This library packs all the handlers and makes hooking up any Modbus device to some of the Arduino® boards (like the MKR family boards) really fast and easy. You can read more about Modbus here.

In the datasheet of the energy meter we can find all the Modbus related information we need like its function codes, address of its registers and also their sizes.

Modbus messages follow a simple structure, for example:

01 03 04 00 16 00 02 25 C7

In this structure:

  • 0x01
    is the device address.
  • 0x03
    is the function code that tells the Modbus device if we want to read or write data. In this tutorial, we want to read data from the holding registers of the energy meter.
  • 0x04
    for byte count, this specifies how may data items are being returned.
  • 00 16
    is register address from the Modbus device we want to read.
  • 00 02
    is the size of the register in words (every word is 2 bytes long).
  • 25 C7
    is a CRC code. This code is generated from a math function over previous bytes, it ensures that the message has been received correctly.

The Arduino Modbus library handles this structure. For example, for reading the register of the energy meter that holds information about current, we have the following function that uses the

requestFrom()
function of the Arduino Modbus library:

1/*
2Function readCurrent()
3Description: read current value from the Finder energy meter holding registers
4
5Created by Alberto Perro (Officine Innesto)
6Modified by José Bagur
7*/
8
9float readCurrent() {
10 float ampere = 0.;
11 // Send reading request over RS485
12 if (!ModbusRTUClient.requestFrom(0x01, HOLDING_REGISTERS, 0x0016, 2)) {
13 // Error handling
14 Serial.print("- Failed to read the current! ");
15 Serial.println(ModbusRTUClient.lastError());
16 } else {
17 // Response handler
18 uint16_t word1 = ModbusRTUClient.read(); // Read word1 from buffer
19 uint16_t word2 = ModbusRTUClient.read(); // Read word2 from buffer
20 int32_t milliamp = word1 << 16 | word2; // Join word1 and word2 to retrieve current value in milliampere
21 ampere = milliamp/1000.0; // Convert current to ampere
22 }
23return ampere;
24}

In the

else
we have the response handler. Since this register is two words long, we have to join them with binary math. We read the words from the buffer and store them in an unsigned 16-bit integer (2 bytes or a word); then we join them in a signed 32-bit integer by bitshifting the first word to the left and apply an OR over the second word. The result is the current measurement in milliampere (in ampere after dividing it by 1000). This process can adapted to everything else we want to read from the energy meter: voltage, power, frequency and energy.

The complete sketch we are going to upload to out MKR WiFi 1010 board can be found below, notice that the Arduino Cloud IoT generated automatically the code that is dedicated to handling Internet connectivity:

1/* -----------------------------------------
2 * Finder Energy Meter to Arduino Cloud IoT
3 * -----------------------------------------
4 * This sketch provides a full bridge between the Finder energy meter and the
5 * Arduino Cloud IoT. This sketch was developed to monitor electricity costs
6 * and usage in Casa Jasmina.
7 *
8 * Created by Alberto Perro (Officine Innesto)
9 * Modified by José Bagur
10*/
11
12#include <ArduinoRS485.h>
13#include <ArduinoModbus.h>
14
15#undef ON
16#undef OFF
17
18#include "thingProperties.h"
19
20unsigned long rate = 60000; // Default refresh rate in ms
21unsigned long lastMillis = 0;
22
23void setup() {
24 // Initialize serial port at 9600 bauds and wait for it to open
25 Serial.begin(9600);
26 delay(1500);
27
28 // Defined in thingProperties.h
29 initProperties();
30
31 // Connect to Arduino Cloud IoT
32 ArduinoCloud.begin(ArduinoIoTPreferredConnection);
33
34 /*
35 The following function allows you to obtain more information
36 related to the state of network and Cloud IoT connection and errors
37 The higher number the more granular information you’ll get
38 The default value is 0 (only errors)
39 Maximum is 4
40 */
41 setDebugMessageLevel(2);
42 ArduinoCloud.printDebugInfo();
43
44 // Start Modbus RTU client
45 if (!ModbusRTUClient.begin(9600)) {
46 Serial.println("- Failed to start Modbus RTU Client!");
47 while (1);
48 }
49}
50
51void loop() {
52 // Update "Energy Thing" variables connected to Arduino Cloud IoT
53 ArduinoCloud.update();
54
55 // Update energy meter data and show it via the Serial Monitor
56 if (millis() - lastMillis > rate) {
57 lastMillis = millis();
58
59 voltage = readVoltage();
60 delay(100);
61 current = readCurrent();
62 delay(100);
63 power = readPower();
64 delay(100);
65 frequency = readFreq();
66 delay(100);
67 energy = readEnergy();
68
69 Serial.print("- " + String(voltage, 3) + "V " + String(current, 3) + "A " + String(power, 3) + "W ");
70 Serial.println(String(frequency, 3) + "Hz " + String(power, 3) + "kWh");
71 delay(100);
72 }
73}
74
75/* Functions to read Finder energy meter holding registers
76 * For more information: https://gfinder.findernet.com/public/attachments/7E/EN/PRT_Modbus_7E_64_68_78_86EN.pdf
77 */
78
79/*
80Function readVoltage()
81Description: read voltage value from the Finder energy meter holding registers
82*/
83float readVoltage() {
84 float volt = 0.;
85 // Send reading request over RS485
86 if (!ModbusRTUClient.requestFrom(0x01, HOLDING_REGISTERS, 0x000C, 2)) {
87 // Error handling
88 Serial.print("- Failed to read the voltage! ");
89 Serial.println(ModbusRTUClient.lastError());
90 } else {
91 // Response handler
92 uint16_t word1 = ModbusRTUClient.read(); // Read word1 from buffer
93 uint16_t word2 = ModbusRTUClient.read(); // Read word2 from buffer
94 uint32_t millivolt = word1 << 16 | word2; // Join word1 and word2 to retrieve voltage value in millivolts
95 volt = millivolt/1000.0; // Convert to volts
96 }
97
98 return volt;
99}
100
101/*
102Function readCurrent()
103Description: read current value from the Finder energy meter holding registers
104*/
105float readCurrent() {
106 float ampere = 0.;
107 // Send reading request over RS485
108 if (!ModbusRTUClient.requestFrom(0x01, HOLDING_REGISTERS, 0x0016, 2)) {
109 // Error handling
110 Serial.print("- Failed to read the current! ");
111 Serial.println(ModbusRTUClient.lastError());
112 } else {
113 // Response handler
114 uint16_t word1 = ModbusRTUClient.read(); // Read word1 from buffer
115 uint16_t word2 = ModbusRTUClient.read(); // Read word2 from buffer
116 int32_t milliamp = word1 << 16 | word2; // Join word1 and word2 to retrieve current value in milliampere
117 ampere = milliamp/1000.0; // Convert current to ampere
118 }
119
120 return ampere;
121}
122
123/*
124Function readPower()
125Description: read power value from the Finder energy meter holding registers
126*/
127double readPower() {
128 double watt = 0.;
129 // Send reading request over RS485
130 if (!ModbusRTUClient.requestFrom(0x01, HOLDING_REGISTERS, 0x0025, 3)) {
131 // Error handling
132 Serial.print("- Failed to read power! ");
133 Serial.println(ModbusRTUClient.lastError());
134 } else {
135 // Response handler
136 uint16_t word1 = ModbusRTUClient.read(); // Read word1 from buffer
137 uint16_t word2 = ModbusRTUClient.read(); // Read word2 from buffer
138 uint16_t word3 = ModbusRTUClient.read(); // Read word3 from buffer
139
140 uint64_t milliwatt;
141
142 // Join word1 and word2 to retrieve power value in milliwatt
143 if (word1 >> 7 == 0) {
144 milliwatt = word1 << 32 | word2 << 16 | word3;
145 } else {
146 word1 &= 0b01111111;
147 milliwatt = 0b1 << 48 | word1 << 32 | word2 << 16 | word3;
148 }
149
150 watt = milliwatt/1000.; // Convert power to watts
151 }
152
153 return watt;
154}
155
156/*
157Function readFreq()
158Description: read frequency value from the Finder energy meter holding registers
159*/
160float readFreq() {
161 float freq = 0.;
162 // Send reading request over RS485
163 if (!ModbusRTUClient.requestFrom(0x01, HOLDING_REGISTERS, 0x0040, 2)) {
164 // Error handling
165 Serial.print("- Failed to read frequency! ");
166 Serial.println(ModbusRTUClient.lastError());
167 } else {
168 // Response handler
169 uint16_t word1 = ModbusRTUClient.read(); // Read word1 from buffer
170 freq = word1/1000.0; // Retrieve frequency value
171 }
172 return freq;
173}
174
175/*
176Function readEnergy()
177Description: read energy value from the Finder energy meter holding registers
178*/
179double readEnergy() {
180 double kwh = 0.;
181 // Send reading request over RS485
182 if (!ModbusRTUClient.requestFrom(0x01, HOLDING_REGISTERS, 0x0109, 3)) {
183 // Error handling
184 Serial.print("- Failed to read energy! ");
185 Serial.println(ModbusRTUClient.lastError());
186 } else {
187 // Response handler
188 uint16_t word1 = ModbusRTUClient.read(); // Read word1 from buffer
189 uint16_t word2 = ModbusRTUClient.read(); // Read word2 from buffer
190 uint16_t word3 = ModbusRTUClient.read(); // Read word3 from buffer
191 uint64_t dwh = word1 << 32 | word2 << 16 | word3; // Join word1 and word2 to retrieve energy value in dwh
192 kwh = dwh/10000.0; // Convert energy to kwh
193 }
194 return kwh;
195}

Over the Air Uploads

Did you know that the Arduino Cloud supports over the air uploads? When you've uploaded a sketch to your board once, it will become available for you to upload a new sketch to the board without connecting it to your computer!

Over the Air uploads require an Entry plan to the Arduino Cloud

To use this feature, make sure the board has power. If your board is already connected to the Cloud, you will be able to upload to it over the air. Navigate to the Things sketch tab in the Arduino Cloud interface, and you should see it being discovered just as if it was connected via USB.

Creating a Dashboard in the Arduino Cloud IoT

After our code has been successfully uploaded to our board, we we will need to create a dashboard for visualizing the energy meter data.

Create a dashboard with the following widgets:

WidgetLinked Variable
Valuevoltage
Valuecurrent
Valuepower
Valuefrequency
Valueenergy

Your dashboard should look something like this:

Dashboard
Dashboard

That's it! You have now a Modbus energy meter connected to the Arduino Cloud IoT!

Troubleshoot

Sometimes errors occur, if the code is not working or data is not in the to the Arduino Cloud IoT there are some common issues we can troubleshoot:

  • Missing a bracket or a semicolon.
  • Accidental interruption of cable connection.
  • Wrong network credentials.
  • No variable linked to a widget.

Conclusion

In this tutorial, we learned how to connect a Modbus energy meter to the Arduino Cloud IoT using a MKR WiFi 1010 board and a MKR 485 Shield. We also learned how to visualize the energy meter data in a dashboard using widgets. More tutorials? You can find them in the Arduino Cloud IoT documentation page.

Suggest changes

The content on docs.arduino.cc is facilitated through a public GitHub repository. If you see anything wrong, you can edit this page here.

License

The Arduino documentation is licensed under the Creative Commons Attribution-Share Alike 4.0 license.