Skip to content

Commit 738f624

Browse files
committed
Add support for Revolt ZX-7717 power meter
1 parent 264a56c commit 738f624

File tree

5 files changed

+186
-0
lines changed

5 files changed

+186
-0
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -359,6 +359,7 @@ See [CONTRIBUTING.md](./docs/CONTRIBUTING.md).
359359
[271] Landis & Gyr Gridstream Power Meters 9.6k
360360
[272] Landis & Gyr Gridstream Power Meters 19.2k
361361
[273] Landis & Gyr Gridstream Power Meters 38.4k
362+
[274] Revolt ZX-7717 power meter
362363
363364
* Disabled by default, use -R n or a conf file to enable
364365

conf/rtl_433.example.conf

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -500,6 +500,7 @@ convert si
500500
protocol 271 # Landis & Gyr Gridstream Power Meters 9.6k
501501
protocol 272 # Landis & Gyr Gridstream Power Meters 19.2k
502502
protocol 273 # Landis & Gyr Gridstream Power Meters 38.4k
503+
protocol 274 # Revolt ZX-7717 power meter
503504

504505
## Flex devices (command line option "-X")
505506

include/rtl_433_devices.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -281,6 +281,7 @@
281281
DECL(gridstream96) \
282282
DECL(gridstream192) \
283283
DECL(gridstream384) \
284+
DECL(revolt_zx7717) \
284285

285286
/* Add new decoders here. */
286287

src/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,7 @@ add_library(r_433 STATIC
206206
devices/rainpoint.c
207207
devices/regency_fan.c
208208
devices/revolt_nc5462.c
209+
devices/revolt_zx7717.c
209210
devices/rftech.c
210211
devices/risco_agility.c
211212
devices/rojaflex.c

src/devices/revolt_zx7717.c

Lines changed: 182 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,182 @@
1+
/** @file
2+
Revolt ZX-7717-675 433 MHz power meter.
3+
4+
Copyright (C) 2024 Christian W. Zuckschwerdt <[email protected]>
5+
Copyright (C) 2024 Boing <[email protected]>
6+
7+
This program is free software; you can redistribute it and/or modify
8+
it under the terms of the GNU General Public License as published by
9+
the Free Software Foundation; either version 2 of the License, or
10+
(at your option) any later version.
11+
*/
12+
13+
#include "decoder.h"
14+
15+
/**
16+
Revolt ZX-7717-675 433 MHz power meter.
17+
18+
- Used with Revolt ZX-7716 Monitor.
19+
- Other names: HPM-27717, ZX-7717-919
20+
- Up to 6 channels
21+
- First seen: 12-2024
22+
- https://www.revolt-power.de/TOP-KAT161-Zusaetzliche-Steckdose-ZX-7717-919.shtml
23+
24+
Outputs are: (in this order)
25+
- Current (A) max 15.999 A , Minimum is >= 0.001 A
26+
- Voltage (V) max 250.0 V
27+
- Power (VA) max 3679.9 VA
28+
- PF (Powerfactor not in message, but calculated)
29+
- 8 bit checksum
30+
- some unknown bytes/flags
31+
32+
Modulation: ASK/OOK with Manchester coding.
33+
Send interval: 5 secs and/or when current changes.
34+
35+
HF Output is 10 mW, but appears much higher (due to antenna maybe),
36+
With RSSI -0.1 dB, SNR 33.0 dB at 31 m distance!
37+
38+
The packet is 14 manchester encoded bytes with a Preamble of 0x2A and
39+
an 8-bit checksum (last byte).
40+
41+
Raw data:
42+
43+
2ab0abe05a15603a14005710840011
44+
2ab0abe05a15603a13005710df0040
45+
2ab0abe05a15603ae2c0e710ca20bb
46+
2ab0abe05a15603a1ac0e710c12078
47+
2ab0abe05a15603a7d007b104f00c7
48+
2a88abe05a950026b880603af5c05710d9a018
49+
2a48abe05a950026b880e000000040000000003e
50+
2ab0abe05a15603a6ec0b7103f20dd
51+
2a70abe05a05e08000001c80000000a4
52+
53+
Example messages:
54+
55+
0d d507 5aa8 06 5c 2800 ea08 2100 88
56+
0d d507 5aa8 06 5c c800 ea08 fb00 02
57+
0d d507 5aa8 06 5c 4703 e708 5304 dd
58+
0d d507 5aa8 06 5c 5803 e708 8304 1e
59+
0d d507 5aa8 06 5c be00 de08 f200 e3
60+
11 d507 5aa9 00 64 1d01 065c af03 ea08 9b05 18
61+
12 d507 5aa9 00 64 1d01 0700 0000 0200 0000 00 7c
62+
0d d507 5aa8 06 5c 7603 ed08 fc04 bb
63+
0e d507 5aa0 07 01 0000 3801 0000 00 25
64+
65+
Data layout:
66+
67+
LL IIII UUUU CC FF AAAA VVVV WWWW XX
68+
69+
- L: (8 bit) 0d : payload_length (13), excluding the length byte, including checksum
70+
- I: (16 bit) d507 : id
71+
- U: (16 bit) 5aa8 : unknown1
72+
- C: (8 bit) 06 : channel (6) // TODO
73+
- F: (8 bit) 5c ; unknown2
74+
- A: (16 bit) be00 : current (0.190)
75+
- V: (16 bit) de08 : voltage (227.0)
76+
- W: (16 bit) f200 : power (24.2)
77+
- X: (8 bit) e3 : checksum
78+
79+
*/
80+
static int revolt_zx7717_decode(r_device *decoder, bitbuffer_t *bitbuffer)
81+
{
82+
uint8_t const preamble[] = {0x2a}; // sync is 0x2a
83+
84+
if (bitbuffer->num_rows != 1) {
85+
return DECODE_ABORT_EARLY;
86+
}
87+
// message length seen are 0d, 0e, 11, 12, i.e. 13, 14, 17, 18 plus sync and length byte
88+
unsigned row_len = bitbuffer->bits_per_row[0];
89+
if (row_len < 15 * 8 || row_len > 31*8) {
90+
return DECODE_ABORT_EARLY; // Unrecognized data
91+
}
92+
93+
unsigned pos = bitbuffer_search(bitbuffer, 0, 0, preamble, 8);
94+
pos += 8; // skip preamble
95+
96+
if (pos > 16) { // match only near the start
97+
return DECODE_ABORT_LENGTH; // preamble not found
98+
}
99+
int len = bitbuffer->bits_per_row[0] - pos;
100+
101+
uint8_t b[32];
102+
bitbuffer_extract_bytes(bitbuffer, 0, pos, b, len);
103+
reflect_bytes(b, (len + 7) / 8);
104+
105+
int msg_len = b[0]; // expected: 0d, 0e, 11, 12, 1b?
106+
if (msg_len < 1) {
107+
return DECODE_FAIL_SANITY;
108+
}
109+
110+
// Is there enough data for a given length of message?
111+
if (len < (msg_len + 1) * 8) {
112+
return DECODE_ABORT_LENGTH; // short buffer
113+
}
114+
115+
int sum = add_bytes(b, msg_len);
116+
if (b[msg_len] != (sum & 0xff)) {
117+
return DECODE_FAIL_MIC; // bad checksum
118+
}
119+
120+
if (msg_len != 13) {
121+
decoder_log_bitrow(decoder, 1, __func__, b, len, "unhandled message");
122+
return DECODE_FAIL_OTHER; // unhandled message
123+
}
124+
125+
decoder_log_bitrow(decoder, 2, __func__, b, len, "message");
126+
127+
int id = (b[1] << 8) | (b[2]); // Big Endian?
128+
int unknown1 = (b[3] << 8) | b[4]; // Big Endian?
129+
int unknown2 = (b[5] << 8) | b[6]; // Big Endian?
130+
int channel = (b[5]); // just a guess
131+
int current = (b[8] << 8) | b[7]; // Little Endian
132+
int voltage = (b[10] << 8) | b[9]; // Little Endian
133+
int power = (b[12] << 8) | b[11]; // Little Endian
134+
// calculation for PF (Powerfactor) is invalid if current is < 0.02 A
135+
// e.g. a standby device will show bad readings
136+
// double va = current * voltage * 0.001; // computed value
137+
// double powerf = va > 1.0 ? power / va : 1.0; // computed value
138+
139+
/* clang-format off */
140+
data_t *data = data_make(
141+
"model", "", DATA_STRING, "Revolt-ZX7717",
142+
"id", "Device ID", DATA_FORMAT, "%04x", DATA_INT, id,
143+
"channel", "Channel", DATA_INT, channel,
144+
"unknown_1", "Unknown 1", DATA_FORMAT, "%04x", DATA_INT, unknown1,
145+
"unknown_2", "Unknown 2", DATA_FORMAT, "%04x", DATA_INT, unknown2,
146+
"current_A", "Current", DATA_FORMAT, "%.3f A", DATA_DOUBLE, current * 0.001,
147+
"voltage_V", "Voltage", DATA_FORMAT, "%.1f V", DATA_DOUBLE, voltage * 0.1,
148+
"power_W", "Power", DATA_FORMAT, "%.1f W", DATA_DOUBLE, power * 0.1,
149+
// "apparentpower_VA", "Apparent Power", DATA_FORMAT, "%.1f VA", DATA_DOUBLE, va * 0.1, // computed value
150+
// "powerfactor", "Power Factor", DATA_DOUBLE, powerf, // computed value
151+
"mic", "Integrity", DATA_STRING, "CHECKSUM",
152+
NULL);
153+
/* clang-format on */
154+
155+
decoder_output_data(decoder, data);
156+
return 1;
157+
}
158+
159+
static char const *const output_fields[] = {
160+
"model",
161+
"id",
162+
"channel",
163+
"unknown_1",
164+
"unknown_2",
165+
"current_A",
166+
"voltage_V",
167+
"power_W",
168+
// "apparentpower_VA", // computed value
169+
// "powerfactor", // computed value
170+
"mic",
171+
NULL,
172+
};
173+
174+
r_device const revolt_zx7717 = {
175+
.name = "Revolt ZX-7717 power meter",
176+
.modulation = OOK_PULSE_MANCHESTER_ZEROBIT,
177+
.short_width = 310, // Nominal width of clock half period [us]
178+
.long_width = 310,
179+
.reset_limit = 900, // Maximum gap size before End Of Message [us]
180+
.decode_fn = &revolt_zx7717_decode,
181+
.fields = output_fields,
182+
};

0 commit comments

Comments
 (0)