kopia lustrzana https://github.com/weetmuts/wmbusmeters
Added temperature support for multical21 and fixed im871a bug.
rodzic
843038a4f9
commit
016578ebd7
|
@ -57,6 +57,7 @@ struct CommandLine {
|
|||
char *usb_device {};
|
||||
LinkMode link_mode {};
|
||||
bool link_mode_set {};
|
||||
bool no_init {};
|
||||
vector<MeterInfo> meters;
|
||||
};
|
||||
|
||||
|
|
14
dvparser.cc
14
dvparser.cc
|
@ -77,7 +77,7 @@ bool parseDV(Telegram *t,
|
|||
int datalen = difLenBytes(dif);
|
||||
DEBUG_PARSER("(dvparser debug) dif=%02x datalen=%d \"%s\"\n", dif, datalen, difType(dif).c_str());
|
||||
if (datalen == -2) {
|
||||
warning("(dvparser) cannot handle dif %02X ignoring rest of telegram.\n", dif);
|
||||
debug("(dvparser) cannot handle dif %02X ignoring rest of telegram.\n", dif);
|
||||
return false;
|
||||
}
|
||||
if (dif == 0x2f) {
|
||||
|
@ -96,7 +96,7 @@ bool parseDV(Telegram *t,
|
|||
(*format)++;
|
||||
}
|
||||
|
||||
if (*format == format_end) { warning("(dvparser) warning: unexpected end of data (vif expected)"); break; }
|
||||
if (*format == format_end) { debug("(dvparser) warning: unexpected end of data (vif expected)"); break; }
|
||||
|
||||
uchar vif = **format;
|
||||
DEBUG_PARSER("(dvparser debug) vif=%02x \"%s\"\n", vif, vifType(vif).c_str());
|
||||
|
@ -110,7 +110,7 @@ bool parseDV(Telegram *t,
|
|||
strprintf(dv, "%02X%02X", dif, vif);
|
||||
if ((vif&0x80) == 0x80) {
|
||||
// vif extension
|
||||
if (*format == format_end) { warning("(dvparser) warning: unexpected end of data (vife expected)"); break; }
|
||||
if (*format == format_end) { debug("(dvparser) warning: unexpected end of data (vife expected)"); break; }
|
||||
uchar vife = **format;
|
||||
DEBUG_PARSER("(dvparser debug) vife=%02x\n", vife);
|
||||
if (full_header) {
|
||||
|
@ -140,7 +140,7 @@ bool parseDV(Telegram *t,
|
|||
|
||||
int remaining = std::distance(data, data_end);
|
||||
if (variable_length) {
|
||||
debug("(dvparser) varlen %02x\n", *(data+0));
|
||||
debug("(dvparser debug) varlen %02x\n", *(data+0));
|
||||
if (remaining > 2) {
|
||||
datalen = *(data);
|
||||
} else {
|
||||
|
@ -149,7 +149,7 @@ bool parseDV(Telegram *t,
|
|||
}
|
||||
DEBUG_PARSER("(dvparser debug) remaining data %d len=%d\n", remaining, datalen);
|
||||
if (remaining < datalen) {
|
||||
warning("(dvparser) warning: unexpected end of data\n");
|
||||
debug("(dvparser) warning: unexpected end of data\n");
|
||||
datalen = remaining;
|
||||
}
|
||||
|
||||
|
@ -164,6 +164,10 @@ bool parseDV(Telegram *t,
|
|||
t->addExplanation(data, datalen, "%s", value.c_str());
|
||||
DEBUG_PARSER("(dvparser debug) data \"%s\"\n", value.c_str());
|
||||
}
|
||||
if (remaining == datalen) {
|
||||
// We are done here!
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
string format_string = bin2hex(format_bytes);
|
||||
|
|
|
@ -43,6 +43,10 @@ struct MeterIperl : public virtual WaterMeter, public virtual MeterCommonImpleme
|
|||
bool hasTargetWaterConsumption();
|
||||
double maxFlow();
|
||||
bool hasMaxFlow();
|
||||
double flowTemperature();
|
||||
bool hasFlowTemperature();
|
||||
double externalTemperature();
|
||||
bool hasExternalTemperature();
|
||||
|
||||
string statusHumanReadable();
|
||||
string status();
|
||||
|
@ -225,6 +229,26 @@ bool MeterIperl::hasMaxFlow()
|
|||
return false;
|
||||
}
|
||||
|
||||
double MeterIperl::flowTemperature()
|
||||
{
|
||||
return 127;
|
||||
}
|
||||
|
||||
bool MeterIperl::hasFlowTemperature()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
double MeterIperl::externalTemperature()
|
||||
{
|
||||
return 127;
|
||||
}
|
||||
|
||||
bool MeterIperl::hasExternalTemperature()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
string MeterIperl::statusHumanReadable()
|
||||
{
|
||||
return "";
|
||||
|
|
|
@ -59,6 +59,12 @@ struct MeterMultical21 : public virtual WaterMeter, public virtual MeterCommonIm
|
|||
// Max flow during last month or last 24 hours depending on meter configuration.
|
||||
double maxFlow();
|
||||
bool hasMaxFlow();
|
||||
// Water temperature
|
||||
double flowTemperature();
|
||||
bool hasFlowTemperature();
|
||||
// Surrounding temperature
|
||||
double externalTemperature();
|
||||
bool hasExternalTemperature();
|
||||
|
||||
// statusHumanReadable: DRY,REVERSED,LEAK,BURST if that status is detected right now, followed by
|
||||
// (dry 15-21 days) which means that, even it DRY is not active right now,
|
||||
|
@ -87,6 +93,10 @@ private:
|
|||
bool has_target_volume_ {};
|
||||
double max_flow_ {};
|
||||
bool has_max_flow_ {};
|
||||
double flow_temperature_ { 127 };
|
||||
bool has_flow_temperature_ {};
|
||||
double external_temperature_ { 127 };
|
||||
bool has_external_temperature_ {};
|
||||
|
||||
const char *meter_name_; // multical21 or flowiq3100
|
||||
int expected_version_ {}; // 0x1b for Multical21 and 0x1d for FlowIQ3100
|
||||
|
@ -138,6 +148,26 @@ bool MeterMultical21::hasMaxFlow()
|
|||
return has_max_flow_;
|
||||
}
|
||||
|
||||
double MeterMultical21::flowTemperature()
|
||||
{
|
||||
return flow_temperature_;
|
||||
}
|
||||
|
||||
bool MeterMultical21::hasFlowTemperature()
|
||||
{
|
||||
return has_flow_temperature_;
|
||||
}
|
||||
|
||||
double MeterMultical21::externalTemperature()
|
||||
{
|
||||
return external_temperature_;
|
||||
}
|
||||
|
||||
bool MeterMultical21::hasExternalTemperature()
|
||||
{
|
||||
return has_external_temperature_;
|
||||
}
|
||||
|
||||
WaterMeter *createMultical21(WMBus *bus, const char *name, const char *id, const char *key, MeterType mt)
|
||||
{
|
||||
if (mt != MULTICAL21_METER && mt != FLOWIQ3100_METER) {
|
||||
|
@ -215,13 +245,6 @@ void MeterMultical21::processContent(Telegram *t)
|
|||
|
||||
if (frame_type == 0x79)
|
||||
{
|
||||
if (t->content.size() != 15) {
|
||||
warning("(%s) warning: Unexpected length of short frame %zu. Expected 15 bytes! ", meter_name_,
|
||||
t->content.size());
|
||||
padWithZeroesTo(&t->content, 15, &t->content);
|
||||
warning("\n");
|
||||
}
|
||||
|
||||
// 0,1 = crc for format signature = hash over DRH (Data Record Header)
|
||||
// The DRH is the dif(dife)vif(vife) bytes for all the records...
|
||||
// This hash should be used to pick up a suitable format string.
|
||||
|
@ -270,18 +293,8 @@ void MeterMultical21::processContent(Telegram *t)
|
|||
else
|
||||
if (frame_type == 0x78)
|
||||
{
|
||||
if (t->content.size() != 22) {
|
||||
warning("(%s) warning: Unexpected length of long frame %zu. Expected 22 bytes! ", meter_name_, t->content.size());
|
||||
padWithZeroesTo(&t->content, 22, &t->content);
|
||||
warning("\n");
|
||||
}
|
||||
|
||||
map<string,pair<int,string>> values;
|
||||
parseDV(t, t->content.begin()+3, t->content.size()-3-2, &values);
|
||||
|
||||
// There are two more bytes in the data. Unknown purpose.
|
||||
int val0 = t->content[20];
|
||||
int val1 = t->content[21];
|
||||
parseDV(t, t->content.begin()+3, t->content.size()-3, &values);
|
||||
|
||||
int offset;
|
||||
|
||||
|
@ -293,13 +306,19 @@ void MeterMultical21::processContent(Telegram *t)
|
|||
t->addMoreExplanation(offset, " total consumption (%f m3)", total_water_consumption_);
|
||||
|
||||
extractDVdouble(&values, "4413", &offset, &target_volume_);
|
||||
has_target_volume_ = true;
|
||||
has_target_volume_ = true;
|
||||
t->addMoreExplanation(offset, " target consumption (%f m3)", target_volume_);
|
||||
|
||||
// To unknown bytes, seems to be very constant.
|
||||
vector<uchar>::iterator unknowns = t->content.begin()+20;
|
||||
t->addExplanation(unknowns, 2, "%02x%02x unknown", val0, val1);
|
||||
} else {
|
||||
extractDVdouble(&values, "615B", &offset, &flow_temperature_);
|
||||
has_flow_temperature_ = true;
|
||||
t->addMoreExplanation(offset, " flow temperature (%f °C)", flow_temperature_);
|
||||
|
||||
extractDVdouble(&values, "6167", &offset, &external_temperature_);
|
||||
has_external_temperature_ = true;
|
||||
t->addMoreExplanation(offset, " external temperature (%f °C)", external_temperature_);
|
||||
}
|
||||
else
|
||||
{
|
||||
warning("(%s) warning: unknown frame %02x (did you use the correct encryption key?)\n", meter_name_, frame_type);
|
||||
}
|
||||
}
|
||||
|
@ -434,21 +453,25 @@ void MeterMultical21::printMeter(string *human_readable,
|
|||
char buf[65536];
|
||||
buf[65535] = 0;
|
||||
|
||||
snprintf(buf, sizeof(buf)-1, "%s\t%s\t% 3.3f m3\t% 3.3f m3\t%s\t%s",
|
||||
snprintf(buf, sizeof(buf)-1, "%s\t%s\t% 3.3f m3\t% 3.3f m3\t% 2.0f°C\t% 2.0f°C\t%s\t%s",
|
||||
name().c_str(),
|
||||
id().c_str(),
|
||||
totalWaterConsumption(),
|
||||
targetWaterConsumption(),
|
||||
flowTemperature(),
|
||||
externalTemperature(),
|
||||
statusHumanReadable().c_str(),
|
||||
datetimeOfUpdateHumanReadable().c_str());
|
||||
|
||||
*human_readable = buf;
|
||||
|
||||
snprintf(buf, sizeof(buf)-1, "%s%c%s%c%f%c%f%c%s%c%s",
|
||||
snprintf(buf, sizeof(buf)-1, "%s%c" "%s%c" "%f%c" "%f%c" "%.0f%c" "%.0f%c" "%s%c" "%s",
|
||||
name().c_str(), separator,
|
||||
id().c_str(), separator,
|
||||
totalWaterConsumption(), separator,
|
||||
targetWaterConsumption(), separator,
|
||||
flowTemperature(), separator,
|
||||
externalTemperature(), separator,
|
||||
statusHumanReadable().c_str(), separator,
|
||||
datetimeOfUpdateRobot().c_str());
|
||||
|
||||
|
@ -465,6 +488,8 @@ void MeterMultical21::printMeter(string *human_readable,
|
|||
QS(id,%s)
|
||||
Q(total_m3,%f)
|
||||
Q(target_m3,%f)
|
||||
Q(flow_temperature,%.0f)
|
||||
Q(external_temperature,%.0f)
|
||||
QS(current_status,%s)
|
||||
QS(time_dry,%s)
|
||||
QS(time_reversed,%s)
|
||||
|
@ -478,6 +503,8 @@ void MeterMultical21::printMeter(string *human_readable,
|
|||
id().c_str(),
|
||||
totalWaterConsumption(),
|
||||
targetWaterConsumption(),
|
||||
flowTemperature(),
|
||||
externalTemperature(),
|
||||
status().c_str(), // DRY REVERSED LEAK BURST
|
||||
timeDry().c_str(),
|
||||
timeReversed().c_str(),
|
||||
|
@ -492,6 +519,8 @@ void MeterMultical21::printMeter(string *human_readable,
|
|||
envs->push_back(string("METER_ID=")+id());
|
||||
envs->push_back(string("METER_TOTAL_M3=")+to_string(totalWaterConsumption()));
|
||||
envs->push_back(string("METER_TARGET_M3=")+to_string(targetWaterConsumption()));
|
||||
envs->push_back(string("METER_FLOW_TEMPERATURE=")+to_string(flowTemperature()));
|
||||
envs->push_back(string("METER_EXTERNAL_TEMPERATURE=")+to_string(externalTemperature()));
|
||||
envs->push_back(string("METER_STATUS=")+status());
|
||||
envs->push_back(string("METER_TIME_DRY=")+timeDry());
|
||||
envs->push_back(string("METER_TIME_REVERSED=")+timeReversed());
|
||||
|
|
|
@ -119,11 +119,12 @@ void MeterMultical302::processContent(Telegram *t) {
|
|||
t->addExplanation(bytes, 1, "%02x frame type (%s)", frame_type, frameTypeKamstrupC1(frame_type).c_str());
|
||||
|
||||
if (frame_type == 0x79) {
|
||||
if (t->content.size() != 17) {
|
||||
/*if (t->content.size() != 17) {
|
||||
|
||||
fprintf(stderr, "(multical302) warning: Unexpected length of frame %zu. Expected 17 bytes! ", t->content.size());
|
||||
padWithZeroesTo(&t->content, 17, &t->content);
|
||||
warning("\n");
|
||||
}
|
||||
}*/
|
||||
|
||||
// This code should be rewritten to use parseDV see the Multical21 code.
|
||||
// But I cannot do this without more examples of 302 telegrams.
|
||||
|
@ -151,11 +152,11 @@ void MeterMultical302::processContent(Telegram *t) {
|
|||
}
|
||||
else if (frame_type == 0x78)
|
||||
{
|
||||
if (t->content.size() != 26) {
|
||||
/*if (t->content.size() != 26) {
|
||||
fprintf(stderr, "(multical302) warning: Unexpected length of frame %zu. Expected 26 bytes! ", t->content.size());
|
||||
padWithZeroesTo(&t->content, 26, &t->content);
|
||||
warning("\n");
|
||||
}
|
||||
}*/
|
||||
|
||||
// This code should be rewritten to use parseDV see the Multical21 code.
|
||||
// But I cannot do this without more examples of 302 telegrams.
|
||||
|
|
|
@ -42,6 +42,10 @@ struct MeterSupercom587 : public virtual WaterMeter, public virtual MeterCommonI
|
|||
bool hasTargetWaterConsumption();
|
||||
double maxFlow();
|
||||
bool hasMaxFlow();
|
||||
double flowTemperature();
|
||||
bool hasFlowTemperature();
|
||||
double externalTemperature();
|
||||
bool hasExternalTemperature();
|
||||
|
||||
string statusHumanReadable();
|
||||
string status();
|
||||
|
@ -223,6 +227,26 @@ bool MeterSupercom587::hasMaxFlow()
|
|||
return false;
|
||||
}
|
||||
|
||||
double MeterSupercom587::flowTemperature()
|
||||
{
|
||||
return 127;
|
||||
}
|
||||
|
||||
bool MeterSupercom587::hasFlowTemperature()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
double MeterSupercom587::externalTemperature()
|
||||
{
|
||||
return 127;
|
||||
}
|
||||
|
||||
bool MeterSupercom587::hasExternalTemperature()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
string MeterSupercom587::statusHumanReadable()
|
||||
{
|
||||
return "";
|
||||
|
|
4
meters.h
4
meters.h
|
@ -73,6 +73,10 @@ struct WaterMeter : public virtual Meter {
|
|||
virtual bool hasTargetWaterConsumption() = 0;
|
||||
virtual double maxFlow() = 0;
|
||||
virtual bool hasMaxFlow() = 0;
|
||||
virtual double flowTemperature() = 0; // °C
|
||||
virtual bool hasFlowTemperature() = 0;
|
||||
virtual double externalTemperature() = 0; // °C
|
||||
virtual bool hasExternalTemperature() = 0;
|
||||
|
||||
virtual string statusHumanReadable() = 0;
|
||||
virtual string status() = 0;
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
# Test Multical21 C1 telegrams
|
||||
|
||||
# short telegram
|
||||
telegram=|23442D2C998734761B168D2093E13CBA20|967F79EDA8047B7100F4180000E918|
|
||||
{"media":"cold water","meter":"multical21","name":"MyTapWater","id":"76348799","total_m3":6.388000,"target_m3":6.377000,"current_status":"DRY","time_dry":"22-31 days","time_reversed":"","time_leaking":"","time_bursting":"","timestamp":"1111-11-11T11:11:11Z"}
|
||||
telegram=|23442D2C998734761B168D208870F81821|09EA79EDA869F57100F8180000F41800007F17|
|
||||
{"media":"cold water","meter":"multical21","name":"MyTapWater","id":"76348799","total_m3":6.392000,"target_m3":6.388000,"flow_temperature":0,"external_temperature":0,"current_status":"DRY","time_dry":"22-31 days","time_reversed":"","time_leaking":"","time_bursting":"","timestamp":"1111-11-11T11:11:11Z"}
|
||||
|
||||
# full telegram
|
||||
telegram=|2A442D2C998734761B168D2049F03FBA20|39A17802FF2071000413F41800004413E9180000615B|
|
||||
{"media":"cold water","meter":"multical21","name":"MyTapWater","id":"76348799","total_m3":6.388000,"target_m3":6.377000,"current_status":"DRY","time_dry":"22-31 days","time_reversed":"","time_leaking":"","time_bursting":"","timestamp":"1111-11-11T11:11:11Z"}
|
||||
telegram=|2A442D2C998734761B168D208971F81821|542F7802FF2071000413F81800004413F4180000615B7F616717|
|
||||
{"media":"cold water","meter":"multical21","name":"MyTapWater","id":"76348799","total_m3":6.392000,"target_m3":6.388000,"flow_temperature":127,"external_temperature":23,"current_status":"DRY","time_dry":"22-31 days","time_reversed":"","time_leaking":"","time_bursting":"","timestamp":"1111-11-11T11:11:11Z"}
|
||||
|
||||
# Test Multical302 C1 telegrams
|
||||
|
||||
|
|
|
@ -1,13 +1,12 @@
|
|||
# Test Supercom587 T1 telegrams
|
||||
|
||||
telegram=|A244EE4D785634123C067A73000000|0C1334190000426CE1F14C130000000082046C21298C0413330000008D04931E3A3CFE3300000033000000330000003300000033000000330000003300000033000000330000003300000033000000330000004300000034180000046D0113412B03FD6CDE120082206C5C290BFD0F0200018C4079678885238310FD3100000082106C01018110FD610002FD66020002|
|
||||
{"media":"warm water","meter":"supercom587","name":"MyWarmWater","id":"12345678","total_m3":1.934000,"timestamp":"1111-11-11T11:11:11Z"}
|
||||
telegram=|A244EE4D785634123C067A8F000000|0C1348550000426CE1F14C130000000082046C21298C0413330000008D04931E3A3CFE3300000033000000330000003300000033000000330000003300000033000000330000003300000033000000330000004300000034180000046D0D0B5C2B03FD6C5E150082206C5C290BFD0F0200018C4079678885238310FD3100000082106C01018110FD610002FD66020002FD170000|
|
||||
{"media":"warm water","meter":"supercom587","name":"MyWarmWater","id":"12345678","total_m3":5.548000,"timestamp":"1111-11-11T11:11:11Z"}
|
||||
|
||||
telegram=|A244EE4D111111113C077A72000000|0C1374140000426CE1F14C130000000082046C21298C0413010000008D04931E3A3CFE0100000001000000010000000100000001000000010000000100000001000000010000000100000001000000010000001600000031130000046D0113412B03FD6CDF120082206C5C290BFD0F0200018C4079629885238310FD3100000082106C01018110FD610002FD66020002|
|
||||
{"media":"water","meter":"supercom587","name":"MyColdWater","id":"11111111","total_m3":1.474000,"timestamp":"1111-11-11T11:11:11Z"}
|
||||
telegram=|A244EE4D111111113C077AAC000000|0C1389490000426CE1F14C130000000082046C21298C0413010000008D04931E3A3CFE0100000001000000010000000100000001000000010000000100000001000000010000000100000001000000010000001600000031130000046D0A0C5C2B03FD6C60150082206C5C290BFD0F0200018C4079629885238310FD3100000082106C01018110FD610002FD66020002FD170000|
|
||||
{"media":"water","meter":"supercom587","name":"MyColdWater","id":"11111111","total_m3":4.989000,"timestamp":"1111-11-11T11:11:11Z"}
|
||||
|
||||
# Test iPerl T1 telegram
|
||||
|
||||
# 12345678 "1234567890ABCDEF1234567890ABCDEF"
|
||||
telegram=|1E44AE4C7856341268077AB5000000|53cb05c02cf40b38e4cfe6a9f6565ec27261ae620df9257179197ef2dc6512f2|
|
||||
{"media":"water","meter":"iperl","name":"MoreWater","id":"12345678","total_m3":3.699000,"timestamp":"1111-11-11T11:11:11Z"}
|
||||
telegram=|1E44AE4C9956341268077A36001005|2F2F0413181E0000023B00002F2F2F2F|
|
||||
{"media":"water","meter":"iperl","name":"MoreWater","id":"12345699","total_m3":7.704000,"timestamp":"1111-11-11T11:11:11Z"}
|
||||
|
|
6
test.sh
6
test.sh
|
@ -14,7 +14,7 @@ then
|
|||
diff test_expected.txt test_responses.txt
|
||||
if [ "$?" == "0" ]
|
||||
then
|
||||
echo OK
|
||||
echo C1 OK
|
||||
fi
|
||||
else
|
||||
Failure.
|
||||
|
@ -24,7 +24,7 @@ cat simulation_t1.txt | grep '^{' > test_expected.txt
|
|||
$PROG --robot=json simulation_t1.txt \
|
||||
MyWarmWater supercom587 12345678 "" \
|
||||
MyColdWater supercom587 11111111 "" \
|
||||
MoreWater iperl 12345678 "1234567890ABCDEF1234567890ABCDEF" \
|
||||
MoreWater iperl 12345699 "" \
|
||||
> test_output.txt
|
||||
if [ "$?" == "0" ]
|
||||
then
|
||||
|
@ -32,7 +32,7 @@ then
|
|||
diff test_expected.txt test_responses.txt
|
||||
if [ "$?" == "0" ]
|
||||
then
|
||||
echo OK
|
||||
echo T1 OK
|
||||
fi
|
||||
else
|
||||
Failure.
|
||||
|
|
32
util.cc
32
util.cc
|
@ -406,3 +406,35 @@ uint16_t crc16_EN13757(uchar *data, size_t len)
|
|||
|
||||
return (~crc);
|
||||
}
|
||||
|
||||
#define CRC16_INIT_VALUE 0xFFFF
|
||||
#define CRC16_GOOD_VALUE 0x0F47
|
||||
#define CRC16_POLYNOM 0x8408
|
||||
|
||||
uint16_t crc16_CCITT(uchar *data, uint16_t length)
|
||||
{
|
||||
uint16_t initVal = CRC16_INIT_VALUE;
|
||||
uint16_t crc = initVal;
|
||||
while(length--)
|
||||
{
|
||||
int bits = 8;
|
||||
uchar byte = *data++;
|
||||
while(bits--)
|
||||
{
|
||||
if((byte & 1) ^ (crc & 1))
|
||||
{
|
||||
crc = (crc >> 1) ^ CRC16_POLYNOM;
|
||||
}
|
||||
else
|
||||
crc >>= 1;
|
||||
byte >>= 1;
|
||||
}
|
||||
}
|
||||
return crc;
|
||||
}
|
||||
|
||||
bool crc16_CCITT_check(uchar *data, uint16_t length)
|
||||
{
|
||||
uint16_t crc = ~crc16_CCITT(data, length);
|
||||
return crc == CRC16_GOOD_VALUE;
|
||||
}
|
||||
|
|
4
util.h
4
util.h
|
@ -72,4 +72,8 @@ int parseTime(std::string time);
|
|||
|
||||
uint16_t crc16_EN13757(uchar *data, size_t len);
|
||||
|
||||
// This crc is used by im871a for its serial communication.
|
||||
uint16_t crc16_CCITT(uchar *data, uint16_t length);
|
||||
bool crc16_CCITT_check(uchar *data, uint16_t length);
|
||||
|
||||
#endif
|
||||
|
|
2
wmbus.h
2
wmbus.h
|
@ -26,7 +26,7 @@
|
|||
|
||||
#define LIST_OF_LINK_MODES X(LinkModeC1)X(LinkModeT1)X(UNKNOWN_LINKMODE)
|
||||
|
||||
// In link mode T1, the meter transmits a telegram every few seconds.
|
||||
// In link mode T1, the meter transmits a telegram every few seconds or minutes.
|
||||
// Suitable for drive-by/walk-by collection of meter values.
|
||||
|
||||
// Link mode C1 is like T1 but uses less energy when transmitting due to
|
||||
|
|
|
@ -222,7 +222,10 @@ void WMBusAmber::waitForResponse() {
|
|||
}
|
||||
|
||||
FrameStatus WMBusAmber::checkAMB8465Frame(vector<uchar> &data,
|
||||
size_t *frame_length, int *msgid_out, int *payload_len_out, int *payload_offset)
|
||||
size_t *frame_length,
|
||||
int *msgid_out,
|
||||
int *payload_len_out,
|
||||
int *payload_offset)
|
||||
{
|
||||
if (data.size() == 0) return PartialFrame;
|
||||
int payload_len = 0;
|
||||
|
@ -387,9 +390,6 @@ bool detectAMB8465(string device, SerialCommunicationManager *manager)
|
|||
|
||||
serial->close();
|
||||
|
||||
string sent = bin2hex(msg);
|
||||
string recv = bin2hex(data);
|
||||
|
||||
if (data.size() < 8 ||
|
||||
data[0] != 0xff ||
|
||||
data[1] != (0x80 | msg[1]) ||
|
||||
|
|
|
@ -251,19 +251,21 @@ void WMBusIM871A::setLinkMode(LinkMode lm)
|
|||
}
|
||||
pthread_mutex_lock(&command_lock_);
|
||||
|
||||
vector<uchar> msg(8);
|
||||
vector<uchar> msg(10);
|
||||
msg[0] = IM871A_SERIAL_SOF;
|
||||
msg[1] = DEVMGMT_ID;
|
||||
sent_command_ = msg[2] = DEVMGMT_MSG_SET_CONFIG_REQ;
|
||||
msg[3] = 3; // Len
|
||||
msg[3] = 6; // Len
|
||||
msg[4] = 0; // Temporary
|
||||
msg[5] = 2; // iff1 bits: Set Radio Mode only
|
||||
msg[5] = 2; // iff1 bits: Set Radio Mode
|
||||
if (lm == LinkModeC1) {
|
||||
msg[6] = (int)im871a_C1a;
|
||||
} else {
|
||||
msg[6] = (int)im871a_T1;
|
||||
}
|
||||
msg[7] = 0; // iff2 bits: Set nothing
|
||||
msg[7] = 16+32; // iff2 bits: Set rssi+timestamp
|
||||
msg[8] = 1; // Enable rssi
|
||||
msg[9] = 1; // Enable timestamp
|
||||
|
||||
verbose("(im871a) set link mode %02x\n", msg[6]);
|
||||
serial()->send(msg);
|
||||
|
@ -293,6 +295,7 @@ FrameStatus WMBusIM871A::checkFrame(vector<uchar> &data,
|
|||
{
|
||||
if (data.size() == 0) return PartialFrame;
|
||||
if (data[0] != 0xa5) return ErrorInFrame;
|
||||
|
||||
int ctrlbits = (data[1] & 0xf0) >> 4;
|
||||
if (ctrlbits & 1) return ErrorInFrame; // Bit 1 is reserved, we do not expect it....
|
||||
bool has_timestamp = ((ctrlbits&2)==2);
|
||||
|
@ -319,6 +322,35 @@ FrameStatus WMBusIM871A::checkFrame(vector<uchar> &data,
|
|||
*frame_length = *payload_offset+payload_len+(has_timestamp?4:0)+(has_rssi?1:0)+(has_crc16?2:0);
|
||||
if (data.size() < *frame_length) return PartialFrame;
|
||||
|
||||
int i = *payload_offset + payload_len;
|
||||
if (has_timestamp) {
|
||||
uint32_t a = data[i];
|
||||
uint32_t b = data[i+1];
|
||||
uint32_t c = data[i+2];
|
||||
uint32_t d = data[i+3];
|
||||
|
||||
uint32_t ts = a+b*256+c*256*256+d*256*256*256;
|
||||
debug("(im871a) timestamp %08x\n", ts);
|
||||
i += 4;
|
||||
}
|
||||
if (has_rssi) {
|
||||
uint32_t rssi = data[i];
|
||||
debug("(im871a) rssi %02x\n", rssi);
|
||||
i++;
|
||||
}
|
||||
if (has_crc16) {
|
||||
uint32_t a = data[i];
|
||||
uint32_t b = data[i+1];
|
||||
uint32_t crc16 = a+b*256;
|
||||
i+=2;
|
||||
uint16_t gotcrc = ~crc16_CCITT(&data[1], i-1-2);
|
||||
bool crcok = crc16_CCITT_check(&data[1], i-1);
|
||||
debug("(im871a) got crc16 %04x expected %04x\n", crc16, gotcrc);
|
||||
if (!crcok) {
|
||||
warning("(im871a) warning: got wrong crc %04x expected %04x\n", gotcrc, crc16);
|
||||
}
|
||||
}
|
||||
|
||||
return FullFrame;
|
||||
}
|
||||
|
||||
|
@ -348,14 +380,18 @@ void WMBusIM871A::processSerialData()
|
|||
if (status == FullFrame) {
|
||||
|
||||
vector<uchar> payload;
|
||||
if (payload_len > 0) {
|
||||
if (payload_len > 0)
|
||||
{
|
||||
if (endpoint == RADIOLINK_ID &&
|
||||
msgid == RADIOLINK_MSG_WMBUSMSG_IND)
|
||||
{
|
||||
uchar l = payload_len;
|
||||
payload.insert(payload.begin(), &l, &l+1); // Re-insert the len byte.
|
||||
}
|
||||
payload.insert(payload.end(), read_buffer_.begin()+payload_offset, read_buffer_.begin()+payload_len);
|
||||
// Insert the payload.
|
||||
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);
|
||||
|
|
|
@ -57,10 +57,6 @@ void decryptMode1_AES_CTR(Telegram *t, vector<uchar> &aeskey)
|
|||
vector<uchar> dec(decrypt, decrypt+remaining);
|
||||
debugPayload("(Mode1) decrypted first block", dec);
|
||||
|
||||
if (content.size() > 22) {
|
||||
warning("(Mode1) warning: decryption received too many bytes of content! "
|
||||
"Got %zu bytes, expected at most 22.\n", content.size());
|
||||
}
|
||||
if (content.size() > 16) {
|
||||
// Yay! Lets decrypt a second block. Full frame content is 22 bytes.
|
||||
// So a second block should enough for everyone!
|
||||
|
@ -146,8 +142,10 @@ bool loadFormatBytesFromSignature(uint16_t format_signature, vector<uchar> *form
|
|||
{
|
||||
hex2bin("02FF2004134413", format_bytes);
|
||||
// The hash of this string should equal the format signature above.
|
||||
uint16_t format_hash = crc16_EN13757(&(*format_bytes)[0], 7);
|
||||
debug("(utils) format signature %4X format hash %4X\n", format_signature, format_hash);
|
||||
uint16_t format_hash1 = crc16_EN13757(&(*format_bytes)[0], 7);
|
||||
uint16_t format_hash2 = ~crc16_CCITT(&(*format_bytes)[0], 7);
|
||||
debug("(utils) format signature %4X format hash a=%4X b=%4X c=%4X d=%4X\n",
|
||||
format_signature, format_hash1, (uint16_t)(~format_hash1), format_hash2, (uint16_t)(~format_hash2));
|
||||
return true;
|
||||
}
|
||||
// Unknown format signature.
|
||||
|
|
Ładowanie…
Reference in New Issue