Domotiga/Plugwise: Protocol.txt

File Protocol.txt, 11.6 KB (added by rdnzl, 20 months ago)
Line 
1A partly protocol description:
2
3Serial port settings
4The serial port settings used to connect to the plugwise stick (USB) are the following:
5
6- Baud rate: 115200
7- Data bits: 8
8- Stop bits: 1
9- Parity: none
10
11Packet header
12Every command send to the plugwise stick must have a valid packet header.
13
14Example packet header:
15
16<code>
17<ENQ><ENQ><ETX><ETX>
18</code>
19
20The hexadecimal representation of the above is:
21<code>
22\x05\x05\x03\x03
23</code>
24
25Packet end
26Every command send must be ended by a control feed followed by a linefeed.
27
28Example packet end:
29<code>
30<CR><LF>
31</code>
32
33The hexadecimal representation of the above is:
34<code>
35\x0d\x0a
36</code>
37
38Analyzing the on/off packet
39In this part I analyze the on/off packet.
40Because examples say more then a thousand words, here's an example of an ON packet:
41<code>
42<ENQ><ENQ><ETX><ETX>0017000A1100003111AB01AC92<CR><LF>
43</code>
44
45The first 4 characters represent a function code, in this case it's the on/off function represented by the "0017" characters.
46
47Followed by the function code is the mac address of your device. In this example the mac address is: 00A1100003111AB
48
49Then followed by the mac address is the function value, in this case it's 01. 01 in this function represents "ON". If it stated "00" it would have meant "OFF"
50
51Last but not least, the toughest nut to crack. The last value, in this case the 4 characters "AC92" is a CRC16 value.
52For more information about CRC please see the following wikipedia article:
53
54http://en.wikipedia.org/wiki/Cyclic_redundancy_check
55
56The CRC16 value is calculated over the following parts of the packet:
57  - Function code
58  - MAC address
59  - Function value/new state
60
61In this example the CRC16 value is calculated over the following string:
62<code>
630017000A1100003111AB01
64</code>
65
66The CRC checksum used is not a standard one, it has the following properties (in some documents refered to as the xmodem or ymodem CRC):
67
68  - Polynomial: 0x11021
69  - Seed value: 0x00000
70  - Xor mask: 0x00000
71  - Width: 16
72
73
74Power measurement:
75
76Calibration
77
78Each plugwise plug has some calibration information. This information can be found in the plugwise access database (PlugwiseData.mdb)
79The values containing calibration information are the following:
80
81- OffRuis
82- OffTot
83- GainA
84- GainB
85
86You can also query the information from the plug itself using the stick.
87
88Let's analyze a calibration request:
89
90<ENQ><ENQ><ETX><ETX>002600A1100003111AB7071<CR><LF>
91
92
93
94The first 4 characters represent the function code, in this case it's the "0026" command for the calibration request.
95The next characters represent the mac address of the plug, in this case "00A1100003111AB"
96The last 4 characters are the CRC16 code, for an explanation of this see my earlier post.
97
98Let's analyze the calibration response:
99
100<ENQ><ENQ><ETX><ETX>0027 00A1100003111AB 3F78BD69 B6FF0876 3CA99962 00000000 EE6D<CR><LF>
101
102
103
104Note: the spaces have been included for readability.
105
106The first 4 characters represent the function code, in this case calibration response.
107The next string is the mac address.
108Now for the interesting part:
109
110- 3F78BD69 represents the GainA value hexadecimal.
111- B6FF0876 represents the GainB value hexadecimal.
112- 3CA99962 represents the OffTot value hexadecimal.
113- 00000000 represents the OffRuis value hexadecimal.
114- The last code is (i think) a CRC16 code again, not sure about this one.
115
116I use a function like this in python to convert the hexadecimal values to a "human readable" float or double:
117
118def hexToFloat(self, hexstr):
119        intval = int(hexstr, 16)
120        bits = struct.pack('L', intval)
121        return struct.unpack('f', bits)[0]
122
123
124
125The calibration information is used for a correct reading of the watt usage.
126
127Power information
128
129Power information is read by using the following command:
130
131<ENQ><ENQ><ETX><ETX>0012 00A1100003111AB AB43<CR><LF>
132
133
134
135It needs no explanation that the function code is "0012" the mac adress is the next string etc.
136
137The powerinfo response looks like this:
138
139<ENQ><ENQ><ETX><ETX>0013 00A1100003111AB 0030 0030 0001D62A 9863<CR><LF>
140
141
142
143The first two parts of this need no explanation.
144The following explains the codes followed by that:
145
146- 0030 pulse information of 8 seconds reading.
147- 0030 pulse information of 1 second reading.
148- 0001D62A yet unknown, still trying to figure out.
149
150The pulse information is again hexadecimal. To convert it to a integer I use the following python code:
151
152def hexToInt(self, hexstr):
153        return int(hexstr, 16)
154
155
156
157How to get the watt I hear you asking?
158First the pulse information has to be corrected based on the calibration information of the plug, that's done using the following formula:
159
1601.0 * (((pow(value + offruis, 2.0) * gain_b) + ((value + offruis) * gain_a)) + offtot)
161
162Where value is the number of pulses in integer format. pow() is a python for the mathematical power.
163
164If the pulse information has been corrected based upon the calibration information you can go to KWH using the following formula:
165
166(pulses / 1) / 468.9385193
167
168Where pulses is the number of pulses offcourse.
169
170To go to watt you'll simply have to do multiply by 1000.
171
172Long story, I hope it's clear enough.
173
174As promised I would publish some information about the power buffers.
175
176Circle information request
177
178You can read some information about the plugwise device, some values are unknown. But, I already want to share this because it's needed for power buffer reading.
179
180Let's analyze the information request:
181
1820023000D6F00002366BB231B
183
184
185
186- The first 4 bits represent the function code, which is in this case "0023"
187- The next 16 bits represent the MAC address of the device, which is in this case "000D6F00002366BB"
188- The last 4 bits represent the CRC16 checksum value, in this case "231B"
189
190Let's analyze the information response:
191
1920024 000D6F00002366BB00003681 000457C8 01 8500000473000748B4253801F74D
193
194
195
196Note: the spaces have been included for readability.
197
198The first 4 characters represent the function code, in this case information response.
199The next string is the mac address.
200Now for the interesting part:
201
202- 000457C8 represents the last logaddress value hexadecimal. This logaddress is used for power buffer information.
203To get the logaddress in nice decimal format, do the following:
204
2051) Convert the hexadecimal value to integer.
2062) Substract 278528 from the outcome.
2073) Divide that outcome by 32.
208
209The result will be the latest log address of the power buffers.
210
211My calculation looks like this:
212
213
214(self.hexToInt(data) - 278528) / 32
215
216
217
218These logaddresses are also visible in the plugwise database.
219
220- 01 represents the current relay state of the device (01=ON, 00=OFF)
221
222The other values are yet unknown. Also because I wasn't interested in these values yet.
223
224Power buffer information
225
226The circle's hold an internal buffer of power usage. This way you can safely close the source software and read that information later on. I have succeeded in reading this without using the source.
227
228Let's analyze the power buffer request:
229
2300048 000D6F0000236317 00045640 439B
231
232
233
234- The first 4 bits represent the function code, which is in this case "0048"
235- The next 16 bits represent the MAC address of the device, which is in this case "000D6F0000236317"
236- The next 8 bits represent the log address of the power buffer you want to fetch, remember we read the latest buffer address with the information request? You can use this to determine the buffers you are missing in your program, for example if the latest log address is 160 and the last processed buffer in your program is 150 you are missing 10 buffers starting from 150 (so you would do a read request here for 151, 152 etc.)
237The log address requires some ca****ation (again) In this case the log address is:
238
239- 00045640 to decimal 284224
240- 284224 - 278528
241- 5696 / 32 = 178
242
243So the log address is 178.
244
245To calculate the hexadecimal for a log address one would just reverse the formula:
246
247The log address is 178.
248278528 + (32 * 178) = 284224
249
250Converting this to hexadecimal is: 00045640
251
252- The last 4 bits represent the CRC16 checksum value, in this case "439B"
253
254Let's analyze the power buffer response:
255
2560049 000D6F0000236317 000036B1 0000ABAA 000036B2 0000AB72 000036B3 0000AB66 000036B4 0000ABAD 00045620 758F
257
258
259
260This is a huge one
261
262- The first 4 bits represent the function code, which is in this case "0049"
263- The next 16 bits represent the MAC address of the device, which is in this case "000D6F0000236317"
264- The next 8 bits represent the hour of the first buffer in abshour format (got this name from the access database :-)) more about this abshour format later.
265- The next 8 bits represent the usage in pulses for the first hour.
266- The next 8 bits represent the hour of the second buffer in abshour format (got this name from the access database :-)) more about this abshour format later.
267- The next 8 bits represent the usage in pulses for the second hour.
268- The next 8 bits represent the hour of the third buffer in abshour format (got this name from the access database :-)) more about this abshour format later.
269- The next 8 bits represent the usage in pulses for the third hour.
270- The next 8 bits represent the hour of the fourth buffer in abshour format (got this name from the access database :-)) more about this abshour format later.
271- The next 8 bits represent the usage in pulses for the fourth hour.
272- The next 8 bits represent logaddress of the buffer, this is the same log address as explained before.
273- The last 4 bits represent the CRC16 checksum value, in this case "758F"
274
275The abshour format
276Let's pick the first buffer address as example: 000036B1
277The decimal value of this abshour value is 14001
278
279This value was a real brain cracker, after comparing the values I figured out the following.
280For illustration let's convert the next abshour value (000036B2) to decimal: 14002
281
282Aha! It increases by one all the time, so there must be a constant in these values. Then I substracted this value as hours from the current date (as it states abshour I was figuring it had something to do with hoours) this brought me to the following date:
283
2841-6-2007
285
286So if you add the number of hours to that date you'll get the datetime of that buffer.
287Example in python:
288
289>>> import datetime
290>>> timestart = datetime.datetime(2007, 6, 1, 2)
291>>> dif = datetime.timedelta(hours=14001)
292>>> datetime = timestart + dif
293>>> datetime
294datetime.datetime(2009, 1, 4, 11, 0)
295
296
297So the date and time of this buffer request is:
2984-1-2009 11:00
299
300The pulses
301I have explained these pulses before in a post. The pulses here are exactly the same pulses, however there is one difference.
302The formula must be changed from:
303
3041.0 * (((pow(value + offruis, 2.0) * gain_b) + ((value + offruis) * gain_a)) + offtot)
305
306To:
307
3083600 * (((pow(value + offruis, 2.0) * gain_b) + ((value + offruis) * gain_a)) + offtot)
309
310Also you need to divide the pulses value by 3600.
311This is needed because the pulses are now logged for an entire hour rather then a second.
312I use a generic function now for the pulsecorrection:
313
314
315    def PulseCorrection(self, pulses, id, timespan):
316        """
317        Corrects plugwise pulses using calibration information from plug.
318        """
319        device = Device.get(int(id))
320        value = pulses / timespan;
321        out = timespan * (((pow(value + device.extension.offruis, 2.0) *\
322        device.extension.gainb) + ((value + device.extension.offruis) * \
323        device.extension.gaina)) + device.extension.offtot)
324        return out
325
326Phew.. this is gotten a really long post, I hope it's clear enough and that it's usefull for you. If you have any questions don't hesitate to ask.
327
328--
329Maarten Damen
330
331www.maartendamen.com