Skip to content

Commit cd8ee1d

Browse files
committed
Add support for Q-walk_by to Wireless-MBus
1 parent 75ad926 commit cd8ee1d

File tree

1 file changed

+70
-5
lines changed

1 file changed

+70
-5
lines changed

src/devices/m_bus.c

Lines changed: 70 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ for further processing by an Application layer (outside this program).
2525
// Convert two BCD encoded nibbles to an integer
2626
static unsigned bcd2int(uint8_t bcd)
2727
{
28-
return 10*(bcd>>4) + (bcd & 0xF);
28+
return 10 * (bcd >> 4) + (bcd & 0xf);
2929
}
3030

3131
// Mapping from 6 bits to 4 bits. "3of6" coding used for Mode T
@@ -155,6 +155,8 @@ typedef struct {
155155
uint8_t l_npci;
156156
uint8_t tpci;
157157
uint8_t apci;
158+
/* Q-walk_by */
159+
int q_mode;
158160
} m_bus_block2_t;
159161

160162
// Data structure for block 1
@@ -666,6 +668,58 @@ static int m_bus_decode_records(data_t **inout_data, const uint8_t *b, uint8_t d
666668

667669
static void parse_payload(data_t *data, const m_bus_block1_t *block1, const m_bus_data_t *out)
668670
{
671+
// check for vendor specific non-standard payload
672+
673+
/* Q-walk_by */
674+
/* 000: CI:0x78 Vendor spec (not used for OMS) */
675+
/* 000: 0x780dff5f Magic for QUNDIS walk_by */
676+
/* 004: 0x35 L:53 Length of walk_by field? */
677+
/* 005: 0x00 ST:0 Status 0= No Error */
678+
/* 006: 0x82 unknown */
679+
/* 007: AC AccessNumber, inc by 1 each message */
680+
/* 008: 0x0000 CW:0 no encryption */
681+
/* 015: 0xffff V:total_follows */
682+
/* 017: 0x67452301 V:total - BCD LSB first -> 01234567 */
683+
/* 021: 0xff2c V:lastyear 31.12 follows */
684+
/* 023: 0x00000000 V:lastyear - BCD LSB first */
685+
/* 027: e.g. 0x1e36 V:lastmonth 30.6 follows */
686+
/* 029: 0x00000000 V:lastmonth - BCD LSB first */
687+
/* timestamps follow */
688+
/* 068: 046d dif (32 Bit Integer/Binary Instantaneous value) vif (Date and time type) */
689+
/* 070: 02090F37 ("meter_datetime":"2024-07-15 09:02") */
690+
if (block1->block2.q_mode) {
691+
uint8_t const *b = out->data + BLOCK1A_SIZE - 2; // start of block2
692+
693+
int q_total = bcd2int(b[20]) * 1000000 + bcd2int(b[19]) * 10000 + bcd2int(b[18]) * 100 + bcd2int(b[17]);
694+
int q_lastyear = bcd2int(b[26]) * 1000000 + bcd2int(b[25]) * 10000 + bcd2int(b[24]) * 100 + bcd2int(b[23]);
695+
int q_lastmonth = bcd2int(b[32]) * 1000000 + bcd2int(b[31]) * 10000 + bcd2int(b[30]) * 100 + bcd2int(b[29]);
696+
697+
if (block1->A_DevType == 6) {
698+
/* WarmWater */
699+
// Value factor is 0.001, e.g. 123.456 m3
700+
701+
/* clang-format off */
702+
data = data_dbl(data, "Q_total_m3", "Q_total_m3", "%.3f m3", q_total * 0.001f);
703+
data = data_dbl(data, "Q_lastyear_m3", "Q_lastyear_m3", "%.3f m3", q_lastyear * 0.001f);
704+
data = data_dbl(data, "Q_lastmonth_m3", "Q_lastmonth_m3", "%.3f m3", q_lastmonth * 0.001f);
705+
/* clang-format on */
706+
}
707+
if (block1->A_DevType == 8) {
708+
/* Heat Cost Allocator */
709+
// Value factor is K (from an invoice), e.g. 123456*K kWh
710+
711+
/* clang-format off */
712+
data = data_dbl(data, "Q_total", "Q_total", NULL, q_total);
713+
data = data_dbl(data, "Q_lastyear", "Q_lastyear", NULL, q_lastyear);
714+
data = data_dbl(data, "Q_lastmonth", "Q_lastmonth", NULL, q_lastmonth);
715+
/* clang-format on */
716+
}
717+
718+
return; // do not process the payload any further
719+
}
720+
721+
// standard payload
722+
669723
uint8_t off = block1->block2.pl_offset;
670724
const uint8_t *b = out->data;
671725

@@ -760,14 +814,25 @@ static int parse_block2(const m_bus_data_t *in, m_bus_block1_t *block1)
760814
b2->CW = b[4]<<8 | b[3];
761815
b2->pl_offset = BLOCK1A_SIZE-2 + 5;
762816
}
817+
818+
/* Q-walk_by */
819+
/* 000: CI:0x78 Vendor spec (not used for OMS) */
820+
/* 000: 0x780dff5f Magic for QUNDIS walk_by */
821+
uint32_t ci_magic = ((uint32_t)b[0] << 24) | (b[1] << 16) | (b[2] << 8) | (b[3]);
822+
if (ci_magic == 0x780dff5f) {
823+
b2->AC = b[7];
824+
b2->ST = b[5];
825+
b2->CW = (b[9] << 8) | (b[8]);
826+
b2->pl_offset = BLOCK1A_SIZE - 2 + 8;
827+
b2->q_mode = 1;
828+
}
763829
// fprintf(stderr, "Instantaneous Value: %02x%02x : %f\n",b[9],b[10],((b[10]<<8)|b[9])*0.01);
764830
}
765831
return 0;
766832
}
767833

768834
static int m_bus_decode_format_a(r_device *decoder, const m_bus_data_t *in, m_bus_data_t *out, m_bus_block1_t *block1)
769835
{
770-
771836
// Get Block 1
772837
block1->L = in->data[0];
773838
block1->C = in->data[1];
@@ -883,7 +948,6 @@ static int m_bus_output_data(r_device *decoder, bitbuffer_t *bitbuffer, const m_
883948
"apci", "APCI", DATA_FORMAT, "0x%02X", DATA_INT, block1->block2.apci,
884949
"data_length","Data Length",DATA_INT, out->length,
885950
"data", "Data", DATA_STRING, str_buf,
886-
"mic", "Integrity", DATA_STRING, "CRC",
887951
NULL);
888952
/* clang-format on */
889953
} else {
@@ -900,15 +964,14 @@ static int m_bus_output_data(r_device *decoder, bitbuffer_t *bitbuffer, const m_
900964
// "L", "Length", DATA_INT, block1->L,
901965
"data_length", "Data Length", DATA_INT, out->length,
902966
"data", "Data", DATA_STRING, str_buf,
903-
"mic", "Integrity", DATA_STRING, "CRC",
904967
NULL);
905968
/* clang-format on */
906969
}
907970
if (block1->block2.CI) {
908971
/* clang-format off */
909972
data = data_int(data, "CI", "Control Info", "0x%02X", block1->block2.CI);
910973
data = data_int(data, "AC", "Access number", "0x%02X", block1->block2.AC);
911-
data = data_int(data, "ST", "Device Type", "0x%02X", block1->block2.ST);
974+
data = data_int(data, "ST", "Status", "0x%02X", block1->block2.ST);
912975
data = data_int(data, "CW", "Configuration Word", "0x%04X", block1->block2.CW);
913976
/* clang-format on */
914977
}
@@ -920,6 +983,8 @@ static int m_bus_output_data(r_device *decoder, bitbuffer_t *bitbuffer, const m_
920983
data = data_int(data, "payload_encrypted", "Payload Encrypted", NULL, 1);
921984
/* clang-format on */
922985
}
986+
987+
data = data_str(data, "mic", "Integrity", NULL, "CRC");
923988
decoder_output_data(decoder, data);
924989
return 1;
925990
}

0 commit comments

Comments
 (0)