Getting started

How to get started with Arduino based DLMS meter

If you have problems you can ask your questions in Gurux Forum. You can get source codes of this example from GitHub. It's amazing what you can do with 8 KB of RAM. Before start, you need to copy src and include folders from the development folder to the server schets folder. If you want to reduce memory usage, open gxignore.h file and un-comment all the objects that you don't need. In default, all the COSEM objects are used and the needed flash size is about 133 KB. Using objects in this example the minimum size is 90 KB.

Serial ports

This example is using two serial ports. SerialPort 0 is used for communication and serial port 1 is used for debugging. You can connect your PC directly to the USB port of the Arduino and upload the sketch. After the sketch is uploaded you can start to communicate with the same connection using GXDLMSDirector. If you want to use debugger you need to connect the external serial port to SerialPort1.


Meter is using SerialPort for communication and is sending debug information to SerialPort1. Debug information is sent in case you don't have Atmel Ice debugger. Debugging using serial port is not recommendable. It's better to buy a real debugger, but you can use it for getting started.


You can connect to the Arduino DLMS meter using HDLC addressing. DLMS supports different authentication levels. Each authentication level gives different kind of control for the meter. Get more information from authentication.

PDU and frame size

When you start to implement the meter you need to think what are PDU and frame sizes that you want to use. Bigger PDU size is better, but there are limitations caused by available memory. Here we are using 512 bytes for maximum PDU size and 128 bytes for maximum frame size. You can change them by setting a new value for PDU_BUFFER_SIZE and HDLC_BUFFER_SIZE. The size of PDU and frame don't affect small data, but when the size of the data is bigger, reading is faster. You can see the difference quite easy reading buffer of profile generic.
#define HDLC_BUFFER_SIZE 128
#define PDU_BUFFER_SIZE 512

COSEM objects

COSEM objects define what kind of meter you want to make. It's up to you what kind of objects you need. Define all COSEM objects you want to use and add them to ALL_OBJECTS list. Like this:
static gxRegister newRegisterObject;
static gxObject* ALL_OBJECTS[] = { ..., BASE(newRegisterObject)};
Then you need to set correct values. There are a few examples and we are updating new values if needed. It's important to set object type and logical name or the reading association view is not working.

Save data to EEPROM

Data is saved to the EEPROM. Because EEPROM has a lifetime of 100,000 write cycles only changed data is saved. All serialized data is added to GXSerializedMeterData. Don't change the order of the items and if you add new items add them to the end of the struct. If modify GXSerializedMeterData change also SERIALIZATION_VERSION. In this way, you can handle new objects and set default values when objects are created for the first time. You need to save changes in postWrite. Example saves simple objects (Clock, HDLC Setup) totally to the EEPROM
if (e->target == BASE(meterData.clock1))
  save(&meterData.clock1, sizeof(meterData.clock1));
With complex objects where are tables, example Push Setup you need to update size of the objects before saving. If there are joins to other object (objects in this case), you need to save them using saveTarget method.
if (e->target == BASE(pushSetup))
  meterData.push.communicationWindow.count = pushSetup.communicationWindow.size;
  saveTargets(&meterData.push.objects, PUSH_OBJECTS, pushSetup.pushObjectList.size);      
  save(&meterData.push, sizeof(meterData.push));

Load data from EEPROM

All the values are load automatically from EEPROM. Initial load serializationVersion is zero and you can set default values like this:
if (serializationVersion == 0)
  //Set initializing value.
  GX_UINT16(newRegisterObject.value) = 1;

Association View

Meter can tell what kind of data it can offer using the association view. When you create a new object remember to add it to the ALL_OBJECTS list. It's then automatically added to the association view. In this example, we have four different association views. One for without, Low, High and HighGMac authentications.


The clock object is saved totally to the EEPROM. Time interrupt is used to increase time for every second. You can set the time zone and DST what you want to use.


You can save the value to the register directly or use a reference pointer to the updated value. In this example, value is increased every time when the user reads it.
//Update new value directly to the COSEM Object.
GX_UINT16(newRegisterObject.value) = 1;
uint16_t activePowerL1Value = 0;
GX_UINT16_BYREF(activePowerL1.value, activePowerL1Value);
activePowerL1Value = 1;
If you are using reference, value is read automatically when object is read or write.

Disconnect Control

Using disconnect control you can close or open the valve. Because there is no valve in Arduino we are using LED in this example. Connect opens the LED and Disconnect closes the LED.

Script Table

There are four scrit table objects in this example. Global meter reset Global meter reset is used to clear all the data from the EEPROM. When next connection is made serializationVersion is zero. Disconnect Control There are two scripts in disconnect control. Script #1 opens the LED and script #2 closes the led. Activate test mode Meter is sending debug information to the serial port in the test mode. Activate Normal mode In normal mode debugging information is not sent to the serial port. Meter is operating much faster in Normal Mode.

Action Schedule

Action schedule calls wanted script table object at the given time. You can give an exact time or use make like *.*.* *.*.00 to execute schedule every minute. Here are few mask examples. Note! Mask is localized and here used date-time format is Day.Month.Year Hour.Minute.Second
  • *.*.* *.*.00 Will execute script every minute.
  • *.*.* *.00.00 Will execute script every hour.
  • *.*.* 01.00.00 Will execute script every day at 1am.


Using Push object you can meter can send reports using serial port connection. Push messages are sent even client is not connected to the meter. They are not shown in GXDLMSDirector, but if you open Serial Monitor you can see them there. You can modify sent data changing items in object list. Using Communication Window you can schedule push messages. Date time mask is the same as described in Action Schedule.

Profile Generic

Profile generics uses a ring buffer. It means that the oldest item (FIFO) is replaced when the buffer is full. Profile generics are challenging if the buffer size is huge. For this reason, only rows that can fit the PDU are loaded. This causes that the data reading mechanism is more complicated, but RAM memory doesn't set any limitations for the size of the buffer. You can read data using three different ways from the meter.
  • #0 Read all data
  • #1 Read by range (Using start and end time)
  • #2
Profile generic read is handled in preRead. Selector parameter will tell how data is fetched. If selector is 0, All data is read from the buffer. If data is read by entry, values are read using 1 based start and end index. If data is read by range client gives start and end time. Read by range is a little bit complicated because first you need to find out where data founds. getProfileGenericDataByRangeFromRingBuffer -method is used for this. It finds start and end indexes using start and end time. Start and end indexes are saved to transactionStartIndex and transactionEndIndex parameters. Using those values reading data from EEPROM is easy. Size of the EEPROM is setting limits for the profile generic buffer. In this example, we have allocated 1024 bytes for load profile and event log. Load profile starts from address 1024 and event log from address 2048.


HSLC setup is last, but it's very important when communicating using HDLC. Serial port communication is connection-less. For this reason, there is an inactivity timeout that is used to close the connection if client hasn't sent any request in the given time (Inactivity timeout). This is used if the client app didn't close the connection properly in a given time and it's very important when communicating Over The Air. In the following example, you can see how to communicate with Arduino DLMS meter using GXDLMSDirector.


Meter is supporting GMAC authentication and ciphering to secure the communication channel. All DLMS ciphering services are supported.
Default keys are:
  • System Title can be any 8 bytes long name.
  • Block Cipher key: D0 D1 D2 D3 D4 D5 D6 D7 D8 D9 DA DB DC DD DE DF
  • Authentication key: 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
  • Dedicated key can be any 16 bytes long value.