Add foundation for iu880b dongle to receive lora.

pull/482/head
Fredrik Öhrström 2022-02-19 08:13:58 +01:00
rodzic 111e276376
commit d254ce5066
13 zmienionych plików z 865 dodań i 5 usunięć

Wyświetl plik

@ -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.

Wyświetl plik

@ -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;
}
}

Wyświetl plik

@ -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) {

572
src/lora_iu880b.cc 100644
Wyświetl plik

@ -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;
}

101
src/lora_iu880b.h 100644
Wyświetl plik

@ -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

Wyświetl plik

@ -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.

Wyświetl plik

@ -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");
}
}

Wyświetl plik

@ -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");
}
}

Wyświetl plik

@ -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();
}
}

Wyświetl plik

@ -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.

Wyświetl plik

@ -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;
}

Wyświetl plik

@ -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);

Wyświetl plik

@ -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());