kopia lustrzana https://github.com/weetmuts/wmbusmeters
commit
c5fc8e4336
1
Makefile
1
Makefile
|
|
@ -177,6 +177,7 @@ PROG_OBJS:=\
|
|||
$(BUILD)/wmbus.o \
|
||||
$(BUILD)/wmbus_amb8465.o \
|
||||
$(BUILD)/wmbus_im871a.o \
|
||||
$(BUILD)/wmbus_iu891a.o \
|
||||
$(BUILD)/wmbus_cul.o \
|
||||
$(BUILD)/wmbus_rtlwmbus.o \
|
||||
$(BUILD)/wmbus_rtl433.o \
|
||||
|
|
|
|||
43
README.md
43
README.md
|
|
@ -90,7 +90,7 @@ Read the wiki for more info on how to use the snap: https://wmbusmeters.github.i
|
|||
|
||||
Building and installing from source is easy and recommended since the
|
||||
development progresses quickly. First remove the wmbus dongle
|
||||
(im871a,amb8465(metis),amb3665,cul,rc1180) or the generic rtlsdr dongle (RTL2832U)
|
||||
(im871a,iu891a,amb8465(metis),amb3665,cul,rc1180) or the generic rtlsdr dongle (RTL2832U)
|
||||
from your computer. Then do:
|
||||
|
||||
`./configure; make; sudo make install` will install wmbusmeters as a daemon.
|
||||
|
|
@ -98,7 +98,7 @@ from your computer. Then do:
|
|||
# Usage
|
||||
|
||||
Check the contents of your `/etc/wmbusmeters.conf` file, assuming it
|
||||
has `device=auto:t1` and you are using a im871a,amb8465(metis),amb3665,rc1180,cul or rtlsdr device,
|
||||
has `device=auto:t1` and you are using a im871a,iu891a,amb8465(metis),amb3665,rc1180,cul or rtlsdr device,
|
||||
then you can now start the daemon with `sudo systemctl start wmbusmeters`
|
||||
or you can try it from the command line `wmbusmeters auto:t1`
|
||||
|
||||
|
|
@ -108,11 +108,11 @@ several dongle types, the scan can take some time!
|
|||
|
||||
Use `auto` for testing and to find your dongle. For production it is very much
|
||||
recommended that you change `auto:t1` to the device name with the full device path
|
||||
(eg `/dev/ttyUSB0:im871a:c1,t1`). This will skip the slow probing for all possible
|
||||
(eg `/dev/ttyAMA0:iu891:c1,t1`). This will skip the slow probing for all possible
|
||||
wmbus dongles when wmbusmeters startup.
|
||||
|
||||
If the serial device (ttyUSB0) might change you can also use `device=im871a:c1,t1`
|
||||
which will probe all serial devices but only scans for im871a which also speeds it up.
|
||||
If the serial device (ttyUSB0) might change you can also use `device=iu891:c1,t1`
|
||||
which will probe all serial devices but only scans for im891a which also speeds it up.
|
||||
|
||||
Note that the rtl-sdr devices are not found under the tty devices (e.g. `/dev/tty...`).
|
||||
Instead the rtl-sdr devices are accessed through character device special files named `/dev/swradio0` to `/dev/swradio255`[^kernel_docs_sdr]. Wmbusmeters uses librtsldr to probe these devices.
|
||||
|
|
@ -139,26 +139,26 @@ When using useconfig, the files/dir should be:
|
|||
`/home/me/.config/wmbusmeters/wmbusmeters.d`
|
||||
|
||||
Check the config file /etc/wmbusmeters.conf and edit the device. For example:
|
||||
`/dev/ttyUSB1:amb8465:c1,t1` or `im871a:c1,t1` or `im871a[457200101056]:t1`.
|
||||
`/dev/ttyUSB1:amb8465:c1,t1` or `iu891:c1,t1` or `iu891a[457200101056]:t1`.
|
||||
|
||||
Adding a device like auto or im871a will trigger an automatic probe of all serial ttys
|
||||
to auto find or to find on which tty the im871a resides.
|
||||
Adding a device like auto or iu891a will trigger an automatic probe of all serial ttys
|
||||
to auto find or to find on which tty the iu891a resides.
|
||||
|
||||
If you specify a full device path like `/dev/ttyUSB0:im871a:c1` or `rtlwmbus` or `rtl433`
|
||||
If you specify a full device path like `/dev/ttyUSB0:iu891a:c1` or `rtlwmbus` or `rtl433`
|
||||
then it will not probe the serial devices. If you must be really sure that it will not probe something
|
||||
you can add `donotprobe=/dev/ttyUSB0` or `donotprobe=all`.
|
||||
|
||||
You can specify combinations like: `device=rc1180:t1` `device=auto:c1`
|
||||
to set the rc1180 dongle to t1 but any other auto-detected dongle to c1.
|
||||
|
||||
Some dongles have identifiers (im871a,amb8465(metis),amb3665 and rtlsdrs) (for example: rtlsdr can be set with `rtl_eeprom -s myname`)
|
||||
Some dongles have identifiers (im871a,iu891a,amb8465(metis),amb3665 and rtlsdrs) (for example: rtlsdr can be set with `rtl_eeprom -s myname`)
|
||||
You might have two rtlsdr dongles, one attached to an antenna tuned to 433MHz and the other
|
||||
attached to an antenna tuned for 868.95MHz, then a more complicated setup could look like this:
|
||||
|
||||
```
|
||||
device=rtlwmbus[555555]:433M
|
||||
device=rtlwmbus[112233]
|
||||
device=/dev/ttyUSB0:im871a[00102759]:c1,t1
|
||||
device=/dev/ttyUSB0:iu891a[00102759]:c1,t1
|
||||
device=/dev/ttyUSB1:rc1180:t1
|
||||
```
|
||||
|
||||
|
|
@ -169,9 +169,9 @@ here we pick the bus alias MAIN for the mbus using 2400 bps for all meters on th
|
|||
```
|
||||
MAIN=/dev/ttyUSB0:mbus:2400
|
||||
```
|
||||
and here we pick the bus alias RADIOMAIN for an im871a dongle:
|
||||
and here we pick the bus alias RADIOMAIN for an iu891a dongle:
|
||||
```
|
||||
RADIOMAIN=/dev/ttyUSB1:im871a:c2
|
||||
RADIOMAIN=/dev/ttyUSB1:iu891a:c2
|
||||
```
|
||||
|
||||
The bus alias is then used in the meter driver specification to specify which
|
||||
|
|
@ -192,7 +192,7 @@ wmbusmeters --pollinterval=60s MAIN=/dev/ttyUSB0:mbus:2400 MyTempMeter piigth:MA
|
|||
loglevel=normal
|
||||
# You can use auto:t1 to find the device you have connected to your system.
|
||||
# But do not use auto here since it will cause unnecessary and slow probing of the serial ports.
|
||||
device=/dev/ttyUSB0:im871a:c1,t1
|
||||
device=/dev/ttyUSB0:iu891a:c1,t1
|
||||
# And mbus
|
||||
device=MAIN=/dev/ttyUSB1:mbus:2400
|
||||
# But do not probe this serial tty.
|
||||
|
|
@ -234,7 +234,7 @@ pollinterval=60s
|
|||
You can use `driver=auto` to have wmbusmeters automatically detect
|
||||
and use the best driver for your meter, but you should >not< use auto in production.
|
||||
|
||||
You can find out which driver is recommended by running `wmbusmeters im871a:t1`.
|
||||
You can find out which driver is recommended by running `wmbusmeters iu891a:t1`.
|
||||
This will print information like:
|
||||
```
|
||||
Received telegram from: 71727374
|
||||
|
|
@ -518,20 +518,20 @@ As {options} you can use:
|
|||
|
||||
As device you can use:
|
||||
|
||||
`auto:c1`, to have wmbusmeters probe for devices: im871a, amb8465(metis), amb3665, cul, rc1180 or rtlsdr (spawns rtlwmbus).
|
||||
`auto:c1`, to have wmbusmeters probe for devices: im871a, iu891a, amb8465(metis), amb3665, cul, rc1180 or rtlsdr (spawns rtlwmbus).
|
||||
|
||||
`im871a:c1` to start all connected *im871a* devices in *c1* mode, ignore all other devices.
|
||||
`iu891a:c1` to start all connected *iu891a* devices in *c1* mode, ignore all other devices.
|
||||
|
||||
`/dev/ttyUSB1:amb8465:c1` to start only this device on this tty. Do not probe for other devices.
|
||||
|
||||
If you have two im871a you can supply both of them with their unique id:s and set different listening modes:
|
||||
`im871a[12345678]:c1` `im871a[11223344]:t1`
|
||||
If you have two iu891a you can supply both of them with their unique id:s and set different listening modes:
|
||||
`iu891a[12345678]:c1` `iu891a[11223344]:t1`
|
||||
|
||||
You can also specify rtlwmbus and if you set the serial in the rtlsdr
|
||||
dongle using `rtl_eeprom -s 1234` you can also refer to a specific
|
||||
rtlsdr dongle like this `rtlwmbus[1234]`.
|
||||
|
||||
`/dev/ttyUSB0:amb8465`, if you have an amb8465(metis) dongle assigned to ttyUSB0. Other suffixes are im871a,cul.
|
||||
`/dev/ttyUSB0:amb8465`, if you have an amb8465(metis) dongle assigned to ttyUSB0. Other suffixes are iu891a,cul.
|
||||
|
||||
(Note that a plain `/dev/ttyUSB0` no longer works, you have to specify the device expected on the device.)
|
||||
|
||||
|
|
@ -615,6 +615,7 @@ As meter quadruples you specify:
|
|||
```
|
||||
Supported wmbus dongles:
|
||||
IMST 871a (im871a)
|
||||
IMST 891a (iu891a)
|
||||
Amber 8465-M/8665-M/8626-M/Metis-II (amb8465) 868MHz
|
||||
Amber 3665-M (amb3665) 169MHz
|
||||
CUL family (cul)
|
||||
|
|
@ -724,6 +725,8 @@ also listen to c1 and t1 telegrams at the same time.
|
|||
If you have the older firmware you can download the upgrader here:
|
||||
https://wireless-solutions.de/downloadfile/wireless-m-bus-software/
|
||||
|
||||
The wmbus dongle iu891a can listen to either s1, c1 or t1 or c1,t1 at the same time.
|
||||
|
||||
The amb8465 dongle (new model name is Metis-II) can listen to either
|
||||
s1, c1 or t1. It can also listen to c1 and t1 at the same time.
|
||||
|
||||
|
|
|
|||
|
|
@ -8,18 +8,13 @@ driver {
|
|||
}
|
||||
use = actuality_duration_s
|
||||
field {
|
||||
name = total
|
||||
quantity = Volume
|
||||
name = total
|
||||
quantity = Volume
|
||||
info = 'The total water consumption.'
|
||||
match {
|
||||
measurement_type = Instantaneous
|
||||
vif_range = Volume
|
||||
}
|
||||
about {
|
||||
de = 'Der Gesamtwasserverbrauch.'
|
||||
en = 'The total water consumption.'
|
||||
fr = '''La consommation totale d'eau.'''
|
||||
sv = 'Den totala vattenförbrukningen.'
|
||||
}
|
||||
}
|
||||
test {
|
||||
args = 'Gas elster 05105025 NOKEY'
|
||||
|
|
|
|||
|
|
@ -200,6 +200,10 @@ shared_ptr<BusDevice> BusManager::createWmbusObject(Detected *detected, Configur
|
|||
verbose("(im170a) on %s\n", detected->found_file.c_str());
|
||||
wmbus = openIM170A(*detected, serial_manager_, serial_override);
|
||||
break;
|
||||
case DEVICE_IU891A:
|
||||
verbose("(iu891a) on %s\n", detected->found_file.c_str());
|
||||
wmbus = openIU891A(*detected, serial_manager_, serial_override);
|
||||
break;
|
||||
case DEVICE_AMB8465:
|
||||
verbose("(amb8465) on %s\n", detected->found_file.c_str());
|
||||
wmbus = openAMB8465(*detected, serial_manager_, serial_override);
|
||||
|
|
|
|||
10
src/wmbus.cc
10
src/wmbus.cc
|
|
@ -5547,6 +5547,16 @@ Detected detectBusDeviceOnTTY(string tty,
|
|||
}
|
||||
}
|
||||
|
||||
// Talk iu891a with it...
|
||||
// assumes this device is configured for 115200 bps, which seems to be the default.
|
||||
if (has_auto || probe_for.count(BusDeviceType::DEVICE_IU891A))
|
||||
{
|
||||
if (detectIU891A(&detected, handler) == AccessCheck::AccessOK)
|
||||
{
|
||||
return detected;
|
||||
}
|
||||
}
|
||||
|
||||
// Talk iu880b with it...
|
||||
// assumes this device is configured for 115200 bps, which seems to be the default.
|
||||
if (has_auto || probe_for.count(BusDeviceType::DEVICE_IU880B))
|
||||
|
|
|
|||
|
|
@ -44,6 +44,7 @@ bool trimCRCsFrameFormatB(std::vector<uchar> &payload);
|
|||
X(CUL,cul,true,false,detectCUL) \
|
||||
X(IM871A,im871a,true,false,detectIM871AIM170A) \
|
||||
X(IM170A,im170a,true,false,detectSKIP) \
|
||||
X(IU891A,iu891a,true,false,detectIU891A) \
|
||||
X(RAWTTY,rawtty,true,false,detectRAWTTY) \
|
||||
X(HEXTTY,hextty,true,false,detectSKIP) \
|
||||
X(RC1180,rc1180,true,false,detectRC1180) \
|
||||
|
|
@ -704,6 +705,9 @@ shared_ptr<BusDevice> openIM871A(Detected detected,
|
|||
shared_ptr<BusDevice> openIM170A(Detected detected,
|
||||
shared_ptr<SerialCommunicationManager> manager,
|
||||
shared_ptr<SerialDevice> serial_override);
|
||||
shared_ptr<BusDevice> openIU891A(Detected detected,
|
||||
shared_ptr<SerialCommunicationManager> manager,
|
||||
shared_ptr<SerialDevice> serial_override);
|
||||
shared_ptr<BusDevice> openIU880B(Detected detected,
|
||||
shared_ptr<SerialCommunicationManager> manager,
|
||||
shared_ptr<SerialDevice> serial_override);
|
||||
|
|
@ -800,6 +804,7 @@ AccessCheck detectAMB8465AMB3665(Detected *detected, shared_ptr<SerialCommunicat
|
|||
AccessCheck detectCUL(Detected *detected, shared_ptr<SerialCommunicationManager> handler);
|
||||
AccessCheck detectD1TC(Detected *detected, shared_ptr<SerialCommunicationManager> manager);
|
||||
AccessCheck detectIM871AIM170A(Detected *detected, shared_ptr<SerialCommunicationManager> handler);
|
||||
AccessCheck detectIU891A(Detected *detected, shared_ptr<SerialCommunicationManager> handler);
|
||||
AccessCheck detectIU880B(Detected *detected, shared_ptr<SerialCommunicationManager> handler);
|
||||
AccessCheck detectRAWTTY(Detected *detected, shared_ptr<SerialCommunicationManager> handler);
|
||||
AccessCheck detectMBUS(Detected *detected, shared_ptr<SerialCommunicationManager> handler);
|
||||
|
|
|
|||
|
|
@ -731,9 +731,9 @@ bool WMBusIM871aIM170A::deviceSetLinkModes(LinkModeSet lms)
|
|||
}
|
||||
|
||||
FrameStatus WMBusIM871aIM170A::checkIM871AFrame(vector<uchar> &data,
|
||||
size_t *frame_length, int *endpoint_out, int *msgid_out,
|
||||
int *payload_len_out, int *payload_offset,
|
||||
int *rssi_dbm)
|
||||
size_t *frame_length, int *endpoint_out, int *msgid_out,
|
||||
int *payload_len_out, int *payload_offset,
|
||||
int *rssi_dbm)
|
||||
{
|
||||
if (data.size() == 0) return PartialFrame;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
// Copyright (C) 2017 Fredrik Öhrström (CC0-1.0)
|
||||
// Definitions from WMBus_HCI_Spec_V1_6.pdf
|
||||
// Found here: https://wireless-solutions.de/products/gateways/wirelessadapter.html
|
||||
// Copyright (C) 2017 Fredrik Öhrström (CC0-1.0) Definitions from
|
||||
// WMBus_HCI_Spec_V1_6.pdf Found here:
|
||||
// https://wireless-solutions.de/products/gateways/wirelessadapter.html
|
||||
|
||||
#define IM871A_SERIAL_SOF 0xA5
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,847 @@
|
|||
/*
|
||||
Copyright (C) 2017-2024 Fredrik Öhrström (gpl-3.0-or-later)
|
||||
|
||||
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 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include"wmbus.h"
|
||||
#include"wmbus_common_implementation.h"
|
||||
#include"wmbus_utils.h"
|
||||
#include"wmbus_iu891a.h"
|
||||
#include"serial.h"
|
||||
#include"threads.h"
|
||||
|
||||
#include<assert.h>
|
||||
#include<pthread.h>
|
||||
#include<errno.h>
|
||||
#include<memory.h>
|
||||
#include<semaphore.h>
|
||||
#include<unistd.h>
|
||||
|
||||
using namespace std;
|
||||
|
||||
struct DeviceInfo_IU891A
|
||||
{
|
||||
uchar module_type; // 109=0x6d=iM891A-XL 110=0x6e=iU891A-XL 163=0xa3=iM881A-XL
|
||||
uint32_t uid;
|
||||
string uids;
|
||||
string product_type;
|
||||
string product_id;
|
||||
|
||||
string str()
|
||||
{
|
||||
string s;
|
||||
/* s += "status=";
|
||||
string st = tostrprintf( "%02x", status);*/
|
||||
//s += st;
|
||||
s += " type=";
|
||||
string mt = tostrprintf("%02x", module_type);
|
||||
if (module_type == 0x6d) s+="im891a ";
|
||||
else if (module_type == 0x6e) s+="iu891a ";
|
||||
else if (module_type == 0xa3) s+="im881 ";
|
||||
else s+="unknown_type("+mt+") ";
|
||||
|
||||
string ss;
|
||||
strprintf(&ss, "uid=%08x", uid);
|
||||
return s+ss;
|
||||
}
|
||||
|
||||
bool decode(vector<uchar> &bytes)
|
||||
{
|
||||
if (bytes.size() < 8) return false;
|
||||
int i = 0;
|
||||
module_type = bytes[i++];
|
||||
uid = bytes[i+3]<<24|bytes[i+2]<<16|bytes[i+1]<<8|bytes[i]; i+=4;
|
||||
strprintf(&uids, "%08x", uid);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
struct WMBusAddressInfo_IU891A
|
||||
{
|
||||
uint16_t mfct {};
|
||||
uint32_t id {};
|
||||
uint8_t version {};
|
||||
uint8_t type {};
|
||||
|
||||
string dongleId()
|
||||
{
|
||||
string s;
|
||||
strprintf(&s, "%08x", id);
|
||||
return s;
|
||||
}
|
||||
|
||||
bool decode(vector<uchar> &bytes)
|
||||
{
|
||||
if (bytes.size() < 8) return false;
|
||||
int i = 0;
|
||||
mfct = bytes[i] << 8 | bytes[i+1];
|
||||
i += 2;
|
||||
id = bytes[i]<<24|bytes[i+1]<<16|bytes[i+1]<<8|bytes[i+3];
|
||||
i+=4;
|
||||
version = bytes[i++];
|
||||
type = bytes[i++];
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
struct Config_IU891A
|
||||
{
|
||||
LinkModeSet link_modes;
|
||||
uint16_t option_bits {};
|
||||
uint16_t ui_option_bits {};
|
||||
uint16_t led_flash_timing {};
|
||||
uint32_t recalibrate_in_ms {};
|
||||
|
||||
string str()
|
||||
{
|
||||
string s;
|
||||
|
||||
if (option_bits & 0x01) s += "RCV_FILTER ";
|
||||
else s += "RCV_ALL ";
|
||||
|
||||
if (option_bits & 0x02) s += "RCV_IND ";
|
||||
if (option_bits & 0x04) s += "SND_IND ";
|
||||
if (option_bits & 0x08) s += "RECALIB ";
|
||||
|
||||
if (ui_option_bits & 0x01) s += "ASSERT_PIN24_ON_TELEGRAM_ARRIVAL ";
|
||||
if (ui_option_bits & 0x02) s += "PIN24_POLARITY_REVERSED ";
|
||||
if (ui_option_bits & 0x04) s += "ASSERT_PIN25_ON_TELEGRAM_SENT ";
|
||||
if (ui_option_bits & 0x08) s += "PIN25_POLARITY_REVERSED ";
|
||||
|
||||
s += tostrprintf("led flash: %d ms ", led_flash_timing);
|
||||
s += tostrprintf("recalibrate: %d ms ", recalibrate_in_ms);
|
||||
|
||||
s.pop_back();
|
||||
return s;
|
||||
}
|
||||
|
||||
void encode(vector<uchar> &bytes, uchar lm)
|
||||
{
|
||||
bytes.clear();
|
||||
bytes.resize(11);
|
||||
size_t i = 0;
|
||||
bytes[i++] = lm;
|
||||
bytes[i++] = option_bits & 0xff;
|
||||
bytes[i++] = (option_bits >> 8) & 0xff;
|
||||
bytes[i++] = ui_option_bits & 0xff;
|
||||
bytes[i++] = (ui_option_bits >> 8) & 0xff;
|
||||
bytes[i++] = led_flash_timing & 0xff;
|
||||
bytes[i++] = (led_flash_timing >> 8) & 0xff;
|
||||
bytes[i++] = recalibrate_in_ms & 0xff;
|
||||
bytes[i++] = (recalibrate_in_ms >> 8) & 0xff;
|
||||
bytes[i++] = (recalibrate_in_ms >> 16) & 0xff;
|
||||
bytes[i++] = (recalibrate_in_ms >> 24) & 0xff;
|
||||
}
|
||||
|
||||
bool decode(vector<uchar> &bytes)
|
||||
{
|
||||
if (bytes.size() < 11) return false;
|
||||
size_t i = 0;
|
||||
uchar c = bytes[i++];
|
||||
link_modes.clear();
|
||||
if (c == LINK_MODE_OFF)
|
||||
{
|
||||
}
|
||||
else if (c == LINK_MODE_S)
|
||||
{
|
||||
link_modes.addLinkMode(LinkMode::S1);
|
||||
}
|
||||
else if (c == LINK_MODE_T)
|
||||
{
|
||||
link_modes.addLinkMode(LinkMode::T1);
|
||||
}
|
||||
else if (c == LINK_MODE_CT)
|
||||
{
|
||||
link_modes.addLinkMode(LinkMode::C1).addLinkMode(LinkMode::T1);
|
||||
}
|
||||
else if (c == LINK_MODE_C)
|
||||
{
|
||||
link_modes.addLinkMode(LinkMode::C1);
|
||||
}
|
||||
option_bits = bytes[i+1]<<8 | bytes[i+0];
|
||||
i += 2;
|
||||
ui_option_bits = bytes[i+1]<<8 | bytes[i+0];
|
||||
i += 2;
|
||||
led_flash_timing = bytes[i+1]<<8 | bytes[i+0];
|
||||
i += 2;
|
||||
recalibrate_in_ms =
|
||||
bytes[i+3]<<24 | bytes[i+2] <<16 |
|
||||
bytes[i+1]<<8 | bytes[i+0];
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
const char *toString(ErrorCodeIU891ADevMgmt ec)
|
||||
{
|
||||
switch (ec)
|
||||
{
|
||||
#define X(num,text,info) case ErrorCodeIU891ADevMgmt::text: return #info;
|
||||
LIST_OF_IU891A_DEVMGMT_ERROR_CODES
|
||||
#undef X
|
||||
default: return "Unknown";
|
||||
}
|
||||
}
|
||||
|
||||
ErrorCodeIU891ADevMgmt toErrorCodeIU891ADevMgmt(uchar c)
|
||||
{
|
||||
switch (c)
|
||||
{
|
||||
#define X(num,text,info) case num: return ErrorCodeIU891ADevMgmt::text;
|
||||
LIST_OF_IU891A_DEVMGMT_ERROR_CODES
|
||||
#undef X
|
||||
default: return ErrorCodeIU891ADevMgmt::UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
const char *toString(ErrorCodeIU891AWMBUSGW ec)
|
||||
{
|
||||
switch (ec)
|
||||
{
|
||||
#define X(num,text,info) case ErrorCodeIU891AWMBUSGW::text: return #info;
|
||||
LIST_OF_IU891A_WMBUSGW_ERROR_CODES
|
||||
#undef X
|
||||
default: return "Unknown";
|
||||
}
|
||||
}
|
||||
|
||||
ErrorCodeIU891AWMBUSGW toErrorCodeIU891AWMBUSGW(uchar c)
|
||||
{
|
||||
switch (c)
|
||||
{
|
||||
#define X(num,text,info) case num: return ErrorCodeIU891AWMBUSGW::text;
|
||||
LIST_OF_IU891A_WMBUSGW_ERROR_CODES
|
||||
#undef X
|
||||
default: return ErrorCodeIU891AWMBUSGW::UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
struct WMBusIU891A : public virtual BusDeviceCommonImplementation
|
||||
{
|
||||
bool ping();
|
||||
string getDeviceId();
|
||||
string getDeviceUniqueId();
|
||||
uchar getFirmwareVersion();
|
||||
LinkModeSet getLinkModes();
|
||||
void deviceReset();
|
||||
bool deviceSetLinkModes(LinkModeSet lms);
|
||||
LinkModeSet supportedLinkModes()
|
||||
{
|
||||
return
|
||||
C1_bit |
|
||||
C2_bit |
|
||||
S1_bit |
|
||||
S1m_bit |
|
||||
T1_bit |
|
||||
T2_bit;
|
||||
}
|
||||
|
||||
int numConcurrentLinkModes()
|
||||
{
|
||||
return 2;
|
||||
}
|
||||
|
||||
bool canSetLinkModes(LinkModeSet lms)
|
||||
{
|
||||
if (lms.empty()) return false;
|
||||
if (!supportedLinkModes().supports(lms)) return false;
|
||||
// Ok, the supplied link modes are compatible.
|
||||
// For iu891a
|
||||
if (2 == countSetBits(lms.asBits()) &&
|
||||
lms.has(LinkMode::C1) &&
|
||||
lms.has(LinkMode::T1))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
// Otherwise its a single link mode.
|
||||
return 1 == countSetBits(lms.asBits());
|
||||
}
|
||||
bool sendTelegram(LinkMode lm, TelegramFormat format, vector<uchar> &content);
|
||||
|
||||
void processSerialData();
|
||||
void simulate() { }
|
||||
|
||||
WMBusIU891A(BusDeviceType type, string alias, shared_ptr<SerialDevice> serial, shared_ptr<SerialCommunicationManager> manager);
|
||||
~WMBusIU891A() {
|
||||
}
|
||||
|
||||
static FrameStatus checkIU891AFrame(vector<uchar> &data,
|
||||
vector<uchar> &out,
|
||||
size_t *frame_length,
|
||||
int *endpoint_id_out,
|
||||
int *msg_id_out,
|
||||
int *status_out,
|
||||
int *rssi_dbm);
|
||||
|
||||
static void extractFrame(vector<uchar> &payload, int *rssi_dbm, vector<uchar> *frame);
|
||||
|
||||
private:
|
||||
|
||||
DeviceInfo_IU891A device_info_ {};
|
||||
WMBusAddressInfo_IU891A device_wmbus_address_ {};
|
||||
Config_IU891A device_config_ {};
|
||||
|
||||
uchar last_set_link_mode_ { 0x00 };
|
||||
|
||||
vector<uchar> read_buffer_;
|
||||
vector<uchar> request_;
|
||||
vector<uchar> response_;
|
||||
|
||||
bool getDeviceInfo();
|
||||
bool loaded_device_info_ {};
|
||||
|
||||
bool getConfig();
|
||||
|
||||
friend AccessCheck detectIU891A(Detected *detected, shared_ptr<SerialCommunicationManager> manager);
|
||||
void handleDevMgmt(int msgid, vector<uchar> &payload);
|
||||
void handleWMbusGateway(int msgid, vector<uchar> &payload);
|
||||
|
||||
};
|
||||
|
||||
shared_ptr<BusDevice> openIU891A(BusDeviceType type, Detected detected, shared_ptr<SerialCommunicationManager> manager, shared_ptr<SerialDevice> serial_override)
|
||||
{
|
||||
string bus_alias = detected.specified_device.bus_alias;
|
||||
string device_file = detected.found_file;
|
||||
assert(device_file != "");
|
||||
if (serial_override)
|
||||
{
|
||||
WMBusIU891A *imp = new WMBusIU891A(type, bus_alias, serial_override, manager);
|
||||
imp->markAsNoLongerSerial();
|
||||
return shared_ptr<BusDevice>(imp);
|
||||
}
|
||||
|
||||
auto serial = manager->createSerialDeviceTTY(device_file.c_str(), 115200, PARITY::NONE, "iu891a");
|
||||
WMBusIU891A *imp = new WMBusIU891A(type, bus_alias, serial, manager);
|
||||
return shared_ptr<BusDevice>(imp);
|
||||
}
|
||||
|
||||
shared_ptr<BusDevice> openIU891A(Detected detected, shared_ptr<SerialCommunicationManager> manager, shared_ptr<SerialDevice> serial_override)
|
||||
{
|
||||
return openIU891A(BusDeviceType::DEVICE_IU891A, detected, manager, serial_override);
|
||||
}
|
||||
|
||||
shared_ptr<BusDevice> open(Detected detected, shared_ptr<SerialCommunicationManager> manager, shared_ptr<SerialDevice> serial_override)
|
||||
{
|
||||
return openIU891A(BusDeviceType::DEVICE_IU891A, detected, manager, serial_override);
|
||||
}
|
||||
|
||||
WMBusIU891A::WMBusIU891A(BusDeviceType type, string alias, shared_ptr<SerialDevice> serial, shared_ptr<SerialCommunicationManager> manager) :
|
||||
BusDeviceCommonImplementation(alias, type, manager, serial, true)
|
||||
{
|
||||
reset();
|
||||
}
|
||||
|
||||
static void buildRequest(int endpoint_id, int msg_id, vector<uchar>& body, vector<uchar>& out)
|
||||
{
|
||||
vector<uchar> request;
|
||||
request.push_back(endpoint_id);
|
||||
request.push_back(msg_id);
|
||||
request.insert(request.end(), body.begin(), body.end());
|
||||
|
||||
uint16_t crc = ~crc16_CCITT(&request[0], request.size()); // Safe to use &[0] since request size > 0
|
||||
request.push_back(crc & 0xff);
|
||||
request.push_back(crc >> 8);
|
||||
|
||||
addSlipFraming(request, out);
|
||||
}
|
||||
|
||||
bool WMBusIU891A::ping()
|
||||
{
|
||||
if (serial()->readonly()) return true; // Feeding from stdin or file.
|
||||
return true;
|
||||
}
|
||||
|
||||
string WMBusIU891A::getDeviceId()
|
||||
{
|
||||
if (serial()->readonly()) return "?"; // Feeding from stdin or file.
|
||||
if (cached_device_id_ != "") return cached_device_id_;
|
||||
|
||||
bool ok = getDeviceInfo();
|
||||
if (!ok) return "ERR";
|
||||
|
||||
ok = getConfig();
|
||||
if (!ok) return "ERR";
|
||||
|
||||
cached_device_id_ = device_wmbus_address_.dongleId();
|
||||
|
||||
verbose("(iu891a) got device id %s\n", cached_device_id_.c_str());
|
||||
|
||||
return cached_device_id_;
|
||||
}
|
||||
|
||||
string WMBusIU891A::getDeviceUniqueId()
|
||||
{
|
||||
if (serial()->readonly()) return "?"; // Feeding from stdin or file.
|
||||
if (cached_device_unique_id_ != "") return cached_device_unique_id_;
|
||||
|
||||
bool ok = getDeviceInfo();
|
||||
if (!ok) return "ERR";
|
||||
|
||||
cached_device_unique_id_ = tostrprintf("%08x", device_info_.uid);
|
||||
|
||||
verbose("(im871a) got device unique id %s\n", cached_device_unique_id_.c_str());
|
||||
|
||||
return cached_device_unique_id_;
|
||||
}
|
||||
|
||||
uchar WMBusIU891A::getFirmwareVersion()
|
||||
{
|
||||
if (serial()->readonly()) return 0x15; // Feeding from stdin or file.
|
||||
|
||||
// bool ok = getDeviceInfo();
|
||||
// if (!ok) return 255;
|
||||
|
||||
return 0; // device_info_.firmware_version;
|
||||
}
|
||||
|
||||
LinkModeSet WMBusIU891A::getLinkModes()
|
||||
{
|
||||
if (serial()->readonly()) { return Any_bit; } // Feeding from stdin or file.
|
||||
|
||||
bool ok = getConfig();
|
||||
if (!ok) return LinkModeSet();
|
||||
|
||||
return device_config_.link_modes;
|
||||
}
|
||||
|
||||
void WMBusIU891A::deviceReset()
|
||||
{
|
||||
// No device specific settings needed right now.
|
||||
// The common code in wmbus.cc reset()
|
||||
// will open the serial device and potentially
|
||||
// set the link modes properly.
|
||||
}
|
||||
|
||||
uchar setupIMSTBusDeviceToReceiveTelegrams(LinkModeSet lms)
|
||||
{
|
||||
if (lms.has(LinkMode::C1) && lms.has(LinkMode::T1))
|
||||
{
|
||||
return LINK_MODE_CT;
|
||||
}
|
||||
else if (lms.has(LinkMode::C1))
|
||||
{
|
||||
return LINK_MODE_C;
|
||||
}
|
||||
else if (lms.has(LinkMode::C2))
|
||||
{
|
||||
return LINK_MODE_C;
|
||||
}
|
||||
else if (lms.has(LinkMode::S1))
|
||||
{
|
||||
return LINK_MODE_S;
|
||||
}
|
||||
else if (lms.has(LinkMode::S1m))
|
||||
{
|
||||
return LINK_MODE_S;
|
||||
}
|
||||
else if (lms.has(LinkMode::T1))
|
||||
{
|
||||
return LINK_MODE_T;
|
||||
}
|
||||
else if (lms.has(LinkMode::T2))
|
||||
{
|
||||
return LINK_MODE_T;
|
||||
}
|
||||
else
|
||||
{
|
||||
return LINK_MODE_C; // Defaults to C
|
||||
}
|
||||
|
||||
assert(false);
|
||||
|
||||
// Error
|
||||
return 0xff;
|
||||
}
|
||||
|
||||
|
||||
bool WMBusIU891A::deviceSetLinkModes(LinkModeSet lms)
|
||||
{
|
||||
if (serial()->readonly()) return true; // Feeding from stdin or file.
|
||||
|
||||
if (!canSetLinkModes(lms))
|
||||
{
|
||||
string modes = lms.hr();
|
||||
error("(iu871a) setting link mode(s) %s is not supported for iu891a\n", modes.c_str());
|
||||
}
|
||||
|
||||
LOCK_WMBUS_EXECUTING_COMMAND(set_link_modes);
|
||||
|
||||
vector<uchar> body;
|
||||
vector<uchar> request;
|
||||
|
||||
device_config_.option_bits &= 0xfffe; // forward all received telegrams to wmbusmeters.
|
||||
device_config_.option_bits |= 0x0006; // get notified when received and sent.
|
||||
device_config_.encode(body, setupIMSTBusDeviceToReceiveTelegrams(lms));
|
||||
|
||||
buildRequest(SAP_WMBUSGW_ID, WMBUSGW_SET_ACTIVE_CONFIGURATION_REQ, body, request);
|
||||
|
||||
verbose("(iu891a) set config\n");
|
||||
bool sent = serial()->send(request);
|
||||
if (!sent) return false;
|
||||
|
||||
bool ok = waitForResponse(WMBUSGW_SET_ACTIVE_CONFIGURATION_RSP);
|
||||
if (!ok) return false; // timeout
|
||||
|
||||
verbose("(iu871a) set config to set link mode %02x\n", body[0]);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void WMBusIU891A::extractFrame(vector<uchar> &payload, int *rssi_dbm, vector<uchar> *frame)
|
||||
{
|
||||
if (payload.size() < 10) return;
|
||||
*rssi_dbm = (int8_t)payload[7];
|
||||
frame->clear();
|
||||
frame->insert(frame->begin(), payload.begin()+8, payload.end());
|
||||
}
|
||||
|
||||
FrameStatus WMBusIU891A::checkIU891AFrame(vector<uchar> &data,
|
||||
vector<uchar> &out,
|
||||
size_t *frame_length_out,
|
||||
int *endpoint_id_out,
|
||||
int *msg_id_out,
|
||||
int *status_byte_out,
|
||||
int *rssi_dbm)
|
||||
{
|
||||
vector<uchar> msg;
|
||||
|
||||
removeSlipFraming(data, frame_length_out, msg);
|
||||
|
||||
if (msg.size() < 5) return PartialFrame;
|
||||
|
||||
*endpoint_id_out = msg[0];
|
||||
*msg_id_out = msg[1];
|
||||
*status_byte_out = msg[2];
|
||||
|
||||
uint16_t crc = ~crc16_CCITT(&msg[0], msg.size()-2);
|
||||
uchar crc_lo = crc & 0xff;
|
||||
uchar crc_hi = crc >> 8;
|
||||
|
||||
if (msg[msg.size()-2] != crc_lo || msg[msg.size()-1] != crc_hi)
|
||||
{
|
||||
debug("(iu880b) bad crc got %02x%02x expected %02x%02x\n",
|
||||
msg[msg.size()-1], msg[msg.size()-2], crc_hi, crc_lo);
|
||||
|
||||
return ErrorInFrame;
|
||||
}
|
||||
|
||||
int payload_offset = 3;
|
||||
int payload_len = msg.size()-2;
|
||||
out.clear();
|
||||
out.insert(out.end(), msg.begin()+payload_offset, msg.begin()+payload_len);
|
||||
|
||||
return FullFrame;
|
||||
}
|
||||
|
||||
void WMBusIU891A::processSerialData()
|
||||
{
|
||||
vector<uchar> data;
|
||||
|
||||
// Receive and accumulated serial data until a full frame has been received.
|
||||
serial()->receive(&data);
|
||||
|
||||
read_buffer_.insert(read_buffer_.end(), data.begin(), data.end());
|
||||
|
||||
size_t frame_length;
|
||||
int endpoint_id;
|
||||
int msg_id;
|
||||
int status_byte;
|
||||
int rssi_dbm = 0;
|
||||
|
||||
vector<uchar> payload;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
FrameStatus status = checkIU891AFrame(read_buffer_,
|
||||
payload,
|
||||
&frame_length,
|
||||
&endpoint_id,
|
||||
&msg_id,
|
||||
&status_byte,
|
||||
&rssi_dbm);
|
||||
|
||||
if (status == PartialFrame)
|
||||
{
|
||||
if (read_buffer_.size() > 0)
|
||||
{
|
||||
debugPayload("(iu891a) partial frame, expecting more.", read_buffer_);
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (status == ErrorInFrame)
|
||||
{
|
||||
debugPayload("(iu891a) bad frame, clearing.", read_buffer_);
|
||||
read_buffer_.clear();
|
||||
break;
|
||||
}
|
||||
if (status == FullFrame)
|
||||
{
|
||||
read_buffer_.erase(read_buffer_.begin(), read_buffer_.begin()+frame_length);
|
||||
|
||||
// We now have a proper message in payload. Let us trigger actions based on it.
|
||||
// It can be wmbus receiver-dongle messages or wmbus remote meter messages received over the radio.
|
||||
switch (endpoint_id) {
|
||||
case SAP_DEVMGMT_ID: handleDevMgmt(msg_id, payload); break;
|
||||
case SAP_WMBUSGW_ID: handleWMbusGateway(msg_id, payload); break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool WMBusIU891A::getDeviceInfo()
|
||||
{
|
||||
if (loaded_device_info_) return true;
|
||||
|
||||
LOCK_WMBUS_EXECUTING_COMMAND(get_device_info);
|
||||
|
||||
vector<uchar> body;
|
||||
vector<uchar> request;
|
||||
|
||||
buildRequest(SAP_DEVMGMT_ID, DEVMGMT_MSG_GET_DEVICE_INFO_REQ, body, request);
|
||||
|
||||
verbose("(iu891a) get device info\n");
|
||||
bool sent = serial()->send(request);
|
||||
if (!sent) return false; // tty overridden with stdin/file
|
||||
|
||||
bool ok = waitForResponse(DEVMGMT_MSG_GET_DEVICE_INFO_RSP);
|
||||
if (!ok) return false; // timeout
|
||||
|
||||
// Now device info response is in response_ vector.
|
||||
device_info_.decode(response_);
|
||||
|
||||
verbose("(iu891a) device info: %s\n", device_info_.str().c_str());
|
||||
|
||||
body.clear();
|
||||
request.clear();
|
||||
buildRequest(SAP_WMBUSGW_ID, WMBUSGW_GET_WMBUS_ADDRESS_REQ, body, request);
|
||||
|
||||
verbose("(iu891a) get wmbus address\n");
|
||||
sent = serial()->send(request);
|
||||
if (!sent) return false; // tty overridden with stdin/file
|
||||
|
||||
ok = waitForResponse(WMBUSGW_GET_WMBUS_ADDRESS_RSP);
|
||||
if (!ok) return false; // timeout
|
||||
|
||||
// Now device info response is in response_ vector.
|
||||
device_wmbus_address_.decode(response_);
|
||||
|
||||
loaded_device_info_ = true;
|
||||
verbose("(iu891a) device info: %s %s\n", device_wmbus_address_.dongleId().c_str(), device_info_.str().c_str());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WMBusIU891A::getConfig()
|
||||
{
|
||||
if (serial()->readonly()) return true;
|
||||
|
||||
LOCK_WMBUS_EXECUTING_COMMAND(get_config);
|
||||
|
||||
vector<uchar> body;
|
||||
vector<uchar> request;
|
||||
|
||||
buildRequest(SAP_WMBUSGW_ID, WMBUSGW_GET_ACTIVE_CONFIGURATION_REQ, body, request);
|
||||
|
||||
verbose("(iu891a) get config\n");
|
||||
bool sent = serial()->send(request);
|
||||
if (!sent) return false; // tty overridden with stdin/file
|
||||
|
||||
bool ok = waitForResponse(WMBUSGW_GET_ACTIVE_CONFIGURATION_RSP);
|
||||
if (!ok) return false; // timeout
|
||||
|
||||
// Now device info response is in response_ vector.
|
||||
device_config_.decode(response_);
|
||||
|
||||
verbose("(iu891a) config: %s link modes: %s\n",
|
||||
device_config_.str().c_str(),
|
||||
device_config_.link_modes.hr().c_str());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WMBusIU891A::sendTelegram(LinkMode lm, TelegramFormat format, vector<uchar> &content)
|
||||
{
|
||||
if (serial()->readonly()) return true;
|
||||
if (content.size() > 250) return false;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
AccessCheck detectIU891A(Detected *detected, shared_ptr<SerialCommunicationManager> manager)
|
||||
{
|
||||
assert(detected->found_file != "");
|
||||
|
||||
// Talk to the device and expect a very specific answer.
|
||||
auto serial = manager->createSerialDeviceTTY(detected->found_file.c_str(), 115200, PARITY::NONE, "detect iu891a");
|
||||
serial->disableCallbacks();
|
||||
bool ok = serial->open(false);
|
||||
if (!ok)
|
||||
{
|
||||
verbose("(iu891a) could not open tty %s for detection\n", detected->found_file.c_str());
|
||||
return AccessCheck::NoSuchDevice;
|
||||
}
|
||||
|
||||
vector<uchar> response;
|
||||
// First clear out any data in the queue.
|
||||
serial->receive(&response);
|
||||
response.clear();
|
||||
|
||||
vector<uchar> init;
|
||||
init.resize(30);
|
||||
for (int i=0; i<30; ++i)
|
||||
{
|
||||
init[i] = 0xc0;
|
||||
}
|
||||
// Wake-up the dongle.
|
||||
serial->send(init);
|
||||
|
||||
vector<uchar> body;
|
||||
vector<uchar> request;
|
||||
buildRequest(SAP_DEVMGMT_ID, DEVMGMT_MSG_GET_DEVICE_INFO_REQ, body, request);
|
||||
serial->send(request);
|
||||
|
||||
// Wait for 100ms so that the USB stick have time to prepare a response.
|
||||
usleep(100*1000);
|
||||
serial->receive(&response);
|
||||
|
||||
int endpoint_id = 0;
|
||||
int msg_id = 0;
|
||||
int status_byte = 0;
|
||||
size_t frame_length = 0;
|
||||
int rssi_dbm = 0;
|
||||
vector<uchar> payload;
|
||||
|
||||
FrameStatus status = WMBusIU891A::checkIU891AFrame(response,
|
||||
payload,
|
||||
&frame_length,
|
||||
&endpoint_id,
|
||||
&msg_id,
|
||||
&status_byte,
|
||||
&rssi_dbm);
|
||||
|
||||
if (status != FullFrame ||
|
||||
endpoint_id != SAP_DEVMGMT_ID ||
|
||||
msg_id != DEVMGMT_MSG_GET_DEVICE_INFO_RSP)
|
||||
{
|
||||
verbose("(iu891a) are you there? no.\n");
|
||||
serial->close();
|
||||
return AccessCheck::NoProperResponse;
|
||||
}
|
||||
|
||||
debugPayload("(iu891a) device info response", payload);
|
||||
|
||||
debug("(iu891a) endpoint %02x msg %02x status %02x\n", endpoint_id, msg_id, status_byte);
|
||||
|
||||
DeviceInfo_IU891A di;
|
||||
di.decode(payload);
|
||||
|
||||
debug("(iu891a) info: %s\n", di.str().c_str());
|
||||
|
||||
body.clear();
|
||||
request.clear();
|
||||
buildRequest(SAP_WMBUSGW_ID, WMBUSGW_GET_WMBUS_ADDRESS_REQ, body, request);
|
||||
serial->send(request);
|
||||
|
||||
// Wait for 100ms so that the USB stick have time to prepare a response.
|
||||
usleep(100*1000);
|
||||
serial->receive(&response);
|
||||
|
||||
status = WMBusIU891A::checkIU891AFrame(response,
|
||||
payload,
|
||||
&frame_length,
|
||||
&endpoint_id,
|
||||
&msg_id,
|
||||
&status_byte,
|
||||
&rssi_dbm);
|
||||
|
||||
if (status != FullFrame ||
|
||||
endpoint_id != SAP_WMBUSGW_ID ||
|
||||
msg_id != WMBUSGW_GET_WMBUS_ADDRESS_RSP)
|
||||
{
|
||||
verbose("(iu891a) are you there? I thought so, but no.\n");
|
||||
serial->close();
|
||||
return AccessCheck::NoProperResponse;
|
||||
}
|
||||
|
||||
debugPayload("(iu891a) wmbus address response", payload);
|
||||
|
||||
WMBusAddressInfo_IU891A wa;
|
||||
wa.decode(payload);
|
||||
|
||||
serial->close();
|
||||
|
||||
detected->setAsFound(wa.dongleId(), BusDeviceType::DEVICE_IU891A, 115200, false, detected->specified_device.linkmodes);
|
||||
|
||||
verbose("(iu891a) are you there? yes %s\n", wa.dongleId().c_str());
|
||||
|
||||
return AccessCheck::AccessOK;
|
||||
}
|
||||
|
||||
void WMBusIU891A::handleDevMgmt(int msgid, vector<uchar> &payload)
|
||||
{
|
||||
switch (msgid) {
|
||||
case DEVMGMT_MSG_PING_RSP:
|
||||
debug("(iu891a) rsp pong\n");
|
||||
break;
|
||||
case DEVMGMT_MSG_GET_DEVICE_INFO_RSP:
|
||||
debug("(iu891a) rsp got device info\n");
|
||||
break;
|
||||
case DEVMGMT_MSG_GET_FW_INFO_RSP:
|
||||
debug("(iu891a) rsp got firmware\n");
|
||||
break;
|
||||
default:
|
||||
warning("(iu891a) Unhandled device management message %d\n", msgid);
|
||||
return;
|
||||
}
|
||||
response_.clear();
|
||||
response_.insert(response_.end(), payload.begin(), payload.end());
|
||||
notifyResponseIsHere(msgid);
|
||||
}
|
||||
|
||||
|
||||
void WMBusIU891A::handleWMbusGateway(int msgid, vector<uchar> &payload)
|
||||
{
|
||||
switch (msgid) {
|
||||
case WMBUSGW_GET_WMBUS_ADDRESS_RSP:
|
||||
debug("(iu891a) rsp got wmbus address\n");
|
||||
break;
|
||||
case WMBUSGW_GET_ACTIVE_CONFIGURATION_RSP:
|
||||
debug("(iu891a) rsp got active config\n");
|
||||
break;
|
||||
case WMBUSGW_SET_ACTIVE_CONFIGURATION_RSP:
|
||||
debug("(iu891a) rsp set active config\n");
|
||||
break;
|
||||
case WMBUSGW_RX_MESSAGE_IND:
|
||||
{
|
||||
// Invoke common telegram reception code in BusDeviceCommonImplementation.
|
||||
vector<uchar> frame;
|
||||
int rssi_dbm;
|
||||
|
||||
extractFrame(payload, &rssi_dbm, &frame);
|
||||
AboutTelegram about("iu891a["+cached_device_id_+"]", rssi_dbm, FrameType::WMBUS);
|
||||
handleTelegram(about, frame);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
warning("(iu891a) Unhandled wmbus gateway message %d\n", msgid);
|
||||
return;
|
||||
}
|
||||
response_.clear();
|
||||
response_.insert(response_.end(), payload.begin(), payload.end());
|
||||
notifyResponseIsHere(msgid);
|
||||
}
|
||||
|
|
@ -0,0 +1,152 @@
|
|||
// Copyright (C) 2024 Fredrik Öhrström (gpl-3.0-or-later)
|
||||
|
||||
// Service access endpoints inside the iu891a dongle that can receive send messages.
|
||||
#define SAP_DEVMGMT_ID 0x01
|
||||
#define SAP_WMBUSGW_ID 0x09
|
||||
|
||||
#define LINK_MODE_OFF 0
|
||||
#define LINK_MODE_S 1
|
||||
#define LINK_MODE_T 2
|
||||
#define LINK_MODE_CT 3
|
||||
#define LINK_MODE_C 5
|
||||
#define LINK_MODE_ENHANCED_T 6
|
||||
|
||||
#define DEVMGMT_MSG_PING_REQ 0x01
|
||||
#define DEVMGMT_MSG_PING_RSP 0x02
|
||||
#define DEVMGMT_MSG_GET_DEVICE_INFO_REQ 0x03
|
||||
#define DEVMGMT_MSG_GET_DEVICE_INFO_RSP 0x04
|
||||
#define DEVMGMT_MSG_GET_FW_INFO_REQ 0x05
|
||||
#define DEVMGMT_MSG_GET_FW_INFO_RSP 0x06
|
||||
#define DEVMGMT_MSG_RESTART_REQ 0x07
|
||||
#define DEVMGMT_MSG_RESTART_RSP 0x08
|
||||
#define DEVMGMT_MSG_SET_SYS_OP_MODE_REQ 0x09
|
||||
#define DEVMGMT_MSG_SET_SYS_OP_MODE_RSP 0x0a
|
||||
#define DEVMGMT_MSG_GET_SYS_OP_MODE_REQ 0x0b
|
||||
#define DEVMGMT_MSG_GET_SYS_OP_MODE_RSP 0x0c
|
||||
#define DEVMGMT_MSG_SET_SYS_DATE_TIME_REQ 0x0d
|
||||
#define DEVMGMT_MSG_SET_SYS_DATE_TIME_RSP 0x0e
|
||||
#define DEVMGMT_MSG_GET_SYS_DATE_TIME_REQ 0x0f
|
||||
#define DEVMGMT_MSG_GET_SYS_DATE_TIME_RSP 0x10
|
||||
#define DEVMGMT_MSG_SET_SYS_OPTIONS_REQ 0xf7
|
||||
#define DEVMGMT_MSG_SET_SYS_OPTIONS_RSP 0xf8
|
||||
#define DEVMGMT_MSG_GET_SYS_OPTIONS_REQ 0xf9
|
||||
#define DEVMGMT_MSG_GET_SYS_OPTIONS_RSP 0xfa
|
||||
|
||||
#define LIST_OF_IU891A_DEVMGMT_ERROR_CODES \
|
||||
X(0, OK, "ok") \
|
||||
X(1, ERROR, "error") \
|
||||
X(2, COMMAND_NOT_SUPPORTED, "command not supported") \
|
||||
X(3, WRONG_PARAMETER, "wrong parameter") \
|
||||
X(4, WRONG_APPLICATION, "wrong application / device mode") \
|
||||
X(5, RESERVED, "reserved") \
|
||||
X(6, BUSY, "application / device busy, try later") \
|
||||
X(7, WRONG_MSG_LENGTH, "wrong message length") \
|
||||
X(8, NVM_WRITE_ERROR, "NVM write error") \
|
||||
X(9, NVM_READ_ERROR, "NVM read error ( NVM content is invalid )") \
|
||||
X(10, COMMAND_REJECTED, "command rejected") \
|
||||
X(11, RESERVED2, "reserved2") \
|
||||
X(12, UNEXPECTED_MSG_FORMAT, "unexpected message format")
|
||||
|
||||
enum class ErrorCodeIU891ADevMgmt {
|
||||
#define X(num,text,info) text,
|
||||
LIST_OF_IU891A_DEVMGMT_ERROR_CODES
|
||||
#undef X
|
||||
UNKNOWN
|
||||
};
|
||||
|
||||
const char *toString(ErrorCodeIU891ADevMgmt ec);
|
||||
ErrorCodeIU891ADevMgmt toErrorCodeIU891ADevMgmt(uchar c);
|
||||
|
||||
#define WMBUSGW_GET_ACTIVE_CONFIGURATION_REQ 0x01
|
||||
#define WMBUSGW_GET_ACTIVE_CONFIGURATION_RSP 0x02
|
||||
#define WMBUSGW_SET_ACTIVE_CONFIGURATION_REQ 0x03
|
||||
#define WMBUSGW_SET_ACTIVE_CONFIGURATION_RSP 0x04
|
||||
#define WMBUSGW_GET_DEFAULT_CONFIGURATION_REQ 0x05
|
||||
#define WMBUSGW_GET_DEFAULT_CONFIGURATION_RSP 0x06
|
||||
#define WMBUSGW_SET_DEFAULT_CONFIGURATION_REQ 0x07
|
||||
#define WMBUSGW_SET_DEFAULT_CONFIGURATION_RSP 0x08
|
||||
#define WMBUSGW_RESET_DEFAULT_CONFIGURATION_REQ 0x09
|
||||
#define WMBUSGW_RESET_DEFAULT_CONFIGURATION_RSP 0x0a
|
||||
|
||||
#define WMBUSGW_CLEAR_WMBUS_DEVICE_LIST_REQ 0x11
|
||||
#define WMBUSGW_CLEAR_WMBUS_DEVICE_LIST_RSP 0x12
|
||||
#define WMBUSGW_APPEND_WMBUS_DEVICE_LIST_REQ 0x13
|
||||
#define WMBUSGW_APPEND_WMBUS_DEVICE_LIST_RSP 0x14
|
||||
#define WMBUSGW_READ_WMBUS_DEVICE_LIST_REQ 0x15
|
||||
#define WMBUSGW_READ_WMBUS_DEVICE_LIST_RSP 0x16
|
||||
#define WMBUSGW_SAVE_WMBUS_DEVICE_LIST_REQ 0x17
|
||||
#define WMBUSGW_SAVE_WMBUS_DEVICE_LIST_RSP 0x18
|
||||
#define WMBUSGW_LOAD_WMBUS_DEVICE_LIST_REQ 0x19
|
||||
#define WMBUSGW_LOAD_WMBUS_DEVICE_LIST_RSP 0x1a
|
||||
|
||||
#define WMBUSGW_RX_MESSAGE_IND 0x20
|
||||
|
||||
#define WMBUSGW_ENABLE_DISABLE_SCAN_MODE_REQ 0x21
|
||||
#define WMBUSGW_ENABLE_DISABLE_SCAN_MODE_RSP 0x22
|
||||
|
||||
#define WMBUSGW_SCAN_MODE_PACKET_IND 0x24
|
||||
|
||||
// Only iM881-XL and iM891A-XL
|
||||
#define WMBUSGW_SEND_MESSAGE_REQ 0x31
|
||||
#define WMBUSGW_SEND_MESSAGE_RSP 0x32
|
||||
|
||||
#define WMBUSGW_MESSAGE_PACKET_TRANSMITTED_IND 0x34
|
||||
|
||||
// Only iM881-XL and iM891A-XL
|
||||
#define WMBUSGW_ENCRYPT_SEND_MESSAGE_REQ 0x35
|
||||
#define WMBUSGW_ENCRYPT_SEND_MESSAGE_RSP 0x36
|
||||
|
||||
#define WMBUSGW_ENCRYPTED_MESSAGE_PACKET_TRANSMITTED_IND 0x38
|
||||
|
||||
// OnlyiU891A-XL
|
||||
#define WMBUSGW_SEND_PACKET_REQ 0x39
|
||||
#define WMBUSGW_SEND_PACKET_RSP 0x3A
|
||||
|
||||
// Only iU891A-XL
|
||||
#define WMBUSGW_ENCRYPT_SEND_PACKET_REQ 0x3b
|
||||
#define WMBUSGW_ENCRYPT_SEND_PACKET_RSP 0x3c
|
||||
|
||||
#define WMBUSGW_GET_APPLICATION_STATUS_REQ 0x41
|
||||
#define WMBUSGW_GET_APPLICATION_STATUS_RSP 0x42
|
||||
|
||||
#define WMBUSGW_RESET_APPLICATION_STATUS_REQ 0x43
|
||||
#define WMBUSGW_RESET_APPLICATION_STATUS_RSP 0x44
|
||||
|
||||
// Only iM881A-XL, iM891A-XL
|
||||
#define WMBUSGW_GET_RADIO_CONTROL_CONFIG_REQ 0x51
|
||||
#define WMBUSGW_GET_RADIO_CONTROL_CONFIG_RSP 0x52
|
||||
#define WMBUSGW_SET_RADIO_CONTROL_CONFIG_REQ 0x53
|
||||
#define WMBUSGW_SET_RADIO_CONTROL_CONFIG_RSP 0x54
|
||||
|
||||
// Only iU891A-XL
|
||||
#define WMBUSGW_GET_WMBUS_ADDRESS_REQ 0x81
|
||||
#define WMBUSGW_GET_WMBUS_ADDRESS_RSP 0x82
|
||||
|
||||
#define LIST_OF_IU891A_WMBUSGW_ERROR_CODES \
|
||||
X(0, OK, "ok") \
|
||||
X(1, ERROR, "error") \
|
||||
X(2, COMMAND_NOT_SUPPORTED, "command not supported") \
|
||||
X(3, WRONG_PARAMETER, "wrong parameter") \
|
||||
X(4, WRONG_APPLICATION_MODE, "wrong application mode") \
|
||||
X(5, NO_MORE_DATA, "no more data") \
|
||||
X(6, APPLICATION_BUSY, "application busy, try later") \
|
||||
X(7, WRONG_MSG_LENGTH, "wrong message length") \
|
||||
X(8, NVM_WRITE_ERROR, "NVM write error") \
|
||||
X(9, NVM_READ_ERROR, "NVM read error ( NVM content is invalid )") \
|
||||
X(10, COMMAND_REJECTED, "command rejected, execution not possible in current application state") \
|
||||
X(11, ACCESS_DENIED, "access denied, operation may need another system operation mode") \
|
||||
X(12, DATA_TRUNCATED, "data truncated") \
|
||||
X(13, UNSUPPORTED_ENCRYPTION_MODE, "unsupported encryption mode") \
|
||||
X(14, NO_ENCRYPTION_KEY_FOUND, "no encryption key found for given meter address") \
|
||||
X(15, ENCRYPTION_PARAMETER_MISSING, "encryption info / parameter missing") \
|
||||
X(16, ENCRYPTION_ERROR, "encryption error")
|
||||
|
||||
enum class ErrorCodeIU891AWMBUSGW {
|
||||
#define X(num,text,info) text,
|
||||
LIST_OF_IU891A_WMBUSGW_ERROR_CODES
|
||||
#undef X
|
||||
UNKNOWN
|
||||
};
|
||||
|
||||
const char *toString(ErrorCodeIU891AWMBUSGW ec);
|
||||
ErrorCodeIU891AWMBUSGW toErrorCodeIU891AWMBUSGW(uchar c);
|
||||
Ładowanie…
Reference in New Issue