-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add support for Revolt ZX-7717 power meter
- Loading branch information
1 parent
a656f2e
commit 176d63a
Showing
5 changed files
with
168 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,164 @@ | ||
/** @file | ||
Revolt ZX-7717-675 433 MHz power meter. | ||
Copyright (C) 2024 Christian W. Zuckschwerdt <zany@triq.net> | ||
Copyright (C) 2024 Boing <dhs_mobil@google.com> | ||
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 2 of the License, or | ||
(at your option) any later version. | ||
*/ | ||
|
||
#include "decoder.h" | ||
|
||
/** | ||
Revolt ZX-7717-675 433 MHz power meter. | ||
- Used with Revolt ZX-7716 Monitor. | ||
- Other names: HPM-27717, ZX-7717-919 | ||
- Up to 6 channels | ||
- First seen: 12-2024 | ||
- https://www.revolt-power.de/TOP-KAT161-Zusaetzliche-Steckdose-ZX-7717-919.shtml | ||
Outputs are: (in this order) | ||
- Current (A) max 15.999 A , Minimum is >= 0.001 A | ||
- Voltage (V) max 250.0 V | ||
- Power (VA) max 3679.9 VA | ||
- PF (Powerfactor not in message, but calculated) | ||
- 8 bit checksum | ||
- some unknown bytes/flags | ||
Modulation: ASK/OOK with Manchester coding. | ||
Send interval: 5 secs and/or when current changes. | ||
HF Output is 10 mW, but appears much higher (due to antenna maybe), | ||
With RSSI -0.1 dB, SNR 33.0 dB at 31 m distance! | ||
The packet is 14 manchester encoded bytes with a Preamble of 0x2A and | ||
an 8-bit checksum (last byte). | ||
Raw data: | ||
2ab0abe05a15603a14005710840011 | ||
2ab0abe05a15603a13005710df0040 | ||
2ab0abe05a15603ae2c0e710ca20bb | ||
2ab0abe05a15603a1ac0e710c12078 | ||
2ab0abe05a15603a7d007b104f00c7 | ||
Example messages: | ||
0d d507 5aa8 06 5c 2800 ea08 2100 88 | ||
0d d507 5aa8 06 5c c800 ea08 fb00 02 | ||
0d d507 5aa8 06 5c 4703 e708 5304 dd | ||
0d d507 5aa8 06 5c 5803 e708 8304 1e | ||
0d d507 5aa8 06 5c be00 de08 f200 e3 | ||
Data layout: | ||
LL IIII UUUU CC FF AAAA VVVV WWWW XX | ||
- L: (8 bit) 0d : payload_length (13) | ||
- I: (16 bit) d507 : id | ||
- U: (16 bit) 5aa8 : unknown1 | ||
- C: (8 bit) 06 : channel (6) // TODO | ||
- F: (8 bit) 5c ; unknown2 | ||
- A: (16 bit) be00 : current (0.190) | ||
- V: (16 bit) de08 : voltage (227.0) | ||
- W: (16 bit) f200 : power (24.2) | ||
- X: (8 bit) e3 : checksum | ||
*/ | ||
static int revolt_zx7717_decode(r_device *decoder, bitbuffer_t *bitbuffer) | ||
{ | ||
uint8_t const preamble[] = {0x2a}; // sync is 0x2a | ||
|
||
if (bitbuffer->num_rows != 1) { | ||
return DECODE_ABORT_EARLY; | ||
} | ||
unsigned row_len = bitbuffer->bits_per_row[0]; | ||
if (row_len < 120 || row_len > 120) { | ||
return DECODE_ABORT_EARLY; // Unrecognized data | ||
} | ||
|
||
unsigned pos = bitbuffer_search(bitbuffer, 0, 0, preamble, 8); | ||
pos += 8; // skip preamble | ||
|
||
if (pos > 16) { // FIXME: needs better checks | ||
return DECODE_ABORT_LENGTH; // short buffer or preamble not found | ||
} | ||
unsigned len = bitbuffer->bits_per_row[0] - pos; | ||
|
||
// There are different length messages | ||
// This handles the 14 bytes (112 bits) message only | ||
if (len < 112) { | ||
return DECODE_ABORT_LENGTH; // short buffer | ||
} | ||
len = 112; // FIXME: cuts the last pulse | ||
|
||
uint8_t b[42]; | ||
bitbuffer_extract_bytes(bitbuffer, 0, pos, b, len); | ||
reflect_bytes(b, (len + 7) / 8); | ||
|
||
int sum = add_bytes(b, 12); | ||
if (b[13] != (sum & 0xff)) { | ||
return DECODE_FAIL_MIC; // bad checksum | ||
} | ||
|
||
decoder_log_bitrow(decoder, 1, __func__, b, len, "message"); | ||
|
||
int id = (b[1] << 8) | (b[2]); // Big Endian? | ||
int unknown1 = (b[3] << 8) | b[4]; // Big Endian? | ||
int unknown2 = (b[5] << 8) | b[6]; // Big Endian? | ||
int channel = (b[5]); // not sure yet | ||
int current = (b[8] << 8) | b[7]; // Little Endian | ||
int voltage = (b[10] << 8) | b[9]; // Little Endian | ||
int power = (b[12] << 8) | b[11]; // Little Endian | ||
// calculation for PF (Powerfactor) is invalid if current is < 0.02 A | ||
// e.g. a standby device will show bad readings | ||
double va = current * voltage * 0.001; | ||
double powerf = va > 1.0 ? power / va : 1.0; | ||
|
||
/* clang-format off */ | ||
data_t *data = data_make( | ||
"model", "", DATA_STRING, "Revolt-ZX7717", | ||
"id", "Device ID", DATA_FORMAT, "%04x", DATA_INT, id, | ||
"channel", "Channel", DATA_INT, channel, | ||
"unknown", "UnknownB3", DATA_FORMAT, "%04x", DATA_INT, unknown1, | ||
"unknown", "UnknownB5", DATA_FORMAT, "%04x", DATA_INT, unknown2, | ||
"current_A", "Current", DATA_FORMAT, "%.3f A", DATA_DOUBLE, current * 0.001, | ||
"voltage_V", "Voltage", DATA_FORMAT, "%.1f V", DATA_DOUBLE, voltage * 0.1, | ||
"power_W", "Power", DATA_FORMAT, "%.1f W", DATA_DOUBLE, power * 0.1, | ||
"pf", "PF(calc)", DATA_DOUBLE, powerf, | ||
"mic", "Integrity", DATA_STRING, "CHECKSUM", | ||
NULL); | ||
/* clang-format on */ | ||
|
||
decoder_output_data(decoder, data); | ||
return 1; | ||
} | ||
|
||
static char const *const output_fields[] = { | ||
"model", | ||
"id", | ||
"channel", | ||
"unknown1", | ||
"unknown2", | ||
"current", | ||
"voltage", | ||
"power", | ||
"pf", | ||
"code", | ||
"mic", | ||
NULL, | ||
}; | ||
|
||
r_device const revolt_zx7717 = { | ||
.name = "Revolt ZX-7717 power meter", | ||
.modulation = OOK_PULSE_MANCHESTER_ZEROBIT, | ||
.short_width = 310, // Nominal width of clok half period [us] | ||
.long_width = 310, | ||
.reset_limit = 900, // Maximum gap size before End Of Message [us] | ||
.decode_fn = &revolt_zx7717_decode, | ||
.fields = output_fields, | ||
}; |