Skip to content

Commit ff0e6a2

Browse files
authored
Update README.md (#9)
1 parent 9ce0cac commit ff0e6a2

1 file changed

Lines changed: 102 additions & 14 deletions

File tree

README.md

Lines changed: 102 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5,26 +5,26 @@ It is designed for embedded and integration-heavy environments such as ESPHome,
55

66
## Features
77

8-
- **Transport decoding**: `RAW`, `HDLC`, `M-Bus`. Including multi-frame segmentation and General Block Transfer
8+
- **Transport decoding**: `RAW`, `HDLC`, `M-Bus`. Auto-detects the frame format based on the leading byte. Includes multi-frame segmentation and General Block Transfer
99
- **Encryption**: AES-128-GCM decryption and optional authentication tag verification for `General-GLO-Ciphering` and `General-DED-Ciphering` APDUs
10+
- **Crypto Backends**: Pluggable decryption backends with built-in support for `mbedTLS`, `BearSSL`, and `TF-PSA`
1011
- **Pattern matching**: DSL-based AXDR descriptor patterns with built-in presets and custom registration
11-
- **Callback API**: cooked callback delivers OBIS code + scaled value; raw callback gives full capture details
12+
- **Callback API**: cooked callback delivers OBIS code + scaled value
1213
- **Embedded-friendly**: no heap allocation during parsing
1314
- **Portable**: builds on ESP32 (IDF/Arduino), ESP8266, Linux, macOS, Windows
1415

1516
## How to use
1617

1718
Complete example with the explanation: [test_example.cpp](https://github.com/esphome-libs/dlms_parser/blob/main/tests/test_example.cpp)
1819

19-
## How to creating custom patterns to match your meter's telegram structure
20+
### Creating custom patterns to match your meter's telegram structure
2021

2122
The parser starts with no registered AXDR patterns. Load the built-ins first unless you want full control:
22-
23-
```cpp
23+
```c++
2424
parser.load_default_patterns();
2525
```
2626

27-
Built-in patterns
27+
**Built-in patterns (available after calling `parser.load_default_patterns()`):**
2828

2929
| Name | Pattern | Priority | Typical use |
3030
|------------------------------------------|------------------|---------:|-------------------------------------------|
@@ -38,10 +38,11 @@ Built-in patterns
3838
| `firstElement-dateTime` | `F, S(TO, TDTM)` | 80 | first-element date-time structure |
3939
| `swappedTagObis-value-scalerUnit` | `TOW, TV, TSU` | 90 | swapped-tag OBIS, value, scaler-unit |
4040

41-
Register a custom pattern when your meter emits a different structure
41+
**Registering Custom Patterns:**
4242

43-
```cpp
44-
// Simple — name="CUSTOM", priority=0 (tried before built-ins)
43+
If your meter emits a layout not covered by the built-ins, you can register custom patterns. Lower priority numbers are evaluated first.
44+
```c++
45+
// Simple — priority 0 (tried before built-ins)
4546
parser.register_pattern("TC, TO, TDTM");
4647

4748
// Named with explicit priority
@@ -91,16 +92,100 @@ parser.register_pattern("TOW, TV, TSU"); // Landis+Gyr swapped OBIS
9192
| `DN` | descend into nested structure | control token |
9293
| `UP` | return from nested structure | control token |
9394

95+
## API Reference
96+
97+
### `DlmsParser` Core Methods
98+
99+
> **⚠️ Warning:** If you intend to use encryption, you **must** provide a concrete `Aes128GcmDecryptor` backend to the constructor before calling `set_decryption_key` or `set_authentication_key`. Calling these methods on a parser initialized with the default `nullptr` decryptor will cause a null pointer dereference.
100+
101+
| Method | Description |
102+
|----------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------|
103+
| `DlmsParser(Aes128GcmDecryptor* = nullptr)` | Constructor accepting an optional pointer to an AES-128-GCM decryptor backend. |
104+
| `set_skip_crc_check(bool)` | Skip CRC/checksum validation for HDLC and M-Bus. |
105+
| `set_decryption_key(const Aes128GcmDecryptionKey&)` | Set AES-128-GCM decryption key (GUEK). **Requires a non-null decryptor.** |
106+
| `set_authentication_key(const Aes128GcmAuthenticationKey&)` | Set AES-128-GCM authentication key (GAK) for GCM tag verification. **Requires a non-null decryptor.** |
107+
| `load_default_patterns()` | Register all built-in patterns (T1, T2, T3, DateTime, etc.). |
108+
| `ParseResult parse(std::span<uint8_t> buf, const DlmsDataCallback&)` | Parse a complete frame; modifies the buffer in-place and triggers the callback. |
109+
### Supported APDU Tags
110+
111+
Common APDU tags accepted by the parser:
112+
113+
| Byte | Meaning |
114+
|--------|-------------------------------------------------------------------------------------|
115+
| `0x0F` | `DATA-NOTIFICATION` |
116+
| `0xE0` | `General-Block-Transfer` — reassembles numbered blocks, then re-enters APDU parsing |
117+
| `0xDB` | `General-GLO-Ciphering` — encrypted, needs decryption key |
118+
| `0xDF` | `General-DED-Ciphering` — encrypted, needs decryption key |
119+
| `0x01` | raw AXDR array |
120+
| `0x02` | raw AXDR structure |
121+
122+
### Basic Example
123+
```c++
124+
#include <vector>
125+
#include "dlms_parser.h"
126+
#include "decryption/aes_128_gcm_decryptor_mbedtls.h"
127+
128+
using namespace dlms_parser;
129+
130+
int main() {
131+
// 1. Initialize a crypto backend (e.g., mbedTLS)
132+
Aes128GcmDecryptorMbedTls decryptor;
133+
134+
// 2. Initialize the parser with a pointer to the decryptor
135+
DlmsParser parser(&decryptor);
136+
137+
// 3. Set keys using the robust hex loader
138+
auto dec_key = Aes128GcmDecryptionKey::from_hex("00112233445566778899AABBCCDDEEFF");
139+
auto auth_key = Aes128GcmAuthenticationKey::from_hex("FFEEDDCCBBAA99887766554433221100");
140+
141+
if (dec_key) parser.set_decryption_key(*dec_key);
142+
if (auth_key) parser.set_authentication_key(*auth_key);
143+
144+
// 4. Load common built-in meter layout patterns
145+
parser.load_default_patterns();
146+
147+
// 5. Provide your data (will be modified in-place during parsing)
148+
std::vector<uint8_t> my_telegram = { /* ... byte data ... */ };
149+
150+
// 6. Define your callback
151+
auto callback = [](const char* obis, float f_val, const char* s_val, bool is_numeric) {
152+
printf("Matched OBIS: %s | String: %s | Float: %f\n", obis, s_val, f_val);
153+
};
154+
155+
// 7. Parse the telegram by explicitly constructing a std::span<uint8_t>
156+
ParseResult result = parser.parse(std::span<uint8_t>(my_telegram.data(), my_telegram.size()), callback);
157+
158+
printf("Successfully parsed %zu COSEM objects!\n", result.count);
159+
return 0;
160+
}
161+
```
162+
163+
## Logging
164+
165+
`dlms_parser` includes a built-in logging system that is useful for debugging frame parsing and pattern matching. You can hook into it by providing a custom log function:
166+
```c++
167+
#include "log.h"
168+
#include <cstdarg>
169+
#include <cstdio>
170+
171+
// ... inside your setup code ...
172+
dlms_parser::Logger::set_log_function([](dlms_parser::LogLevel level, const char* fmt, va_list args) {
173+
// Implement your platform-specific print here
174+
vprintf(fmt, args);
175+
printf("\n");
176+
});
177+
```
178+
94179
## How to add the library to your project
95180
96181
### PlatformIO package
97-
TODO: add link
182+
[https://registry.platformio.org/libraries/esphome/dlms_parser](https://registry.platformio.org/libraries/esphome/dlms_parser)
98183
99184
### ESP-IDF component
100-
TODO: add link
185+
[https://components.espressif.com/components/esphome/dlms_parser](https://components.espressif.com/components/esphome/dlms_parser)
101186
102187
### CMake
103-
```
188+
```cmake
104189
FetchContent_Declare(
105190
dlms_parser
106191
GIT_REPOSITORY https://github.com/esphome-libs/dlms_parser
@@ -111,8 +196,11 @@ add_executable(your_project_name main.cpp)
111196
target_link_libraries(your_project_name PRIVATE dlms_parser)
112197
```
113198

114-
## How to work with the codebase
115-
You can open the repository using any IDE that supports CMake.
199+
### Acknowledgements
200+
201+
This library builds on foundational work and protocol insights from:
202+
- [esphome-dlms-cosem](https://github.com/latonita/esphome-dlms-cosem) - original ESPHome DLMS/COSEM component and AXDR parser by **latonita**.
203+
- [xt211](https://github.com/Tomer27cz/xt211) - Sagemcom XT211 parser by **Tomer27cz**, instrumental in de-Guruxing the protocol handling.
116204

117205
## References
118206
- [DLMS/COSEM Architecture and Protocols. Green Book Edition 11](https://github.com/zhuyangfei/DLMS-green-book/blob/main/Green-Book-Ed-11-V1-0.pdf)

0 commit comments

Comments
 (0)