### Verify ESP-IDF Installation Source: https://github.com/gurux/guruxdlms.c/blob/master/Espressif/readme.md Check if the ESP-IDF development framework is installed correctly by running the version command. ```bash idf.py --version ``` -------------------------------- ### Initialize Clock Object Source: https://github.com/gurux/guruxdlms.c/blob/master/GuruxDLMSSimpleServerExample/readme.md This method is intended for initializing the example clock object. The implementation details for initialization are to be provided. ```c /////////////////////////////////////////////////////////////////////// //This method adds example clock object. /////////////////////////////////////////////////////////////////////// int addClockObject() { int ret; ``` -------------------------------- ### Main Server Initialization and Loop Source: https://github.com/gurux/guruxdlms.c/blob/master/GuruxDLMSServerExample2/readme.md Initializes the DLMS server settings, including interface type and buffer sizes. It then starts the server and enters a loop to accept and handle client connections. ```c int main() unsigned char data; struct sockaddr_in client; int ret, ls, s; struct sockaddr_in add = { 0 }; #if defined(_WIN32) || defined(_WIN64)//If Windows int len; int AddrLen = sizeof(add); #else //If Linux socklen_t len; socklen_t AddrLen = sizeof(add); #endif #if defined(_WIN32) || defined(_WIN64)//If Windows WSADATA wsaData; if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) { // Tell the user that we could not find a usable WinSock DLL. return 1; } #endif //Initialize reply data. bb_init(&reply); //Start server using logical name referencing and HDLC framing. svr_init(&settings, 1, DLMS_INTERFACE_TYPE_HDLC, HDLC_BUFFER_SIZE, PDU_BUFFER_SIZE, frame, HDLC_BUFFER_SIZE, pdu, PDU_BUFFER_SIZE); //Add COSEM objects. svr_InitObjects(&settings); //Start server if ((ret = svr_initialize(&settings)) != 0) { //TODO: Show error. return; } ls = socket(AF_INET, SOCK_STREAM, 0); add.sin_port = htons(4061); add.sin_addr.s_addr = htonl(INADDR_ANY); add.sin_family = AF_INET; if ((ret = bind(ls, (struct sockaddr*) &add, sizeof(add))) == -1) { return -1; } while (1) { if ((ret = listen(ls, 1)) == -1) { //socket listen failed. return -1; } len = sizeof(client); s = accept(ls, (struct sockaddr*)&client, &len); while (1) { //Read one char at the time. if ((ret = recv(s, (char*)&data, 1, 0)) == -1) { #if defined(_WIN32) || defined(_WIN64)//If Windows closesocket(s); s = INVALID_SOCKET; #else //If Linux close(s); s = -1; #endif break; } //If client closes the connection. if (ret == 0) { #if defined(_WIN32) || defined(_WIN64)//If Windows closesocket(s); s = INVALID_SOCKET; #else //If Linux close(s); s = -1; #endif break; } if (svr_handleRequest3(&settings, data, &reply) != 0) { #if defined(_WIN32) || defined(_WIN64)//If Windows closesocket(s); s = INVALID_SOCKET; #else //If Linux close(s); s = -1; #endif break; } if (reply.size != 0) { ``` -------------------------------- ### Setup DLMS Server and Serial Port Source: https://github.com/gurux/guruxdlms.c/blob/master/Atmel/AtMega2560/GuruxDLMSServerExample/readme.md Configures and initializes the DLMS server and the serial port for communication. This function is typically called once during the setup phase of the application. ```c void setup() { int ret; bb_init(&reply); //Start server using logical name referencing and HDLC framing. svr_init(&settings, 1, DLMS_INTERFACE_TYPE_HDLC, HDLC_BUFFER_SIZE, PDU_BUFFER_SIZE, frame, HDLC_BUFFER_SIZE, pdu, PDU_BUFFER_SIZE); //Add COSEM objects. svr_InitObjects(&settings); //Start server if ((ret = svr_initialize(&settings)) != 0) { //TODO: Show error. return; } // start serial port at 9600 bps: Serial.begin(9600); while (!Serial) { ; // wait for serial port to connect. Needed for native USB port only } } ``` -------------------------------- ### Gurux DLMS Byte Buffer Examples Source: https://context7.com/gurux/guruxdlms.c/llms.txt Demonstrates the usage of `gxByteBuffer` for PDU construction, data accumulation, and value encoding. Supports heap-based and static buffers. ```c #include "bytebuffer.h" void byte_buffer_example(void) { // Heap-based buffer gxByteBuffer heapBuf; bb_init(&heapBuf); bb_setUInt8(&heapBuf, 0x7E); // append one byte bb_setUInt16(&heapBuf, 0x1234); // append big-endian uint16 bb_setUInt32(&heapBuf, 0xDEADBEEF); // append big-endian uint32 bb_clear(&heapBuf); // free and reset // Static (no-malloc) buffer static unsigned char rawData[256]; gxByteBuffer staticBuf; BB_ATTACH(staticBuf, rawData, 0); // size=0, capacity=sizeof(rawData) bb_addString(&staticBuf, "Hello"); // Reading unsigned char b; uint16_t u16; staticBuf.position = 0; bb_getUInt8(&staticBuf, &b); bb_getUInt16(&staticBuf, &u16); // Convert to hex string (requires malloc) char* hex = bb_toHexString(&heapBuf); printf("%s\n", hex); free(hex); } ``` -------------------------------- ### Configure Push Setup Object for DLMS Source: https://github.com/gurux/guruxdlms.c/blob/master/GuruxDLMSDataCollector/README.md Configures a push setup object to define which data objects to send in a push message. This includes the logical device name, clock, and register values. ```C /////////////////////////////////////////////////////////////////////// //Add push setup object. /////////////////////////////////////////////////////////////////////// int addPushSetup() { int ret = 0; const unsigned char ln[6] = { 0,0,25,9,0,255 }; INIT_OBJECT(push, DLMS_OBJECT_TYPE_PUSH_SETUP, ln); static gxTarget PUSH_OBJECTS[5]; //Add Logical Device Name so we can identify the meter. PUSH_OBJECTS[0].target = &ldn.base; PUSH_OBJECTS[0].attributeIndex = 2; PUSH_OBJECTS[0].dataIndex = 0; //Read time. PUSH_OBJECTS[1].target = &clock1.base; PUSH_OBJECTS[1].attributeIndex = 2; PUSH_OBJECTS[1].dataIndex = 0; //Register value. PUSH_OBJECTS[2].target = &activePowerL1.base; PUSH_OBJECTS[2].attributeIndex = 2; PUSH_OBJECTS[2].dataIndex = 0; //Register scaler and unit. PUSH_OBJECTS[3].target = &activePowerL1.base; PUSH_OBJECTS[3].attributeIndex = 3; PUSH_OBJECTS[3].dataIndex = 0; ARR_ATTACH(push.pushObjectList, PUSH_OBJECTS, 4); return ret; } ``` -------------------------------- ### HDLC Link Setup (Client) (C) Source: https://context7.com/gurux/guruxdlms.c/llms.txt Performs SNRM/UA exchange for HDLC interfaces before application-layer AARQ. Parses UA reply to update settings for frame and window sizes. ```c #include "client.h" int hdlc_connect(dlmsSettings* settings, connection* con) { int ret; message messages; gxReplyData reply; mes_init(&messages); reply_init(&reply); // Step 1: SNRM → UA ret = cl_snrmRequest(settings, &messages); if (ret != DLMS_ERROR_CODE_OK) goto cleanup; ret = com_readDataBlock(con, &messages, &reply); if (ret != DLMS_ERROR_CODE_OK) goto cleanup; ret = cl_parseUAResponse(settings, &reply.data); if (ret != DLMS_ERROR_CODE_OK) goto cleanup; mes_clear(&messages); reply_clear(&reply); // Step 2: AARQ → AARE ret = cl_aarqRequest(settings, &messages); if (ret != DLMS_ERROR_CODE_OK) goto cleanup; ret = com_readDataBlock(con, &messages, &reply); if (ret != DLMS_ERROR_CODE_OK) goto cleanup; ret = cl_parseAAREResponse(settings, &reply.data); cleanup: mes_clear(&messages); reply_clear(&reply); return ret; } ``` -------------------------------- ### Add Register Object Source: https://github.com/gurux/guruxdlms.c/blob/master/GuruxDLMSServerExample2/readme.md Adds an example register object with a specified logical name. It links a value to the object and sets scaler and unit. ```c int addRegisterObject() { int ret; const unsigned char ln[6] = { 1,1,21,25,0,255 }; if ((ret = INIT_OBJECT(activePowerL1, DLMS_OBJECT_TYPE_REGISTER, ln)) == 0) { GX_UINT32_BYREF(activePowerL1.value) = &activePowerL1Value; //10 ^ 3 = 1000 activePowerL1.scaler = -2; activePowerL1.unit = DLMS_UNIT_ACTIVE_ENERGY; } return ret; } ``` -------------------------------- ### Initialize Register Object for DLMS Source: https://github.com/gurux/guruxdlms.c/blob/master/GuruxDLMSDataCollector/README.md Initializes a register object for DLMS communication, specifying the logical name, scaler, and unit. This example sets up an object for active power on L1. ```C static gxRegister activePowerL1; /////////////////////////////////////////////////////////////////////// //This method adds register object. /////////////////////////////////////////////////////////////////////// int addRegisterObject() { int ret; const unsigned char ln[6] = { 1,1,21,25,0,255 }; if ((ret = INIT_OBJECT(activePowerL1, DLMS_OBJECT_TYPE_REGISTER, ln)) == 0) { //10 ^ 3 = 1000 activePowerL1.scaler = -2; activePowerL1.unit = DLMS_UNIT_ACTIVE_ENERGY; } return ret; } ``` -------------------------------- ### Post Attribute Write Actions with svr_postWrite Source: https://context7.com/gurux/guruxdlms.c/llms.txt Fires after an object has been updated. This is the appropriate place to persist changes to EEPROM. The example shows a call to `saveSettings` to persist all changed objects. ```c void svr_postWrite(dlmsSettings* settings, gxValueEventCollection* args) { // Persist all changed objects to EEPROM after any write saveSettings(settings); } ``` -------------------------------- ### Handle Attribute Read Events with svr_preRead Source: https://context7.com/gurux/guruxdlms.c/llms.txt Called before an attribute value is serialized. Set `e->handled = 1` if you have already placed the value in `e->value`. This example shows how to provide real-time clock data and live ADC readings for specific attributes. ```c #include "serverevents.h" #include "date.h" void svr_preRead(dlmsSettings* settings, gxValueEventCollection* args) { gxValueEventArg* e; int pos, ret; for (pos = 0; pos != args->size; ++pos) { if ((ret = vec_getByIndex(args, pos, &e)) != DLMS_ERROR_CODE_OK) break; // Handle real-time clock attribute 2 (current time) if (e->target == BASE(clock_obj) && e->index == 2) { gxtime now; time_now(&now); // Copy current time into the clock object so the library // serialises the up-to-date value memcpy(&clock_obj.time, &now, sizeof(gxtime)); // e->handled = 0 — library reads clock_obj.time automatically } // Handle register value — supply live reading from ADC else if (e->target == BASE(activeEnergy) && e->index == 2) { GX_UINT32(activeEnergy.value) = adc_read_wh(); // e->handled = 0; library serialises activeEnergy.value } } } ``` -------------------------------- ### Configure Wi-Fi Credentials Source: https://github.com/gurux/guruxdlms.c/blob/master/Espressif/readme.md Set the Wi-Fi SSID and password using the ESP-IDF menuconfig utility. This is typically done once. ```bash idf.py menuconfig # Component config → Wi-Fi Configuration → set SSID and password. ``` -------------------------------- ### Main Server Initialization and Loop Source: https://github.com/gurux/guruxdlms.c/blob/master/NordicSemiconductor/GuruxDLMSSimpleServerExample2/readme.md The main function initializes the DLMS server, including settings, COSEM objects, and UART communication. It then enters an infinite loop to keep the server running. ```c void main(void) { int ret; started = _impl_k_uptime_get(); characters = 0; bb_init(&reply); //Start server using logical name referencing and HDLC framing. svr_init(&settings, 1, DLMS_INTERFACE_TYPE_HDLC, HDLC_BUFFER_SIZE, PDU_BUFFER_SIZE, frame, HDLC_BUFFER_SIZE, pdu, PDU_BUFFER_SIZE); //Add COSEM objects. svr_InitObjects(&settings); //Start server if ((ret = svr_initialize(&settings)) != 0) { printf("svr_initialize failed.\r\n"); return; } printf("Gurux DLMS sample started.\n"); ret = gx_uart_init("UART_0"); if (ret != 0) { printf("Gurux DLMS sample failed.\n"); return; } while (true) { } } ``` -------------------------------- ### Attach Value to COSEM Attribute Source: https://github.com/gurux/guruxdlms.c/blob/master/GuruxDLMSServerExample2/readme.md Example of attaching a C standard value type to a COSEM attribute for easier management. ```c //Active power value. We are updating this. unsigned long activePowerL1Value = 0; //Attach value to COSEM attribute. GX_UINT32_BYREF(activePowerL1.value) = &activePowerL1Value; ``` -------------------------------- ### Build ESP-IDF Firmware Source: https://github.com/gurux/guruxdlms.c/blob/master/Espressif/readme.md Compile the ESP-IDF project to build the firmware for the target device. ```bash idf.py build ``` -------------------------------- ### Add Logical Device Name Source: https://github.com/gurux/guruxdlms.c/blob/master/GuruxDLMSServerExample2/readme.md Adds a COSEM Logical Device Name. The name is an octet-string of 16 octets, starting with manufacturer identification. ```c void addLogicalDeviceName() { sprintf(LDN, "GRX%.13lu", SERIAL_NUMBER); const unsigned char ln[6] = { 0, 0, 42, 0, 0, 255 }; INIT_OBJECT(ldn, DLMS_OBJECT_TYPE_DATA, ln); GX_OCTECT_STRING(ldn.value, LDN, sizeof(LDN)); } ``` -------------------------------- ### Flash and Monitor ESP-IDF Firmware Source: https://github.com/gurux/guruxdlms.c/blob/master/Espressif/readme.md Deploy the built firmware to the target device and open a serial monitor to view output and debug. ```bash idf.py flash monitor ``` -------------------------------- ### Attach Value to COSEM Attribute Source: https://github.com/gurux/guruxdlms.c/blob/master/Espressif/readme.md Example of attaching a value to a COSEM attribute using ANSI C standard types. The `activePowerL1Value` will be updated by the library. ```c // Active power value. This will be updated. unsigned long activePowerL1Value = 0; // Attach value to COSEM attribute. GX_UINT32_BYREF(activePowerL1.value) = &activePowerL1Value; ``` -------------------------------- ### Define Server Buffers and Settings Source: https://github.com/gurux/guruxdlms.c/blob/master/GuruxDLMSServerExample2/readme.md Define buffer sizes for HDLC frames and PDUs, and declare buffers for frame, PDU, and reply data. ```c dlmsServerSettings settings; //HDLC frame size. #define HDLC_BUFFER_SIZE 128 //PDU size. #define PDU_BUFFER_SIZE 1024 //HDLC frame buffer. unsigned char frame[11 + HDLC_BUFFER_SIZE]; //PDU buffer. unsigned char pdu[PDU_BUFFER_SIZE]; //Reply data that is sent back to the client. gxByteBuffer reply; ``` -------------------------------- ### Main Server Loop Source: https://github.com/gurux/guruxdlms.c/blob/master/GuruxDLMSSimpleServerExample/readme.md The main function sets up the DLMS server, initializes objects, and enters a loop to listen for and handle client connections and requests. It includes platform-specific socket initialization for Windows and Linux. ```c int main() unsigned char data; struct sockaddr_in client; int ret, ls, s; struct sockaddr_in add = { 0 }; #if defined(_WIN32) || defined(_WIN64)//If Windows int len; int AddrLen = sizeof(add); #else //If Linux socklen_t len; socklen_t AddrLen = sizeof(add); #endif #if defined(_WIN32) || defined(_WIN64)//If Windows WSADATA wsaData; if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) { // Tell the user that we could not find a usable WinSock DLL. return 1; } #endif //Initialize reply data. bb_init(&reply); //Start server using logical name referencing and HDLC framing. svr_init(&settings, 1, DLMS_INTERFACE_TYPE_HDLC, HDLC_BUFFER_SIZE, PDU_BUFFER_SIZE, frame, HDLC_BUFFER_SIZE, pdu, PDU_BUFFER_SIZE); //Add COSEM objects. svr_InitObjects(&settings); //Start server if ((ret = svr_initialize(&settings)) != 0) { //TODO: Show error. return; } ls = socket(AF_INET, SOCK_STREAM, 0); add.sin_port = htons(4061); add.sin_addr.s_addr = htonl(INADDR_ANY); add.sin_family = AF_INET; if ((ret = bind(ls, (struct sockaddr*) &add, sizeof(add))) == -1) { return -1; } while (1) { if ((ret = listen(ls, 1)) == -1) { //socket listen failed. return -1; } len = sizeof(client); s = accept(ls, (struct sockaddr*)&client, &len); while (1) { //Read one char at the time. if ((ret = recv(s, (char*)&data, 1, 0)) == -1) { #if defined(_WIN32) || defined(_WIN64)//If Windows closesocket(s); s = INVALID_SOCKET; #else //If Linux close(s); s = -1; #endif break; } //If client closes the connection. if (ret == 0) { #if defined(_WIN32) || defined(_WIN64)//If Windows closesocket(s); s = INVALID_SOCKET; #else //If Linux close(s); s = -1; #endif break; } if (svr_handleRequest3(&settings, data, &reply) != 0) { #if defined(_WIN32) || defined(_WIN64)//If Windows closesocket(s); s = INVALID_SOCKET; #else //If Linux close(s); s = -1; #endif break; } if (reply.size != 0) { if (send(s, (const char*)reply.data, reply.size - reply.position, 0) == -1) { #if defined(_WIN32) || defined(_WIN64)//If Windows closesocket(s); s = INVALID_SOCKET; #else //If Linux close(s); s = -1; #endif break; } bb_clear(&reply); } } } #if defined(_WIN32) || defined(_WIN64)//Windows WSACleanup(); #if _MSC_VER > 1400 _CrtDumpMemoryLeaks(); #endif #endif return 0; } ``` -------------------------------- ### Include DLMS Settings and Utilities Source: https://github.com/gurux/guruxdlms.c/blob/master/GuruxDLMSServerExample2/readme.md Include necessary header files for DLMS settings, variant handling, COSEM objects, and server functionalities. ```c #include "dlms/include/dlmssettings.h" #include "dlms/include/variant.h" #include "dlms/include/cosem.h" #include "dlms/include/server.h" ``` -------------------------------- ### Handle Attribute Write Events with svr_preWrite Source: https://context7.com/gurux/guruxdlms.c/llms.txt Called before the COSEM object struct is updated with a client-sent value. Inspect `e->value` to validate or transform data. This example demonstrates rejecting writes to the clock from unprivileged associations. ```c void svr_preWrite(dlmsSettings* settings, gxValueEventCollection* args) { gxValueEventArg* e; int pos, ret; for (pos = 0; pos != args->size; ++pos) { if ((ret = vec_getByIndex(args, pos, &e)) != DLMS_ERROR_CODE_OK) break; // Reject writes to the clock from an unprivileged association if (e->target == BASE(clock_obj) && e->index == 2) { if (settings->authentication < DLMS_AUTHENTICATION_HIGH) { e->error = DLMS_ERROR_CODE_READ_WRITE_DENIED; } } } } ``` -------------------------------- ### svr_isTarget Source: https://context7.com/gurux/guruxdlms.c/llms.txt Filter Incoming Frames by Address: This function is called for every received HDLC/Wrapper frame. It determines if the frame is addressed to the server, returning 1 to accept or 0 to discard. It's used for multi-logical-device setups and configuring security contexts. ```APIDOC ### `svr_isTarget` — Filter Incoming Frames by Address Called for every received HDLC/Wrapper frame before any further processing. Return `1` if the frame is addressed to this server, `0` to silently discard it. Use this callback to implement multi-logical-device setups, to configure which security context applies to the connection, or to set `settings->expectedSecurityPolicy`. ```c unsigned char svr_isTarget( dlmsSettings* settings, uint32_t serverAddress, uint32_t clientAddress) { // Accept management client (address 1) with high-security association if (clientAddress == 1 && serverAddress == 1) { settings->expectedSecuritySuite = 0; settings->expectedSecurityPolicy = DLMS_SECURITY_POLICY_AUTHENTICATED_ENCRYPTED; return 1; } // Accept public client (address 16) without security if (clientAddress == 16 && serverAddress == 1) { settings->expectedSecuritySuite = 0; settings->expectedSecurityPolicy = DLMS_SECURITY_POLICY_NOTHING; return 1; } return 0; // discard } ``` ``` -------------------------------- ### cl_init Source: https://context7.com/gurux/guruxdlms.c/llms.txt Initializes a dlmsSettings structure for the DLMS client (master) role. This function must be called before any client request function and configures the client and server addresses, authentication, and interface type. ```APIDOC ## cl_init ### Description Initializes a `dlmsSettings` structure for the client (master) role. Sets the client address, server address, authentication level, password, and interface type. Must be called before any client request function. ### Method `void cl_init(dlmsSettings *settings, unsigned int useLogicalNameReferencing, unsigned int clientAddress, unsigned int serverAddress, unsigned int authentication, const char *password, unsigned int interfaceType);` ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body None ### Request Example ```c #include "dlmssettings.h" static dlmsSettings settings; void client_init(void) { cl_init( &settings, 1, // use LN referencing 16, // client address (public client = 16) 1, // server address (logical device 1) DLMS_AUTHENTICATION_LOW, // password-based auth "GuruxDLMS", // password string DLMS_INTERFACE_TYPE_WRAPPER); // TCP/UDP wrapper framing } ``` ### Response None (initializes settings in place) ``` -------------------------------- ### Initialize DLMS Server Settings Source: https://context7.com/gurux/guruxdlms.c/llms.txt Initializes the DLMS server settings structure with interface type, buffer sizes, and referencing mode. Requires pre-allocated buffers for static operation. ```c #include "server.h" #include "dlmssettings.h" // Static buffers — no heap required static unsigned char frameBuffer[512]; static unsigned char pduBuffer[1024]; static dlmsServerSettings settings; void meter_init(void) { // useLogicalNameReferencing=1, HDLC interface, // maxFrameSize=128, maxPduSize=1024 svr_init( &settings, 1, // use LN referencing DLMS_INTERFACE_TYPE_HDLC, // HDLC framing 128, // max HDLC frame size 1024, // max PDU size frameBuffer, sizeof(frameBuffer), pduBuffer, sizeof(pduBuffer)); } ``` -------------------------------- ### List All COSEM Objects Source: https://github.com/gurux/guruxdlms.c/blob/master/GuruxDLMSServerExample2/readme.md Create an array of all COSEM objects to be managed by the framework. Use BASE() macro for object inclusion. ```c gxObject* ALL_OBJECTS[] = { BASE(association), BASE(ldn), BASE(clock1), BASE(activePowerL1) }; ``` -------------------------------- ### Initialize Association Object Source: https://github.com/gurux/guruxdlms.c/blob/master/GuruxDLMSServerExample2/readme.md Initialize the association object with specified parameters, including object type, logical name, and authentication mechanism. Attach the object list and user list. ```c /////////////////////////////////////////////////////////////////////// //This method adds example Logical Name Association object. /////////////////////////////////////////////////////////////////////// int addAssociation() { int ret; //User list. static gxUser USER_LIST[10] = { 0 }; //Dedicated key. static char CYPHERING_INFO[20] = { 0 }; const unsigned char ln[6] = { 0, 0, 40, 0, 1, 255 }; if ((ret = INIT_OBJECT(associationNone, DLMS_OBJECT_TYPE_ASSOCIATION_LOGICAL_NAME, ln)) == 0) { //Only Logical Device Name is add to this Association View. //Use this if you need to save heap. OA_ATTACH(associationNone.objectList, ALL_OBJECTS); associationNone.authenticationMechanismName.mechanismId = DLMS_AUTHENTICATION_NONE; ARR_ATTACH(associationNone.userList, USER_LIST, 0); ``` -------------------------------- ### Initialize Association Object Source: https://github.com/gurux/guruxdlms.c/blob/master/GuruxDLMSSimpleServerExample2/readme.md Initialize the Association Logical Name object with basic settings. This includes setting the object type, logical device name, and attaching the object list and user list. ```c /////////////////////////////////////////////////////////////////////// //This method adds example Logical Name Association object. /////////////////////////////////////////////////////////////////////// int addAssociation() { int ret; //User list. static gxUser USER_LIST[10] = { 0 }; //Dedicated key. static char CYPHERING_INFO[20] = { 0 }; const unsigned char ln[6] = { 0, 0, 40, 0, 1, 255 }; if ((ret = INIT_OBJECT(associationNone, DLMS_OBJECT_TYPE_ASSOCIATION_LOGICAL_NAME, ln)) == 0) { //Only Logical Device Name is add to this Association View. //Use this if you need to save heap. OA_ATTACH(associationNone.objectList, ALL_OBJECTS); associationNone.authenticationMechanismName.mechanismId = DLMS_AUTHENTICATION_NONE; ARR_ATTACH(associationNone.userList, USER_LIST, 0); ``` -------------------------------- ### Initialize DLMS Client Settings Source: https://context7.com/gurux/guruxdlms.c/llms.txt Initializes the DLMS client settings structure, configuring client and server addresses, authentication, and interface type. Must be called before any client request. ```c #include "dlmssettings.h" static dlmsSettings settings; void client_init(void) { cl_init( &settings, 1, // use LN referencing 16, // client address (public client = 16) 1, // server address (logical device 1) DLMS_AUTHENTICATION_LOW, // password-based auth "GuruxDLMS", // password string DLMS_INTERFACE_TYPE_WRAPPER); // TCP/UDP wrapper framing } ``` -------------------------------- ### Enable Microcontroller Optimization Source: https://github.com/gurux/guruxdlms.c/blob/master/GuruxDLMSDataCollector/README.md Adds a compiler flag to the make file to enable microcontroller-specific optimizations. This is recommended for memory-constrained environments. ```C CFLAGS += -DGX_DLMS_MICROCONTROLLER ``` -------------------------------- ### svr_init Source: https://context7.com/gurux/guruxdlms.c/llms.txt Initializes a dlmsServerSettings structure for the DLMS server. This function must be called once at startup before any other server function. It configures the interface type, buffer sizes, and referencing mode, supporting static memory allocation. ```APIDOC ## svr_init ### Description Initializes a `dlmsServerSettings` structure with the supplied interface type, frame/PDU buffer sizes, and referencing mode. Must be called once at startup before any other server function. The caller provides pre-allocated byte arrays for the frame receive buffer and the PDU buffer, enabling fully static operation. ### Method `void svr_init(dlmsServerSettings *settings, unsigned int useLogicalNameReferencing, unsigned int interfaceType, unsigned int maxFrameSize, unsigned int maxPduSize, unsigned char *frameBuffer, unsigned int frameBufferLen, unsigned char *pduBuffer, unsigned int pduBufferLen);` ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body None ### Request Example ```c #include "server.h" #include "dlmssettings.h" // Static buffers — no heap required static unsigned char frameBuffer[512]; static unsigned char pduBuffer[1024]; static dlmsServerSettings settings; void meter_init(void) { // useLogicalNameReferencing=1, HDLC interface, // maxFrameSize=128, maxPduSize=1024 svr_init( &settings, 1, // use LN referencing DLMS_INTERFACE_TYPE_HDLC, // HDLC framing 128, // max HDLC frame size 1024, // max PDU size frameBuffer, sizeof(frameBuffer), pduBuffer, sizeof(pduBuffer)); } ``` ### Response None (initializes settings in place) ``` -------------------------------- ### List All COSEM Objects Source: https://github.com/gurux/guruxdlms.c/blob/master/GuruxDLMSSimpleServerExample/readme.md Create a list of all COSEM objects to be managed by the framework, ensuring that each object is registered for the server to recognize. ```c const gxObject* ALL_OBJECTS[] = { &association.base, &ldn.base, &clock1.base, &activePowerL1.base }; ``` -------------------------------- ### INIT_OBJECT / cosem_init Source: https://context7.com/gurux/guruxdlms.c/llms.txt Initializes a COSEM object, setting its type and Logical Name. `INIT_OBJECT` is a macro for static objects, while `cosem_init` and `cosem_init2` are for dynamically allocated objects. All server-side COSEM objects must be initialized before being added to the server settings. ```APIDOC ## INIT_OBJECT / cosem_init ### Description `INIT_OBJECT` is a macro that zero-initialises a typed COSEM object struct and sets its object type and Logical Name. `cosem_init` (and the heap variant `cosem_init2`) perform the same work on dynamically allocated objects. Every COSEM object on the server must be initialised this way before it is added to `settings.base.objects`. ### Method `INIT_OBJECT(object, type, logicalName)` `void cosem_init(void *object, unsigned int objectType, unsigned char *logicalName, unsigned int logicalNameLen);` ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body None ### Request Example ```c #include "gxobjects.h" #include "cosem.h" // Static COSEM objects static gxClock clock_obj; static gxRegister activeEnergy; static gxProfileGeneric loadProfile; // OBIS logical names static unsigned char CLOCK_LN[] = {0, 0, 1, 0, 0, 255}; static unsigned char ENERGY_LN[] = {1, 0, 1, 8, 0, 255}; static unsigned char LOAD_PROFILE_LN[] = {1, 0, 99, 1, 0, 255}; void svr_InitObjects(dlmsServerSettings* settings) { // Initialise each object INIT_OBJECT(clock_obj, DLMS_OBJECT_TYPE_CLOCK, CLOCK_LN); INIT_OBJECT(activeEnergy, DLMS_OBJECT_TYPE_REGISTER, ENERGY_LN); INIT_OBJECT(loadProfile, DLMS_OBJECT_TYPE_PROFILE_GENERIC, LOAD_PROFILE_LN); // Add to association view oa_push(&settings->base.objects, BASE(clock_obj)); oa_push(&settings->base.objects, BASE(activeEnergy)); oa_push(&settings->base.objects, BASE(loadProfile)); // Finalise server (assigns short names, validates config) svr_initialize(settings); } ``` ### Response None (initializes objects in place) ``` -------------------------------- ### Initialize UART Source: https://github.com/gurux/guruxdlms.c/blob/master/NordicSemiconductor/GuruxDLMSSimpleServerExample2/readme.md Initializes the specified UART device and configures it for receiving data via interrupts. It checks for errors and sets up the interrupt callback. ```c static int gx_uart_init(char *uart_dev_name) { int err; uart_dev = device_get_binding(uart_dev_name); if (uart_dev == NULL) { printf("Cannot bind %s\n", uart_dev_name); return EINVAL; } err = uart_err_check(uart_dev); if (err) { printf("UART check failed\n"); return EINVAL; } uart_irq_rx_enable(uart_dev); uart_irq_callback_set(uart_dev, isr); return err; } ``` -------------------------------- ### Include DLMS Server Headers Source: https://github.com/gurux/guruxdlms.c/blob/master/GuruxDLMSSimpleServerExample/readme.md Include the necessary header files for DLMS settings, variant types, COSEM objects, and server functionalities. ```c #include "../../development/include/dlmssettings.h" #include "../../development/include/variant.h" #include "../../development/include/cosem.h" #include "../../development/include/server.h" ``` -------------------------------- ### Initialize COSEM Objects Source: https://github.com/gurux/guruxdlms.c/blob/master/GuruxDLMSServerExample2/readme.md Initializes all COSEM objects for the DLMS server, including logical device names, clocks, registers, and associations. ```c int svr_InitObjects( dlmsServerSettings *settings) { addLogicalDeviceName(); addClockObject(); addRegisterObject(); addAssociation(); oa_attach(&settings->base.objects, ALL_OBJECTS, sizeof(ALL_OBJECTS) / sizeof(ALL_OBJECTS[0])); return oa_verify(&settings->base.objects); } ``` -------------------------------- ### Handle COSEM Method Invocations with svr_preAction Source: https://context7.com/gurux/guruxdlms.c/llms.txt Implement custom logic before default method handling. Use to trigger actions like profile capture or image transfer. Set 'e->handled = 1' to prevent default library processing. ```c void svr_preAction(dlmsSettings* settings, gxValueEventCollection* args) { gxValueEventArg* e; int pos, ret; for (pos = 0; pos != args->size; ++pos) { if ((ret = vec_getByIndex(args, pos, &e)) != DLMS_ERROR_CODE_OK) break; // Profile generic — method 1 = capture if (e->target == BASE(loadProfile) && e->index == 1) { // Append one row of captured data to the ring-buffer file captureProfileGeneric(settings, &loadProfile); e->handled = 1; } // Profile generic — method 3 = reset (clear buffer) else if (e->target == BASE(loadProfile) && e->index == 3) { // Remove ring-buffer file so it starts fresh remove(LOAD_PROFILE_FILE); e->handled = 1; } } } ``` -------------------------------- ### Initialize Server Objects Source: https://github.com/gurux/guruxdlms.c/blob/master/GuruxDLMSSimpleServerExample/readme.md Initializes various COSEM objects required for the DLMS server, including logical device names, clocks, registers, and associations. This function should be called after basic server settings are configured. ```c /////////////////////////////////////////////////////////////////////// //Initialize COSEM objects. /////////////////////////////////////////////////////////////////////// int svr_InitObjects( dlmsServerSettings *settings) { addLogicalDeviceName(); addClockObject(); addRegisterObject(); addAssociation(); oa_attach(&settings->base.objects, ALL_OBJECTS, sizeof(ALL_OBJECTS) / sizeof(ALL_OBJECTS[0])); return 0; } ``` -------------------------------- ### Initialize Clock Object Source: https://github.com/gurux/guruxdlms.c/blob/master/GuruxDLMSSimpleServerExample/readme.md Initializes a clock object with a specified logical name and time range. Ensure the logical name is correctly formatted. ```c //Add default clock. Clock's Logical Name is 0.0.1.0.0.255. const unsigned char ln[6] = { 0,0,1,0,0,255 }; if ((ret = cosem_init2(&clock1.base, DLMS_OBJECT_TYPE_CLOCK, ln)) != 0) { return ret; } time_init3(&clock1.begin, -1, 9, 1, -1, -1, -1, -1); time_init3(&clock1.end, -1, 3, 1, -1, -1, -1, -1); return 0; } ``` -------------------------------- ### Define Server Settings and Buffers Source: https://github.com/gurux/guruxdlms.c/blob/master/GuruxDLMSSimpleServerExample/readme.md Define the DLMS server settings, including buffer sizes for HDLC frames and PDUs, and declare buffers for frame data, PDU data, and reply data. ```c dlmsServerSettings settings; /HDLC frame size. #define HDLC_BUFFER_SIZE 128 //PDU size. #define PDU_BUFFER_SIZE 256 //HDLC frame buffer. unsigned char frame[HDLC_BUFFER_SIZE]; //PDU buffer. unsigned char pdu[PDU_BUFFER_SIZE]; //Reply data that is sent back to the client. gxByteBuffer reply; ``` -------------------------------- ### Define DLMS Server Buffers and Settings Source: https://github.com/gurux/guruxdlms.c/blob/master/GuruxDLMSSimpleServerExample2/readme.md Define settings and buffer sizes for the DLMS server, including HDLC frame size, PDU size, frame and PDU buffers, and the reply data buffer. ```c dlmsServerSettings settings; /HDLC frame size. #define HDLC_BUFFER_SIZE 128 //PDU size. #define PDU_BUFFER_SIZE 1024 //HDLC frame buffer. unsigned char frame[11 + HDLC_BUFFER_SIZE]; //PDU buffer. unsigned char pdu[PDU_BUFFER_SIZE]; //Reply data that is sent back to the client. gxByteBuffer reply; ``` -------------------------------- ### Generate Unsolicited Push Notifications (C) Source: https://context7.com/gurux/guruxdlms.c/llms.txt Builds a Data-Notification PDU and sends it over TCP. Encryption and authentication are applied based on ciphering settings. ```c #include "notify.h" static gxPushSetup pushSetup; int sendPush(dlmsSettings* settings) { message messages; mes_init(&messages); struct tm now_tm; time_t t = time(NULL); gmtime_r(&t, &now_tm); int ret = notify_generatePushSetupMessages( settings, &now_tm, // timestamp embedded in notification (NULL = omit) &pushSetup, &messages); if (ret != DLMS_ERROR_CODE_OK) { mes_clear(&messages); return ret; } // Open a TCP connection to pushSetup destination and send each frame for (int i = 0; i < messages.size; ++i) { tcp_send(messages.data[i]->data, messages.data[i]->size); } mes_clear(&messages); return DLMS_ERROR_CODE_OK; } ``` -------------------------------- ### Read Profile Generic Data (Client) (C) Source: https://context7.com/gurux/guruxdlms.c/llms.txt Retrieves historical data from Profile Generic objects. Supports reading by sequential index or by a specified time range. Parsed rows are available in `pg->buffer`. ```c #include "client.h" int read_load_profile(dlmsSettings* settings, connection* con, gxProfileGeneric* pg) { int ret; message data; gxReplyData reply; gxtime start, end; mes_init(&data); reply_init(&reply); // Read last 24 hours time_now(&end); start = end; time_addHours(&start, -24); ret = cl_readRowsByRange2(settings, pg, &start, &end, &data); if (ret != DLMS_ERROR_CODE_OK) goto done; ret = com_readDataBlock(con, &data, &reply); if (ret != DLMS_ERROR_CODE_OK) goto done; ret = cl_updateValue(settings, BASE(*pg), 2, &reply.dataValue); done: mes_clear(&data); reply_clear(&reply); return ret; } ``` -------------------------------- ### Declare Method Access Rights with svr_getMethodAccess Source: https://context7.com/gurux/guruxdlms.c/llms.txt Grant or deny access to object methods based on client authentication level. Authenticated clients are granted access to all methods by default. ```c DLMS_METHOD_ACCESS_MODE svr_getMethodAccess( dlmsSettings* settings, gxObject* obj, unsigned char index) { // Allow all methods for authenticated clients if (settings->authentication >= DLMS_AUTHENTICATION_LOW) return DLMS_METHOD_ACCESS_MODE_ACCESS; return DLMS_METHOD_ACCESS_MODE_NO_ACCESS; } ``` -------------------------------- ### Set ESP32-S3 Target Device Source: https://github.com/gurux/guruxdlms.c/blob/master/Espressif/readme.md Configure the ESP-IDF build system to target the ESP32-S3 microcontroller. ```bash idf.py set-target esp32s3 ``` -------------------------------- ### Initialize Logical Device Name Object Source: https://github.com/gurux/guruxdlms.c/blob/master/Atmel/AtMega2560/GuruxDLMSServerExample/readme.md Initializes the Logical Device Name object, which is an octet-string of 16 octets. The first three octets identify the manufacturer, and the remaining thirteen are assigned by the manufacturer for uniqueness. ```c /////////////////////////////////////////////////////////////////////// //Add Logical Device Name. 123456 is meter serial number. /////////////////////////////////////////////////////////////////////// // COSEM Logical Device Name is defined as an octet-string of 16 octets. // The first three octets uniquely identify the manufacturer of the device and it corresponds // to the manufacturer's identification in IEC 62056-21. // The following 13 octets are assigned by the manufacturer. //The manufacturer is responsible for guaranteeing the uniqueness of these octets. void addLogicalDeviceName(){ char buff[17]; sprintf(buff, "GRX%.13lu", SERIAL_NUMBER); const unsigned char ln[6] = { 0,0,42,0,0,255 }; cosem_init2((gxObject*)&ldn.base, DLMS_OBJECT_TYPE_DATA, ln); var_addBytes(&ldn.value, (unsigned char*)buff, 16); } ``` -------------------------------- ### Register COSEM Objects Source: https://context7.com/gurux/guruxdlms.c/llms.txt Initializes and registers COSEM objects (Clock, Register, ProfileGeneric) with the DLMS server. Uses `INIT_OBJECT` macro for static objects and `oa_push` to add them to the association view. ```c #include "gxobjects.h" #include "cosem.h" // Static COSEM objects static gxClock clock_obj; static gxRegister activeEnergy; static gxProfileGeneric loadProfile; // OBIS logical names static unsigned char CLOCK_LN[] = {0, 0, 1, 0, 0, 255}; static unsigned char ENERGY_LN[] = {1, 0, 1, 8, 0, 255}; static unsigned char LOAD_PROFILE_LN[] = {1, 0, 99, 1, 0, 255}; void svr_InitObjects(dlmsServerSettings* settings) { // Initialise each object INIT_OBJECT(clock_obj, DLMS_OBJECT_TYPE_CLOCK, CLOCK_LN); INIT_OBJECT(activeEnergy, DLMS_OBJECT_TYPE_REGISTER, ENERGY_LN); INIT_OBJECT(loadProfile, DLMS_OBJECT_TYPE_PROFILE_GENERIC, LOAD_PROFILE_LN); // Add to association view oa_push(&settings->base.objects, BASE(clock_obj)); oa_push(&settings->base.objects, BASE(activeEnergy)); oa_push(&settings->base.objects, BASE(loadProfile)); // Finalise server (assigns short names, validates config) svr_initialize(settings); } ``` -------------------------------- ### Configure Default Network Address Source: https://github.com/gurux/guruxdlms.c/blob/master/GuruxDLMSDataCollector/README.md Set a unique network address for each NIC. This value must be changed before compilation. ```makefile # TODO: Change this. This must be unique for each NIC. default_network_address ?= 0x123456 ``` -------------------------------- ### Initialize Clock Object Source: https://github.com/gurux/guruxdlms.c/blob/master/Atmel/AtMega2560/GuruxDLMSServerExample/readme.md Initializes the Clock object with a default logical name (0.0.1.0.0.255). This function handles the initialization process and returns an error code if it fails. ```c /////////////////////////////////////////////////////////////////////// //This method adds example clock object. /////////////////////////////////////////////////////////////////////// int addClockObject() { int ret; //Add default clock. Clock's Logical Name is 0.0.1.0.0.255. const unsigned char ln[6] = { 0,0,1,0,0,255 }; if ((ret = cosem_init2(&clock1.base, DLMS_OBJECT_TYPE_CLOCK, ln)) != 0) { return ret; } ```