From 3a18f6c59a4a99d50761b60e399418a9fb14b5c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20=C3=96hrstr=C3=B6m?= Date: Sun, 21 Aug 2022 18:43:45 +0200 Subject: [PATCH] Improved support for amber wmbus device. --- src/wmbus.h | 4 +- src/wmbus_amb8465.cc | 211 +++++++++++++++++++++++++++++++++++++++---- src/wmbus_amb8465.h | 42 ++++++++- src/wmbus_im871a.cc | 21 ++--- 4 files changed, 243 insertions(+), 35 deletions(-) diff --git a/src/wmbus.h b/src/wmbus.h index 8adbced..0ea4439 100644 --- a/src/wmbus.h +++ b/src/wmbus.h @@ -178,8 +178,8 @@ private: LinkModeSet parseLinkModes(string modes); bool isValidLinkModes(string modes); -// A wmbus specified device is supplied on the command line or in the config file. -// It has this format "alias=file:type(id):fq:bps:linkmods:CMD(command)" +// A specified bus device is supplied on the command line or in the config file. +// It has this format "alias=file:type[id](extras):fq:bps:linkmods:CMD(command)" struct SpecifiedDevice { std::string bus_alias; // A bus alias, necessary for C2/T2 meters and mbus. diff --git a/src/wmbus_amb8465.cc b/src/wmbus_amb8465.cc index 9d9a547..85164c7 100644 --- a/src/wmbus_amb8465.cc +++ b/src/wmbus_amb8465.cc @@ -129,7 +129,7 @@ struct ConfigAMB8465 }; /* -Which receive mode can hear which transmit mode: +Which receive mode can hear which transmit mode? 868 MHz @@ -142,9 +142,9 @@ S2 0x03 --> S2 (0x03) T1-Meter 0x05 (to_collector) --> T2-Other (0x08) or T2/C2-Other (0x09) T1-Other 0x06 (to_meter) --> T2-Meter (0x07) T2-Meter 0x07 (to_collector) --> T2-Other (0x08) or T2/C2-Other (0x09) -T2-Other 0x08 (to_collector) --> T2-Meter (0x07) +T2-Other 0x08 (to_meter) --> T2-Meter (0x07) -T2/C2-Other 0x09 (to_collector) --> transmit uses last received mode T2 or C2. +T2/C2-Other 0x09 (to_collector) transmit uses last received mode T2 or C2. --> R2-Meter 0x0A (to_collector) --> R2-Other (0x0B) R2-Other 0x0B (to_meter) --> R2-Meter (0x0A) @@ -173,31 +173,129 @@ N2g 0x0D --> N2f (0x0D) */ -uchar setupBusDeviceToReceiveTelegramsFromMeter(LinkModeSet lms) +uchar setupBusDeviceToReceiveTelegrams(LinkModeSet lms) { if (lms.has(LinkMode::C1) && lms.has(LinkMode::T1)) { // Listening to meter transmissions on C1 and T1. - // Using collector receive mode C2/T2-Other (0x09). - return 0x09; + // Using receive mode C2/T2-Other (0x09). + return (int)LinkModeAMB::C2T2Other; } - else if (lms.has(LinkMode::C1)) + if (lms.has(LinkMode::S1) || lms.has(LinkMode::S1m) || lms.has(LinkMode::S2)) + { + // Listening to S1, S1-m. + // Using collector receive (and bi-directional) mode S2 (0x03). + return (int)LinkModeAMB::S2; + } + if (lms.has(LinkMode::T1)) + { + // Listening to meter transmissions T1 only. + // Using collector receive mode T2-Other (0x08) + return (int)LinkModeAMB::T2Other; + } + if (lms.has(LinkMode::T2)) + { + // Listening to collector transmissions T1 only. + // Using meter receive mode T2-Meter (0x07) + return (int)LinkModeAMB::T2Meter; + } + if (lms.has(LinkMode::C1)) { // Listening to meter transmissions on C1 only. // Using collector receive mode C2-Other (0x0e) - return 0x0E; + return (int)LinkModeAMB::C2Other; } - else if (lms.has(LinkMode::T1)) + if (lms.has(LinkMode::C2)) { - // Listening to meter transmissions T1 only. - // Using collector received mode T2-Other (0x08) - return 0x08; + // Listening to collector transmissions on C1 only. + // Using meter receive mode C2-Meter (0x0d) + return (int)LinkModeAMB::C2Meter; } - else if (lms.has(LinkMode::S1) || lms.has(LinkMode::S1m)) + if (lms.has(LinkMode::N1a)) { - // Listening only to S1 and S1-m - return 0x03; + // Listening to meter transmission N1a. + // Using collector receive mode N2a (0x02). + return (int)LinkModeAMB::N2a; } + if (lms.has(LinkMode::N1b)) + { + // Listening to meter transmission N1b. + // Using collector receive mode N2b (0x04). + return (int)LinkModeAMB::N2b; + } + if (lms.has(LinkMode::N1c)) + { + // Listening to meter transmission N1c. + // Using collector receive mode N2c (0x06). + return (int)LinkModeAMB::N2c; + } + if (lms.has(LinkMode::N1d)) + { + // Listening to meter transmission N1d. + // Using collector receive mode N2d (0x08). + return (int)LinkModeAMB::N2d; + } + if (lms.has(LinkMode::N1e)) + { + // Listening to meter transmission N1e. + // Using collector receive mode N2e (0x0a). + return (int)LinkModeAMB::N2e; + } + if (lms.has(LinkMode::N1f)) + { + // Listening to meter transmission N1f. + // Using collector receive mode N2f (0x0c). + return (int)LinkModeAMB::N2f; + } + + // Error + return 0xff; +} + +uchar setupBusDeviceToSendTelegram(LinkMode lm) +{ + if (lm == LinkMode::S1) + { + // Send S1 telegram using mode S1 (0x01). + return (int)LinkModeAMB::S1; + } + if (lm == LinkMode::S1m) + { + // Send S1 telegram using mode S1m (0x02). + return (int)LinkModeAMB::S1m; + } + if (lm == LinkMode::S2) + { + // Bi-directional communication with meter using mode S2 (0x03). + return (int)LinkModeAMB::S2; + } + if (lm == LinkMode::T1) + { + // Send T1 telegram using mode T1-Meter (0x05). + return (int)LinkModeAMB::T1Meter; + } + if (lm == LinkMode::T2) + { + // Send T2 telegram to meter using mode T2-Other (0x06). + return (int)LinkModeAMB::T2Other; + } + if (lm == LinkMode::C1) + { + // Send C1 telegram using mode C1-Meter (0x0c). + return (int)LinkModeAMB::C1Meter; + } + if (lm == LinkMode::C2) + { + // Send C2 telegram to meter using mode C2-Other (0x0e). + return (int)LinkModeAMB::C2Other; + } + + if (lm == LinkMode::N1a) return (int)LinkModeAMB::N1a; + if (lm == LinkMode::N1b) return (int)LinkModeAMB::N1b; + if (lm == LinkMode::N1c) return (int)LinkModeAMB::N1c; + if (lm == LinkMode::N1d) return (int)LinkModeAMB::N1d; + if (lm == LinkMode::N1e) return (int)LinkModeAMB::N1e; + if (lm == LinkMode::N1f) return (int)LinkModeAMB::N1f; // Error return 0xff; @@ -215,9 +313,11 @@ struct WMBusAmber : public virtual BusDeviceCommonImplementation { return C1_bit | + C2_bit | S1_bit | S1m_bit | - T1_bit; + T1_bit | + T2_bit; } int numConcurrentLinkModes() { return 1; } bool canSetLinkModes(LinkModeSet desired_modes) @@ -253,6 +353,7 @@ private: vector response_; LinkModeSet link_modes_ {}; + uchar last_set_link_mode_ { 0x01 }; bool rssi_expected_ {}; struct timeval timestamp_last_rx_ {}; @@ -418,11 +519,11 @@ bool WMBusAmber::deviceSetLinkModes(LinkModeSet lms) LOCK_WMBUS_EXECUTING_COMMAND(devicesSetLinkModes); - request_.resize(8); + request_.resize(5); request_[0] = AMBER_SERIAL_SOF; request_[1] = CMD_SET_MODE_REQ; request_[2] = 1; // Len - request_[3] = setupBusDeviceToReceiveTelegramsFromMeter(lms); + request_[3] = setupBusDeviceToReceiveTelegrams(lms); request_[4] = xorChecksum(request_, 0, 4); verbose("(amb8465) set link mode %02x\n", request_[3]); @@ -443,6 +544,8 @@ bool WMBusAmber::deviceSetLinkModes(LinkModeSet lms) } link_modes_ = lms; + last_set_link_mode_ = request_[3]; + return rc; } @@ -687,6 +790,50 @@ bool WMBusAmber::sendTelegram(LinkMode lm, TelegramFormat format, vector bool rc = false; + if (serial()->readonly()) return true; // Feeding from stdin or file. + + uchar link_mode = setupBusDeviceToSendTelegram(lm); + + if (link_mode == 0xff) + { + error("(amb8465) setting link mode %s for sending is not supported for amb8465 \n", toString(lm)); + } + + { + // Empty the read buffer we do not want any partial data lying around + // because we expect a response to arrive. + LOCK_WMBUS_RECEIVING_BUFFER(deviceSetLinkMode_ClearBuffer); + read_buffer_.clear(); + } + + if (link_mode != last_set_link_mode_) + { + // Switch to the send link mode. + request_.resize(5); + request_[0] = AMBER_SERIAL_SOF; + request_[1] = CMD_SET_MODE_REQ; + request_[2] = 1; // Len + request_[3] = link_mode; + request_[4] = xorChecksum(request_, 0, 4); + + verbose("(amb8465) set link mode %02x for sending\n", request_[3]); + bool sent = serial()->send(request_); + + if (sent) + { + bool ok = waitForResponse(CMD_SET_MODE_REQ | 0x80); + if (ok) + { + rc = true; + } + else + { + warning("Warning! Did not get confirmation on set link mode for amb8465 for sending\n"); + rc = false; + } + } + } + request_.resize(content.size()+4); request_[0] = AMBER_SERIAL_SOF; request_[1] = CMD_DATA_REQ; @@ -714,6 +861,34 @@ bool WMBusAmber::sendTelegram(LinkMode lm, TelegramFormat format, vector } } + if (link_mode != last_set_link_mode_) + { + // Restore the link mode. + request_.resize(5); + request_[0] = AMBER_SERIAL_SOF; + request_[1] = CMD_SET_MODE_REQ; + request_[2] = 1; // Len + request_[3] = last_set_link_mode_; + request_[4] = xorChecksum(request_, 0, 4); + + verbose("(amb8465) set link mode %02x for restore after sending\n", request_[3]); + bool sent = serial()->send(request_); + + if (sent) + { + bool ok = waitForResponse(CMD_SET_MODE_REQ | 0x80); + if (ok) + { + rc = true; + } + else + { + warning("Warning! Did not get confirmation on set link mode for amb8465 for restore after sending\n"); + rc = false; + } + } + } + return rc; } diff --git a/src/wmbus_amb8465.h b/src/wmbus_amb8465.h index 0130436..d23dbcf 100644 --- a/src/wmbus_amb8465.h +++ b/src/wmbus_amb8465.h @@ -1,4 +1,4 @@ -// Copyright (C) 2019 Fredrik Öhrström (CC0-1.0) +// Copyright (C) 2019-2022 Fredrik Öhrström (CC0-1.0) // Defines documented in the Manual for the AMBER wM-Bus Modules Version 2.7 #define AMBER_SERIAL_SOF 0xFF @@ -21,3 +21,43 @@ #define CMD_SET_AES_KEY_REQ 0x50 #define CMD_CLR_AES_KEY_REQ 0x51 #define CMD_GET_AES_DEV_REQ 0x52 + +// Both 8465 868MHz and 8336 169Mhz link modes. +#define LIST_OF_AMBER_8465_8336_LINK_MODES \ + X(USER_SELECTED,0x00,txrx,All parameters user selected.) \ + X(S1,0x01,tx,Meter transmit once per day long preamble.) \ + X(S1m,0x02,tx,Meter transmit once per day short preamble.) \ + X(S2,0x03,txrx,Collector receive mode for S1 and S1-m. Might need tuning of preamble.) \ + X(Reserved,0x04,reserved,) \ + X(T1Meter,0x05,tx,Meter transmits often.) \ + X(T1Other,0x06,tx,Collector transmits to meter.) \ + X(T2Meter,0x07,txrx,Meter bi-directional setting.) \ + X(T2Other,0x08,txrx,Collector bi-directional setting.) \ + X(C2T2Other,0x09,txrx,Collector receive C/T send using latest received type.) \ + X(R2Meter,0x0a,txrx,Meter bi-directional.) \ + X(R2Other,0x0b,txrx,Collector bi-directional.) \ + X(C1Meter,0x0c,tx,Meter transmits often more energy efficient that T.) \ + X(C2Meter,0x0d,txrx,Meter bi-directional setting.) \ + X(C2Other,0x0e,txrx,Collector bi-directional setting.) \ + X(PinSelect,0x0f,pinselect,) \ + X(N1a,0x01,txrx,) \ + X(N2a,0x02,txrx,) \ + X(N1b,0x03,txrx,) \ + X(N2b,0x04,txrx,) \ + X(N1c,0x05,txrx,) \ + X(N2c,0x06,txrx,) \ + X(N1d,0x07,txrx,) \ + X(N2d,0x08,txrx,) \ + X(N1e,0x09,txrx,) \ + X(N2e,0x0a,txrx,) \ + X(N1f,0x0b,txrx,) \ + X(N2f,0x0c,txrx,) \ + X(UNKNOWN,0xff,unknown,) + +enum class LinkModeAMB { +#define X(name,num,sendrcv,info) name = num, +LIST_OF_AMBER_8465_8336_LINK_MODES +#undef X +}; + +string toString(LinkModeAMB lm); diff --git a/src/wmbus_im871a.cc b/src/wmbus_im871a.cc index a00d17d..d05ae00 100644 --- a/src/wmbus_im871a.cc +++ b/src/wmbus_im871a.cc @@ -160,19 +160,6 @@ LIST_OF_IM871A_LINK_MODES return "unknown"; } -/* - -Which receive mode can hear which transmit mode: - -868 MHz - -Transmit Receive ------------------------------------------------------------------------- - -S1 (to_collector) --> - -*/ - struct WMBusIM871aIM170A : public virtual BusDeviceCommonImplementation { bool ping(); @@ -188,9 +175,11 @@ struct WMBusIM871aIM170A : public virtual BusDeviceCommonImplementation { return C1_bit | + C2_bit | S1_bit | S1m_bit | - T1_bit; + T1_bit | + T2_bit; } else { @@ -567,12 +556,16 @@ bool WMBusIM871aIM170A::deviceSetLinkModes(LinkModeSet lms) request_[6] = (int)LinkModeIM871A::CT_N1A; } else if (lms.has(LinkMode::C1)) { request_[6] = (int)LinkModeIM871A::C1a; + } else if (lms.has(LinkMode::C2)) { + request_[6] = (int)LinkModeIM871A::C2b; } else if (lms.has(LinkMode::S1)) { request_[6] = (int)LinkModeIM871A::S1; } else if (lms.has(LinkMode::S1m)) { request_[6] = (int)LinkModeIM871A::S1m; } else if (lms.has(LinkMode::T1)) { request_[6] = (int)LinkModeIM871A::T1; + } else if (lms.has(LinkMode::T2)) { + request_[6] = (int)LinkModeIM871A::T2; } else if (lms.has(LinkMode::N1a)) { request_[6] = (int)LinkModeIM871A::CT_N1A; } else if (lms.has(LinkMode::N1b)) {