Parsing P1 smart meter info May 2016
The smart meter at JeeLabs looks like this:
It’s a Landis & Gyr E350, which monitors all power coming into the house and going out (when solar PV production exceeds local consumption). There’s an RJ12 jack on the bottom right, with serial data coming out at 9,600 baud (newer units send at 115,200 baud).
Every 10 seconds, a “telegram” of information is sent out, which looks something like this:
/XMX5XMXABCE000046099
0-0:96.1.1(30313337323430332020202020202020)
1-0:1.8.1(00003.540*kWh)
1-0:1.8.2(00011.199*kWh)
1-0:2.8.1(00000.000*kWh)
1-0:2.8.2(00004.667*kWh)
0-0:96.14.0(0002)
1-0:1.7.0(0000.35*kW)
1-0:2.7.0(0000.00*kW)
0-0:17.0.0(999*A)
0-0:96.3.10(1)
0-0:96.13.1()
0-0:96.13.0()
0-1:96.1.0(3131323838323030303336383037303132)
0-1:24.1.0(03)
0-1:24.3.0(121129160000)(00)(60)(1)(0-1:24.2.0)(m3)
(00014.684)
0-1:24.4.0(2)
!
This unit has been in operation since end 2012, with a JeeNode attached to pick up P1 data and send out wireless RFM12 packets, using the same variable format described in a recent article:
OK 18 129 1 83 111 232 1 47 58 201 1 55 1 142 3 26 45 233 130 144 [...]
The sketch used to extract data from P1 packets is called p1scanner and can be found on GitHub.
Here is an essentially equivalent re-implementation in Mecrisp Forth:
8 constant p1#
p1# cells buffer: p1.buf
0 variable p1.type
0 variable p1.value
: p1clear p1.buf p1# cells 0 fill ;
: p1save ( pos -- ) cells p1.buf + p1.value @ swap ! ;
: p1dump cr p1# 0 do i cells p1.buf + @ . loop ;
: p1select ( type -- ) \ these values are for a Landys & Gyr E350 meter:
case
181 of 0 p1save endof \ cumulative electricity consumption, normal
182 of 1 p1save endof \ cumulative electricity consumption, low
281 of 2 p1save endof \ cumulative electricity production normal
282 of 3 p1save endof \ cumulative electricity production low
96140 of 4 p1save endof \ tariff
170 of 5 p1save endof \ actual consumption
270 of 6 p1save endof \ actual production
2420 of 7 p1save endof \ cumulative gas consumption
endcase ;
: p1char ( c -- )
case
[char] / of p1clear endof
[char] : of 0 p1.type ! 0 p1.value ! endof
[char] ( of p1.type @ 0= if p1.value @ p1.type ! then 0 p1.value ! endof
[char] ) of p1.type @ p1select endof
[char] ! of p1dump endof
dup digit if p1.value @ 10 * + p1.value ! then
endcase
;
: p1test
begin
uart-irq-key? if uart-irq-key p1char then
key? until ;
The p1select
word filters specific values in the data and stores them in an
array of 8 entries. The p1test
word simply listens to the 2nd serial port, and
feeds all incoming characters to p1char
. This 2nd port uses interrupts with a
ring buffer to avoid losing incoming data. So at 9600 baud and with a 128-byte
ring buffer, serial processing needs to start within ≈ 120 ms.
The logic of p1char
is the same as the p1_scanner()
function in the original
C++ code. It plays the same tricks to ignore many of the incoming characters,
only triggering on a few specific ones, while also identifying and parsing each
numeric value.
Here is a test run, with p1test
reporting on Mecrisp’s serial UART1 console,
while the above test packet was manually pasted three times into a second
terminal session tied to UART2:
p1test
3540 11199 0 4667 2 35 0 14684
3540 11199 0 4667 2 35 0 14684
3540 11199 0 4667 2 35 0 14684
As you can see, all the important values have been properly isolated and parsed, ready to be sent out in the reporting section of the JeeLabs Energy Monitor’s code.
The actual hookup will need some more testing. The interface is described in an old P1 revisited weblog post, but it’s not clear yet whether this will also work at 3.3V and perhaps even without inverting transistor stage. Some more experimentation is needed…