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_rc1180.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.
|
||||
# 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(CUL, "cul") \
|
||||
X(IM871A, "im871a") \
|
||||
X(RC1180, "rc1180")
|
||||
X(RC1180, "rc1180") \
|
||||
X(IU880B, "iu880b")
|
||||
|
||||
enum class ReceiversType {
|
||||
#define X(name,description) name,
|
||||
|
@ -245,6 +246,9 @@ void detectWMBUSReceiver()
|
|||
case ReceiversType::IM871A:
|
||||
probeFor("im871a/im170a", detectIM871AIM170A);
|
||||
break;
|
||||
case ReceiversType::IU880B:
|
||||
probeFor("iu880b", detectIU880B);
|
||||
break;
|
||||
case ReceiversType::RC1180:
|
||||
probeFor("rc1180", detectRC1180);
|
||||
break;
|
||||
|
@ -292,6 +296,9 @@ void resetWMBUSReceiver()
|
|||
case ReceiversType::RC1180:
|
||||
notImplementedYet("Resetting RC1180");
|
||||
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);
|
||||
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:
|
||||
warning("(main) internal error! cannot create an unknown device! exiting!\n");
|
||||
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.
|
||||
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
|
||||
// telegrams into json, fields that are written into log files
|
||||
// or sent to shell invocations.
|
||||
|
|
|
@ -247,9 +247,11 @@ void addDefaultManufacturerKeyIfAny(const vector<uchar> &frame, TPLSecurityMode
|
|||
&& tpl_sec_mode == TPLSecurityMode::AES_CBC_IV
|
||||
&& 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.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_hex();
|
||||
void test_translate();
|
||||
void test_slip();
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
|
@ -76,6 +77,7 @@ int main(int argc, char **argv)
|
|||
test_sbc();
|
||||
test_hex();
|
||||
test_translate();
|
||||
test_slip();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -1085,3 +1087,67 @@ void test_translate()
|
|||
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());
|
||||
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);
|
||||
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.
|
||||
// If end char == -1, then do not expect any end char, get all until eof.
|
||||
// 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.
|
||||
return detected;
|
||||
}
|
||||
|
|
|
@ -44,6 +44,7 @@ bool trimCRCsFrameFormatB(std::vector<uchar> &payload);
|
|||
X(RC1180,rc1180,true,false,detectRC1180) \
|
||||
X(RTL433,rtl433,false,true,detectRTL433) \
|
||||
X(RTLWMBUS,rtlwmbus,false,true,detectRTLWMBUS) \
|
||||
X(IU880B,iu880b,true,false,detectIU880B) \
|
||||
X(SIMULATION,simulation,false,false,detectSIMULATION)
|
||||
|
||||
enum WMBusDeviceType {
|
||||
|
@ -89,6 +90,7 @@ void setIgnoreDuplicateTelegrams(bool idt);
|
|||
X(N1e,n1e,--n1e,0x100) \
|
||||
X(N1f,n1f,--n1f,0x200) \
|
||||
X(MBUS,mbus,--mbus,0x400) \
|
||||
X(LORA,lora,--lora,0x800) \
|
||||
X(UNKNOWN,unknown,----,0x0)
|
||||
|
||||
enum class LinkMode {
|
||||
|
@ -662,6 +664,9 @@ shared_ptr<WMBus> openIM871A(Detected detected,
|
|||
shared_ptr<WMBus> openIM170A(Detected detected,
|
||||
shared_ptr<SerialCommunicationManager> manager,
|
||||
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<SerialCommunicationManager> manager,
|
||||
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 detectD1TC(Detected *detected, shared_ptr<SerialCommunicationManager> manager);
|
||||
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 detectMBUS(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_13_C_OR_T 0x13
|
||||
|
||||
struct DeviceInfo
|
||||
struct IM871ADeviceInfo
|
||||
{
|
||||
uchar module_type; // 0x33 = im871a 0x36 = im170a
|
||||
uchar device_mode; // 0 = other 1 = meter
|
||||
|
@ -228,7 +228,7 @@ struct WMBusIM871aIM170A : public virtual WMBusCommonImplementation
|
|||
|
||||
private:
|
||||
|
||||
DeviceInfo device_info_ {};
|
||||
IM871ADeviceInfo device_info_ {};
|
||||
Config device_config_ {};
|
||||
|
||||
vector<uchar> read_buffer_;
|
||||
|
@ -1014,7 +1014,7 @@ AccessCheck detectIM871AIM170A(Detected *detected, shared_ptr<SerialCommunicatio
|
|||
|
||||
debugPayload("(device info bytes)", payload);
|
||||
|
||||
DeviceInfo di;
|
||||
IM871ADeviceInfo di;
|
||||
di.decode(payload);
|
||||
|
||||
debug("(im871a/im170a) info: %s\n", di.str().c_str());
|
||||
|
|
Ładowanie…
Reference in New Issue