kopia lustrzana https://github.com/weetmuts/wmbusmeters
Add foundation for iu880b dongle to receive lora.
rodzic
111e276376
commit
d254ce5066
1
Makefile
1
Makefile
|
@ -134,6 +134,7 @@ PROG_OBJS:=\
|
||||||
$(BUILD)/wmbus_rawtty.o \
|
$(BUILD)/wmbus_rawtty.o \
|
||||||
$(BUILD)/wmbus_rc1180.o \
|
$(BUILD)/wmbus_rc1180.o \
|
||||||
$(BUILD)/wmbus_utils.o \
|
$(BUILD)/wmbus_utils.o \
|
||||||
|
$(BUILD)/lora_iu880b.o \
|
||||||
|
|
||||||
# If you run: "make DRIVER=minomess" then only driver_minomess.cc will be compiled into wmbusmeters.
|
# If you run: "make DRIVER=minomess" then only driver_minomess.cc will be compiled into wmbusmeters.
|
||||||
# The old style drivers meter_xyz.cc must always be compiled in, but eventually they will be gone.
|
# The old style drivers meter_xyz.cc must always be compiled in, but eventually they will be gone.
|
||||||
|
|
|
@ -57,7 +57,8 @@ LIST_OF_MAIN_MENU
|
||||||
X(AMB8465, "amb8465") \
|
X(AMB8465, "amb8465") \
|
||||||
X(CUL, "cul") \
|
X(CUL, "cul") \
|
||||||
X(IM871A, "im871a") \
|
X(IM871A, "im871a") \
|
||||||
X(RC1180, "rc1180")
|
X(RC1180, "rc1180") \
|
||||||
|
X(IU880B, "iu880b")
|
||||||
|
|
||||||
enum class ReceiversType {
|
enum class ReceiversType {
|
||||||
#define X(name,description) name,
|
#define X(name,description) name,
|
||||||
|
@ -245,6 +246,9 @@ void detectWMBUSReceiver()
|
||||||
case ReceiversType::IM871A:
|
case ReceiversType::IM871A:
|
||||||
probeFor("im871a/im170a", detectIM871AIM170A);
|
probeFor("im871a/im170a", detectIM871AIM170A);
|
||||||
break;
|
break;
|
||||||
|
case ReceiversType::IU880B:
|
||||||
|
probeFor("iu880b", detectIU880B);
|
||||||
|
break;
|
||||||
case ReceiversType::RC1180:
|
case ReceiversType::RC1180:
|
||||||
probeFor("rc1180", detectRC1180);
|
probeFor("rc1180", detectRC1180);
|
||||||
break;
|
break;
|
||||||
|
@ -292,6 +296,9 @@ void resetWMBUSReceiver()
|
||||||
case ReceiversType::RC1180:
|
case ReceiversType::RC1180:
|
||||||
notImplementedYet("Resetting RC1180");
|
notImplementedYet("Resetting RC1180");
|
||||||
break;
|
break;
|
||||||
|
case ReceiversType::IU880B:
|
||||||
|
notImplementedYet("Resetting IU880B");
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -235,6 +235,12 @@ shared_ptr<WMBus> BusManager::createWmbusObject(Detected *detected, Configuratio
|
||||||
wmbus = openRC1180(*detected, serial_manager_, serial_override);
|
wmbus = openRC1180(*detected, serial_manager_, serial_override);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case DEVICE_IU880B:
|
||||||
|
{
|
||||||
|
verbose("(iu880b) on %s\n", detected->found_file.c_str());
|
||||||
|
wmbus = openIU880B(*detected, serial_manager_, serial_override);
|
||||||
|
break;
|
||||||
|
}
|
||||||
case DEVICE_UNKNOWN:
|
case DEVICE_UNKNOWN:
|
||||||
warning("(main) internal error! cannot create an unknown device! exiting!\n");
|
warning("(main) internal error! cannot create an unknown device! exiting!\n");
|
||||||
if (config->daemon) {
|
if (config->daemon) {
|
||||||
|
|
|
@ -0,0 +1,572 @@
|
||||||
|
/*
|
||||||
|
Copyright (C) 2022 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"lora_iu880b.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;
|
||||||
|
|
||||||
|
static void buildRequest(int endpoint_id, int msg_id, vector<uchar>& body, vector<uchar>& out);
|
||||||
|
|
||||||
|
struct IU880BDeviceInfo
|
||||||
|
{
|
||||||
|
// 0x90 = iM880A (obsolete) 0x92 = iM880A-L (128k) 0x93 = iU880A (128k) 0x98 = iM880B 0x99 = iU880B 0xA0 = iM881A 0xA1 = iU881A
|
||||||
|
uchar module_type {};
|
||||||
|
uint16_t device_address {};
|
||||||
|
uchar group_address {};
|
||||||
|
string uid;
|
||||||
|
|
||||||
|
string str()
|
||||||
|
{
|
||||||
|
string s;
|
||||||
|
if (module_type == 0x90) s += "im880a ";
|
||||||
|
else if (module_type == 0x92) s += "im880al ";
|
||||||
|
else if (module_type == 0x93) s += "iu880a ";
|
||||||
|
else if (module_type == 0x98) s += "im880b ";
|
||||||
|
else if (module_type == 0x99) s += "iu880b ";
|
||||||
|
else if (module_type == 0xa0) s += "im881a ";
|
||||||
|
else if (module_type == 0xa1) s += "iu881a ";
|
||||||
|
else s += "unknown_type("+to_string(module_type)+") ";
|
||||||
|
|
||||||
|
s += tostrprintf("address %04x/%02x uid %s", device_address, group_address, uid.c_str());
|
||||||
|
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool decode(vector<uchar> &bytes)
|
||||||
|
{
|
||||||
|
if (bytes.size() < 9) return false;
|
||||||
|
int i = 0;
|
||||||
|
module_type = bytes[i++];
|
||||||
|
device_address = bytes[i] | bytes[i+1];
|
||||||
|
i+=2;
|
||||||
|
group_address = bytes[i++];
|
||||||
|
i++; // skip reserved
|
||||||
|
uid = tostrprintf("%02x%02x%02x%02x", bytes[i+3], bytes[i+2], bytes[i+1], bytes[i]);
|
||||||
|
i+=4;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct IU880BConfig
|
||||||
|
{
|
||||||
|
// 0x90 = iM880A (obsolete) 0x92 = iM880A-L (128k) 0x93 = iU880A (128k) 0x98 = iM880B 0x99 = iU880B 0xA0 = iM881A 0xA1 = iU881A
|
||||||
|
uchar module_type {};
|
||||||
|
uint16_t device_address {};
|
||||||
|
uchar group_address {};
|
||||||
|
string uid;
|
||||||
|
|
||||||
|
string str()
|
||||||
|
{
|
||||||
|
string s;
|
||||||
|
if (module_type == 0x90) s += "im880a ";
|
||||||
|
else if (module_type == 0x92) s += "im880al ";
|
||||||
|
else if (module_type == 0x93) s += "iu880a ";
|
||||||
|
else if (module_type == 0x98) s += "im880b ";
|
||||||
|
else if (module_type == 0x99) s += "iu880b ";
|
||||||
|
else if (module_type == 0xa0) s += "im881a ";
|
||||||
|
else if (module_type == 0xa1) s += "iu881a ";
|
||||||
|
else s += "unknown_type("+to_string(module_type)+") ";
|
||||||
|
|
||||||
|
s += tostrprintf("address %04x/%02x uid %s", device_address, group_address, uid.c_str());
|
||||||
|
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool decode(vector<uchar> &bytes)
|
||||||
|
{
|
||||||
|
if (bytes.size() < 9) return false;
|
||||||
|
int i = 0;
|
||||||
|
module_type = bytes[i++];
|
||||||
|
device_address = bytes[i] | bytes[i+1];
|
||||||
|
i+=2;
|
||||||
|
group_address = bytes[i++];
|
||||||
|
i++; // skip reserved
|
||||||
|
uid = tostrprintf("%02x%02x%02x%02x", bytes[i+3], bytes[i+2], bytes[i+1], bytes[i]);
|
||||||
|
i+=4;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct LoRaIU880B : public virtual WMBusCommonImplementation
|
||||||
|
{
|
||||||
|
bool ping();
|
||||||
|
string getDeviceId();
|
||||||
|
string getDeviceUniqueId();
|
||||||
|
uchar getFirmwareVersion();
|
||||||
|
LinkModeSet getLinkModes();
|
||||||
|
void deviceReset();
|
||||||
|
void deviceSetLinkModes(LinkModeSet lms);
|
||||||
|
LinkModeSet supportedLinkModes()
|
||||||
|
{
|
||||||
|
return LORA_bit;
|
||||||
|
}
|
||||||
|
int numConcurrentLinkModes() {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
bool canSetLinkModes(LinkModeSet lms)
|
||||||
|
{
|
||||||
|
if (lms.empty()) return false;
|
||||||
|
if (!supportedLinkModes().supports(lms)) return false;
|
||||||
|
// Otherwise its a single link mode.
|
||||||
|
return 1 == countSetBits(lms.asBits());
|
||||||
|
}
|
||||||
|
bool sendTelegram(ContentStartsWith starts_with, vector<uchar> &content);
|
||||||
|
void processSerialData();
|
||||||
|
void simulate() { }
|
||||||
|
|
||||||
|
LoRaIU880B(string alias, shared_ptr<SerialDevice> serial, shared_ptr<SerialCommunicationManager> manager);
|
||||||
|
~LoRaIU880B() {
|
||||||
|
}
|
||||||
|
|
||||||
|
static FrameStatus checkIU880BFrame(vector<uchar> &data,
|
||||||
|
vector<uchar> &out,
|
||||||
|
size_t *frame_length,
|
||||||
|
int *endpoint_id_out,
|
||||||
|
int *msg_id_out,
|
||||||
|
int *status_out,
|
||||||
|
int *rssi_dbm);
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
vector<uchar> read_buffer_;
|
||||||
|
vector<uchar> request_;
|
||||||
|
vector<uchar> response_;
|
||||||
|
|
||||||
|
bool getDeviceInfo();
|
||||||
|
bool loaded_device_info_ {};
|
||||||
|
IU880BDeviceInfo device_info_;
|
||||||
|
|
||||||
|
friend AccessCheck detectIU880B(Detected *detected, shared_ptr<SerialCommunicationManager> manager);
|
||||||
|
void handleDevMgmt(int msgid, vector<uchar> &payload);
|
||||||
|
void handleRadioLink(int msgid, vector<uchar> &payload, int rssi_dbm);
|
||||||
|
void handleRadioLinkTest(int msgid, vector<uchar> &payload);
|
||||||
|
void handleHWTest(int msgid, vector<uchar> &payload);
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
shared_ptr<WMBus> openIU880B(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)
|
||||||
|
{
|
||||||
|
LoRaIU880B *imp = new LoRaIU880B(bus_alias, serial_override, manager);
|
||||||
|
imp->markAsNoLongerSerial();
|
||||||
|
return shared_ptr<WMBus>(imp);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto serial = manager->createSerialDeviceTTY(device_file.c_str(), 115200, PARITY::NONE, "iu880b");
|
||||||
|
LoRaIU880B *imp = new LoRaIU880B(bus_alias, serial, manager);
|
||||||
|
return shared_ptr<WMBus>(imp);
|
||||||
|
}
|
||||||
|
|
||||||
|
LoRaIU880B::LoRaIU880B(string alias, shared_ptr<SerialDevice> serial, shared_ptr<SerialCommunicationManager> manager) :
|
||||||
|
WMBusCommonImplementation(alias, WMBusDeviceType::DEVICE_IU880B, manager, serial, true)
|
||||||
|
{
|
||||||
|
reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool LoRaIU880B::ping()
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
if (serial()->readonly()) return true; // Feeding from stdin or file.
|
||||||
|
|
||||||
|
LOCK_WMBUS_EXECUTING_COMMAND(ping);
|
||||||
|
|
||||||
|
request_.resize(4);
|
||||||
|
request_[0] = IU880B_SERIAL_SOF;
|
||||||
|
request_[1] = DEVMGMT_ID;
|
||||||
|
request_[2] = DEVMGMT_MSG_PING_REQ;
|
||||||
|
request_[3] = 0;
|
||||||
|
|
||||||
|
verbose("(iu880b) ping\n");
|
||||||
|
bool sent = serial()->send(request_);
|
||||||
|
|
||||||
|
if (sent) return waitForResponse(DEVMGMT_MSG_PING_RSP);
|
||||||
|
*/
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
string LoRaIU880B::getDeviceId()
|
||||||
|
{
|
||||||
|
if (serial()->readonly()) return "?"; // Feeding from stdin or file.
|
||||||
|
if (cached_device_id_ != "") return cached_device_id_;
|
||||||
|
|
||||||
|
bool ok = getDeviceInfo();
|
||||||
|
if (!ok) return "ER1R";
|
||||||
|
|
||||||
|
cached_device_id_ = device_info_.uid;
|
||||||
|
|
||||||
|
verbose("(iu880b) got device id %s\n", cached_device_id_.c_str());
|
||||||
|
|
||||||
|
return cached_device_id_;
|
||||||
|
}
|
||||||
|
|
||||||
|
string LoRaIU880B::getDeviceUniqueId()
|
||||||
|
{
|
||||||
|
return getDeviceId();
|
||||||
|
}
|
||||||
|
|
||||||
|
uchar LoRaIU880B::getFirmwareVersion()
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
LinkModeSet LoRaIU880B::getLinkModes()
|
||||||
|
{
|
||||||
|
return LORA_bit;
|
||||||
|
}
|
||||||
|
|
||||||
|
void LoRaIU880B::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.
|
||||||
|
}
|
||||||
|
|
||||||
|
void LoRaIU880B::deviceSetLinkModes(LinkModeSet lms)
|
||||||
|
{
|
||||||
|
if (serial()->readonly()) return; // Feeding from stdin or file.
|
||||||
|
|
||||||
|
if (!canSetLinkModes(lms))
|
||||||
|
{
|
||||||
|
string modes = lms.hr();
|
||||||
|
error("(iu880b) setting link mode(s) %s is not supported for iu880b\n", modes.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
LOCK_WMBUS_EXECUTING_COMMAND(set_link_modes);
|
||||||
|
|
||||||
|
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 = { 0x02 }; // 2 means listen to all traffic
|
||||||
|
request_.clear();
|
||||||
|
buildRequest(DEVMGMT_ID, DEVMGMT_MSG_SET_RADIO_MODE_REQ, body, request_);
|
||||||
|
|
||||||
|
verbose("(iu880b) set link mode lora listen to all\n");
|
||||||
|
|
||||||
|
bool sent = serial()->send(request_);
|
||||||
|
if (!sent) return; // tty overridden with stdin/file
|
||||||
|
|
||||||
|
bool ok = waitForResponse(DEVMGMT_MSG_SET_RADIO_MODE_RSP);
|
||||||
|
if (!ok) return; // timeout
|
||||||
|
}
|
||||||
|
|
||||||
|
FrameStatus LoRaIU880B::checkIU880BFrame(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 LoRaIU880B::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 = checkIU880BFrame(read_buffer_,
|
||||||
|
payload,
|
||||||
|
&frame_length,
|
||||||
|
&endpoint_id,
|
||||||
|
&msg_id,
|
||||||
|
&status_byte,
|
||||||
|
&rssi_dbm);
|
||||||
|
|
||||||
|
if (status == PartialFrame)
|
||||||
|
{
|
||||||
|
if (read_buffer_.size() > 0)
|
||||||
|
{
|
||||||
|
debugPayload("(iu880b) partial frame, expecting more.", read_buffer_);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (status == ErrorInFrame)
|
||||||
|
{
|
||||||
|
debugPayload("(iu880b) 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 DEVMGMT_ID: handleDevMgmt(msg_id, payload); break;
|
||||||
|
case RADIOLINK_ID: handleRadioLink(msg_id, payload, rssi_dbm); break;
|
||||||
|
case HWTEST_ID: handleHWTest(msg_id, payload); break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void LoRaIU880B::handleDevMgmt(int msgid, vector<uchar> &payload)
|
||||||
|
{
|
||||||
|
switch (msgid) {
|
||||||
|
case DEVMGMT_MSG_PING_RSP:
|
||||||
|
verbose("(iu880b) pong\n");
|
||||||
|
notifyResponseIsHere(DEVMGMT_MSG_PING_RSP);
|
||||||
|
break;
|
||||||
|
case DEVMGMT_MSG_GET_DEVICE_INFO_RSP:
|
||||||
|
verbose("(iu880b) device info completed\n");
|
||||||
|
response_.clear();
|
||||||
|
response_.insert(response_.end(), payload.begin(), payload.end());
|
||||||
|
notifyResponseIsHere(DEVMGMT_MSG_GET_DEVICE_INFO_RSP);
|
||||||
|
break;
|
||||||
|
case DEVMGMT_MSG_SET_RADIO_MODE_RSP:
|
||||||
|
verbose("(iu880b) device set radio mode completed\n");
|
||||||
|
response_.clear();
|
||||||
|
response_.insert(response_.end(), payload.begin(), payload.end());
|
||||||
|
notifyResponseIsHere(DEVMGMT_MSG_SET_RADIO_MODE_RSP);
|
||||||
|
break;
|
||||||
|
case DEVMGMT_MSG_GET_FW_INFO_RSP:
|
||||||
|
verbose("(iu880b) device get firmware\n");
|
||||||
|
response_.clear();
|
||||||
|
response_.insert(response_.end(), payload.begin(), payload.end());
|
||||||
|
notifyResponseIsHere(DEVMGMT_MSG_GET_FW_INFO_RSP);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
verbose("(iu880b) Unhandled device management message %d\n", msgid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void LoRaIU880B::handleRadioLink(int msgid, vector<uchar> &frame, int rssi_dbm)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void LoRaIU880B::handleRadioLinkTest(int msgid, vector<uchar> &payload)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void LoRaIU880B::handleHWTest(int msgid, vector<uchar> &payload)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool LoRaIU880B::getDeviceInfo()
|
||||||
|
{
|
||||||
|
if (loaded_device_info_) return true;
|
||||||
|
|
||||||
|
LOCK_WMBUS_EXECUTING_COMMAND(get_device_info);
|
||||||
|
|
||||||
|
vector<uchar> empty_body;
|
||||||
|
request_.clear();
|
||||||
|
buildRequest(DEVMGMT_ID, DEVMGMT_MSG_GET_DEVICE_INFO_REQ, empty_body, request_);
|
||||||
|
|
||||||
|
verbose("(iu880b) get device info\n");
|
||||||
|
|
||||||
|
vector<uchar> init;
|
||||||
|
init.resize(30);
|
||||||
|
for (int i=0; i<30; ++i)
|
||||||
|
{
|
||||||
|
init[i] = 0xc0;
|
||||||
|
}
|
||||||
|
// Wake-up the dongle.
|
||||||
|
serial()->send(init);
|
||||||
|
|
||||||
|
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_);
|
||||||
|
|
||||||
|
loaded_device_info_ = true;
|
||||||
|
verbose("(iu880b) device info: %s\n", device_info_.str().c_str());
|
||||||
|
|
||||||
|
request_.clear();
|
||||||
|
buildRequest(DEVMGMT_ID, DEVMGMT_MSG_GET_FW_INFO_REQ, empty_body, request_);
|
||||||
|
|
||||||
|
serial()->send(init);
|
||||||
|
sent = serial()->send(request_);
|
||||||
|
if (!sent) return false; // tty overridden with stdin/file
|
||||||
|
|
||||||
|
ok = waitForResponse(DEVMGMT_MSG_GET_FW_INFO_RSP);
|
||||||
|
if (!ok) return false; // timeout
|
||||||
|
|
||||||
|
verbose("(iu880b) get firmware\n");
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool LoRaIU880B::sendTelegram(ContentStartsWith starts_with, vector<uchar> &content)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
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());
|
||||||
|
request.push_back(crc & 0xff);
|
||||||
|
request.push_back(crc >> 8);
|
||||||
|
|
||||||
|
addSlipFraming(request, out);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
AccessCheck detectIU880B(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 iu880b");
|
||||||
|
serial->disableCallbacks();
|
||||||
|
bool ok = serial->open(false);
|
||||||
|
if (!ok)
|
||||||
|
{
|
||||||
|
verbose("(iu880b) 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(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 = LoRaIU880B::checkIU880BFrame(response,
|
||||||
|
payload,
|
||||||
|
&frame_length,
|
||||||
|
&endpoint_id,
|
||||||
|
&msg_id,
|
||||||
|
&status_byte,
|
||||||
|
&rssi_dbm);
|
||||||
|
|
||||||
|
if (status != FullFrame ||
|
||||||
|
endpoint_id != DEVMGMT_ID ||
|
||||||
|
msg_id != DEVMGMT_MSG_GET_DEVICE_INFO_RSP)
|
||||||
|
{
|
||||||
|
verbose("(iu880b) are you there? no.\n");
|
||||||
|
serial->close();
|
||||||
|
return AccessCheck::NoProperResponse;
|
||||||
|
}
|
||||||
|
|
||||||
|
serial->close();
|
||||||
|
|
||||||
|
debugPayload("(iu880b) device info response", payload);
|
||||||
|
|
||||||
|
debug("(iu880b) endpoint %02x msg %02x status %02x\n", endpoint_id, msg_id, status_byte);
|
||||||
|
|
||||||
|
IU880BDeviceInfo di;
|
||||||
|
di.decode(payload);
|
||||||
|
|
||||||
|
debug("(iu880b) info: %s\n", di.str().c_str());
|
||||||
|
|
||||||
|
detected->setAsFound(di.uid, WMBusDeviceType::DEVICE_IU880B, 115200, false, detected->specified_device.linkmodes);
|
||||||
|
|
||||||
|
verbose("(iu880b) are you there? yes %s\n", di.uid.c_str());
|
||||||
|
|
||||||
|
return AccessCheck::AccessOK;
|
||||||
|
}
|
|
@ -0,0 +1,101 @@
|
||||||
|
// Copyright (C) 2022 Fredrik Öhrström (CC0-1.0)
|
||||||
|
// Definitions from WiMOD_LR_Base_HCI_Spec_V1_10.pdf
|
||||||
|
|
||||||
|
#define DEVMGMT_ID 0x01
|
||||||
|
#define RTL_ID 0x02
|
||||||
|
#define RADIOLINK_ID 0x03
|
||||||
|
#define REMOTE_CTRL_ID 0x04
|
||||||
|
#define HWTEST_ID 0xA1
|
||||||
|
|
||||||
|
#define DEVMGMT_STATUS_OK 0x00
|
||||||
|
#define DEVMGMT_STATUS_ERROR 0x01
|
||||||
|
#define DEVMGMT_STATUS_CMD_NOT_SUPPORTED 0x02
|
||||||
|
#define DEVMGMT_STATUS_WRONG_PARAMETER 0x03
|
||||||
|
|
||||||
|
#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_RESET_REQ 0x07
|
||||||
|
#define DEVMGMT_MSG_RESET_RSP 0x08
|
||||||
|
#define DEVMGMT_MSG_SET_OPMODE_REQ 0x09
|
||||||
|
#define DEVMGMT_MSG_SET_OPMODE_RSP 0x0A
|
||||||
|
#define DEVMGMT_MSG_GET_OPMODE_REQ 0x0B
|
||||||
|
#define DEVMGMT_MSG_GET_OPMODE_RSP 0x0C
|
||||||
|
#define DEVMGMT_MSG_SET_RTC_REQ 0x0D
|
||||||
|
#define DEVMGMT_MSG_SET_RTC_RSP 0x0E
|
||||||
|
#define DEVMGMT_MSG_GET_RTC_REQ 0x0F
|
||||||
|
#define DEVMGMT_MSG_GET_RTC_RSP 0x10
|
||||||
|
#define DEVMGMT_MSG_SET_RADIO_CONFIG_REQ 0x11
|
||||||
|
#define DEVMGMT_MSG_SET_RADIO_CONFIG_RSP 0x12
|
||||||
|
#define DEVMGMT_MSG_GET_RADIO_CONFIG_REQ 0x13
|
||||||
|
#define DEVMGMT_MSG_GET_RADIO_CONFIG_RSP 0x14
|
||||||
|
#define DEVMGMT_MSG_RESET_RADIO_CONFIG_REQ 0x15
|
||||||
|
|
||||||
|
#define DEVMGMT_MSG_RESET_RADIO_CONFIG_RSP 0x16
|
||||||
|
#define DEVMGMT_MSG_GET_SYSTEM_STATUS_REQ 0x17
|
||||||
|
#define DEVMGMT_MSG_GET_SYSTEM_STATUS_RSP 0x18
|
||||||
|
#define DEVMGMT_MSG_SET_RADIO_MODE_REQ 0x19
|
||||||
|
#define DEVMGMT_MSG_SET_RADIO_MODE_RSP 0x1A
|
||||||
|
// obsolete in V1.9
|
||||||
|
#define DEVMGMT_MSG_ENTER_LPM_REQ 0x1B
|
||||||
|
// obsolete in V1.9
|
||||||
|
#define DEVMGMT_MSG_ENTER_LPM_RSP 0x1C
|
||||||
|
// Firmware V1.6
|
||||||
|
#define DEVMGMT_MSG_POWER_UP_IND 0x20
|
||||||
|
// Firmware V1.10
|
||||||
|
#define DEVMGMT_MSG_SET_AES_KEY_REQ 0x21
|
||||||
|
#define DEVMGMT_MSG_SET_AES_KEY_RSP 0x22
|
||||||
|
#define DEVMGMT_MSG_GET_AES_KEY_REQ 0x23
|
||||||
|
#define DEVMGMT_MSG_GET_AES_KEY_RSP 0x24
|
||||||
|
|
||||||
|
#define RADIOLINK_MSG_SEND_U_DATA_REQ 0x01
|
||||||
|
#define RADIOLINK_MSG_SEND_U_DATA_RSP 0x02
|
||||||
|
#define RADIOLINK_MSG_U_DATA_RX_IND 0x04
|
||||||
|
// Firmware V1.6
|
||||||
|
#define RADIOLINK_MSG_U_DATA_TX_IND 0x06
|
||||||
|
#define RADIOLINK_MSG_RAW_DATA_RX_IND 0x08
|
||||||
|
#define RADIOLINK_MSG_SEND_C_DATA_REQ 0x09
|
||||||
|
#define RADIOLINK_MSG_SEND_C_DATA_RSP 0x0A
|
||||||
|
#define RADIOLINK_MSG_C_DATA_RX_IND 0x0C
|
||||||
|
#define RADIOLINK_MSG_C_DATA_TX_IND 0x0E
|
||||||
|
#define RADIOLINK_MSG_ACK_RX_IND 0x10
|
||||||
|
#define RADIOLINK_MSG_ACK_TIMEOUT_IND 0x12
|
||||||
|
#define RADIOLINK_MSG_ACK_TX_IND 0x14
|
||||||
|
#define RADIOLINK_MSG_SET_ACK_DATA_REQ 0x15
|
||||||
|
#define RADIOLINK_MSG_SET_ACK_DATA_RSP 0x16
|
||||||
|
|
||||||
|
#define RADIOLINK_STATUS_OK 0x00
|
||||||
|
#define RADIOLINK_STATUS_ERROR 0x01
|
||||||
|
#define RADOLINK_STATUS_CMD_NOT_SUPPORTED 0x02
|
||||||
|
#define RADIOLINK_STATUS_WRONG_PARAMETER 0x03
|
||||||
|
#define RADIOLINK_STATUS_WRONG_RADIO_MODE 0x04
|
||||||
|
// Firmware V1.0
|
||||||
|
#define RADIOLINK_STATUS_MEDIA_BUSY 0x05
|
||||||
|
#define RADIOLINK_STATUS_BUFFER_FULL 0x07
|
||||||
|
#define RADIOLINK_STATUS_LENGTH_ERROR 0x08
|
||||||
|
|
||||||
|
|
||||||
|
#define RLT_MSG_START_REQ 0x01
|
||||||
|
#define RLT_MSG_START_RSP 0x02
|
||||||
|
#define RLT_MSG_STOP_REQ 0x03
|
||||||
|
#define RLT_MSG_STOP_RSP 0x04
|
||||||
|
#define RLT_MSG_STATUS_IND 0x06
|
||||||
|
|
||||||
|
#define RLT_STATUS_OK 0x00
|
||||||
|
#define RLT_STATUS_ERROR 0x01
|
||||||
|
#define RLT_STATUS_CMD_NOT_SUPPORTED 0x02
|
||||||
|
#define RLT_STATUS_WRONG_PARAMETER 0x03
|
||||||
|
#define RLT_STATUS_WRONG_RADIO_MODE 0x04
|
||||||
|
|
||||||
|
#define HWTEST_MSG_RADIO_TEST_REQ 0x01
|
||||||
|
#define HWTEST_MSG_RADIO_TEST_RSP 0x02
|
||||||
|
|
||||||
|
#define HWTEST_STATUS_OK 0x00
|
||||||
|
#define HWTEST_STATUS_ERROR 0x01
|
||||||
|
#define HWTEST_STATUS_CMD_NOT_SUPPORTED 0x02
|
||||||
|
#define HWTEST_STATUS_WRONG_PARAMETER 0x03
|
||||||
|
|
||||||
|
#define REMOTE_CTRL_MSG_BUTTON_PRESSED_IND 0x02
|
|
@ -489,6 +489,14 @@ bool start(Configuration *config)
|
||||||
// to achive a nice shutdown.
|
// to achive a nice shutdown.
|
||||||
onExit(call(serial_manager_.get(),stop));
|
onExit(call(serial_manager_.get(),stop));
|
||||||
|
|
||||||
|
/*
|
||||||
|
Detected d;
|
||||||
|
d.specified_device.file = "/dev/ttyUSB0";
|
||||||
|
d.found_file = "/dev/ttyUSB0";
|
||||||
|
d.specified_device.type = WMBusDeviceType::DEVICE_IU880B;
|
||||||
|
|
||||||
|
detectIU880B(&d, serial_manager_);
|
||||||
|
*/
|
||||||
// Create the printer object that knows how to translate
|
// Create the printer object that knows how to translate
|
||||||
// telegrams into json, fields that are written into log files
|
// telegrams into json, fields that are written into log files
|
||||||
// or sent to shell invocations.
|
// or sent to shell invocations.
|
||||||
|
|
|
@ -247,9 +247,11 @@ void addDefaultManufacturerKeyIfAny(const vector<uchar> &frame, TPLSecurityMode
|
||||||
&& tpl_sec_mode == TPLSecurityMode::AES_CBC_IV
|
&& tpl_sec_mode == TPLSecurityMode::AES_CBC_IV
|
||||||
&& detectDiehlFrameInterpretation(frame) == DiehlFrameInterpretation::OMS)
|
&& detectDiehlFrameInterpretation(frame) == DiehlFrameInterpretation::OMS)
|
||||||
{
|
{
|
||||||
vector<uchar> half; hex2bin(PRIOS_DEFAULT_KEY2, &half);
|
vector<uchar> half;
|
||||||
|
hex2bin(PRIOS_DEFAULT_KEY2, &half);
|
||||||
meter_keys->confidentiality_key = vector<uchar>(half.begin(), half.end());
|
meter_keys->confidentiality_key = vector<uchar>(half.begin(), half.end());
|
||||||
meter_keys->confidentiality_key.insert(meter_keys->confidentiality_key.end(), half.begin(), half.end());
|
meter_keys->confidentiality_key.insert(meter_keys->confidentiality_key.end(), half.begin(), half.end());
|
||||||
|
debug("(mfct) added default key\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -45,6 +45,7 @@ void test_aes();
|
||||||
void test_sbc();
|
void test_sbc();
|
||||||
void test_hex();
|
void test_hex();
|
||||||
void test_translate();
|
void test_translate();
|
||||||
|
void test_slip();
|
||||||
|
|
||||||
int main(int argc, char **argv)
|
int main(int argc, char **argv)
|
||||||
{
|
{
|
||||||
|
@ -76,6 +77,7 @@ int main(int argc, char **argv)
|
||||||
test_sbc();
|
test_sbc();
|
||||||
test_hex();
|
test_hex();
|
||||||
test_translate();
|
test_translate();
|
||||||
|
test_slip();
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -1085,3 +1087,67 @@ void test_translate()
|
||||||
printf("ERROR expected \"%s\" but got \"%s\"\n", e.c_str(), s.c_str());
|
printf("ERROR expected \"%s\" but got \"%s\"\n", e.c_str(), s.c_str());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void test_slip()
|
||||||
|
{
|
||||||
|
vector<uchar> from = { 1, 0xc0, 3, 4, 5, 0xdb };
|
||||||
|
vector<uchar> expected_to = { 0xc0, 1, 0xdb, 0xdc, 3, 4, 5, 0xdb, 0xdd, 0xc0 };
|
||||||
|
vector<uchar> to;
|
||||||
|
vector<uchar> back;
|
||||||
|
|
||||||
|
addSlipFraming(from, to);
|
||||||
|
|
||||||
|
if (expected_to != to)
|
||||||
|
{
|
||||||
|
printf("ERROR slip 1\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t frame_length = 0;
|
||||||
|
removeSlipFraming(to, &frame_length, back);
|
||||||
|
|
||||||
|
if (back != from)
|
||||||
|
{
|
||||||
|
printf("ERROR slip 2\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (to.size() != frame_length)
|
||||||
|
{
|
||||||
|
printf("ERROR slip 3\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
vector<uchar> more = { 0xc0, 0xc0, 0xc0, 1, 2, 3, 4, 5, 6, 7, 8 };
|
||||||
|
addSlipFraming(more, to);
|
||||||
|
|
||||||
|
frame_length = 0;
|
||||||
|
removeSlipFraming(to, &frame_length, back);
|
||||||
|
|
||||||
|
if (back != from)
|
||||||
|
{
|
||||||
|
printf("ERROR slip 4\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
to.erase(to.begin(), to.begin()+frame_length);
|
||||||
|
removeSlipFraming(to, &frame_length, back);
|
||||||
|
|
||||||
|
if (back != more)
|
||||||
|
{
|
||||||
|
printf("ERROR slip 5\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
vector<uchar> again = { 0xc0 };
|
||||||
|
removeSlipFraming(again, &frame_length, back);
|
||||||
|
|
||||||
|
if (frame_length != 0)
|
||||||
|
{
|
||||||
|
printf("ERROR slip 6\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
vector<uchar> againn = { 0xc0, 1, 2, 3, 4, 5 };
|
||||||
|
removeSlipFraming(againn, &frame_length, back);
|
||||||
|
|
||||||
|
if (frame_length != 0)
|
||||||
|
{
|
||||||
|
printf("ERROR slip 7\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
77
src/util.cc
77
src/util.cc
|
@ -2084,3 +2084,80 @@ string reverseBinaryAsciiSafeToString(string v)
|
||||||
reverse(bytes.begin(), bytes.end());
|
reverse(bytes.begin(), bytes.end());
|
||||||
return safeString(bytes);
|
return safeString(bytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define SLIP_END 0xc0 /* indicates end of packet */
|
||||||
|
#define SLIP_ESC 0xdb /* indicates byte stuffing */
|
||||||
|
#define SLIP_ESC_END 0xdc /* ESC ESC_END means END data byte */
|
||||||
|
#define SLIP_ESC_ESC 0xdd /* ESC ESC_ESC means ESC data byte */
|
||||||
|
|
||||||
|
void addSlipFraming(vector<uchar>& from, vector<uchar> &to)
|
||||||
|
{
|
||||||
|
to.push_back(SLIP_END);
|
||||||
|
for (uchar c : from)
|
||||||
|
{
|
||||||
|
if (c == SLIP_END)
|
||||||
|
{
|
||||||
|
to.push_back(SLIP_ESC);
|
||||||
|
to.push_back(SLIP_ESC_END);
|
||||||
|
}
|
||||||
|
else if (c == SLIP_ESC)
|
||||||
|
{
|
||||||
|
to.push_back(SLIP_ESC);
|
||||||
|
to.push_back(SLIP_ESC_ESC);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
to.push_back(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
to.push_back(SLIP_END);
|
||||||
|
}
|
||||||
|
|
||||||
|
void removeSlipFraming(vector<uchar>& from, size_t *frame_length, vector<uchar> &to)
|
||||||
|
{
|
||||||
|
*frame_length = 0;
|
||||||
|
to.clear();
|
||||||
|
to.reserve(from.size());
|
||||||
|
bool esc = false;
|
||||||
|
size_t i;
|
||||||
|
bool found_end = false;
|
||||||
|
|
||||||
|
for (i = 0; i < from.size(); ++i)
|
||||||
|
{
|
||||||
|
uchar c = from[i];
|
||||||
|
if (c == SLIP_END)
|
||||||
|
{
|
||||||
|
if (to.size() > 0)
|
||||||
|
{
|
||||||
|
found_end = true;
|
||||||
|
i++;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (c == SLIP_ESC)
|
||||||
|
{
|
||||||
|
esc = true;
|
||||||
|
}
|
||||||
|
else if (esc)
|
||||||
|
{
|
||||||
|
if (c == SLIP_ESC_END) to.push_back(SLIP_END);
|
||||||
|
else if (c == SLIP_ESC_ESC) to.push_back(SLIP_ESC);
|
||||||
|
else to.push_back(c); // This is an error......
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
to.push_back(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (found_end)
|
||||||
|
{
|
||||||
|
*frame_length = i;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
*frame_length = 0;
|
||||||
|
to.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -171,6 +171,10 @@ uint16_t crc16_EN13757(uchar *data, size_t len);
|
||||||
uint16_t crc16_CCITT(uchar *data, uint16_t length);
|
uint16_t crc16_CCITT(uchar *data, uint16_t length);
|
||||||
bool crc16_CCITT_check(uchar *data, uint16_t length);
|
bool crc16_CCITT_check(uchar *data, uint16_t length);
|
||||||
|
|
||||||
|
void addSlipFraming(std::vector<uchar>& from, std::vector<uchar> &to);
|
||||||
|
// Frame length is set to zero if no frame was found.
|
||||||
|
void removeSlipFraming(std::vector<uchar>& from, size_t *frame_length, std::vector<uchar> &to);
|
||||||
|
|
||||||
// Eat characters from the vector v, iterating using i, until the end char c is found.
|
// Eat characters from the vector v, iterating using i, until the end char c is found.
|
||||||
// If end char == -1, then do not expect any end char, get all until eof.
|
// If end char == -1, then do not expect any end char, get all until eof.
|
||||||
// If the end char is not found, return error.
|
// If the end char is not found, return error.
|
||||||
|
|
10
src/wmbus.cc
10
src/wmbus.cc
|
@ -5172,6 +5172,16 @@ Detected detectWMBusDeviceOnTTY(string tty,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Talk iu880b with it...
|
||||||
|
// assumes this device is configured for 115200 bps, which seems to be the default.
|
||||||
|
if (type == WMBusDeviceType::DEVICE_AUTO || type == WMBusDeviceType::DEVICE_IU880B)
|
||||||
|
{
|
||||||
|
if (detectIU880B(&detected, handler) == AccessCheck::AccessOK)
|
||||||
|
{
|
||||||
|
return detected;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// We could not auto-detect either. default is DEVICE_UNKNOWN.
|
// We could not auto-detect either. default is DEVICE_UNKNOWN.
|
||||||
return detected;
|
return detected;
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,6 +44,7 @@ bool trimCRCsFrameFormatB(std::vector<uchar> &payload);
|
||||||
X(RC1180,rc1180,true,false,detectRC1180) \
|
X(RC1180,rc1180,true,false,detectRC1180) \
|
||||||
X(RTL433,rtl433,false,true,detectRTL433) \
|
X(RTL433,rtl433,false,true,detectRTL433) \
|
||||||
X(RTLWMBUS,rtlwmbus,false,true,detectRTLWMBUS) \
|
X(RTLWMBUS,rtlwmbus,false,true,detectRTLWMBUS) \
|
||||||
|
X(IU880B,iu880b,true,false,detectIU880B) \
|
||||||
X(SIMULATION,simulation,false,false,detectSIMULATION)
|
X(SIMULATION,simulation,false,false,detectSIMULATION)
|
||||||
|
|
||||||
enum WMBusDeviceType {
|
enum WMBusDeviceType {
|
||||||
|
@ -89,6 +90,7 @@ void setIgnoreDuplicateTelegrams(bool idt);
|
||||||
X(N1e,n1e,--n1e,0x100) \
|
X(N1e,n1e,--n1e,0x100) \
|
||||||
X(N1f,n1f,--n1f,0x200) \
|
X(N1f,n1f,--n1f,0x200) \
|
||||||
X(MBUS,mbus,--mbus,0x400) \
|
X(MBUS,mbus,--mbus,0x400) \
|
||||||
|
X(LORA,lora,--lora,0x800) \
|
||||||
X(UNKNOWN,unknown,----,0x0)
|
X(UNKNOWN,unknown,----,0x0)
|
||||||
|
|
||||||
enum class LinkMode {
|
enum class LinkMode {
|
||||||
|
@ -662,6 +664,9 @@ shared_ptr<WMBus> openIM871A(Detected detected,
|
||||||
shared_ptr<WMBus> openIM170A(Detected detected,
|
shared_ptr<WMBus> openIM170A(Detected detected,
|
||||||
shared_ptr<SerialCommunicationManager> manager,
|
shared_ptr<SerialCommunicationManager> manager,
|
||||||
shared_ptr<SerialDevice> serial_override);
|
shared_ptr<SerialDevice> serial_override);
|
||||||
|
shared_ptr<WMBus> openIU880B(Detected detected,
|
||||||
|
shared_ptr<SerialCommunicationManager> manager,
|
||||||
|
shared_ptr<SerialDevice> serial_override);
|
||||||
shared_ptr<WMBus> openAMB8465(Detected detected,
|
shared_ptr<WMBus> openAMB8465(Detected detected,
|
||||||
shared_ptr<SerialCommunicationManager> manager,
|
shared_ptr<SerialCommunicationManager> manager,
|
||||||
shared_ptr<SerialDevice> serial_override);
|
shared_ptr<SerialDevice> serial_override);
|
||||||
|
@ -745,6 +750,7 @@ AccessCheck detectAMB8465(Detected *detected, shared_ptr<SerialCommunicationMana
|
||||||
AccessCheck detectCUL(Detected *detected, shared_ptr<SerialCommunicationManager> handler);
|
AccessCheck detectCUL(Detected *detected, shared_ptr<SerialCommunicationManager> handler);
|
||||||
AccessCheck detectD1TC(Detected *detected, shared_ptr<SerialCommunicationManager> manager);
|
AccessCheck detectD1TC(Detected *detected, shared_ptr<SerialCommunicationManager> manager);
|
||||||
AccessCheck detectIM871AIM170A(Detected *detected, shared_ptr<SerialCommunicationManager> handler);
|
AccessCheck detectIM871AIM170A(Detected *detected, shared_ptr<SerialCommunicationManager> handler);
|
||||||
|
AccessCheck detectIU880B(Detected *detected, shared_ptr<SerialCommunicationManager> handler);
|
||||||
AccessCheck detectRAWTTY(Detected *detected, shared_ptr<SerialCommunicationManager> handler);
|
AccessCheck detectRAWTTY(Detected *detected, shared_ptr<SerialCommunicationManager> handler);
|
||||||
AccessCheck detectMBUS(Detected *detected, shared_ptr<SerialCommunicationManager> handler);
|
AccessCheck detectMBUS(Detected *detected, shared_ptr<SerialCommunicationManager> handler);
|
||||||
AccessCheck detectRC1180(Detected *detected, shared_ptr<SerialCommunicationManager> handler);
|
AccessCheck detectRC1180(Detected *detected, shared_ptr<SerialCommunicationManager> handler);
|
||||||
|
|
|
@ -37,7 +37,7 @@ using namespace std;
|
||||||
#define FIRMWARE_14_C_AND_T 0x14
|
#define FIRMWARE_14_C_AND_T 0x14
|
||||||
#define FIRMWARE_13_C_OR_T 0x13
|
#define FIRMWARE_13_C_OR_T 0x13
|
||||||
|
|
||||||
struct DeviceInfo
|
struct IM871ADeviceInfo
|
||||||
{
|
{
|
||||||
uchar module_type; // 0x33 = im871a 0x36 = im170a
|
uchar module_type; // 0x33 = im871a 0x36 = im170a
|
||||||
uchar device_mode; // 0 = other 1 = meter
|
uchar device_mode; // 0 = other 1 = meter
|
||||||
|
@ -228,7 +228,7 @@ struct WMBusIM871aIM170A : public virtual WMBusCommonImplementation
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
DeviceInfo device_info_ {};
|
IM871ADeviceInfo device_info_ {};
|
||||||
Config device_config_ {};
|
Config device_config_ {};
|
||||||
|
|
||||||
vector<uchar> read_buffer_;
|
vector<uchar> read_buffer_;
|
||||||
|
@ -1014,7 +1014,7 @@ AccessCheck detectIM871AIM170A(Detected *detected, shared_ptr<SerialCommunicatio
|
||||||
|
|
||||||
debugPayload("(device info bytes)", payload);
|
debugPayload("(device info bytes)", payload);
|
||||||
|
|
||||||
DeviceInfo di;
|
IM871ADeviceInfo di;
|
||||||
di.decode(payload);
|
di.decode(payload);
|
||||||
|
|
||||||
debug("(im871a/im170a) info: %s\n", di.str().c_str());
|
debug("(im871a/im170a) info: %s\n", di.str().c_str());
|
||||||
|
|
Ładowanie…
Reference in New Issue