Add partial support for RC1180.

pull/156/head
Fredrik Öhrström 2020-09-26 13:52:24 +02:00
rodzic e4f9eb23db
commit d443fd4ea2
18 zmienionych plików z 430 dodań i 80 usunięć

Wyświetl plik

@ -160,6 +160,7 @@ METER_OBJS:=\
$(BUILD)/wmbus_rtl433.o \
$(BUILD)/wmbus_simulator.o \
$(BUILD)/wmbus_rawtty.o \
$(BUILD)/wmbus_rc1180.o \
$(BUILD)/wmbus_wmb13u.o \
$(BUILD)/wmbus_utils.o

Wyświetl plik

@ -29,12 +29,12 @@ Availability of **wmbusmeters** for other Linux distributions can be checked on
# Run as a daemon
Remove the wmbus dongle (im871a,amb8465,rfmrx2,cul,d1tc) or the generic rtlsdr dongle (RTL2838) from your computer.
Remove the wmbus dongle (im871a,amb8465,cul,rc1180,rfmrx2,d1tc) or the generic rtlsdr dongle (RTL2838) from your computer.
`make; sudo make install` will install wmbusmeters as a daemon.
Check the contents of your `/etc/wmbusmeters.conf` file, assuming it
has `device=auto` and you are using a im871a,amb8465 or cul device,
has `device=auto` and you are using a im871a,amb8465,rc1180 or cul device,
then you can now start the daemon with `sudo systemctl start wmbusmeters`.
When the daemon is running it will scan for wmbus devices every few seconds
@ -166,7 +166,7 @@ As <options> you can use:
--meterfilestimestamp=(never|day|hour|minute|micros) the meter file is suffixed with a
timestamp (localtime) with the given resolution.
--oneshot wait for an update from each meter, then quit
--resetafter=<time> reset the wmbus dongle regularly, default is 24h
--resetafter=<time> reset the wmbus dongle regularly, default is 23h
--selectfields=id,timestamp,total_m3 select fields to be printed
--separator=<c> change field separator to c
--shell=<cmdline> invokes cmdline with env variables containing the latest reading
@ -223,10 +223,11 @@ As meter quadruples you specify:
Supported wmbus dongles:
IMST 871a (im871a)
Amber 8465 (amb8465)
BMeters RFM-RX2 (rfmrx2)
CUL family (cul)
Radiocraft (RC1180) work in progress
rtl_wmbus (rtlwmbus)
rtl_433 (rtl433)
BMeters RFM-RX2 (rfmrx2)
Supported water meters:
Apator at-wmbus-08 (apator08) (non-standard protocol)

Wyświetl plik

@ -54,7 +54,8 @@ LIST_OF_MAIN_MENU
#define LIST_OF_WMBUS_RECEIVERS \
X(AMB8465, "amb8465") \
X(CUL, "cul") \
X(IM871A, "im871a")
X(IM871A, "im871a") \
X(RC1180, "rc1180") \
enum class ReceiversType {
#define X(name,description) name,
@ -238,6 +239,9 @@ void detectWMBUSReceiver()
case ReceiversType::IM871A:
probeFor("im871a", detectIM871A);
break;
case ReceiversType::RC1180:
probeFor("rc1180", detectRC1180);
break;
}
}
@ -279,6 +283,9 @@ void resetWMBUSReceiver()
case ReceiversType::IM871A:
notImplementedYet("Resetting im871a");
break;
case ReceiversType::RC1180:
notImplementedYet("Resetting RC1180");
break;
}
}

Wyświetl plik

@ -190,7 +190,7 @@ shared_ptr<WMBus> createWMBusDeviceFrom(Detected *detected, Configuration *confi
if (detected->override_tty)
{
serial_override = manager->createSerialDeviceFile(detected->device.file);
serial_override = manager->createSerialDeviceFile(detected->device.file, string("override ")+detected->device.file.c_str());
verbose("(serial) override with devicefile: %s\n", detected->device.file.c_str());
link_modes_matter = false;
}
@ -307,6 +307,12 @@ shared_ptr<WMBus> createWMBusDeviceFrom(Detected *detected, Configuration *confi
wmbus = openD1TC(detected->device.file, manager, serial_override);
break;
}
case DEVICE_RC1180:
{
verbose("(rc1180) on %s\n", detected->device.file.c_str());
wmbus = openRC1180(detected->device.file, manager, serial_override);
break;
}
case DEVICE_WMB13U:
{
verbose("(wmb13u) on %s\n", detected->device.file.c_str());
@ -556,7 +562,14 @@ void open_wmbus_device(Configuration *config, string how, string device, Detecte
}
else
{
verbose("(main) started %s on %s (%s)\n", toString(detected->type), device.c_str(), how.c_str());
if (detected->type != DEVICE_SIMULATOR)
{
notice("Started %s on %s (%s)\n", toString(detected->type), device.c_str(), how.c_str());
}
else
{
verbose("Started %s on %s (%s)\n", toString(detected->type), device.c_str(), how.c_str());
}
}
shared_ptr<WMBus> w = createWMBusDeviceFrom(detected, config, serial_manager_);
@ -564,11 +577,12 @@ void open_wmbus_device(Configuration *config, string how, string device, Detecte
WMBus *wmbus = wmbus_devices_.back().get();
wmbus->setLinkModes(config->listen_to_link_modes);
// By default, reset your dongle once every day.
int regular_reset = 24*3600;
// By default, reset your dongle once every 23 hours,
// so that the reset is not at the exact same time every day.
int regular_reset = 23*3600;
if (config->resetafter != 0) regular_reset = config->resetafter;
wmbus->setResetInterval(regular_reset);
verbose("(main) regular reset %d\n", regular_reset);
verbose("(main) regular reset of %s %s will happen every %d seconds\n", toString(detected->type), device.c_str(), regular_reset);
string using_link_modes = wmbus->getLinkModes().hr();
verbose("(config) listen to link modes: %s\n", using_link_modes.c_str());
@ -604,7 +618,7 @@ void perform_auto_scan_of_serial_devices(Configuration *config)
{
debug("(main) device %s not currently used, detect contents...\n", device.c_str());
// This serial device is not in use.
Detected detected = detectImstAmberCul(device, "", "", serial_manager_, true, false, false);
Detected detected = detectImstAmberCulRC(device, "", "", serial_manager_, true, false, false);
if (detected.type != DEVICE_UNKNOWN)
{
open_wmbus_device(config, "auto", device, &detected);

Wyświetl plik

@ -70,10 +70,10 @@ struct SerialCommunicationManagerImp : public SerialCommunicationManager
SerialCommunicationManagerImp(time_t exit_after_seconds, bool start_event_loop);
~SerialCommunicationManagerImp();
shared_ptr<SerialDevice> createSerialDeviceTTY(string dev, int baud_rate);
shared_ptr<SerialDevice> createSerialDeviceTTY(string dev, int baud_rate, string purpose);
shared_ptr<SerialDevice> createSerialDeviceCommand(string device, string command, vector<string> args, vector<string> envs,
function<void()> on_exit);
shared_ptr<SerialDevice> createSerialDeviceFile(string file);
function<void()> on_exit, string purpose);
shared_ptr<SerialDevice> createSerialDeviceFile(string file, string purpose);
shared_ptr<SerialDevice> createSerialDeviceSimulator();
void listenTo(SerialDevice *sd, function<void()> cb);
@ -86,6 +86,7 @@ struct SerialCommunicationManagerImp : public SerialCommunicationManager
bool isRunning();
shared_ptr<SerialDevice> addSerialDeviceForManagement(SerialDevice *sd);
void tickleEventLoop();
void removeNonWorkingSerialDevices();
void closeAllDoNotRemove();
@ -106,7 +107,6 @@ private:
bool running_ {};
bool expect_devices_to_work_ {}; // false during detection phase, true when running.
int max_fd_ {};
time_t start_time_ {};
time_t exit_after_seconds_ {};
@ -144,6 +144,7 @@ struct SerialDeviceImp : public SerialDevice
int receive(vector<uchar> *data);
bool working() { return resetting_ || fd_ != -1; }
bool opened() { return resetting_ || fd_ != -2; }
bool isClosed() { return fd_ == -1 && !resetting_; }
bool readonly() { return is_stdin_ || is_file_; }
void expectAscii() { expecting_ascii_ = true; }
@ -163,9 +164,10 @@ struct SerialDeviceImp : public SerialDevice
return available > 0;
}
SerialDeviceImp(SerialCommunicationManagerImp *manager)
SerialDeviceImp(SerialCommunicationManagerImp *manager, string purpose)
{
manager_ = manager;
purpose_ = purpose;
}
~SerialDeviceImp() = default;
@ -186,6 +188,7 @@ protected:
bool no_callbacks_ = false;
SerialCommunicationManagerImp *manager_;
bool resetting_ {}; // Set to true while resetting.
string purpose_; // Can be set to identify a serial device purose.
friend struct SerialCommunicationManagerImp;
};
@ -252,7 +255,7 @@ int SerialDeviceImp::receive(vector<uchar> *data)
struct SerialDeviceTTY : public SerialDeviceImp
{
SerialDeviceTTY(string device, int baud_rate, SerialCommunicationManagerImp * manager);
SerialDeviceTTY(string device, int baud_rate, SerialCommunicationManagerImp * manager, string purpose);
~SerialDeviceTTY();
AccessCheck open(bool fail_if_not_ok);
@ -268,8 +271,9 @@ struct SerialDeviceTTY : public SerialDeviceImp
};
SerialDeviceTTY::SerialDeviceTTY(string device, int baud_rate,
SerialCommunicationManagerImp *manager)
: SerialDeviceImp(manager)
SerialCommunicationManagerImp *manager,
string purpose)
: SerialDeviceImp(manager, purpose)
{
device_ = device;
baud_rate_ = baud_rate;
@ -310,7 +314,7 @@ AccessCheck SerialDeviceTTY::open(bool fail_if_not_ok)
}
}
}
verbose("(serialtty) opened %s\n", device_.c_str());
verbose("(serialtty) opened %s %d (%s)\n", device_.c_str(), fd_, purpose_.c_str());
return AccessCheck::AccessOK;
}
@ -325,7 +329,9 @@ void SerialDeviceTTY::close()
on_disappear_();
on_disappear_ = NULL;
}
verbose("(serialtty) closed %s\n", device_.c_str());
manager_->tickleEventLoop();
verbose("(serialtty) closed %s (%s)\n", device_.c_str(), purpose_.c_str());
}
bool SerialDeviceTTY::send(vector<uchar> &data)
@ -376,7 +382,8 @@ struct SerialDeviceCommand : public SerialDeviceImp
{
SerialDeviceCommand(string device, string command, vector<string> args, vector<string> envs,
SerialCommunicationManagerImp *manager,
function<void()> on_exit);
function<void()> on_exit,
string purpose);
~SerialDeviceCommand();
AccessCheck open(bool fail_if_not_ok);
@ -405,8 +412,9 @@ SerialDeviceCommand::SerialDeviceCommand(string device,
vector<string> args,
vector<string> envs,
SerialCommunicationManagerImp *manager,
function<void()> on_exit)
: SerialDeviceImp(manager)
function<void()> on_exit,
string purpose)
: SerialDeviceImp(manager, purpose)
{
assert(device != "");
device_ = device;
@ -428,7 +436,7 @@ AccessCheck SerialDeviceCommand::open(bool fail_if_not_ok)
assert(fd_ >= 0);
if (!ok) return AccessCheck::NotThere;
setIsStdin();
verbose("(serialcmd) opened %s pid %d fd %d\n", command_.c_str(), pid_, fd_);
verbose("(serialcmd) opened %s pid %d fd %d (%s)\n", command_.c_str(), pid_, fd_, purpose_.c_str());
return AccessCheck::AccessOK;
}
@ -449,7 +457,10 @@ void SerialDeviceCommand::close()
::flock(fd_, LOCK_UN);
::close(fd_);
fd_ = -1;
verbose("(serialcmd) closed %s pid=%d fd=%d\n", command_.c_str(), p, f);
manager_->tickleEventLoop();
verbose("(serialcmd) closed %s pid=%d fd=%d (%s)\n", command_.c_str(), p, f, purpose_.c_str());
}
bool SerialDeviceCommand::working()
@ -502,7 +513,7 @@ bool SerialDeviceCommand::send(vector<uchar> &data)
struct SerialDeviceFile : public SerialDeviceImp
{
SerialDeviceFile(string file, SerialCommunicationManagerImp *manager);
SerialDeviceFile(string file, SerialCommunicationManagerImp *manager, string purpose);
~SerialDeviceFile();
AccessCheck open(bool fail_if_not_ok);
@ -518,8 +529,9 @@ struct SerialDeviceFile : public SerialDeviceImp
};
SerialDeviceFile::SerialDeviceFile(string file,
SerialCommunicationManagerImp *manager)
: SerialDeviceImp(manager)
SerialCommunicationManagerImp *manager,
string purpose)
: SerialDeviceImp(manager, purpose)
{
file_ = file;
}
@ -538,7 +550,7 @@ AccessCheck SerialDeviceFile::open(bool fail_if_not_ok)
flags |= O_NONBLOCK;
fcntl(0, F_SETFL, flags);
setIsStdin();
verbose("(serialfile) reading from stdin\n");
verbose("(serialfile) reading from stdin (%s)\n", purpose_.c_str());
}
else
{
@ -557,13 +569,9 @@ AccessCheck SerialDeviceFile::open(bool fail_if_not_ok)
}
}
setIsFile();
verbose("(serialfile) reading from file %s\n", file_.c_str());
}
if (signalsInstalled())
{
// Tickle the event loop to use the new file descriptor in the select.
if (getEventLoopThread()) pthread_kill(getEventLoopThread(), SIGUSR1);
verbose("(serialfile) reading from file %s (%s)\n", file_.c_str(), purpose_.c_str());
}
manager_->tickleEventLoop();
return AccessCheck::AccessOK;
}
@ -574,7 +582,10 @@ void SerialDeviceFile::close()
::flock(fd_, LOCK_UN);
::close(fd_);
fd_ = -1;
verbose("(serialfile) closed %s %d\n", file_.c_str(), fd_);
manager_->tickleEventLoop();
verbose("(serialfile) closed %s %d (%s)\n", file_.c_str(), fd_, purpose_.c_str());
}
bool SerialDeviceFile::working()
@ -601,10 +612,10 @@ bool SerialDeviceFile::send(vector<uchar> &data)
struct SerialDeviceSimulator : public SerialDeviceImp
{
SerialDeviceSimulator(SerialCommunicationManagerImp *m) :
SerialDeviceImp(m)
SerialDeviceSimulator(SerialCommunicationManagerImp *m, string purpose) :
SerialDeviceImp(m, purpose)
{
verbose("(serialsimulator) opened\n");
verbose("(serialsimulator) opened (%s)\n", purpose_.c_str());
};
~SerialDeviceSimulator() { };
@ -633,7 +644,6 @@ SerialCommunicationManagerImp::SerialCommunicationManagerImp(time_t exit_after_s
bool start_event_loop)
{
running_ = true;
max_fd_ = 0;
// Block the event loop until everything is configured.
if (start_event_loop)
{
@ -647,28 +657,30 @@ SerialCommunicationManagerImp::SerialCommunicationManagerImp(time_t exit_after_s
}
shared_ptr<SerialDevice> SerialCommunicationManagerImp::createSerialDeviceTTY(string device,
int baud_rate)
int baud_rate,
string purpose)
{
return addSerialDeviceForManagement(new SerialDeviceTTY(device, baud_rate, this));
return addSerialDeviceForManagement(new SerialDeviceTTY(device, baud_rate, this, purpose));
}
shared_ptr<SerialDevice> SerialCommunicationManagerImp::createSerialDeviceCommand(string device,
string command,
vector<string> args,
vector<string> envs,
function<void()> on_exit)
function<void()> on_exit,
string purpose)
{
return addSerialDeviceForManagement(new SerialDeviceCommand(device, command, args, envs, this, on_exit));
return addSerialDeviceForManagement(new SerialDeviceCommand(device, command, args, envs, this, on_exit, purpose));
}
shared_ptr<SerialDevice> SerialCommunicationManagerImp::createSerialDeviceFile(string file)
shared_ptr<SerialDevice> SerialCommunicationManagerImp::createSerialDeviceFile(string file, string purpose)
{
return addSerialDeviceForManagement(new SerialDeviceFile(file, this));
return addSerialDeviceForManagement(new SerialDeviceFile(file, this, purpose));
}
shared_ptr<SerialDevice> SerialCommunicationManagerImp::createSerialDeviceSimulator()
{
return addSerialDeviceForManagement(new SerialDeviceSimulator(this));
return addSerialDeviceForManagement(new SerialDeviceSimulator(this, ""));
}
void SerialCommunicationManagerImp::listenTo(SerialDevice *sd, function<void()> cb)
@ -764,12 +776,24 @@ shared_ptr<SerialDevice> SerialCommunicationManagerImp::addSerialDeviceForManage
{
LOCK_SERIAL_DEVICES(opened);
max_fd_ = max(sd->fd(), max_fd_);
shared_ptr<SerialDevice> ptr = shared_ptr<SerialDevice>(sd);
serial_devices_.push_back(ptr);
tickleEventLoop();
return ptr;
}
void SerialCommunicationManagerImp::tickleEventLoop()
{
LOCK_SERIAL_DEVICES(tickle);
if (signalsInstalled())
{
// Tickle the event loop to use the new file descriptor in the select.
if (getEventLoopThread()) pthread_kill(getEventLoopThread(), SIGUSR1);
}
}
void SerialCommunicationManagerImp::removeNonWorkingSerialDevices()
{
LOCK_SERIAL_DEVICES(remove_non_working_serial_devices);
@ -786,14 +810,6 @@ void SerialCommunicationManagerImp::removeNonWorkingSerialDevices()
}
}
max_fd_ = 0;
for (shared_ptr<SerialDevice> &sp : serial_devices_)
{
if (sp->fd() > max_fd_)
{
max_fd_ = sp->fd();
}
}
if (serial_devices_.size() == 0 && expect_devices_to_work_)
{
debug("(serial) no devices working emergency exit!\n");
@ -938,7 +954,16 @@ void *SerialCommunicationManagerImp::eventLoop()
break;
}
int activity = select(max_fd_+1 , &readfds, NULL , NULL, &timeout);
int max_fd = 0;
for (shared_ptr<SerialDevice> &sp : serial_devices_)
{
if (sp->fd() > max_fd)
{
max_fd = sp->fd();
}
}
int activity = select(max_fd+1 , &readfds, NULL , NULL, &timeout);
if (activity == -1 && errno == EINTR)
{
@ -983,7 +1008,7 @@ void *SerialCommunicationManagerImp::eventLoop()
for (shared_ptr<SerialDevice> &sd : serial_devices_)
{
if (sd->opened() && !sd->working()) non_working.push_back(sd);
if (sd->opened() && !sd->working() && !sd->isClosed()) non_working.push_back(sd);
}
}

Wyświetl plik

@ -39,6 +39,8 @@ struct SerialDevice
// If fail_if_not_ok then forcefully exit the program if cannot be opened.
virtual AccessCheck open(bool fail_if_not_ok) = 0;
virtual void close() = 0;
// Explicitly closed fd == -1
virtual bool isClosed() = 0;
// Send will return true only if sending on a tty.
virtual bool send(std::vector<uchar> &data) = 0;
// Receive returns the number of bytes received.
@ -67,15 +69,16 @@ struct SerialDevice
struct SerialCommunicationManager
{
// Read from a /dev/ttyUSB0 or /dev/ttyACM0 device with baud settings.
virtual shared_ptr<SerialDevice> createSerialDeviceTTY(string dev, int baud_rate) = 0;
virtual shared_ptr<SerialDevice> createSerialDeviceTTY(string dev, int baud_rate, string purpose) = 0;
// Read from a sub shell.
virtual shared_ptr<SerialDevice> createSerialDeviceCommand(string device,
string command,
vector<string> args,
vector<string> envs,
function<void()> on_exit) = 0;
function<void()> on_exit,
string purpose) = 0;
// Read from stdin (file="stdin") or a specific file.
virtual shared_ptr<SerialDevice> createSerialDeviceFile(string file) = 0;
virtual shared_ptr<SerialDevice> createSerialDeviceFile(string file, string purpose) = 0;
// A serial device simulator used for internal testing.
virtual shared_ptr<SerialDevice> createSerialDeviceSimulator() = 0;

Wyświetl plik

@ -471,7 +471,7 @@ string mediaTypeJSON(int a_field_device_type)
return "Unknown";
}
Detected detectImstAmberCul(string file,
Detected detectImstAmberCulRC(string file,
string suffix,
string linkmodes,
shared_ptr<SerialCommunicationManager> handler,
@ -489,18 +489,28 @@ Detected detectImstAmberCul(string file,
// Anyway by testing for the amb8465 first, we can immediately continue
// with the test for the im871a, without the need for a 1s delay.
// Talk amb8465 with it...
// assumes this device is configured for 9600 bps, which seems to be the default.
if (detectAMB8465(file, &detected, handler) == AccessCheck::AccessOK)
{
return detected;
}
// Talk RC1180 with it...
// assumes this device is configured for 19200 bps, which seems to be the default.
if (detectRC1180(file, &detected, handler) == AccessCheck::AccessOK)
{
return detected;
}
// Talk im871a with it...
// assumes this device is configured for 57600 bps, which seems to be the default.
if (detectIM871A(file, &detected, handler) == AccessCheck::AccessOK)
{
return detected;
}
// Talk CUL with it...
// assumes this device is configured for 38400 bps, which seems to be the default.
if (detectCUL(file, &detected, handler) == AccessCheck::AccessOK)
@ -580,6 +590,7 @@ Detected detectWMBusDeviceSetting(string file,
if (suffix == "amb8465") return { { file, suffix }, DEVICE_AMB8465, 0, override_tty, is_tty, is_stdin, is_file };
if (suffix == "im871a") return { { file, suffix }, DEVICE_IM871A, 0, override_tty, is_tty, is_stdin, is_file };
if (suffix == "rc1180") return { { file, suffix }, DEVICE_RC1180, 0, override_tty, is_tty, is_stdin, is_file };
if (suffix == "rfmrx2") return { { file, suffix }, DEVICE_RFMRX2, 0, override_tty, is_tty, is_stdin, is_file };
if (suffix == "rtlwmbus") return { { file, suffix }, DEVICE_RTLWMBUS, 0, override_tty, is_tty, is_stdin, is_file };
if (suffix == "rtl433") return { { file, suffix}, DEVICE_RTL433, 0, override_tty, is_tty, is_stdin, is_file };
@ -602,7 +613,7 @@ Detected detectWMBusDeviceSetting(string file,
// Ok, we are left with a single /dev/ttyUSB0 lets talk to it
// to figure out what is connected to it. We currently only
// know how to detect Imst, Amber or CUL dongles.
return detectImstAmberCul(file, suffix, linkmodes, handler, is_tty, is_stdin, is_file);
return detectImstAmberCulRC(file, suffix, linkmodes, handler, is_tty, is_stdin, is_file);
}
/*

Wyświetl plik

@ -75,6 +75,7 @@ struct Device
X(DEVICE_AMB8465)\
X(DEVICE_RFMRX2)\
X(DEVICE_SIMULATOR)\
X(DEVICE_RC1180)\
X(DEVICE_RTLWMBUS)\
X(DEVICE_RTL433)\
X(DEVICE_RAWTTY)\
@ -515,6 +516,8 @@ shared_ptr<WMBus> openAMB8465(string device, shared_ptr<SerialCommunicationManag
shared_ptr<SerialDevice> serial_override);
shared_ptr<WMBus> openRawTTY(string device, int baudrate, shared_ptr<SerialCommunicationManager> manager,
shared_ptr<SerialDevice> serial_override);
shared_ptr<WMBus> openRC1180(string device, shared_ptr<SerialCommunicationManager> manager,
shared_ptr<SerialDevice> serial_override);
shared_ptr<WMBus> openRTLWMBUS(string device, string command, shared_ptr<SerialCommunicationManager> manager, std::function<void()> on_exit,
shared_ptr<SerialDevice> serial_override);
shared_ptr<WMBus> openRTL433(string device, string command, shared_ptr<SerialCommunicationManager> manager, std::function<void()> on_exit,
@ -577,6 +580,7 @@ FrameStatus checkWMBusFrame(vector<uchar> &data,
AccessCheck detectIM871A(string file, Detected *detected, shared_ptr<SerialCommunicationManager> handler);
AccessCheck detectAMB8465(string file, Detected *detected, shared_ptr<SerialCommunicationManager> handler);
AccessCheck detectRawTTY(string file, int baud, Detected *detected, shared_ptr<SerialCommunicationManager> handler);
AccessCheck detectRC1180(string file, Detected *detected, shared_ptr<SerialCommunicationManager> handler);
AccessCheck detectRTLSDR(string file, Detected *detected, shared_ptr<SerialCommunicationManager> handler);
AccessCheck detectCUL(string file, Detected *detected, shared_ptr<SerialCommunicationManager> handler);
AccessCheck detectWMB13U(string file, Detected *detected, shared_ptr<SerialCommunicationManager> handler);
@ -585,7 +589,7 @@ AccessCheck detectWMB13U(string file, Detected *detected, shared_ptr<SerialCommu
// restore to factory settings.
AccessCheck factoryResetAMB8465(string device, shared_ptr<SerialCommunicationManager> handler, int *was_baud);
Detected detectImstAmberCul(string file,
Detected detectImstAmberCulRC(string file,
string suffix,
string linkmodes,
shared_ptr<SerialCommunicationManager> handler,

Wyświetl plik

@ -102,7 +102,7 @@ shared_ptr<WMBus> openAMB8465(string device, shared_ptr<SerialCommunicationManag
return shared_ptr<WMBus>(imp);
}
auto serial = manager->createSerialDeviceTTY(device.c_str(), 9600);
auto serial = manager->createSerialDeviceTTY(device.c_str(), 9600, "amb8465");
WMBusAmber *imp = new WMBusAmber(serial, manager);
return shared_ptr<WMBus>(imp);
}
@ -508,7 +508,7 @@ void WMBusAmber::handleMessage(int msgid, vector<uchar> &frame)
AccessCheck detectAMB8465(string device, Detected *detected, shared_ptr<SerialCommunicationManager> manager)
{
// Talk to the device and expect a very specific answer.
auto serial = manager->createSerialDeviceTTY(device.c_str(), 9600);
auto serial = manager->createSerialDeviceTTY(device.c_str(), 9600, "detect amb8465");
serial->doNotUseCallbacks();
AccessCheck rc = serial->open(false);
if (rc != AccessCheck::AccessOK) return AccessCheck::NotThere;
@ -562,7 +562,7 @@ AccessCheck detectAMB8465(string device, Detected *detected, shared_ptr<SerialCo
static AccessCheck tryFactoryResetAMB8465(string device, shared_ptr<SerialCommunicationManager> manager, int baud)
{
// Talk to the device and expect a very specific answer.
auto serial = manager->createSerialDeviceTTY(device.c_str(), baud);
auto serial = manager->createSerialDeviceTTY(device.c_str(), baud, "reset amb8465");
AccessCheck rc = serial->open(false);
if (rc != AccessCheck::AccessOK) {
verbose("(amb8465) could not open device %s using baud %d\n", device.c_str(), baud);

Wyświetl plik

@ -87,7 +87,7 @@ shared_ptr<WMBus> openCUL(string device, shared_ptr<SerialCommunicationManager>
return shared_ptr<WMBus>(imp);
}
auto serial = manager->createSerialDeviceTTY(device.c_str(), 38400);
auto serial = manager->createSerialDeviceTTY(device.c_str(), 38400, "cul");
WMBusCUL *imp = new WMBusCUL(serial, manager);
return shared_ptr<WMBus>(imp);
}
@ -340,7 +340,7 @@ FrameStatus WMBusCUL::checkCULFrame(vector<uchar> &data,
AccessCheck detectCUL(string device, Detected *detected, shared_ptr<SerialCommunicationManager> manager)
{
// Talk to the device and expect a very specific answer.
auto serial = manager->createSerialDeviceTTY(device.c_str(), 38400);
auto serial = manager->createSerialDeviceTTY(device.c_str(), 38400, "detect cul");
serial->doNotUseCallbacks();
AccessCheck rc = serial->open(false);
if (rc != AccessCheck::AccessOK) return AccessCheck::NotThere;

Wyświetl plik

@ -63,7 +63,7 @@ shared_ptr<WMBus> openD1TC(string device, shared_ptr<SerialCommunicationManager>
WMBusD1TC *imp = new WMBusD1TC(serial_override, manager);
return shared_ptr<WMBus>(imp);
}
auto serial = manager->createSerialDeviceTTY(device.c_str(), 115200);
auto serial = manager->createSerialDeviceTTY(device.c_str(), 115200, "d1tc");
WMBusD1TC *imp = new WMBusD1TC(serial, manager);
return shared_ptr<WMBus>(imp);
}
@ -213,7 +213,7 @@ AccessCheck detectD1TC(string device, int baud, shared_ptr<SerialCommunicationMa
{
// Since we do not know how to talk to the other end, it might not
// even respond. The only thing we can do is to try to open the serial device.
auto serial = manager->createSerialDeviceTTY(device.c_str(), baud);
auto serial = manager->createSerialDeviceTTY(device.c_str(), baud, "detect d1tc");
AccessCheck rc = serial->open(false);
if (rc != AccessCheck::AccessOK) return AccessCheck::NotThere;

Wyświetl plik

@ -93,7 +93,7 @@ shared_ptr<WMBus> openIM871A(string device, shared_ptr<SerialCommunicationManage
return shared_ptr<WMBus>(imp);
}
auto serial = manager->createSerialDeviceTTY(device.c_str(), 57600);
auto serial = manager->createSerialDeviceTTY(device.c_str(), 57600, "im871a");
WMBusIM871A *imp = new WMBusIM871A(serial, manager);
return shared_ptr<WMBus>(imp);
}
@ -640,7 +640,7 @@ void WMBusIM871A::handleHWTest(int msgid, vector<uchar> &payload)
AccessCheck detectIM871A(string file, Detected *detected, shared_ptr<SerialCommunicationManager> manager)
{
// Talk to the device and expect a very specific answer.
auto serial = manager->createSerialDeviceTTY(file.c_str(), 57600);
auto serial = manager->createSerialDeviceTTY(file.c_str(), 57600, "detect im871a");
serial->doNotUseCallbacks();
AccessCheck rc = serial->open(false);
if (rc != AccessCheck::AccessOK) return AccessCheck::NotThere;

Wyświetl plik

@ -58,7 +58,7 @@ shared_ptr<WMBus> openRawTTY(string device, int baudrate, shared_ptr<SerialCommu
WMBusRawTTY *imp = new WMBusRawTTY(serial_override, manager);
return shared_ptr<WMBus>(imp);
}
auto serial = manager->createSerialDeviceTTY(device.c_str(), baudrate);
auto serial = manager->createSerialDeviceTTY(device.c_str(), baudrate, "rawtty");
WMBusRawTTY *imp = new WMBusRawTTY(serial, manager);
return shared_ptr<WMBus>(imp);
}
@ -139,7 +139,7 @@ AccessCheck detectRawTTY(string device, int baud, Detected *detected, shared_ptr
{
// Since we do not know how to talk to the other end, it might not
// even respond. The only thing we can do is to try to open the serial device.
auto serial = manager->createSerialDeviceTTY(device.c_str(), baud);
auto serial = manager->createSerialDeviceTTY(device.c_str(), baud, "detect rawtty");
AccessCheck rc = serial->open(false);
if (rc != AccessCheck::AccessOK) return AccessCheck::NotThere;

283
src/wmbus_rc1180.cc 100644
Wyświetl plik

@ -0,0 +1,283 @@
/*
Copyright (C) 2020 Fredrik Öhrström
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_utils.h"
#include"wmbus_cul.h"
#include"serial.h"
#include<assert.h>
#include<fcntl.h>
#include<grp.h>
#include<pthread.h>
#include<semaphore.h>
#include<string.h>
#include<errno.h>
#include<sys/stat.h>
#include<sys/types.h>
#include<unistd.h>
using namespace std;
#define SET_LINK_MODE 1
#define SET_X01_MODE 2
struct WMBusRC1180 : public virtual WMBusCommonImplementation
{
bool ping();
uint32_t getDeviceId();
LinkModeSet getLinkModes();
void deviceReset();
void deviceSetLinkModes(LinkModeSet lms);
LinkModeSet supportedLinkModes() {
return
C1_bit |
S1_bit |
T1_bit;
}
int numConcurrentLinkModes() { return 1; }
bool canSetLinkModes(LinkModeSet lms)
{
if (0 == countSetBits(lms.bits())) return false;
if (!supportedLinkModes().supports(lms)) return false;
// Ok, the supplied link modes are compatible,
// but im871a can only listen to one at a time.
return 1 == countSetBits(lms.bits());
}
void processSerialData();
void simulate();
WMBusRC1180(shared_ptr<SerialDevice> serial, shared_ptr<SerialCommunicationManager> manager);
~WMBusRC1180() { }
private:
LinkModeSet link_modes_ {};
vector<uchar> read_buffer_;
vector<uchar> received_payload_;
string sent_command_;
string received_response_;
FrameStatus checkRC1180Frame(vector<uchar> &data,
size_t *hex_frame_length,
vector<uchar> &payload);
string setup_;
};
shared_ptr<WMBus> openRC1180(string device, shared_ptr<SerialCommunicationManager> manager, shared_ptr<SerialDevice> serial_override)
{
if (serial_override)
{
WMBusRC1180 *imp = new WMBusRC1180(serial_override, manager);
return shared_ptr<WMBus>(imp);
}
auto serial = manager->createSerialDeviceTTY(device.c_str(), 38400, "rc1180");
WMBusRC1180 *imp = new WMBusRC1180(serial, manager);
return shared_ptr<WMBus>(imp);
}
WMBusRC1180::WMBusRC1180(shared_ptr<SerialDevice> serial, shared_ptr<SerialCommunicationManager> manager) :
WMBusCommonImplementation(DEVICE_RC1180, manager, serial)
{
reset();
}
bool WMBusRC1180::ping()
{
verbose("(rc1180) ping\n");
return true;
}
uint32_t WMBusRC1180::getDeviceId()
{
verbose("(rc1180) getDeviceId\n");
return 0x11111111;
}
LinkModeSet WMBusRC1180::getLinkModes()
{
return link_modes_;
}
void WMBusRC1180::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 WMBusRC1180::deviceSetLinkModes(LinkModeSet lms)
{
if (serial()->readonly()) return; // Feeding from stdin or file.
return;
if (!canSetLinkModes(lms))
{
string modes = lms.hr();
error("(rc1180) setting link mode(s) %s is not supported\n", modes.c_str());
}
// 'brc' command: b - wmbus, r - receive, c - c mode (with t)
vector<uchar> msg(5);
msg[0] = 'b';
msg[1] = 'r';
if (lms.has(LinkMode::C1)) {
msg[2] = 'c';
} else if (lms.has(LinkMode::S1)) {
msg[2] = 's';
} else if (lms.has(LinkMode::T1)) {
msg[2] = 't';
}
msg[3] = 0xa;
msg[4] = 0xd;
verbose("(rc1180) set link mode %c\n", msg[2]);
sent_command_ = string(&msg[0], &msg[3]);
received_response_ = "";
bool sent = serial()->send(msg);
if (sent) waitForResponse();
sent_command_ = "";
debug("(rc1180) received \"%s\"", received_response_.c_str());
bool ok = true;
if (lms.has(LinkMode::C1)) {
if (received_response_ != "CMODE") ok = false;
} else if (lms.has(LinkMode::S1)) {
if (received_response_ != "SMODE") ok = false;
} else if (lms.has(LinkMode::T1)) {
if (received_response_ != "TMODE") ok = false;
}
if (!ok)
{
string modes = lms.hr();
error("(rc1180) setting link mode(s) %s is not supported for this cul device!\n", modes.c_str());
}
// X01 - start the receiver
msg[0] = 'X';
msg[1] = '0';
msg[2] = '1';
msg[3] = 0xa;
msg[4] = 0xd;
sent = serial()->send(msg);
// Any response here, or does it silently move into listening mode?
}
void WMBusRC1180::simulate()
{
}
void WMBusRC1180::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 payload_len, payload_offset;
for (;;)
{
FrameStatus status = checkWMBusFrame(read_buffer_, &frame_length, &payload_len, &payload_offset);
if (status == PartialFrame)
{
// Partial frame, stop eating.
break;
}
if (status == ErrorInFrame)
{
verbose("(rawtty) protocol error in message received!\n");
string msg = bin2hex(read_buffer_);
debug("(rawtty) protocol error \"%s\"\n", msg.c_str());
read_buffer_.clear();
break;
}
if (status == FullFrame)
{
vector<uchar> payload;
if (payload_len > 0)
{
uchar l = payload_len;
payload.insert(payload.end(), &l, &l+1); // Re-insert the len byte.
payload.insert(payload.end(), read_buffer_.begin()+payload_offset, read_buffer_.begin()+payload_offset+payload_len);
}
read_buffer_.erase(read_buffer_.begin(), read_buffer_.begin()+frame_length);
handleTelegram(payload);
}
}
}
AccessCheck detectRC1180(string device, Detected *detected, shared_ptr<SerialCommunicationManager> manager)
{
// Talk to the device and expect a very specific answer.
auto serial = manager->createSerialDeviceTTY(device.c_str(), 19200, "detect rc1180");
serial->doNotUseCallbacks();
AccessCheck rc = serial->open(false);
if (rc != AccessCheck::AccessOK) return AccessCheck::NotThere;
verbose("(rc1180) are you there?\n");
vector<uchar> data;
vector<uchar> msg(1);
// Send a single 0x00 byte. This will trigger the device to enter command mode
// the device then responds with '>'
msg[0] = 0;
serial->send(msg);
usleep(200*1000);
serial->receive(&data);
if (data[0] != '>') {
// no RC1180 device detected
serial->close();
return AccessCheck::NotThere;
}
data.clear();
// send '0' to get get the version string: "V 1.67 nanoRC1180868" or similar
msg[0] = '0';
serial->send(msg);
// Wait for 200ms so that the USB stick have time to prepare a response.
usleep(1000*200);
serial->receive(&data);
string hex = bin2hex(data);
msg[0] = 'X';
serial->send(msg);
serial->close();
detected->set(WMBusDeviceType::DEVICE_RC1180, 19200, false);
return AccessCheck::AccessOK;
}

Wyświetl plik

@ -84,7 +84,7 @@ shared_ptr<WMBus> openRTL433(string device, string command, shared_ptr<SerialCom
WMBusRTL433 *imp = new WMBusRTL433(serial_override, manager);
return shared_ptr<WMBus>(imp);
}
auto serial = manager->createSerialDeviceCommand(device, "/bin/sh", args, envs, on_exit);
auto serial = manager->createSerialDeviceCommand(device, "/bin/sh", args, envs, on_exit, "rtl433");
WMBusRTL433 *imp = new WMBusRTL433(serial, manager);
return shared_ptr<WMBus>(imp);
}

Wyświetl plik

@ -85,7 +85,7 @@ shared_ptr<WMBus> openRTLWMBUS(string device, string command, shared_ptr<SerialC
WMBusRTLWMBUS *imp = new WMBusRTLWMBUS(serial_override, manager);
return shared_ptr<WMBus>(imp);
}
auto serial = manager->createSerialDeviceCommand(device, "/bin/sh", args, envs, on_exit);
auto serial = manager->createSerialDeviceCommand(device, "/bin/sh", args, envs, on_exit, "rtlwmbus");
WMBusRTLWMBUS *imp = new WMBusRTLWMBUS(serial, manager);
return shared_ptr<WMBus>(imp);
}

Wyświetl plik

@ -104,7 +104,7 @@ shared_ptr<WMBus> openWMB13U(string device, shared_ptr<SerialCommunicationManage
return shared_ptr<WMBus>(imp);
}
auto serial = manager->createSerialDeviceTTY(device.c_str(), 19200);
auto serial = manager->createSerialDeviceTTY(device.c_str(), 19200, "wmb13u");
WMBusWMB13U *imp = new WMBusWMB13U(serial, manager);
return shared_ptr<WMBus>(imp);
}
@ -344,7 +344,7 @@ bool WMBusWMB13U::getConfiguration()
AccessCheck detectWMB13U(string device, shared_ptr<SerialCommunicationManager> manager)
{
// Talk to the device and expect a very specific answer.
auto serial = manager->createSerialDeviceTTY(device.c_str(), 19200);
auto serial = manager->createSerialDeviceTTY(device.c_str(), 19200, "detect wmb13u");
AccessCheck rc = serial->open(false);
if (rc != AccessCheck::AccessOK) return AccessCheck::NotThere;

Wyświetl plik

@ -27,6 +27,7 @@ then
fi
cat <<EOF > $TEST/test_expected.txt
Started DEVICE_RTLWMBUS on stdin:rtlwmbus (config)
(wmbus) decrypted content failed check, did you use the correct decryption key? Ignoring telegram.
(wmbus) decrypted payload crc failed check, did you use the correct decryption key? Ignoring telegram.
(wmbus) decrypted content failed check, did you use the correct decryption key? Ignoring telegram.