16static uint8_t checksum(
const uint8_t *p,
size_t n)
19 for (
size_t i = 0; i < n; i++)
21 s = (uint8_t)(s + p[i]);
26size_t pc_mbus_build_ack(uint8_t *buf,
size_t cap)
36size_t pc_mbus_build_short(uint8_t *buf,
size_t cap, uint8_t c, uint8_t a)
42 buf[0] = MBUS_START_SHORT;
45 buf[3] = (uint8_t)(c + a);
50size_t pc_mbus_build_long(uint8_t *buf,
size_t cap, uint8_t c, uint8_t a, uint8_t ci,
const uint8_t *data,
53 if (!buf || data_len > MBUS_MAX_DATA || (data_len && !data))
57 uint8_t L = (uint8_t)(3 + data_len);
58 size_t total = (size_t)6 + L;
63 buf[0] = MBUS_START_LONG;
66 buf[3] = MBUS_START_LONG;
72 memcpy(buf + 7, data, data_len);
74 buf[4 + L] = checksum(buf + 4, L);
75 buf[5 + L] = MBUS_STOP;
79size_t pc_mbus_build_snd_nke(uint8_t *buf,
size_t cap, uint8_t a)
81 return pc_mbus_build_short(buf, cap, MBUS_C_SND_NKE, a);
84size_t pc_mbus_build_req_ud2(uint8_t *buf,
size_t cap, uint8_t a,
bool fcb)
86 return pc_mbus_build_short(buf, cap, (uint8_t)(fcb ? 0x7Bu : MBUS_C_REQ_UD2), a);
89size_t pc_mbus_build_req_ud1(uint8_t *buf,
size_t cap, uint8_t a,
bool fcb)
91 return pc_mbus_build_short(buf, cap, (uint8_t)(fcb ? 0x7Au : MBUS_C_REQ_UD1), a);
94bool pc_mbus_parse(
const uint8_t *buf,
size_t len, MbusFrame *out,
size_t *consumed)
96 if (!buf || !out || len < 1)
100 out->type = MbusFrameType::MBUS_FRAME_NONE;
101 out->c = out->a = out->ci = 0;
105 if (buf[0] == MBUS_ACK)
107 out->type = MbusFrameType::MBUS_FRAME_ACK;
114 if (buf[0] == MBUS_START_SHORT)
116 if (len < 5 || buf[4] != MBUS_STOP)
120 if (buf[3] != (uint8_t)(buf[1] + buf[2]))
124 out->type = MbusFrameType::MBUS_FRAME_SHORT;
133 if (buf[0] == MBUS_START_LONG)
140 if (L < 3 || buf[2] != L || buf[3] != MBUS_START_LONG)
144 size_t total = (size_t)6 + L;
145 if (len < total || buf[5 + L] != MBUS_STOP)
149 if (checksum(buf + 4, L) != buf[4 + L])
153 out->type = MbusFrameType::MBUS_FRAME_LONG;
157 out->data_len = (uint8_t)(L - 3);
158 out->data = out->data_len ? buf + 7 :
nullptr;
168uint8_t pc_mbus_dif_data_len(uint8_t coding)
170 switch ((MbusDifCoding)(coding & 0x0Fu))
172 case MbusDifCoding::MBUS_DIF_NONE:
174 case MbusDifCoding::MBUS_DIF_INT8:
176 case MbusDifCoding::MBUS_DIF_INT16:
178 case MbusDifCoding::MBUS_DIF_INT24:
180 case MbusDifCoding::MBUS_DIF_INT32:
182 case MbusDifCoding::MBUS_DIF_REAL32:
184 case MbusDifCoding::MBUS_DIF_INT48:
186 case MbusDifCoding::MBUS_DIF_INT64:
188 case MbusDifCoding::MBUS_DIF_READOUT:
190 case MbusDifCoding::MBUS_DIF_BCD2:
192 case MbusDifCoding::MBUS_DIF_BCD4:
194 case MbusDifCoding::MBUS_DIF_BCD6:
196 case MbusDifCoding::MBUS_DIF_BCD8:
198 case MbusDifCoding::MBUS_DIF_VARIABLE:
200 case MbusDifCoding::MBUS_DIF_BCD12:
207bool pc_mbus_record_next(
const uint8_t *body,
size_t len,
size_t *pos, MbusRecord *out)
209 if (!body || !pos || !out || *pos >= len)
214 uint8_t dif = body[p++];
215 uint8_t coding = (uint8_t)(dif & 0x0Fu);
229 out->coding = coding;
234 if (coding == (uint8_t)MbusDifCoding::MBUS_DIF_SPECIAL)
245 out->vif = body[p++];
257 if (coding == (uint8_t)MbusDifCoding::MBUS_DIF_VARIABLE)
263 uint8_t lvar = body[p++];
272 dlen = pc_mbus_dif_data_len(coding);
279 out->data = dlen ? body + p :
nullptr;
280 out->data_len = dlen;
288static bool mbus_decode_int(
const uint8_t *d, uint8_t n, int64_t *out)
295 for (
int i = (
int)n - 1; i >= 0; i--)
299 if (n < 8 && (d[n - 1] & 0x80u))
301 u |= ~(uint64_t)0 << (n * 8);
308static bool mbus_decode_bcd(
const uint8_t *d, uint8_t n, int64_t *out)
314 bool neg = (uint8_t)(d[n - 1] >> 4) == 0x0Fu;
317 for (uint8_t i = 0; i < n; i++)
319 uint8_t lo = (uint8_t)(d[i] & 0x0Fu);
320 uint8_t hi = (uint8_t)(d[i] >> 4);
321 if (i == (uint8_t)(n - 1) && neg)
325 if (lo > 9 || hi > 9)
329 v += (int64_t)(hi * 10 + lo) * mult;
336bool pc_mbus_record_value_int(
const MbusRecord *r, int64_t *out)
338 if (!r || !out || !r->data)
342 const uint8_t *d = r->data;
343 uint8_t n = r->data_len;
344 switch ((MbusDifCoding)r->coding)
346 case MbusDifCoding::MBUS_DIF_INT8:
347 case MbusDifCoding::MBUS_DIF_INT16:
348 case MbusDifCoding::MBUS_DIF_INT24:
349 case MbusDifCoding::MBUS_DIF_INT32:
350 case MbusDifCoding::MBUS_DIF_INT48:
351 case MbusDifCoding::MBUS_DIF_INT64:
352 return mbus_decode_int(d, n, out);
353 case MbusDifCoding::MBUS_DIF_BCD2:
354 case MbusDifCoding::MBUS_DIF_BCD4:
355 case MbusDifCoding::MBUS_DIF_BCD6:
356 case MbusDifCoding::MBUS_DIF_BCD8:
357 case MbusDifCoding::MBUS_DIF_BCD12:
358 return mbus_decode_bcd(d, n, out);
364bool pc_mbus_record_value_real(
const MbusRecord *r,
float *out)
366 if (!r || !out || !r->data || (MbusDifCoding)r->coding != MbusDifCoding::MBUS_DIF_REAL32 || r->data_len < 4)
370 uint32_t bits = (uint32_t)r->data[0] | ((uint32_t)r->data[1] << 8) | ((uint32_t)r->data[2] << 16) |
371 ((uint32_t)r->data[3] << 24);
372 memcpy(out, &bits, 4);
376bool pc_mbus_vif_decode(uint8_t vif, MbusUnit *unit, int8_t *exp10)
378 MbusUnit u = MbusUnit::MBUS_UNIT_UNKNOWN;
380 uint8_t v = (uint8_t)(vif & 0x7Fu);
383 u = MbusUnit::MBUS_UNIT_WH;
384 e = (int8_t)((v & 7) - 3);
388 u = MbusUnit::MBUS_UNIT_J;
393 u = MbusUnit::MBUS_UNIT_M3;
394 e = (int8_t)((v & 7) - 6);
398 u = MbusUnit::MBUS_UNIT_KG;
399 e = (int8_t)((v & 7) - 3);
401 else if (v >= 0x28 && v <= 0x2F)
403 u = MbusUnit::MBUS_UNIT_W;
404 e = (int8_t)((v & 7) - 3);
406 else if (v >= 0x30 && v <= 0x37)
408 u = MbusUnit::MBUS_UNIT_J_PER_H;
411 else if (v >= 0x38 && v <= 0x3F)
413 u = MbusUnit::MBUS_UNIT_M3_PER_H;
414 e = (int8_t)((v & 7) - 6);
416 else if ((v >= 0x58 && v <= 0x5F) || (v >= 0x64 && v <= 0x67))
420 u = MbusUnit::MBUS_UNIT_CELSIUS;
421 e = (int8_t)((v & 3) - 3);
423 else if (v >= 0x60 && v <= 0x63)
425 u = MbusUnit::MBUS_UNIT_K;
426 e = (int8_t)((v & 3) - 3);
428 else if (v >= 0x68 && v <= 0x6B)
430 u = MbusUnit::MBUS_UNIT_BAR;
431 e = (int8_t)((v & 3) - 3);
441 return u != MbusUnit::MBUS_UNIT_UNKNOWN;
444bool pc_mbus_parse_var_header(
const uint8_t *body,
size_t len, MbusVarHeader *out)
446 if (!body || !out || len < MBUS_VAR_HEADER_LEN)
452 for (
int i = 3; i >= 0; i--)
454 uint8_t hi = (uint8_t)(body[i] >> 4);
455 uint8_t lo = (uint8_t)(body[i] & 0x0Fu);
456 if (hi > 9 || lo > 9)
460 id =
id * 100u + (uint32_t)(hi * 10u + lo);
464 uint16_t man = (uint16_t)(body[4] | (body[5] << 8));
465 out->manufacturer_raw = man;
466 out->manufacturer[0] = (char)(((man >> 10) & 0x1Fu) + 64u);
467 out->manufacturer[1] = (char)(((man >> 5) & 0x1Fu) + 64u);
468 out->manufacturer[2] = (char)((man & 0x1Fu) + 64u);
469 out->manufacturer[3] =
'\0';
470 out->version = body[6];
471 out->medium = body[7];
472 out->access_no = body[8];
473 out->status = body[9];
474 out->signature = (uint16_t)(body[10] | (body[11] << 8));
Wired M-Bus (Meter-Bus, EN 13757-2/-3) frame codec (PC_ENABLE_MBUS).