diff --git a/README.md b/README.md index 88e11cf..e4ef2a5 100644 --- a/README.md +++ b/README.md @@ -291,6 +291,12 @@ These telegrams are expected to have the data link layer crc bytes removed alrea `telegrams.bin`, to read raw wmbus telegrams from this file. These telegrams are expected to have the data link layer crc bytes removed already! +`stdin:hex`, to read hex characters wbus telegrams from stdin. +These telegrams are expected to have the data link layer crc bytes removed laready! + +`telegrams.txt:hex`, to read hex characters wmbus telegrams from this file. +These telegrams are expected to have the data link layer crc bytes removed already! + `stdin:rtlwmbus`, to read telegrams formatted using the rtlwmbus format from stdin. Works for rtl433 as well. `telegrams.msg:rtlwmbus`, to read rtlwmbus formatted telegrams from this file. Works for rtl433 as well. @@ -536,9 +542,9 @@ wmbusmeters --format=json --meterfiles /dev/ttyUSB0:im871a:c1 MyTapWater multica rtl_sdr -f 868.625M -s 1600000 - 2>/dev/null | rtl_wmbus -s | wmbusmeters --format=json stdin:rtlwmbus MyMeter auto 12345678 NOKEY | ...more processing... ``` -# Decoding a telegram supplied on the command line as a hex string +# Decoding hex string telegrams -If you have a single telegram you want decoded, you do not need to create a simulation file, +If you have a single telegram as hex, which you want decoded, you do not need to create a simulation file, you can just supply the telegram as a hex string on the command line. ```shell @@ -567,6 +573,16 @@ which will output: {"media":"other","meter":"lansenpu","name":"MyCounter","id":"00010206","counter_a_int":4711,"counter_b_int":1234,"timestamp":"2021-09-12T08:45:52Z"} ``` +You can also pipe the hex into wmbusmeters like this: + +```shell +echo 234433300602010014007a8e0000002f2f0efd3a1147000000008e40fd3a341200000000 | ./build/wmbusmeters --silent --format=json stdin:hex MyCounter auto 00010206 NOKEY +``` + +or read hex data from a a file, `wmbusmeters myfile.txt:hex` + +Any non-hex characters are ignored when using the suffix `:hex`. The hex string must be proper +with no spaces nor bad characters, when supplied on the command line. # Additional tools diff --git a/src/bus.cc b/src/bus.cc index d9c787c..735d5d5 100644 --- a/src/bus.cc +++ b/src/bus.cc @@ -213,6 +213,10 @@ shared_ptr BusManager::createWmbusObject(Detected *detected, Configuratio verbose("(rawtty) on %s\n", detected->found_file.c_str()); wmbus = openRawTTY(*detected, serial_manager_, serial_override); break; + case DEVICE_HEXTTY: + verbose("(hextty) on %s\n", detected->found_file.c_str()); + wmbus = openHexTTY(*detected, serial_manager_, serial_override); + break; case DEVICE_RTLWMBUS: wmbus = openRTLWMBUS(*detected, config->bin_dir, config->daemon, serial_manager_, serial_override); break; diff --git a/src/util.cc b/src/util.cc index 4a60cb6..d9a8d84 100644 --- a/src/util.cc +++ b/src/util.cc @@ -141,6 +141,11 @@ int char2int(char input) return -1; } +bool isHexChar(uchar c) +{ + return char2int(c) != -1; +} + // The byte 0x13 i converted into the integer value 13. uchar bcd2bin(uchar c) { diff --git a/src/util.h b/src/util.h index 24bc6fb..345d97b 100644 --- a/src/util.h +++ b/src/util.h @@ -39,6 +39,9 @@ typedef unsigned char uchar; uchar bcd2bin(uchar c); uchar revbcd2bin(uchar c); uchar reverse(uchar c); + +bool isHexChar(uchar c); + bool isHexString(const char* txt, bool *invalid); bool isHexString(const std::string &txt, bool *invalid); bool hex2bin(const char* src, std::vector *target); diff --git a/src/wmbus.cc b/src/wmbus.cc index 7d9a467..1adc878 100644 --- a/src/wmbus.cc +++ b/src/wmbus.cc @@ -4187,7 +4187,7 @@ FrameStatus checkWMBusFrame(vector &data, // Ugly: 00615B2A442D2C998734761B168D2021D0871921|58387802FF2071000413F81800004413F8180000615B // Here the frame is prefixed with some random data. - debugPayload("(wmbus) checkWMBUSFrame\n", data); + debugPayload("(wmbus) checkWMBUSFrame", data); if (data.size() < 11) { @@ -4447,6 +4447,7 @@ bool is_type_id_extras(string t, WMBusDeviceType *out_type, string *out_id, stri // im871a im871a[12345678] im871a(foo=123) // auto // rtlwmbus rtlwmbus[plast123] rtlwmbus(device extras) rtlwmbus[hej](extras) + // hex if (t == "auto") { *out_type = WMBusDeviceType::DEVICE_AUTO; @@ -4454,6 +4455,13 @@ bool is_type_id_extras(string t, WMBusDeviceType *out_type, string *out_id, stri return true; } + if (t == "hex") + { + *out_type = WMBusDeviceType::DEVICE_HEXTTY; + *out_id = ""; + return true; + } + size_t bs = t.find('['); size_t be = t.find(']'); diff --git a/src/wmbus.h b/src/wmbus.h index 9650c00..f5a231f 100644 --- a/src/wmbus.h +++ b/src/wmbus.h @@ -39,6 +39,7 @@ bool trimCRCsFrameFormatB(std::vector &payload); X(IM871A,im871a,true,false,detectIM871AIM170A) \ X(IM170A,im170a,true,false,detectSKIP) \ X(RAWTTY,rawtty,true,false,detectRAWTTY) \ + X(HEXTTY,hextty,true,false,detectSKIP) \ X(RC1180,rc1180,true,false,detectRC1180) \ X(RTL433,rtl433,false,true,detectRTL433) \ X(RTLWMBUS,rtlwmbus,false,true,detectRTLWMBUS) \ @@ -632,6 +633,9 @@ shared_ptr openAMB8465(Detected detected, shared_ptr openRawTTY(Detected detected, shared_ptr manager, shared_ptr serial_override); +shared_ptr openHexTTY(Detected detected, + shared_ptr manager, + shared_ptr serial_override); shared_ptr openMBUS(Detected detected, shared_ptr manager, shared_ptr serial_override); diff --git a/src/wmbus_rawtty.cc b/src/wmbus_rawtty.cc index 94758d4..11973da 100644 --- a/src/wmbus_rawtty.cc +++ b/src/wmbus_rawtty.cc @@ -43,19 +43,24 @@ struct WMBusRawTTY : public virtual WMBusCommonImplementation void processSerialData(); void simulate() { } - WMBusRawTTY(string bus_alias, shared_ptr serial, shared_ptr manager); + WMBusRawTTY(string bus_alias, shared_ptr serial, + shared_ptr manager, bool use_hex); ~WMBusRawTTY() { } private: + void copy(vector *from, vector *to); + vector read_buffer_; + vector data_buffer_; LinkModeSet link_modes_; vector received_payload_; }; -shared_ptr openRawTTY(Detected detected, - shared_ptr manager, - shared_ptr serial_override) +shared_ptr openRawTTYInternal(Detected detected, + shared_ptr manager, + shared_ptr serial_override, + bool use_hex) { string bus_alias = detected.specified_device.bus_alias; string device = detected.found_file; @@ -65,17 +70,32 @@ shared_ptr openRawTTY(Detected detected, if (serial_override) { - WMBusRawTTY *imp = new WMBusRawTTY(bus_alias, serial_override, manager); + WMBusRawTTY *imp = new WMBusRawTTY(bus_alias, serial_override, manager, use_hex); imp->markAsNoLongerSerial(); return shared_ptr(imp); } - auto serial = manager->createSerialDeviceTTY(device.c_str(), bps, PARITY::NONE, "rawtty"); - WMBusRawTTY *imp = new WMBusRawTTY(bus_alias, serial, manager); + auto serial = manager->createSerialDeviceTTY(device.c_str(), bps, PARITY::NONE, use_hex?"hextty":"rawtty"); + WMBusRawTTY *imp = new WMBusRawTTY(bus_alias, serial, manager, use_hex); return shared_ptr(imp); } -WMBusRawTTY::WMBusRawTTY(string bus_alias, shared_ptr serial, shared_ptr manager) : - WMBusCommonImplementation(bus_alias, DEVICE_RAWTTY, manager, serial, true) +shared_ptr openRawTTY(Detected detected, + shared_ptr manager, + shared_ptr serial_override) +{ + return openRawTTYInternal(detected, manager, serial_override, false); +} + +shared_ptr openHexTTY(Detected detected, + shared_ptr manager, + shared_ptr serial_override) +{ + return openRawTTYInternal(detected, manager, serial_override, true); +} + +WMBusRawTTY::WMBusRawTTY(string bus_alias, shared_ptr serial, + shared_ptr manager, bool use_hex) : + WMBusCommonImplementation(bus_alias, use_hex?DEVICE_HEXTTY:DEVICE_RAWTTY, manager, serial, true) { reset(); } @@ -107,6 +127,59 @@ void WMBusRawTTY::deviceSetLinkModes(LinkModeSet lms) { } +void WMBusRawTTY::copy(vector *from, vector *to) +{ + if (type() == WMBusDeviceType::DEVICE_RAWTTY) + { + // We expect binary bytes incoming. + to->insert(to->end(), from->begin(), from->end()); + debug("copied %zu binary bytes\n", from->size()); + from->clear(); + return; + } + + if (type() == WMBusDeviceType::DEVICE_HEXTTY) + { + // We expect hex chars incoming. Everything else is thrown away. + vector hex; + int num_hex = 0; + int num_other = 0; + for (uchar c : *from) + { + if (isHexChar(c)) + { + hex.push_back(c); // Just ignore any non-hex chars! + num_hex++; + } + else + { + num_other++; + } + } + debug("found %d hex chars and %d other bytes\n", num_hex, num_other); + from->clear(); + if (hex.size() > 0) + { + if (hex.size() % 2 == 1) + { + // An odd hexadecimal char at the end! + // Save it for later! + from->push_back(hex.back()); + hex.pop_back(); + } + // We now have an even number of hex chars to work with! + vector bin; + bool ok = hex2bin(hex, &bin); + assert(ok); + debug("converted %zu hex bytes into %zu binary bytes.\n", hex.size(), bin.size()); + to->insert(to->end(), bin.begin(), bin.end()); + } + return; + } + + assert(0); +} + void WMBusRawTTY::processSerialData() { vector data; @@ -116,12 +189,14 @@ void WMBusRawTTY::processSerialData() read_buffer_.insert(read_buffer_.end(), data.begin(), data.end()); + copy(&read_buffer_, &data_buffer_); + size_t frame_length; int payload_len, payload_offset; for (;;) { - FrameStatus status = checkWMBusFrame(read_buffer_, &frame_length, &payload_len, &payload_offset); + FrameStatus status = checkWMBusFrame(data_buffer_, &frame_length, &payload_len, &payload_offset); if (status == PartialFrame) { @@ -131,9 +206,9 @@ void WMBusRawTTY::processSerialData() if (status == ErrorInFrame) { verbose("(rawtty) protocol error in message received!\n"); - string msg = bin2hex(read_buffer_); + string msg = bin2hex(data_buffer_); debug("(rawtty) protocol error \"%s\"\n", msg.c_str()); - read_buffer_.clear(); + data_buffer_.clear(); break; } if (status == FullFrame) @@ -143,9 +218,9 @@ void WMBusRawTTY::processSerialData() { uchar l = payload_len; payload.insert(payload.end(), &l, &l+1); // Re-insert the len byte. - payload.insert(payload.end(), read_buffer_.begin()+payload_offset, read_buffer_.begin()+payload_offset+payload_len); + payload.insert(payload.end(), data_buffer_.begin()+payload_offset, data_buffer_.begin()+payload_offset+payload_len); } - read_buffer_.erase(read_buffer_.begin(), read_buffer_.begin()+frame_length); + data_buffer_.erase(data_buffer_.begin(), data_buffer_.begin()+frame_length); AboutTelegram about("", 0, FrameType::WMBUS); handleTelegram(about, payload); } diff --git a/tests/test_hex_cmdline.sh b/tests/test_hex_cmdline.sh index 3dd7131..426c089 100755 --- a/tests/test_hex_cmdline.sh +++ b/tests/test_hex_cmdline.sh @@ -78,3 +78,30 @@ else echo "=======================" cat $TEST/test_stderr.txt fi + + +TESTNAME="Test hex on stdin" + +cat > $TEST/test_expected.txt < $TEST/test_output.txt 2> $TEST/test_stderr.txt + +if [ "$?" = "0" ] +then + cat $TEST/test_output.txt | sed 's/"timestamp":"....-..-..T..:..:..Z"/"timestamp":"1111-11-11T11:11:11Z"/' > $TEST/test_responses.txt + diff $TEST/test_expected.txt $TEST/test_responses.txt + if [ "$?" = "0" ] + then + echo OK: $TESTNAME + TESTRESULT="OK" + fi +else + echo "wmbusmeters returned error code: $?" + cat $TEST/test_output.txt + cat $TEST/test_stderr.txt +fi