#include <functional>
const uint8_t packet1[] {0x1B, 0x1B, 0x1B, 0x1B, 0x01, 0x01, 0x01, 0x01, 0x76, 0x05, 0xA6, 0x9F, 0x1A, 0x03, 0x62, 0x00, 0x62, 0x00, 0x72, 0x63, 0x01, 0x01, 0x76, 0x01, 0x01, 0x02, 0x31, 0x0B, 0x0A, 0x01, 0x44, 0x5A, 0x47, 0x00, 0x03, 0xA0, 0x86, 0xBC, 0x72, 0x62, 0x01, 0x65, 0x01, 0x08, 0xE0, 0x31, 0x62, 0x02, 0x63, 0xA6, 0xDF, 0x00, 0x76, 0x05, 0xA7, 0x9F, 0x1A, 0x03, 0x62, 0x00, 0x62, 0x00, 0x72, 0x63, 0x07, 0x01, 0x77, 0x01, 0x0B, 0x0A, 0x01, 0x44, 0x5A, 0x47, 0x00, 0x03, 0xA0, 0x86, 0xBC, 0x07, 0x01, 0x00, 0x62, 0x0A, 0xFF, 0xFF, 0x72, 0x62, 0x01, 0x65, 0x01, 0x08, 0xE0, 0x31, 0x75, 0x77, 0x07, 0x01, 0x00, 0x60, 0x32, 0x01, 0x01, 0x01, 0x72, 0x62, 0x01, 0x62, 0x00, 0x62, 0x00, 0x52, 0x00, 0x04, 0x44, 0x5A, 0x47, 0x01, 0x77, 0x07, 0x01, 0x00, 0x60, 0x01, 0x00, 0xFF, 0x01, 0x72, 0x62, 0x01, 0x62, 0x00, 0x62, 0x00, 0x52, 0x00, 0x0B, 0x0A, 0x01, 0x44, 0x5A, 0x47, 0x00, 0x03, 0xA0, 0x86, 0xBC, 0x01, 0x77, 0x07, 0x01, 0x00, 0x01, 0x08, 0x00, 0xFF, 0x64, 0x1C, 0x41, 0x04, 0x72, 0x62, 0x01, 0x62, 0x00, 0x62, 0x1E, 0x52, 0xFF, 0x65, 0x01, 0x27, 0x8A, 0x37, 0x01, 0x77, 0x07, 0x01, 0x00, 0x02, 0x08, 0x00, 0xFF, 0x01, 0x72, 0x62, 0x01, 0x62, 0x00, 0x62, 0x1E, 0x52, 0xFF, 0x64, 0x05, 0x46, 0x0B, 0x01, 0x77, 0x07, 0x01, 0x00, 0x10, 0x07, 0x00, 0xFF, 0x01, 0x72, 0x62, 0x01, 0x62, 0x00, 0x62, 0x1B, 0x52, 0xFE, 0x53, 0x41, 0x28, 0x01, 0x01, 0x01, 0x63, 0x7A, 0x27, 0x00, 0x76, 0x05, 0xA8, 0x9F, 0x1A, 0x03, 0x62, 0x00, 0x62, 0x00, 0x72, 0x63, 0x02, 0x01, 0x71, 0x01, 0x63, 0x7C, 0x9E, 0x00, 0x00, 0x00, 0x1B, 0x1B, 0x1B, 0x1B, 0x1A, 0x02, 0xAE, 0xE0};
const uint8_t packet2[] {0x1B, 0x1B, 0x1B, 0x1B, 0x01, 0x01, 0x01, 0x01, 0x76, 0x05, 0xD1, 0xE6, 0x02, 0x03, 0x62, 0x00, 0x62, 0x00, 0x72, 0x63, 0x01, 0x01, 0x76, 0x01, 0x01, 0x02, 0x31, 0x0B, 0x0A, 0x01, 0x44, 0x5A, 0x47, 0x00, 0x03, 0xA0, 0x86, 0xBC, 0x72, 0x62, 0x01, 0x65, 0x01, 0x00, 0xF7, 0xEA, 0x62, 0x02, 0x63, 0x46, 0x63, 0x00, 0x76, 0x05, 0xD2, 0xE6, 0x02, 0x03, 0x62, 0x00, 0x62, 0x00, 0x72, 0x63, 0x07, 0x01, 0x77, 0x01, 0x0B, 0x0A, 0x01, 0x44, 0x5A, 0x47, 0x00, 0x03, 0xA0, 0x86, 0xBC, 0x07, 0x01, 0x00, 0x62, 0x0A, 0xFF, 0xFF, 0x72, 0x62, 0x01, 0x65, 0x01, 0x00, 0xF7, 0xEA, 0x75, 0x77, 0x07, 0x01, 0x00, 0x60, 0x32, 0x01, 0x01, 0x01, 0x72, 0x62, 0x01, 0x62, 0x00, 0x62, 0x00, 0x52, 0x00, 0x04, 0x44, 0x5A, 0x47, 0x01, 0x77, 0x07, 0x01, 0x00, 0x60, 0x01, 0x00, 0xFF, 0x01, 0x72, 0x62, 0x01, 0x62, 0x00, 0x62, 0x00, 0x52, 0x00, 0x0B, 0x0A, 0x01, 0x44, 0x5A, 0x47, 0x00, 0x03, 0xA0, 0x86, 0xBC, 0x01, 0x77, 0x07, 0x01, 0x00, 0x01, 0x08, 0x00, 0xFF, 0x64, 0x1C, 0x01, 0x04, 0x72, 0x62, 0x01, 0x62, 0x00, 0x62, 0x1E, 0x52, 0xFF, 0x65, 0x01, 0x23, 0x26, 0xA2, 0x01, 0x77, 0x07, 0x01, 0x00, 0x02, 0x08, 0x00, 0xFF, 0x01, 0x72, 0x62, 0x01, 0x62, 0x00, 0x62, 0x1E, 0x52, 0xFF, 0x64, 0x04, 0x1A, 0x24, 0x01, 0x77, 0x07, 0x01, 0x00, 0x10, 0x07, 0x00, 0xFF, 0x01, 0x72, 0x62, 0x01, 0x62, 0x00, 0x62, 0x1B, 0x52, 0xFE, 0x54, 0x00, 0x97, 0xB9, 0x01, 0x01, 0x01, 0x63, 0x91, 0xDB, 0x00, 0x76, 0x05, 0xD3, 0xE6, 0x02, 0x03, 0x62, 0x00, 0x62, 0x00, 0x72, 0x63, 0x02, 0x01, 0x71, 0x01, 0x63, 0xF6, 0x79, 0x00, 0x00, 0x1B, 0x1B, 0x1B, 0x1B, 0x1A, 0x01, 0x68, 0x86};
const uint8_t OBIS_1_8_0[] {0x07, 0x01, 0x00, 0x01, 0x08, 0x00};
const uint8_t OBIS_2_8_0[] {0x07, 0x01, 0x00, 0x02, 0x08, 0x00};
const uint8_t OBIS_16_7_0[] {0x07, 0x01, 0x00, 0x10, 0x07, 0x00};
const char* unit_str[] = {
"", // (0) dummy
"a", // (1) a time year
"mo", // (2) mo time month
"wk", // (3) wk time week 7*24*60*60 s
"d", // (4) d time day 24*60*60 s
"h", // (5) h time hour 60*60 s
"min", // (6) min time minute 60 s
"s", // (7) s time (t) second s
"deg", // (8) ° (phase) angle degree rad*180/π
"degC", // (9) °C temperature (T) degree-celsius K-273.15
"", // (10) currency (local) currency
"m", // (11) m length (l) metre m
"m/s", // (12) m/s speed (v) metre per second m/s
"m3", // (13) m 3 volume (V) rV , meter constant or pulse value (volume) cubic metre m 3
"m3", // (14) m 3 corrected volume cubic metre m 3
"m3/h", // (15) m 3/h volume flux cubic metre per hour m 3/(60*60s)
"m3/h", // (16) m 3/h corrected volume flux cubic metre per hour m 3/(60*60s)
"m3/d", // (17) m 3/d volume flux m 3/(24*60*60s)
"m3/d", // (18) m 3/d corrected volume flux m 3/(24*60*60s)
"l", // (19) l volume litre 10- 3 m 3
"kg", // (20) kg mass (m) kilogram
"N", // (21) N force (F) newton
"Nm", // (22) Nm energy newton meter J = Nm = Ws
"Pa", // (23) Pa pressure (p) pascal N/m2
"bar", // (24) bar pressure (p) bar 105 N/m2
"J", // (25) J energy joule J = Nm = Ws
"J/h", // (26) J/h thermal power joule per hour J/(60*60s)
"W", // (27) W active power (P) watt W = J/s
"VA", // (28) VA apparent power (S) volt-ampere
"var", // (29) var reactive power (Q) var
"Wh", // (30) Wh active energy rW , active energy meter constant or pulse value watt-hour W*(60*60s)
"VAh", // (31) VAh apparent energy rS , apparent energy meter constant or pulse value volt-ampere-hour VA*(60*60s)
"varh", // (32) varh reactive energy rB , reactive energy meter constant or pulse value var-hour var*(60*60s)
"A", // (33) A current (I) ampere A
"C", // (34) C electrical charge (Q) coulomb C = As
"V", // (35) V voltage (U) volt V
"V/m", // (36) V/m electric field strength (E) volt per metre V/m
"F", // (37) F capacitance (C) farad C/V = As/V
"Ohm", // (38) Ω resistance (R) ohm Ω = V/A
"Ohmm 2/m", // (39) Ωm 2/m resistivity (ρ) Ωm
"Wb", // (40) Wb magnetic flux (Φ) weber Wb = Vs
"T", // (41) T magnetic flux density (B) tesla Wb/m2
"A/m", // (42) A/m magnetic field strength (H) ampere per metre A/m
"H", // (43) H inductance (L) henry H = W b/A
"Hz", // (44) Hz frequency (f, ω) hertz 1/s
"1/(W h)", // (45) 1/(W h) RW , active energy meter constant or pulse value
"1/(varh)", // (46) 1/(varh) RB , reactive energy meter constant or pulse value
"1/(VAh)", // (47) 1/(VAh) RS , apparent energy meter constant or pulse value
"V2h", // (48) V2h volt-squared hour, rU 2h , volt-squared hour meter constant or pulse value volt-squared-hours V2(60*60s)
"A2h", // (49) A2h ampere-squared hour, rI2 h , ampere-squared hour meter constant or pulse value ampere-squared-hours A2(60*60s)
"kg/s", // (50) kg/s mass flux kilogram per second kg/s
"S", // (51) S, mho conductance siemens 1/Ω
"K", // (52) K temperature (T) kelvin
"1/(V2h)", // (53) 1/(V2h) RU2 h , volt-squared hour meter constant or pulse value
"1/(A2h)", // (54) 1/(A2h) RI2h , ampere-squared hour meter constant or pulse value
"1/m3", // (55) 1/m3 RV , meter constant or pulse value (volume)
"%", // (56) percentage %
"Ah", // (57) Ah ampere-hours Ampere-hour ...
"", // (58) dummy
"", // (59) dummy
"Wh/m3", // (60) Wh/m3 energy per volume 3,6*103 J/m3
"J/m3", // (61) J/m3 calorific value, wobbe
"Mol", // (62) Mol % molar fraction of gas composition mole percent (Basic gas composition unit)
"g/m3", // (63) g/m3 mass density, quantity of material (Gas analysis, accompanying elements)
"Pas", // (64) Pas dynamic viscosity pascal second (Characteristic of gas stream)
"J/kg", // (65) J/kg specific energy NOTE The amount of energy per unit of mass of a substance Joule / kilogram m 2 . kg . s -2 / kg = m2 . s –2 ....
"", // (66) dummy
"", // (67) dummy
"", // (68) dummy
"", // (69) dummy
"dBm", // (70) dBm signal strength, dB milliwatt (e.g. of GSM radio systems)
"dbmV", // (71) dbμV signal strength, dB microvolt
"dB", // (72) dB logarithmic unit that expresses the ratio between two values of a physical quantity ...
};
#define SML_TYPE_MASK 0x70
#define SML_LENGTH_MASK 0x0F
#define SML_ARRAY 0x00
#define SML_BOOL 0x40
#define SML_INT 0x50
#define SML_UINT 0x60
#define SML_LIST 0x70
const char* SML_ARRAY_STR = "SML_ARRAY";
const char* SML_BOOL_STR = "SML_BOOL";
const char* SML_INT_STR = "SML_INT";
const char* SML_UINT_STR = "SML_UINT";
const char* SML_LIST_STR = "SML_LIST";
const char* SML_UNKNOWN = "UNKNWON-TYPE";
#define MAX_DEPTH 15
#define MIN_LEVEL 0
using ParseFunction = std::function<void(const uint8_t* data, uint8_t type, uint8_t length)>;
struct ListInfo {
uint8_t maxItems = 0;
uint8_t currentItem = 0;
};
const char* getFieldTypeStr(uint8_t field_type) {
switch (field_type) {
case SML_ARRAY : return SML_ARRAY_STR;
case SML_BOOL : return SML_BOOL_STR;
case SML_INT : return SML_INT_STR;
case SML_UINT : return SML_UINT_STR;
case SML_LIST : return SML_LIST_STR;
}
return SML_UNKNOWN;
}
void printArray(const uint8_t* arr, uint8_t len) {
const uint8_t* ptr = arr;
if (len == 0) {
Serial.println();
return;
}
Serial.print(" [");
for (size_t i = 0; i < len; i++) Serial.printf("%s%02X", i ? ", 0x" : "0x", *(ptr++));
Serial.println("]");
}
void parseList(const uint8_t* data, size_t len) {
const uint8_t* ptr = data;
ListInfo li[MAX_DEPTH];
int current_level = MIN_LEVEL;
while (*(ptr) >> 4 != 0x07) {
ptr++;
}
if (!ptr || *(ptr) >> 4 != 0x07) {
Serial.println("Invalid list");
return;
}
const uint8_t listLength = *(ptr++) & SML_LENGTH_MASK;
Serial.printf("SML_LIST (%d)\r\n", listLength);
current_level++;
li[current_level].maxItems = listLength;
li[current_level].currentItem = 0;
// while (li[MIN_LEVEL].currentItem < li[MIN_LEVEL].maxItems) {
while (ptr < data + len) {
uint8_t field_type = *(ptr) & SML_TYPE_MASK;
uint8_t field_length = *(ptr) & SML_LENGTH_MASK;
if (field_type == SML_LIST) {
li[current_level + 1].maxItems = field_length;
}
li[current_level].currentItem++;
Serial.printf("%02d | ", current_level); for (int i = 0; i < current_level; i++) Serial.print(" ");
Serial.printf("[%d/%d] %02X = %s (%d)",
li[current_level].currentItem,
li[current_level].maxItems,
*(ptr),
getFieldTypeStr(field_type),
field_type == SML_LIST ? field_length : field_length - 1
);
if (current_level == 0 && li[current_level].currentItem == 6) {
const uint8_t* ptr2 = ptr;
for (size_t i = 0; i < field_length; i++) {
Serial.printf(" %02X", *(ptr2++));
}
}
if (field_type == SML_LIST) {
current_level++;
ptr++;
Serial.println();
} else {
// if (pf && current_level == sLevel && li[current_level].currentItem == sField) pf(ptr, field_type, field_length);
if (field_length >= 1) printArray(ptr + 1, field_length - 1);
else Serial.println();
ptr += field_length ? field_length : 1;
}
// while (current_level > MIN_LEVEL && li[current_level].currentItem >= li[current_level].maxItems) {
while (li[current_level].currentItem >= li[current_level].maxItems && current_level >= MIN_LEVEL) {
li[current_level].currentItem = 0;
li[current_level].maxItems = 0;
current_level--;
// if (current_level < MIN_LEVEL) return;
}
}
Serial.println();
}
void setup() {
Serial.begin(115200);
Serial.println("Begin parsing...");
parseList(packet1, sizeof(packet1));
// parseList(findOBIS(packet1, sizeof(packet1), OBIS_2_8_0), parseOBIS_16_7_0, 0, 6);
// parseList(findOBIS(packet1, sizeof(packet1), OBIS_16_7_0), parseOBIS_16_7_0, 0, 6);
// parseList(findOBIS(packet2, sizeof(packet2), OBIS_1_8_0) + 1);
// parseList(nullptr);
// parseList(findOBIS(packet2, sizeof(packet2), OBIS_2_8_0));
// parseList(findOBIS(packet2, sizeof(packet2), OBIS_16_7_0));
}
void loop() {
delay(10); // this speeds up the simulation
}