From 3018ae9aef5844ea7e2994ee3802b8293eb8fe27 Mon Sep 17 00:00:00 2001 From: Peter Shipley <peter.shipley@gmail.com> Date: Wed, 24 Jun 2020 13:14:43 -0700 Subject: [PATCH] Scmplus (#1410) Added SCM+ decoder. --- README.md | 1 + include/rtl_433_devices.h | 1 + man/man1/rtl_433.1 | 3 + src/CMakeLists.txt | 1 + src/Makefile.am | 1 + src/devices/scmplus.c | 200 +++++++++++++++++++++++++++++++++++ vs15/rtl_433.vcxproj | 1 + vs15/rtl_433.vcxproj.filters | 3 + 8 files changed, 211 insertions(+) create mode 100644 src/devices/scmplus.c diff --git a/README.md b/README.md index 49565aed..32496125 100644 --- a/README.md +++ b/README.md @@ -227,6 +227,7 @@ See [CONTRIBUTING.md](./docs/CONTRIBUTING.md). [151] Visonic powercode [152] Eurochron EFTH-800 temperature and humidity sensor [153] Cotech 36-7959 wireless weather station with USB + [154] Standard Consumption Message Plus (SCMplus) * Disabled by default, use -R n or -G diff --git a/include/rtl_433_devices.h b/include/rtl_433_devices.h index 1d9c36d0..5db2de61 100644 --- a/include/rtl_433_devices.h +++ b/include/rtl_433_devices.h @@ -161,6 +161,7 @@ DECL(visonic_powercode) \ DECL(eurochron_efth800) \ DECL(cotech_36_7959) \ + DECL(scmplus) \ /* Add new decoders here. */ diff --git a/man/man1/rtl_433.1 b/man/man1/rtl_433.1 index 42c66008..51432e11 100644 --- a/man/man1/rtl_433.1 +++ b/man/man1/rtl_433.1 @@ -580,6 +580,9 @@ Eurochron EFTH\-800 temperature and humidity sensor .TP [ \fB153\fI\fP ] Cotech 36\-7959 wireless weather station with USB +.TP +[ \fB154\fI\fP ] +Standard Consumption Message Plus (SCMplus) * Disabled by default, use \-R n or \-G .SS "Input device selection" diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 55599626..d496187e 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -129,6 +129,7 @@ add_library(r_433 STATIC devices/rubicson_48659.c devices/s3318p.c devices/schraeder.c + devices/scmplus.c devices/silvercrest.c devices/simplisafe.c devices/smoke_gs558.c diff --git a/src/Makefile.am b/src/Makefile.am index defe1efa..ad934928 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -128,6 +128,7 @@ rtl_433_SOURCES = abuf.c \ devices/rubicson_48659.c \ devices/s3318p.c \ devices/schraeder.c \ + devices/scmplus.c \ devices/silvercrest.c \ devices/simplisafe.c \ devices/smoke_gs558.c \ diff --git a/src/devices/scmplus.c b/src/devices/scmplus.c new file mode 100644 index 00000000..b5f318e9 --- /dev/null +++ b/src/devices/scmplus.c @@ -0,0 +1,200 @@ +/** @file + ERT SCM+ sensors. + + Copyright (C) 2020 Peter Shipley <peter.shipley@gmail.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. +*/ + +// #include <arpa/inet.h> +#include "decoder.h" + +/** + +Freq 912600155 + +Random information: + +https://github.com/bemasher/rtlamr/wiki/Protocol +http://www.gridinsight.com/community/documentation/itron-ert-technology/ + +Units: "Some meter types transmit consumption in 1 kWh units, while others use more granular 10 Wh units" + + +*/ + +static int scmplus_callback(r_device *decoder, bitbuffer_t *bitbuffer) +{ + uint8_t b[16]; + data_t *data; + unsigned sync_index; + const uint8_t scmplus_frame_sync[] = {0x16, 0xA3, 0x1E}; + + if (bitbuffer->bits_per_row[0] < 128) { + return (DECODE_ABORT_LENGTH); + } + + sync_index = bitbuffer_search(bitbuffer, 0, 0, scmplus_frame_sync, 24); + + if (sync_index >= bitbuffer->bits_per_row[0]) { + return DECODE_ABORT_EARLY; + } + + if ( (bitbuffer->bits_per_row[0] - sync_index) < 128) { + return DECODE_ABORT_LENGTH; + } + + if (decoder->verbose) { + fprintf(stderr, "%s: row len=%hu\n", __func__, bitbuffer->bits_per_row[0]); + fprintf(stderr, "%s: sync_index=%d\n", __func__, sync_index); + } + + // bitbuffer_debug(bitbuffer); + bitbuffer_extract_bytes(bitbuffer, 0, sync_index, b, 16 * 8); + + + // uint32_t t_16; // temp vars + // uint32_t t_32; + + uint16_t crc, pkt_checksum; + // memcpy(&t_16, &b[14], 2); + // pkt_checksum = ntohs(t_16); + pkt_checksum = (b[14] << 8 | b[15]); + + + crc = crc16(&b[2], 12, 0x1021, 0x0971); + // fprintf(stderr, "CRC = %d %04X == %d %04X\n", pkt_checksum,pkt_checksum, crc, crc); + if (crc != pkt_checksum) { + return DECODE_FAIL_MIC; + } + + if (decoder->verbose) { // print bytes with aligned offset + bitrow_printf(b, 16 * 8, "%s bitrow_printf", __func__); + } + + // uint8_t protocol_id; + uint32_t endpoint_id; + // uint8_t endpoint_type; + uint32_t consumption_data; + uint16_t physical_tamper; + + char crc_str[8]; + char protocol_id_str[5]; + char endpoint_type_str[5]; + char physical_tamper_str[8]; + + // protocol_id = b[2]; + snprintf(protocol_id_str, sizeof(protocol_id_str), "0x%02X", b[2]); // protocol_id); // b[2] + + // endpoint_type = b[3]; + snprintf(endpoint_type_str, sizeof(endpoint_type_str), "0x%02X", b[3]); // endpoint_type); // b[3] + + // memcpy(&t_32, &b[4], 4); + // endpoint_id = ntohl(t_32); + endpoint_id = ((uint32_t)b[4] << 24) | (b[5] << 16) | (b[6] << 8) | (b[7]); + + // memcpy(&t_32, &b[8], 4); + // consumption_data = ntohl(t_32); + consumption_data = ((uint32_t)b[8] << 24) | (b[9] << 16) | (b[10] << 8) | (b[11]); + + // memcpy(&t_16, &b[12], 2); + // physical_tamper = ntohs(t_16); + // physical_tamper = ((t_16 & 0xFF00) >> 8 | (t_16 & 0x00FF) << 8); + physical_tamper = (b[12] << 8 | b[13]); + snprintf(physical_tamper_str, sizeof(physical_tamper_str), "0x%04X", physical_tamper); + + snprintf(crc_str, sizeof(crc_str), "0x%04X", crc); + + /* + if (decoder->verbose && 0) { + fprintf(stderr, "protocol_id = %d %02X\n", protocol_id,protocol_id); + bitrow_printf(&b[3], 8, "%s\t%2d\t%02X\t", "endpoint_type ", endpoint_type, endpoint_type); + bitrow_printf(&b[4], 32, "%s\t%2d\t%02X\t", "endpoint_id ", endpoint_id, endpoint_id); + bitrow_printf(&b[8], 32, "%s\t%2d\t%02X\t", "consumption_data", consumption_data, consumption_data); + // fprintf(stderr, "consumption_data = %d %08X\n", consumption_data,consumption_data); + fprintf(stderr, "physical_tamper = %d %04X\n", physical_tamper,physical_tamper); + fprintf(stderr, "pkt_checksum = %d %04X\n", pkt_checksum,pkt_checksum); + } + */ + + // Least significant nibble of endpoint_type is equivalent to SCM's endpoint type field + // id info from https://github.com/bemasher/rtlamr/wiki/Compatible-Meters + char *meter_type; + switch (b[3] & 0x0f) { + case 4: + case 5: + case 7: + case 8: + meter_type = "Electric"; + break; + case 2: + case 9: + case 12: + meter_type = "Gas"; + break; + case 11: + case 13: + meter_type = "Water"; + break; + default: + meter_type = "unknown"; + break; + } + // fprintf(stderr, "meter_type = %s\n", meter_type); + + /* + Field key names and format set to match rtlamr field names + + {Time:2020-06-20T09:58:19.074 Offset:49152 Length:49152 + SCM+:{ProtocolID:0x1E EndpointType:0xAB EndpointID: 68211547 Consumption: 6883 Tamper:0x4900 PacketCRC:0x39BE}} + */ + + /* clang-format off */ + data = data_make( + "model", "", DATA_STRING, "SCM+", + "ProtocolID", "Protocol_ID", DATA_STRING, protocol_id_str, + "EndpointType", "Endpoint_Type", DATA_STRING, endpoint_type_str, + "EndpointID", "Endpoint_ID", DATA_INT, endpoint_id, + "Consumption", "", DATA_INT, consumption_data, + "Tamper", "", DATA_STRING, physical_tamper_str, + "PacketCRC", "crc", DATA_STRING, crc_str, + "MeterType", "Meter_Type", DATA_STRING, meter_type, + "mic", "Integrity", DATA_STRING, "CRC", + NULL); + /* clang-format on */ + + + decoder_output_data(decoder, data); + return 1; +} + +static char *output_fields[] = { + "model", + "ProtocolID", + "EndpointType", + "EndpointID", + "Consumption", + "Tamper", + "PacketCRC", + "MeterType", + "mic", + NULL, +}; + +// Freq 912600155 +// -X n=L58,m=OOK_MC_ZEROBIT,s=30,l=30,g=20000,r=20000,match={24}0x16a31e,preamble={1}0x00 + +r_device scmplus = { + .name = "Standard Consumption Message Plus (SCMplus)", + .modulation = OOK_PULSE_MANCHESTER_ZEROBIT, + .short_width = 30, + .long_width = 30, + .gap_limit = 0, + .reset_limit = 64, + .decode_fn = &scmplus_callback, + .disabled = 0, + .fields = output_fields, +}; diff --git a/vs15/rtl_433.vcxproj b/vs15/rtl_433.vcxproj index c3d724b6..f950de0a 100644 --- a/vs15/rtl_433.vcxproj +++ b/vs15/rtl_433.vcxproj @@ -252,6 +252,7 @@ COPY ..\..\libusb\MS64\dll\libusb*.dll $(TargetDir)</Command> <ClCompile Include="..\src\devices\rubicson_48659.c" /> <ClCompile Include="..\src\devices\s3318p.c" /> <ClCompile Include="..\src\devices\schraeder.c" /> + <ClCompile Include="..\src\devices\scmplus.c" /> <ClCompile Include="..\src\devices\silvercrest.c" /> <ClCompile Include="..\src\devices\simplisafe.c" /> <ClCompile Include="..\src\devices\smoke_gs558.c" /> diff --git a/vs15/rtl_433.vcxproj.filters b/vs15/rtl_433.vcxproj.filters index f5aa3bff..f718cbde 100644 --- a/vs15/rtl_433.vcxproj.filters +++ b/vs15/rtl_433.vcxproj.filters @@ -493,6 +493,9 @@ <ClCompile Include="..\src\devices\schraeder.c"> <Filter>Source Files\devices</Filter> </ClCompile> + <ClCompile Include="..\src\devices\scmplus.c"> + <Filter>Source Files\devices</Filter> + </ClCompile> <ClCompile Include="..\src\devices\silvercrest.c"> <Filter>Source Files\devices</Filter> </ClCompile>