├── README.md
├── arduino
├── oregon_owl.ino
├── owl_oled_ada
│ └── owl_oled_ada.ino
└── owl_u8glib
│ └── owl_u8glib.ino
├── images
├── diy-owl-cm180.jpg
├── oregon_owl_serial_output.png
└── rf433-receiver.jpg
└── src
├── Makefile
├── RCSwitch.cpp
├── RCSwitch.d
├── RCSwitch.h
├── RcOok.cpp
├── RcOok.d
├── RcOok.h
├── Sensor.cpp
├── Sensor.d
├── Sensor.h
├── core_433.cpp
├── core_433.d
├── core_433.h
├── eventManager.cpp
├── eventManager.d
├── eventManager.h
├── ledManager.cpp
├── ledManager.d
├── ledManager.h
├── osv3_frame.txt
├── output
├── rfrpi_test.cpp
├── rfrpi_test.d
├── singleton.cpp
├── singleton.d
├── singleton.h
├── tools.cpp
├── tools.d
├── tools.h
└── version.h
/README.md:
--------------------------------------------------------------------------------
1 | # OWL-CM180
2 | Decode and parse the Oregon Scientific V3 radio data transmitted by OWL CM180 Energy sensor (433.92MHz)
3 |
4 | Owl micro+ Wireless electricity monitor runs on Oregon Scientific V3 protocol.
5 |
6 | After buying one of those wonderful little devices to study the energy consumed at home, i browsed the internet to find a way to decode the radio frames sent by the transmitter (CMR180) but I have not found any application describing the coding system. So I decided to study the radio packets to decode the system myself.
7 |
8 | 
9 | ##Packet format
10 | 1. [Primary]
11 | OSV3 62803CE8006047D9120000D085[CMR180,...] Id:6280, size:13 ,Flags:8 ,power:224 ,total:316229472 ,total Wh:87.84
12 | 628 : id
13 | 0: primary packet
14 | 3C: type (?? constant value, and i suppose it's owl-cmr180 type. i use it that way in the script)
15 | E800: little-indian 0x00E8 (0x00E8 & 0xFFF0 = 224 Watts) last nibble (8) considered as Flags-1 (OSV3 protocol documentation)
16 | 6047D9120000: Little-indian 0x000012D94760 (316229472 Watts/ 3600/1000 = 87.84 kWh)
17 | ...: ??
18 |
19 | 1. [Secondary]
20 | OSV3 6284 3C480B60
21 | Same as primary without total energy and nibble#4 from 1 to 4 ( one every 12 seconds)
22 | CMR180 sends secondary packets in case of significative high power changes.
23 |
24 | ##Getting Started
25 | 
26 | ##Arduino
27 |
28 | Wiring RFx433 receiver
29 |
30 | DATA --> Arduino pin D3 (int-1)
31 | VCC --> 5V
32 | GND --> GND
33 |
34 | Load arduino sketch (https://raw.github.com/onlinux/OWL-CMR180/master/arduino/oregon_owl.ino)
35 | 
36 |
37 | ## Raspberry Pi
38 | Based on http://www.disk91.com/2013/technology/hardware/oregon-scientific-sensors-with-raspberry-pi/
39 | I added support for owl-cmr180 transmitter ( files Sensor.cpp and RcOok.cpp)
40 |
41 | 1. [RcOok.cpp] (https://github.com/onlinux/OWL-CMR180/blob/master/src/RcOok.cpp) support for OSV3 108bits and 46bits packet length.
42 | 2. [Sensor.cpp] (https://github.com/onlinux/OWL-CMR180/blob/master/src/Sensor.cpp) Parsing packets OSV3
43 |
44 | As mentioned by disk91,
45 | Rfrpi is using wiringPI for accessing GPIO. The installation process is the following, from the raspberry command line:
46 | ```bash
47 | pi@raspberrypi ~ $ git clone git://git.drogon.net/wiringPi
48 | pi@raspberrypi ~ $ cd wiringPi
49 | pi@raspberrypi ~/wiringPi $ ./build
50 | ```
51 | GPIO used is the wiringPi GPIO0 corresponding to PIN 11 (GPIO17)
52 |
53 | then
54 | ```bash
55 | $ git clone https://github.com/onlinux/OWL-CMR180.git
56 | $ cd OWL-CMR180/src
57 | $ make
58 | $ sudo ./rfrpi_test
59 |
60 | pi@raspi ~/rfrpi/rfrpi_my_src $ sudo ./rfrpi_test
61 |
62 | {"datetime": "2015-01-20 13:58:50", "name": "THGR122NX", "temperature": "18.90", "humidity": "51", "channel": "1" }
63 | {"datetime": "2015-01-20 13:58:50", "name": "THGR122NX", "temperature": "18.90", "humidity": "51", "channel": "1" }
64 | {"datetime": "2015-01-20 13:59:01", "name": "THGR122NX", "temperature": "22.30", "humidity": "40", "channel": "1" }
65 | {"datetime": "2015-01-20 13:59:01", "name": "THGR122NX", "temperature": "22.30", "humidity": "40", "channel": "1" }
66 | {"datetime": "2015-01-20 13:59:13", "name": "OWL micro+", "power": "288", "total": "88033"}
67 | "datetime": "2015-01-20 13:59:29", "name": "THGR122NX", "temperature": "18.90", "humidity": "51", "channel": "1" }
68 | {"datetime": "2015-01-20 13:59:40", "name": "THGR122NX", "temperature": "22.40", "humidity": "40", "channel": "1" }
69 | {"datetime": "2015-01-20 14:00:13", "name": "OWL micro+", "power": "288", "total": "88038"}
70 | {"datetime": "2015-01-20 14:00:19", "name": "THGR122NX", "temperature": "22.50", "humidity": "40", "channel": "1" }
71 | {"datetime": "2015-01-20 14:00:47", "name": "THGR122NX", "temperature": "18.90", "humidity": "51", "channel": "1" }
72 |
73 | ```
74 |
--------------------------------------------------------------------------------
/arduino/oregon_owl.ino:
--------------------------------------------------------------------------------
1 | // New code to decode OOK signals from Energy OWL CMR180 sensor
2 | // Oregon V3 decoder added - Eric Vandecasteele (onlinux)
3 | //
4 | // Oregon V2 decoder modfied - Olivier Lebrun
5 | // Oregon V2 decoder added - Dominique Pierre
6 | // New code to decode OOK signals from weather sensors, etc.
7 | // 2010-04-11 http://opensource.org/licenses/mit-license.php
8 | // $Id: ookDecoder.pde 5331 2010-04-17 10:45:17Z jcw $
9 |
10 | class DecodeOOK {
11 | protected:
12 | byte total_bits, bits, flip, state, pos, data[31];
13 |
14 | virtual char decode (word width) =0;
15 |
16 | public:
17 |
18 | enum { UNKNOWN, T0, T1, T2, T3, OK, DONE };
19 |
20 | DecodeOOK () { resetDecoder(); }
21 |
22 | bool nextPulse (word width) {
23 | if (state != DONE)
24 |
25 | switch (decode(width)) {
26 | case -1: resetDecoder(); break;
27 | case 1: done(); break;
28 | }
29 | return isDone();
30 | }
31 |
32 | bool isDone () const { return state == DONE; }
33 |
34 | const byte* getData (byte& count) const {
35 | count = pos;
36 | return data;
37 | }
38 |
39 | void resetDecoder () {
40 | total_bits = bits = pos = flip = 0;
41 | state = UNKNOWN;
42 | }
43 |
44 | // add one bit to the packet data buffer
45 |
46 | virtual void gotBit (char value) {
47 | total_bits++;
48 | byte *ptr = data + pos;
49 | *ptr = (*ptr >> 1) | (value << 7);
50 |
51 | if (++bits >= 8) {
52 | bits = 0;
53 | if (++pos >= sizeof data) {
54 | resetDecoder();
55 | return;
56 | }
57 | }
58 | state = OK;
59 | }
60 |
61 | // store a bit using Manchester encoding_rx
62 | void manchester (char value) {
63 | flip ^= value; // manchester code, long pulse flips the bit
64 | gotBit(flip);
65 | }
66 |
67 | // move bits to the front so that all the bits are aligned to the end
68 | void alignTail (byte max =0) {
69 | // align bits
70 | if (bits != 0) {
71 | data[pos] >>= 8 - bits;
72 | for (byte i = 0; i < pos; ++i)
73 | data[i] = (data[i] >> bits) | (data[i+1] << (8 - bits));
74 | bits = 0;
75 | }
76 | // optionally shift bytes down if there are too many of 'em
77 | if (max > 0 && pos > max) {
78 | byte n = pos - max;
79 | pos = max;
80 | for (byte i = 0; i < pos; ++i)
81 | data[i] = data[i+n];
82 | }
83 | }
84 |
85 | void reverseBits () {
86 | for (byte i = 0; i < pos; ++i) {
87 | byte b = data[i];
88 | for (byte j = 0; j < 8; ++j) {
89 | data[i] = (data[i] << 1) | (b & 1);
90 | b >>= 1;
91 | }
92 | }
93 | }
94 |
95 | void reverseNibbles () {
96 | for (byte i = 0; i < pos; ++i)
97 | data[i] = (data[i] << 4) | (data[i] >> 4);
98 | }
99 |
100 | void done () {
101 | while (bits)
102 | gotBit(0); // padding
103 | state = DONE;
104 | }
105 | };
106 |
107 | class OregonDecoderV2 : public DecodeOOK {
108 | public:
109 |
110 | OregonDecoderV2() {}
111 |
112 | // add one bit to the packet data buffer
113 | virtual void gotBit (char value) {
114 | if(!(total_bits & 0x01))
115 | {
116 | data[pos] = (data[pos] >> 1) | (value ? 0x80 : 00);
117 | }
118 | total_bits++;
119 | pos = total_bits >> 4;
120 | if (pos >= sizeof data) {
121 | Serial.println("sizeof data");
122 | resetDecoder();
123 | return;
124 | }
125 | state = OK;
126 | }
127 |
128 | virtual char decode (word width) {
129 | if (200 <= width && width < 1200) {
130 | //Serial.println(width);
131 | byte w = width >= 700;
132 |
133 | switch (state) {
134 | case UNKNOWN:
135 | if (w != 0) {
136 | // Long pulse
137 | ++flip;
138 | } else if (w == 0 && 24 <= flip) {
139 | // Short pulse, start bit
140 | flip = 0;
141 | state = T0;
142 | } else {
143 | // Reset decoder
144 | return -1;
145 | }
146 | break;
147 | case OK:
148 | if (w == 0) {
149 | // Short pulse
150 | state = T0;
151 | } else {
152 | // Long pulse
153 | manchester(1);
154 | }
155 | break;
156 | case T0:
157 | if (w == 0) {
158 | // Second short pulse
159 | manchester(0);
160 | } else {
161 | // Reset decoder
162 | return -1;
163 | }
164 | break;
165 | }
166 | } else if (width >= 2500 && pos >= 8) {
167 | return 1;
168 | } else {
169 | return -1;
170 | }
171 | return 0;
172 | }
173 | };
174 |
175 | //===================================================================
176 | class OregonDecoderV3 : public DecodeOOK {
177 | public:
178 |
179 | OregonDecoderV3() {}
180 |
181 | // add one bit to the packet data buffer
182 | virtual void gotBit (char value) {
183 | data[pos] = (data[pos] >> 1) | (value ? 0x80 : 00);
184 | total_bits++;
185 | pos = total_bits >> 3;
186 | if (pos >= sizeof data) {
187 | //Serial.println("sizeof data");
188 | resetDecoder();
189 | return;
190 | }
191 | state = OK;
192 | }
193 |
194 | virtual char decode (word width) {
195 | if (200 <= width && width < 1200) {
196 | //Serial.println(width);
197 | byte w = width >= 700;
198 |
199 | switch (state) {
200 | case UNKNOWN:
201 | if (w == 0) {
202 | // Long pulse
203 | ++flip;
204 | } else if (32 <= flip) {
205 | flip = 1;
206 | manchester(1);
207 | } else {
208 | // Reset decoder
209 | return -1;
210 | }
211 | break;
212 | case OK:
213 | if (w == 0) {
214 | // Short pulse
215 | state = T0;
216 | } else {
217 | // Long pulse
218 | manchester(1);
219 | }
220 | break;
221 | case T0:
222 | if (w == 0) {
223 | // Second short pulse
224 | manchester(0);
225 | } else {
226 | // Reset decoder
227 | return -1;
228 | }
229 | break;
230 | }
231 | } else {
232 | // Trame intermédiaire 48bits ex: [OSV3 6281 3C 6801 70]
233 | return (total_bits <104 && total_bits>=40 ) ? 1: -1;
234 | }
235 |
236 | return (total_bits == 104) ? 1: 0;
237 | }
238 | };
239 |
240 | //===================================================================
241 |
242 | OregonDecoderV2 orscV2;
243 | OregonDecoderV3 orscV3;
244 |
245 |
246 | volatile word pulse;
247 |
248 | void ext_int_1(void)
249 | {
250 | static word last;
251 | // determine the pulse length in microseconds, for either polarity
252 | pulse = micros() - last;
253 | last += pulse;
254 | }
255 | float temperature(const byte* data)
256 | {
257 | int sign = (data[6]&0x8) ? -1 : 1;
258 | float temp = ((data[5]&0xF0) >> 4)*10 + (data[5]&0xF) + (float)(((data[4]&0xF0) >> 4) / 10.0);
259 | return sign * temp;
260 | }
261 |
262 | byte humidity(const byte* data)
263 | {
264 | return (data[7]&0xF) * 10 + ((data[6]&0xF0) >> 4);
265 | }
266 |
267 | int pressure(const byte* data)
268 | {
269 | return data[8] + 856;
270 | }
271 |
272 | // Ne retourne qu'un apercu de l'etat de la batterie : 10 = faible
273 | byte battery(const byte* data)
274 | {
275 | return (data[4] & 0x4) ? 10 : 90;
276 | }
277 |
278 | byte channel(const byte* data)
279 | {
280 | byte channel;
281 | switch (data[2])
282 | {
283 | case 0x10:
284 | channel = 1;
285 | break;
286 | case 0x20:
287 | channel = 2;
288 | break;
289 | case 0x40:
290 | channel = 3;
291 | break;
292 | }
293 |
294 | return channel;
295 | }
296 |
297 | unsigned int power(const byte* d){
298 | unsigned int val = 0;
299 | val += d[4] << 8;
300 | val += d[3];
301 | return val & 0xFFF0 ;
302 | }
303 |
304 | unsigned long total(const byte* d){
305 | long val = 0;
306 | val = (unsigned long)d[8]<<24;
307 | // Serial.println();
308 | // Serial.print(" val:"); Serial.print(val,HEX); Serial.print(" ");
309 | // Serial.println(d[8], HEX);
310 | val += (unsigned long)d[7] << 16;
311 | // Serial.print(" val:"); Serial.print(val,HEX); Serial.print(" ");
312 | // Serial.println(d[7], HEX);
313 | val += d[6] << 8;
314 | // Serial.print(" val:"); Serial.print(val,HEX); Serial.print(" ");
315 | // Serial.println(d[6], HEX);
316 | val += d[5];
317 | // Serial.print(" val:"); Serial.print(val,HEX); Serial.print(" ");
318 | // Serial.println(d[5], HEX);
319 | return val ;
320 | }
321 |
322 |
323 | void reportSerial (const char* s, class DecodeOOK& decoder) {
324 | byte pos;
325 | const byte* data = decoder.getData(pos);
326 | Serial.print(s);
327 | Serial.print(' ');
328 | for (byte i = 0; i < pos; ++i) {
329 | Serial.print(data[i] >> 4, HEX);
330 | Serial.print(data[i] & 0x0F, HEX);
331 | }
332 |
333 | // Energy OWL : CMR180
334 | if(data[2] == 0x3C )
335 | {
336 | Serial.print("[CMR180,...] Id:");
337 | Serial.print(data[0], HEX);Serial.print(data[1], HEX);
338 | Serial.print(", size:");
339 | Serial.print(pos);
340 | Serial.print(" ,Flags:");
341 | Serial.print(data[3] & 0x0F, HEX);
342 | Serial.print(" ,power:");
343 | Serial.print(power(data));
344 | if (pos > 6) {
345 | // Display only for main frame
346 | // Secondary frame is only 6 Bytes long
347 | Serial.print(" ,total:");
348 | Serial.print(total(data));
349 | Serial.print(" ,total kWh:");
350 | Serial.print(total(data)/3600/1000);
351 | }
352 | Serial.println();
353 | }
354 |
355 |
356 | // Outside/Water Temp : THN132N,...
357 | if(data[0] == 0xEA && data[1] == 0x4C)
358 | {
359 | Serial.print("[THN132N,...] Id:");
360 | Serial.print(data[3], HEX);
361 | Serial.print(" ,Channel:");
362 | Serial.print(channel(data));
363 | Serial.print(" ,temp:");
364 | Serial.print(temperature(data));
365 | Serial.print(" ,bat:");
366 | Serial.print(battery(data));
367 | Serial.println();
368 | }
369 | // Inside Temp-Hygro : THGR228N,...
370 | else if(data[0] == 0x1A && data[1] == 0x2D)
371 | {
372 | Serial.print("[THGR228N,...] Id:");
373 | Serial.print(data[3], HEX);
374 | Serial.print(" ,Channel:");
375 | Serial.print(channel(data));
376 | Serial.print(" ,temp:");
377 | Serial.print(temperature(data));
378 | Serial.print(" ,hum:");
379 | Serial.print(humidity(data));
380 | Serial.print(" ,bat:");
381 | Serial.print(battery(data));
382 | Serial.println();
383 | }
384 | // Inside Temp-Hygro-Baro : BTHR918N,...
385 | else if(data[0] == 0x5A && data[1] == 0x6D)
386 | {
387 | Serial.print("[BTHR918N,...] Id:");
388 | Serial.print(data[3], HEX);
389 | Serial.print(" ,Channel:");
390 | Serial.print(channel(data));
391 | Serial.print(" ,temp:");
392 | Serial.print(temperature(data));
393 | Serial.print(" ,hum:");
394 | Serial.print(humidity(data));
395 | Serial.print(" ,press:");
396 | Serial.print(pressure(data));
397 | Serial.print(" ,bat:");
398 | Serial.print(battery(data));
399 | Serial.println();
400 | }
401 |
402 | decoder.resetDecoder();
403 | }
404 |
405 | void setup ()
406 | {
407 | Serial.begin(115200);
408 | Serial.println("\n[ookDecoder]");
409 | attachInterrupt(1, ext_int_1, CHANGE);
410 |
411 | //DDRE &= ~_BV(PE5); //input with pull-up
412 | //PORTE &= ~_BV(PE5);
413 | }
414 |
415 | void loop () {
416 | static int i = 0;
417 | cli();
418 | word p = pulse;
419 |
420 | pulse = 0;
421 | sei();
422 |
423 | if (p != 0)
424 | {
425 |
426 | if (orscV3.nextPulse(p))
427 | reportSerial("OSV3", orscV3);
428 |
429 | if (orscV2.nextPulse(p))
430 | reportSerial("OSV2", orscV2);
431 | }
432 | }
433 |
--------------------------------------------------------------------------------
/arduino/owl_oled_ada/owl_oled_ada.ino:
--------------------------------------------------------------------------------
1 | // New code to decode OOK signals from Energy OWL CMR180 sensor
2 | // Oregon V3 decoder added - Eric Vandecasteele (onlinux)
3 | //
4 | // Oregon V2 decoder modfied - Olivier Lebrun
5 | // Oregon V2 decoder added - Dominique Pierre
6 | // New code to decode OOK signals from weather sensors, etc.
7 | // 2010-04-11 http://opensource.org/licenses/mit-license.php
8 | // $Id: ookDecoder.pde 5331 2010-04-17 10:45:17Z jcw $
9 | #include
10 | #include
11 | #include
12 | #include
13 |
14 | #define OLED_RESET 4
15 | Adafruit_SSD1306 display(OLED_RESET);
16 |
17 |
18 | #define SDA_PIN 8
19 | #define SCL_PIN 9
20 | //Adafruit_ssd1306syp display(SDA_PIN,SCL_PIN);
21 |
22 |
23 | class DecodeOOK {
24 | protected:
25 | byte total_bits, bits, flip, state, pos, data[31];
26 |
27 | virtual char decode (word width) =0;
28 |
29 | public:
30 |
31 | enum { UNKNOWN, T0, T1, T2, T3, OK, DONE };
32 |
33 | DecodeOOK () { resetDecoder(); }
34 |
35 | bool nextPulse (word width) {
36 | if (state != DONE)
37 |
38 | switch (decode(width)) {
39 | case -1: resetDecoder(); break;
40 | case 1: done(); break;
41 | }
42 | return isDone();
43 | }
44 |
45 | bool isDone () const { return state == DONE; }
46 |
47 | const byte* getData (byte& count) const {
48 | count = pos;
49 | return data;
50 | }
51 |
52 | void resetDecoder () {
53 | total_bits = bits = pos = flip = 0;
54 | state = UNKNOWN;
55 | }
56 |
57 | // add one bit to the packet data buffer
58 |
59 | virtual void gotBit (char value) {
60 | total_bits++;
61 | byte *ptr = data + pos;
62 | *ptr = (*ptr >> 1) | (value << 7);
63 |
64 | if (++bits >= 8) {
65 | bits = 0;
66 | if (++pos >= sizeof data) {
67 | resetDecoder();
68 | return;
69 | }
70 | }
71 | state = OK;
72 | }
73 |
74 | // store a bit using Manchester encoding_rx
75 | void manchester (char value) {
76 | flip ^= value; // manchester code, long pulse flips the bit
77 | gotBit(flip);
78 | }
79 |
80 | // move bits to the front so that all the bits are aligned to the end
81 | void alignTail (byte max =0) {
82 | // align bits
83 | if (bits != 0) {
84 | data[pos] >>= 8 - bits;
85 | for (byte i = 0; i < pos; ++i)
86 | data[i] = (data[i] >> bits) | (data[i+1] << (8 - bits));
87 | bits = 0;
88 | }
89 | // optionally shift bytes down if there are too many of 'em
90 | if (max > 0 && pos > max) {
91 | byte n = pos - max;
92 | pos = max;
93 | for (byte i = 0; i < pos; ++i)
94 | data[i] = data[i+n];
95 | }
96 | }
97 |
98 | void reverseBits () {
99 | for (byte i = 0; i < pos; ++i) {
100 | byte b = data[i];
101 | for (byte j = 0; j < 8; ++j) {
102 | data[i] = (data[i] << 1) | (b & 1);
103 | b >>= 1;
104 | }
105 | }
106 | }
107 |
108 | void reverseNibbles () {
109 | for (byte i = 0; i < pos; ++i)
110 | data[i] = (data[i] << 4) | (data[i] >> 4);
111 | }
112 |
113 | void done () {
114 | while (bits)
115 | gotBit(0); // padding
116 | state = DONE;
117 | }
118 | };
119 |
120 | class OregonDecoderV2 : public DecodeOOK {
121 | public:
122 |
123 | OregonDecoderV2() {}
124 |
125 | // add one bit to the packet data buffer
126 | virtual void gotBit (char value) {
127 | if(!(total_bits & 0x01))
128 | {
129 | data[pos] = (data[pos] >> 1) | (value ? 0x80 : 00);
130 | }
131 | total_bits++;
132 | pos = total_bits >> 4;
133 | if (pos >= sizeof data) {
134 | Serial.println("sizeof data");
135 | resetDecoder();
136 | return;
137 | }
138 | state = OK;
139 | }
140 |
141 | virtual char decode (word width) {
142 | if (200 <= width && width < 1200) {
143 | //Serial.println(width);
144 | byte w = width >= 700;
145 |
146 | switch (state) {
147 | case UNKNOWN:
148 | if (w != 0) {
149 | // Long pulse
150 | ++flip;
151 | } else if (w == 0 && 24 <= flip) {
152 | // Short pulse, start bit
153 | flip = 0;
154 | state = T0;
155 | } else {
156 | // Reset decoder
157 | return -1;
158 | }
159 | break;
160 | case OK:
161 | if (w == 0) {
162 | // Short pulse
163 | state = T0;
164 | } else {
165 | // Long pulse
166 | manchester(1);
167 | }
168 | break;
169 | case T0:
170 | if (w == 0) {
171 | // Second short pulse
172 | manchester(0);
173 | } else {
174 | // Reset decoder
175 | return -1;
176 | }
177 | break;
178 | }
179 | } else if (width >= 2500 && pos >= 8) {
180 | return 1;
181 | } else {
182 | return -1;
183 | }
184 | return 0;
185 | }
186 | };
187 |
188 | //===================================================================
189 | class OregonDecoderV3 : public DecodeOOK {
190 | public:
191 |
192 | OregonDecoderV3() {}
193 |
194 | // add one bit to the packet data buffer
195 | virtual void gotBit (char value) {
196 | data[pos] = (data[pos] >> 1) | (value ? 0x80 : 00);
197 | total_bits++;
198 | pos = total_bits >> 3;
199 | if (pos >= sizeof data) {
200 | //Serial.println("sizeof data");
201 | resetDecoder();
202 | return;
203 | }
204 | state = OK;
205 | }
206 |
207 | virtual char decode (word width) {
208 | if (200 <= width && width < 1200) {
209 | //Serial.println(width);
210 | byte w = width >= 700;
211 |
212 | switch (state) {
213 | case UNKNOWN:
214 | if (w == 0) {
215 | // Long pulse
216 | ++flip;
217 | } else if (32 <= flip) {
218 | flip = 1;
219 | manchester(1);
220 | } else {
221 | // Reset decoder
222 | return -1;
223 | }
224 | break;
225 | case OK:
226 | if (w == 0) {
227 | // Short pulse
228 | state = T0;
229 | } else {
230 | // Long pulse
231 | manchester(1);
232 | }
233 | break;
234 | case T0:
235 | if (w == 0) {
236 | // Second short pulse
237 | manchester(0);
238 | } else {
239 | // Reset decoder
240 | return -1;
241 | }
242 | break;
243 | }
244 | } else {
245 | // Trame intermédiaire 48bits ex: [OSV3 6281 3C 6801 70]
246 | return (total_bits <104 && total_bits>=40 ) ? 1: -1;
247 | }
248 |
249 | return (total_bits == 104) ? 1: 0;
250 | }
251 | };
252 |
253 | //===================================================================
254 |
255 | OregonDecoderV2 orscV2;
256 | OregonDecoderV3 orscV3;
257 |
258 |
259 | volatile word pulse;
260 |
261 | void ext_int_1(void)
262 | {
263 | static word last;
264 | // determine the pulse length in microseconds, for either polarity
265 | pulse = micros() - last;
266 | last += pulse;
267 | }
268 | float temperature(const byte* data)
269 | {
270 | int sign = (data[6]&0x8) ? -1 : 1;
271 | float temp = ((data[5]&0xF0) >> 4)*10 + (data[5]&0xF) + (float)(((data[4]&0xF0) >> 4) / 10.0);
272 | return sign * temp;
273 | }
274 |
275 | byte humidity(const byte* data)
276 | {
277 | return (data[7]&0xF) * 10 + ((data[6]&0xF0) >> 4);
278 | }
279 |
280 | // Ne retourne qu'un apercu de l'etat de la batterie : 10 = faible
281 | byte battery(const byte* data)
282 | {
283 | return (data[4] & 0x4) ? 10 : 90;
284 | }
285 |
286 | byte channel(const byte* data)
287 | {
288 | byte channel;
289 | switch (data[2])
290 | {
291 | case 0x10:
292 | channel = 1;
293 | break;
294 | case 0x20:
295 | channel = 2;
296 | break;
297 | case 0x40:
298 | channel = 3;
299 | break;
300 | }
301 |
302 | return channel;
303 | }
304 |
305 | unsigned int power(const byte* d){
306 | unsigned int val = 0;
307 | val += d[4] << 8;
308 | val += d[3];
309 | return val & 0xFFF0 ;
310 | }
311 |
312 | unsigned long total(const byte* d){
313 | long val = 0;
314 | val = (unsigned long)d[8]<<24;
315 | // Serial.println();
316 | // Serial.print(" val:"); Serial.print(val,HEX); Serial.print(" ");
317 | // Serial.println(d[8], HEX);
318 | val += (unsigned long)d[7] << 16;
319 | // Serial.print(" val:"); Serial.print(val,HEX); Serial.print(" ");
320 | // Serial.println(d[7], HEX);
321 | val += d[6] << 8;
322 | // Serial.print(" val:"); Serial.print(val,HEX); Serial.print(" ");
323 | // Serial.println(d[6], HEX);
324 | val += d[5];
325 | // Serial.print(" val:"); Serial.print(val,HEX); Serial.print(" ");
326 | // Serial.println(d[5], HEX);
327 | return val ;
328 | }
329 |
330 |
331 | void reportSerial (const char* s, class DecodeOOK& decoder) {
332 | byte pos;
333 | const byte* data = decoder.getData(pos);
334 | Serial.print(s);
335 | Serial.print(' ');
336 | for (byte i = 0; i < pos; ++i) {
337 | Serial.print(data[i] >> 4, HEX);
338 | Serial.print(data[i] & 0x0F, HEX);
339 | }
340 |
341 | // Energy OWL : CMR180
342 | if(data[2] == 0x3C )
343 | {
344 | Serial.print("[CMR180,...] Id:");
345 | Serial.print(data[0], HEX);Serial.print(data[1], HEX);
346 | Serial.print(", size:");
347 | Serial.print(pos);
348 | Serial.print(" ,Flags:");
349 | Serial.print(data[3] & 0x0F, HEX);
350 | Serial.print(" ,power:");
351 | display.setCursor(0,15);
352 | display.fillRect(0, 15, 128, 16, 0x0000);
353 | display.setTextSize(2);
354 | Serial.print(power(data));
355 | display.print(power(data));display.print("W");
356 |
357 | //display.updateRow(2);
358 |
359 | if (pos > 6) {
360 | // Display only for main frame
361 | // Secondary frame is only 6 Bytes long
362 | Serial.print(" ,total:");
363 | Serial.print(total(data));
364 | Serial.print(" ,total Wh:");
365 | Serial.print(total(data)/3600);
366 | display.setCursor(0,40);
367 | display.fillRect(0, 40, 128, 16, 0x0000);
368 | //display.print("Total: ");
369 | display.print(total(data)/3600); display.print("Wh");
370 | }
371 | Serial.println();
372 | display.println();
373 | display.display();
374 | delay(50);
375 | }
376 |
377 |
378 | // Outside/Water Temp : THN132N,...
379 | if(data[0] == 0xEA && data[1] == 0x4C)
380 | {
381 | Serial.print("[THN132N,...] Id:");
382 | Serial.print(data[3], HEX);
383 | Serial.print(" ,Channel:");
384 | Serial.print(channel(data));
385 | Serial.print(" ,temp:");
386 | Serial.print(temperature(data));
387 | Serial.print(" ,bat:");
388 | Serial.print(battery(data));
389 | Serial.println();
390 | }
391 | // Inside Temp-Hygro : THGR228N,...
392 | else if(data[0] == 0x1A && data[1] == 0x2D)
393 | {
394 | Serial.print("[THGR228N,...] Id:");
395 | Serial.print(data[3], HEX);
396 | Serial.print(" ,Channel:");
397 | Serial.print(channel(data));
398 | Serial.print(" ,temp:");
399 | Serial.print(temperature(data));
400 | Serial.print(" ,hum:");
401 | Serial.print(humidity(data));
402 | Serial.print(" ,bat:");
403 | Serial.print(battery(data));
404 | Serial.println();
405 | }
406 |
407 | decoder.resetDecoder();
408 | }
409 |
410 | void setup ()
411 | {
412 | display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
413 | display.display();
414 | delay(2000);
415 |
416 | display.clearDisplay();
417 | display.setTextSize(1);
418 | display.setTextColor(WHITE);
419 | display.setCursor(0,0);
420 | display.println("ENERGY OWL micro+");
421 |
422 | display.display();
423 |
424 | Serial.begin(115200);
425 | Serial.println("\n[ookDecoder]");
426 | attachInterrupt(1, ext_int_1, CHANGE);
427 |
428 | //DDRE &= ~_BV(PE5); //input with pull-up
429 | //PORTE &= ~_BV(PE5);
430 | }
431 |
432 | void loop () {
433 | static int i = 0;
434 | cli();
435 | word p = pulse;
436 |
437 | pulse = 0;
438 | sei();
439 |
440 | if (p != 0)
441 | {
442 |
443 | if (orscV3.nextPulse(p))
444 | reportSerial("OSV3", orscV3);
445 |
446 | if (orscV2.nextPulse(p))
447 | reportSerial("OSV2", orscV2);
448 | }
449 | }
450 |
--------------------------------------------------------------------------------
/arduino/owl_u8glib/owl_u8glib.ino:
--------------------------------------------------------------------------------
1 | #include
2 |
3 | // New code to decode OOK signals from Energy OWL CMR180 sensor
4 | // Oregon V3 decoder added - Eric Vandecasteele (onlinux)
5 | //
6 | // Oregon V2 decoder modfied - Olivier Lebrun
7 | // Oregon V2 decoder added - Dominique Pierre
8 | // New code to decode OOK signals from weather sensors, etc.
9 | // 2010-04-11 http://opensource.org/licenses/mit-license.php
10 | // $Id: ookDecoder.pde 5331 2010-04-17 10:45:17Z jcw $
11 |
12 | #include "U8glib.h"
13 |
14 | float temp;
15 | uint16_t ipower;
16 | float ftotal;
17 |
18 | //U8GLIB_SSD1306_128X64 u8g(U8G_I2C_OPT_DEV_1|U8G_I2C_OPT_NO_ACK|U8G_I2C_OPT_FAST); // Fast I2C / TWI OLED
19 | U8GLIB_PCD8544 u8g(13, 11, 10, 9, 8); // SPI Com: SCK = 13, MOSI = 11, CS = 10, A0 = 9, Reset = 8 NOKIA 5110
20 | //U8GLIB_SH1106_128X64 u8g(U8G_I2C_OPT_NO_ACK); // Display which does not send ACK
21 |
22 |
23 | class DecodeOOK {
24 | protected:
25 | byte total_bits, bits, flip, state, pos, data[31];
26 |
27 | virtual char decode (word width) =0;
28 |
29 | public:
30 |
31 | enum { UNKNOWN, T0, T1, T2, T3, OK, DONE };
32 |
33 |
34 | DecodeOOK () { resetDecoder(); }
35 |
36 | bool nextPulse (word width) {
37 | if (state != DONE)
38 |
39 | switch (decode(width)) {
40 | case -1: resetDecoder(); break;
41 | case 1: done(); break;
42 | }
43 | return isDone();
44 | }
45 |
46 | bool isDone () const { return state == DONE; }
47 |
48 | const byte* getData (byte& count) const {
49 | count = pos;
50 | return data;
51 | }
52 |
53 | void resetDecoder () {
54 | total_bits = bits = pos = flip = 0;
55 | state = UNKNOWN;
56 | }
57 |
58 | // add one bit to the packet data buffer
59 |
60 | virtual void gotBit (char value) {
61 | total_bits++;
62 | byte *ptr = data + pos;
63 | *ptr = (*ptr >> 1) | (value << 7);
64 |
65 | if (++bits >= 8) {
66 | bits = 0;
67 | if (++pos >= sizeof data) {
68 | resetDecoder();
69 | return;
70 | }
71 | }
72 | state = OK;
73 | }
74 |
75 | // store a bit using Manchester encoding_rx
76 | void manchester (char value) {
77 | flip ^= value; // manchester code, long pulse flips the bit
78 | gotBit(flip);
79 | }
80 |
81 | // move bits to the front so that all the bits are aligned to the end
82 | void alignTail (byte max =0) {
83 | // align bits
84 | if (bits != 0) {
85 | data[pos] >>= 8 - bits;
86 | for (byte i = 0; i < pos; ++i)
87 | data[i] = (data[i] >> bits) | (data[i+1] << (8 - bits));
88 | bits = 0;
89 | }
90 | // optionally shift bytes down if there are too many of 'em
91 | if (max > 0 && pos > max) {
92 | byte n = pos - max;
93 | pos = max;
94 | for (byte i = 0; i < pos; ++i)
95 | data[i] = data[i+n];
96 | }
97 | }
98 |
99 | void reverseBits () {
100 | for (byte i = 0; i < pos; ++i) {
101 | byte b = data[i];
102 | for (byte j = 0; j < 8; ++j) {
103 | data[i] = (data[i] << 1) | (b & 1);
104 | b >>= 1;
105 | }
106 | }
107 | }
108 |
109 | void reverseNibbles () {
110 | for (byte i = 0; i < pos; ++i)
111 | data[i] = (data[i] << 4) | (data[i] >> 4);
112 | }
113 |
114 | void done () {
115 | while (bits)
116 | gotBit(0); // padding
117 | state = DONE;
118 | }
119 | };
120 |
121 | class OregonDecoderV2 : public DecodeOOK {
122 | public:
123 |
124 | OregonDecoderV2() {}
125 |
126 | // add one bit to the packet data buffer
127 | virtual void gotBit (char value) {
128 | if(!(total_bits & 0x01))
129 | {
130 | data[pos] = (data[pos] >> 1) | (value ? 0x80 : 00);
131 | }
132 | total_bits++;
133 | pos = total_bits >> 4;
134 | if (pos >= sizeof data) {
135 | Serial.println("sizeof data");
136 | resetDecoder();
137 | return;
138 | }
139 | state = OK;
140 | }
141 |
142 | virtual char decode (word width) {
143 | if (200 <= width && width < 1200) {
144 | //Serial.println(width);
145 | byte w = width >= 700;
146 |
147 | switch (state) {
148 | case UNKNOWN:
149 | if (w != 0) {
150 | // Long pulse
151 | ++flip;
152 | } else if (w == 0 && 24 <= flip) {
153 | // Short pulse, start bit
154 | flip = 0;
155 | state = T0;
156 | } else {
157 | // Reset decoder
158 | return -1;
159 | }
160 | break;
161 | case OK:
162 | if (w == 0) {
163 | // Short pulse
164 | state = T0;
165 | } else {
166 | // Long pulse
167 | manchester(1);
168 | }
169 | break;
170 | case T0:
171 | if (w == 0) {
172 | // Second short pulse
173 | manchester(0);
174 | } else {
175 | // Reset decoder
176 | return -1;
177 | }
178 | break;
179 | }
180 | } else if (width >= 2500 && pos >= 8) {
181 | return 1;
182 | } else {
183 | return -1;
184 | }
185 | return 0;
186 | }
187 | };
188 |
189 | //===================================================================
190 | class OregonDecoderV3 : public DecodeOOK {
191 | public:
192 |
193 | OregonDecoderV3() {}
194 |
195 | // add one bit to the packet data buffer
196 | virtual void gotBit (char value) {
197 | data[pos] = (data[pos] >> 1) | (value ? 0x80 : 00);
198 | total_bits++;
199 | pos = total_bits >> 3;
200 | if (pos >= sizeof data) {
201 | //Serial.println("sizeof data");
202 | resetDecoder();
203 | return;
204 | }
205 | state = OK;
206 | }
207 |
208 | virtual char decode (word width) {
209 | if (200 <= width && width < 1200) {
210 | //Serial.println(width);
211 | byte w = width >= 700;
212 |
213 | switch (state) {
214 | case UNKNOWN:
215 | if (w == 0) {
216 | // Long pulse
217 | ++flip;
218 | } else if (32 <= flip) {
219 | flip = 1;
220 | manchester(1);
221 | } else {
222 | // Reset decoder
223 | return -1;
224 | }
225 | break;
226 | case OK:
227 | if (w == 0) {
228 | // Short pulse
229 | state = T0;
230 | } else {
231 | // Long pulse
232 | manchester(1);
233 | }
234 | break;
235 | case T0:
236 | if (w == 0) {
237 | // Second short pulse
238 | manchester(0);
239 | } else {
240 | // Reset decoder
241 | return -1;
242 | }
243 | break;
244 | }
245 | } else {
246 | // Trame intermédiaire 48bits ex: [OSV3 6281 3C 6801 70]
247 | return (total_bits <104 && total_bits>=40 ) ? 1: -1;
248 | }
249 |
250 | return (total_bits == 104) ? 1: 0;
251 | }
252 | };
253 |
254 | //===================================================================
255 |
256 | OregonDecoderV2 orscV2;
257 | OregonDecoderV3 orscV3;
258 |
259 |
260 | volatile word pulse;
261 |
262 | void ext_int_1(void)
263 | {
264 | static word last;
265 | // determine the pulse length in microseconds, for either polarity
266 | pulse = micros() - last;
267 | last += pulse;
268 | }
269 | float temperature(const byte* data)
270 | {
271 | int sign = (data[6]&0x8) ? -1 : 1;
272 | float temp = ((data[5]&0xF0) >> 4)*10 + (data[5]&0xF) + (float)(((data[4]&0xF0) >> 4) / 10.0);
273 | return sign * temp;
274 | }
275 |
276 | byte humidity(const byte* data)
277 | {
278 | return (data[7]&0xF) * 10 + ((data[6]&0xF0) >> 4);
279 | }
280 |
281 | // Ne retourne qu'un apercu de l'etat de la batterie : 10 = faible
282 | byte battery(const byte* data)
283 | {
284 | return (data[4] & 0x4) ? 10 : 90;
285 | }
286 |
287 | byte channel(const byte* data)
288 | {
289 | byte channel;
290 | switch (data[2])
291 | {
292 | case 0x10:
293 | channel = 1;
294 | break;
295 | case 0x20:
296 | channel = 2;
297 | break;
298 | case 0x40:
299 | channel = 3;
300 | break;
301 | }
302 |
303 | return channel;
304 | }
305 |
306 | uint16_t power(const byte* d){
307 | unsigned int val = 0;
308 | val += d[4] << 8;
309 | val += d[3];
310 | return val & 0xFFF0 ;
311 | }
312 |
313 | unsigned long total(const byte* d){
314 | long val = 0;
315 | val = (unsigned long)d[8]<<24;
316 | // Serial.println();
317 | // Serial.print(" val:"); Serial.print(val,HEX); Serial.print(" ");
318 | // Serial.println(d[8], HEX);
319 | val += (unsigned long)d[7] << 16;
320 | // Serial.print(" val:"); Serial.print(val,HEX); Serial.print(" ");
321 | // Serial.println(d[7], HEX);
322 | val += d[6] << 8;
323 | // Serial.print(" val:"); Serial.print(val,HEX); Serial.print(" ");
324 | // Serial.println(d[6], HEX);
325 | val += d[5];
326 | // Serial.print(" val:"); Serial.print(val,HEX); Serial.print(" ");
327 | // Serial.println(d[5], HEX);
328 | return val ;
329 | }
330 |
331 |
332 | void reportSerial (const char* s, class DecodeOOK& decoder) {
333 | byte pos;
334 | const byte* data = decoder.getData(pos);
335 | Serial.print(s);
336 | Serial.print(' ');
337 | //Serial.println(freeMemory());
338 |
339 | for (byte i = 0; i < pos; ++i) {
340 | Serial.print(data[i] >> 4, HEX);
341 | Serial.print(data[i] & 0x0F, HEX);
342 | }
343 |
344 | // Energy OWL : CMR180
345 | if(data[2] == 0x3C )
346 | {
347 | Serial.print("[CMR180,...] Id:");
348 | Serial.print(data[0], HEX);Serial.print(data[1], HEX);
349 | Serial.print(", size:");
350 | Serial.print(pos);
351 | Serial.print(" ,Flags:");
352 | Serial.print(data[3] & 0x0F, HEX);
353 | Serial.print(" ,power:");
354 | Serial.print(power(data));
355 | ipower = power(data);
356 | ftotal = total(data)/3600000.0;// kWh
357 | if (pos > 6) {
358 | // Display main frames
359 | // Ignore secondary frames (6 Bytes long)
360 | Serial.print(" ,total:");
361 | Serial.print(total(data));
362 | Serial.print(" ,total kWh:");
363 | Serial.print(ftotal);
364 | //drawOwl(power(data), total(data)/3600);
365 | }
366 | drawOwl(ipower, ftotal);
367 | Serial.println();
368 | }
369 |
370 |
371 | // Outside/Water Temp : THN132N,...
372 | if(data[0] == 0xEA && data[1] == 0x4C)
373 | {
374 | Serial.print("[THN132N,...] Id:");
375 | Serial.print(data[3], HEX);
376 | Serial.print(" ,Channel:");
377 | Serial.print(channel(data));
378 | Serial.print(" ,temp:");
379 | Serial.print(temperature(data));
380 | Serial.print(" ,bat:");
381 | Serial.print(battery(data));
382 | Serial.println();
383 | }
384 | // Inside Temp-Hygro : THGR228N,...
385 | else if(data[0] == 0x1A && data[1] == 0x2D)
386 | {
387 | Serial.print("[THGR228N,...] Id:");
388 | Serial.print(data[3], HEX);
389 | Serial.print(" ,Channel:");
390 | Serial.print(channel(data));
391 |
392 | Serial.print(" ,temp:");
393 | Serial.print(temperature(data));
394 | Serial.print(" ,hum:");
395 | Serial.print(humidity(data));
396 | Serial.print(" ,bat:");
397 | Serial.print(battery(data));
398 | Serial.println();
399 | temp = temperature(data);
400 | u8g.firstPage();
401 | do {
402 | draw_TH(temperature(data), humidity(data), data[3]);
403 | } while( u8g.nextPage() );
404 |
405 | }
406 |
407 | decoder.resetDecoder();
408 | delay(500);
409 | }
410 | void draw_TH( float temp, byte hum, byte id){
411 | char* label;
412 |
413 | switch (id) {
414 | case 0xEC: label = "Salon";
415 | break;
416 | case 0xBB: label = "Garage";
417 | break;
418 | case 0x22: label = "Ch.Caro";
419 | break;
420 | case 0x86: label = "Parents";
421 | break;
422 | case 0xCB: label = "Ch.Gaby";
423 | break;
424 | default: label="Other";
425 | }
426 | //u8g.setFont(u8g_font_7x13);
427 | //u8g.setPrintPos(15, 10);
428 | //u8g.print("Id: ");
429 | //u8g.print(data[3], HEX);
430 | //u8g.setFont(u8g_font_fub20r);
431 | u8g.setPrintPos(1, 10);
432 |
433 | //u8g.print(" T: ");
434 | u8g.print(temp, 1); u8g.print("C");
435 |
436 | u8g.setPrintPos(1, 22);
437 | //u8g.print(" H: ");
438 | u8g.print(hum); u8g.print("%");
439 | //u8g.setFont(u8g_font_unifont);
440 | //u8g.setPrintPos(1, 43);
441 | u8g.print(" ");
442 | u8g.print(label);
443 | u8g.setPrintPos(1, 34);
444 | if (ipower > 0){
445 | u8g.print(ipower); u8g.print("W "); u8g.print(ftotal,2); u8g.print("kWh");
446 | }
447 | else {
448 | u8g.print("----W "); u8g.print("--,--kWh");
449 | }
450 |
451 | }
452 | void drawOwl( uint16_t power, float total){
453 | //u8g.setFont(u8g_font_7x13);
454 | //u8g.setPrintPos(15, 10);
455 | //u8g.print("Id: ");
456 | //u8g.print(data[3], HEX);
457 | //u8g.setFont(u8g_font_fub20r);
458 | u8g.setPrintPos(1, 20);
459 | u8g.print(power); u8g.print("W");
460 |
461 | u8g.setPrintPos(1, 42);
462 | u8g.print(total); u8g.print("kWh");
463 |
464 |
465 | }
466 |
467 | void draw(void) {
468 | // graphic commands to redraw the complete screen should be placed here
469 | u8g.setFont(u8g_font_unifont);
470 | u8g.setPrintPos(1, 14);
471 |
472 | u8g.print("OWL micro+");
473 | u8g.setPrintPos(1, 32);
474 | u8g.print("ONLINUX.FR");
475 |
476 | }
477 |
478 | void setup ()
479 | {
480 |
481 | Serial.begin(115200);
482 | Serial.println(freeMemory());
483 | Serial.println("\n[ookDecoder]");
484 | attachInterrupt(1, ext_int_1, CHANGE);
485 |
486 | //u8g.setRot180(); // flip screnn
487 |
488 | //DDRE &= ~_BV(PE5); //input with pull-up
489 | //PORTE &= ~_BV(PE5);
490 |
491 | // picture loop
492 | u8g.firstPage();
493 | do {
494 | draw();
495 | } while( u8g.nextPage() );
496 |
497 | delay(1000);
498 | }
499 |
500 | void loop () {
501 | static int i = 0;
502 | cli();
503 | word p = pulse;
504 |
505 | pulse = 0;
506 | sei();
507 |
508 | if (p != 0)
509 | {
510 |
511 | if (orscV3.nextPulse(p))
512 | reportSerial("OSV3", orscV3);
513 |
514 | if (orscV2.nextPulse(p))
515 | reportSerial("OSV2", orscV2);
516 | }
517 | }
518 |
--------------------------------------------------------------------------------
/images/diy-owl-cm180.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/onlinux/OWL-CM180/584564852a48774793113dc8b4df2431a815c1de/images/diy-owl-cm180.jpg
--------------------------------------------------------------------------------
/images/oregon_owl_serial_output.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/onlinux/OWL-CM180/584564852a48774793113dc8b4df2431a815c1de/images/oregon_owl_serial_output.png
--------------------------------------------------------------------------------
/images/rf433-receiver.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/onlinux/OWL-CM180/584564852a48774793113dc8b4df2431a815c1de/images/rf433-receiver.jpg
--------------------------------------------------------------------------------
/src/Makefile:
--------------------------------------------------------------------------------
1 | CC = gcc
2 | CX = g++
3 | OBJ_DIR = .
4 | WPI_DIR = /tmp
5 |
6 | INC = -I/usr/local/include -I .
7 | TRFLAGS =
8 | NOTUSEDTRFLAGS = -DTRACE_RCOOK -DTRACESINGLETON -DTRACECORE433 -DTRACEEVENTMNG
9 |
10 | CFLAGS = -c -MMD
11 | LDFLAGS += -Xlinker --defsym -Xlinker RFRPI_BUILD_DATE=$$(date +'%Y%m%d') -L/usr/local/lib
12 |
13 | rfrpi_dir = ./
14 | rfrpi_files = ./RCSwitch.cpp ./RcOok.cpp ./Sensor.cpp ./core_433.cpp ./RCSwitch.cpp ./eventManager.cpp ./singleton.cpp
15 | rfrpi_files+= ./tools.cpp ./ledManager.cpp
16 | rfrpi_objects=$(addsuffix .o,$(addprefix $(OBJ_DIR)/,$(basename $(notdir $(rfrpi_files)))))
17 |
18 | target_dir = .
19 | target_files+= rfrpi_test.cpp
20 | target_objects=$(addsuffix .o,$(addprefix $(OBJ_DIR)/,$(basename $(notdir $(target_files)))))
21 | target=./rfrpi_test
22 |
23 |
24 | all: $(target)
25 |
26 | $(target): $(target_objects) $(rfrpi_objects)
27 | $(CX) $(CXXFLAGS) $(LDFLAGS) $^ -o $@ -lwiringPi -lpthread
28 | chmod +x $@
29 |
30 | $(OBJ_DIR)/%.o: $(rfrpi_dir)/%.cpp $(rfrpi_dir)/%.h
31 | @echo '----------------------------------'
32 | @echo compiling $@
33 | $(CX) $(CFLAGS) $(INC) $< -o $@ $(TRFLAGS)
34 | @echo '-------------'
35 |
36 | $(OBJ_DIR)/%.o: $(target_dir)/%.cpp
37 | @echo '----------------------------------'
38 | @echo compiling $@
39 | $(CX) $(CFLAGS) $(INC) $< -o $@ $(TRFLAGS)
40 | @echo '-------------'
41 |
42 | clean:
43 | @rm $(OBJ_DIR)/*.o
44 | @rm $(target)
45 |
--------------------------------------------------------------------------------
/src/RCSwitch.cpp:
--------------------------------------------------------------------------------
1 | /* ==========================================================================
2 | * RCSwitch.h
3 | * --------------------------------------------------------------------------
4 | * RF433 demonstrator for rfrpi Raspberry PI shield
5 | * see : http://www.disk91.com/?p=1323
6 | * --------------------------------------------------------------------------
7 | * See License on next header
8 | * --------------------------------------------------------------------------
9 | *
10 | * Created on: 23 Feb. 2014
11 | * Author: disk91 - Paul Pinault (c) 2014
12 | * --------------------------------------------------------------------------
13 | * This Class is managing the RF433 reception, it is based on RCSwitch Arduino library
14 | * Adapted for the RFRPI card. Author and license are indicated in the next header
15 | *
16 | * This Class is the hardware interface and the layer 1 decoder based on the
17 | * different known decoder.
18 | * By default, only Oregon Scientic, OOk and Dio are activated, you can modify
19 | * code to activate others, depending on your sensors. Activating too much decoders
20 | * will consume cpu time and can create decodeur collisions
21 | *
22 | * --------------------------------------------------------------------------
23 | */
24 | /* ---------------------------------------------------------------------------
25 | RCSwitch - Arduino libary for remote control outlet switches
26 | Copyright (c) 2011 Suat �zg�r. All right reserved.
27 |
28 | Contributors:
29 | - Andre Koehler / info(at)tomate-online(dot)de
30 | - Gordeev Andrey Vladimirovich / gordeev(at)openpyro(dot)com
31 |
32 | Project home: http://code.google.com/p/rc-switch/
33 |
34 | This library is free software; you can redistribute it and/or
35 | modify it under the terms of the GNU Lesser General Public
36 | License as published by the Free Software Foundation; either
37 | version 2.1 of the License, or (at your option) any later version.
38 |
39 | This library is distributed in the hope that it will be useful,
40 | but WITHOUT ANY WARRANTY; without even the implied warranty of
41 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
42 | Lesser General Public License for more details.
43 |
44 | You should have received a copy of the GNU Lesser General Public
45 | License along with this library; if not, write to the Free Software
46 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
47 | -------------------------------------------------------------------------------
48 | */
49 | #include
50 | #include
51 |
52 | #include "RCSwitch.h"
53 | #include "RcOok.h"
54 |
55 | char RCSwitch::OokReceivedCode[RCSWITCH_MAX_MESS_SIZE];
56 | bool RCSwitch::OokAvailableCode;
57 |
58 | OregonDecoderV2 * orscV2;
59 | OregonDecoderV3 * orscV3;
60 | RCSwitch_ * rcswp1;
61 | DIO * dio;
62 | #ifdef __ALLDECODER
63 | RCSwitch_ * rcswp2;
64 | CrestaDecoder * cres;
65 | KakuDecoder * kaku;
66 | XrfDecoder * xrf;
67 | HezDecoder * hez;
68 | VisonicDecoder * viso;
69 | EMxDecoder * emx;
70 | KSxDecoder * ksx;
71 | FSxDecoder * fsx;
72 | #endif
73 |
74 | /* =================================================
75 | * Construct RCSwitch
76 | */
77 | RCSwitch::RCSwitch(int rxpin, int txpin, int _rxepin, int _txepin) {
78 |
79 | RCSwitch::OokAvailableCode = false;
80 | RCSwitch::OokReceivedCode[0] = '\0';
81 |
82 | orscV2 = new OregonDecoderV2(this);
83 | orscV3 = new OregonDecoderV3(this);
84 | rcswp1 = new RCSwitch_(1,this);
85 | dio = new DIO(this);
86 |
87 | #ifdef __ALLDECODER
88 | // @TODO - Ajouter les autres ...
89 | rcswp2.configure(2,this);
90 | #endif
91 |
92 | if (rxpin != -1 && _rxepin != -1) {
93 | this->nReceiverEnablePin = _rxepin;
94 | pinMode(this->nReceiverEnablePin,OUTPUT);
95 | digitalWrite(this->nReceiverEnablePin,LOW);
96 | this->enableReceive(rxpin);
97 | } else this->nReceiverInterrupt = -1;
98 |
99 | if (txpin != -1 && _txepin != -1) {
100 | this->nTransmitterEnablePin = _txepin;
101 | pinMode(this->nTransmitterEnablePin,OUTPUT);
102 | digitalWrite(this->nTransmitterEnablePin,LOW);
103 | this->enableTransmit(txpin);
104 | } else this->nTransmitterPin = -1;
105 |
106 | }
107 |
108 | RCSwitch::~RCSwitch() {
109 | delete orscV2;
110 | delete orscV3;
111 | delete rcswp1;
112 | delete dio;
113 | #ifdef __ALLDECODER
114 | delete rcswp2;
115 | delete cres;
116 | delete kaku;
117 | delete xrf;
118 | delete hez;
119 | delete viso;
120 | delete emx;
121 | delete ksx;
122 | delete fsx;
123 | #endif
124 | }
125 |
126 | /**
127 | * Enable transmissions
128 | *
129 | * @param nTransmitterPin Arduino Pin to which the sender is connected to
130 | */
131 | void RCSwitch::enableTransmit(int nTransmitterPin) {
132 | this->nTransmitterPin = nTransmitterPin;
133 | pinMode(this->nTransmitterPin, OUTPUT);
134 | digitalWrite(this->nTransmitterPin, LOW);
135 | }
136 |
137 | /**
138 | * Disable transmissions
139 | */
140 | void RCSwitch::disableTransmit() {
141 | this->nTransmitterPin = -1;
142 | }
143 |
144 | /**
145 | * Enable receiving data
146 | */
147 | void RCSwitch::enableReceive(int interrupt) {
148 | this->nReceiverInterrupt = interrupt;
149 | this->enableReceive();
150 | }
151 |
152 | void RCSwitch::enableReceive() {
153 | if (this->nReceiverInterrupt != -1) {
154 | wiringPiISR(this->nReceiverInterrupt, INT_EDGE_BOTH, &handleInterrupt);
155 | }
156 | if (this->nReceiverEnablePin != -1 && this->nTransmitterEnablePin != -1) {
157 | digitalWrite(this->nTransmitterEnablePin,LOW);
158 | digitalWrite(this->nReceiverEnablePin,HIGH);
159 | }
160 | }
161 |
162 | /**
163 | * Disable receiving data
164 | */
165 | void RCSwitch::disableReceive() {
166 | this->nReceiverInterrupt = -1;
167 | if (this->nReceiverEnablePin != -1 && this->nTransmitterEnablePin != -1) {
168 | digitalWrite(this->nTransmitterEnablePin,LOW);
169 | digitalWrite(this->nReceiverEnablePin,LOW);
170 | }
171 | }
172 |
173 | /* ======================================================
174 | * Antenna switch management
175 | * ------------------------------------------------------
176 | */
177 | void RCSwitch::switch2transmit() {
178 | if (this->nReceiverEnablePin != -1 && this->nTransmitterEnablePin != -1) {
179 | digitalWrite(this->nReceiverEnablePin,LOW);
180 | digitalWrite(this->nTransmitterEnablePin,HIGH);
181 | }
182 | }
183 |
184 | void RCSwitch::switch2receive() {
185 | if (this->nReceiverEnablePin != -1 && this->nTransmitterEnablePin != -1) {
186 | digitalWrite(this->nTransmitterEnablePin,LOW);
187 | digitalWrite(this->nReceiverEnablePin,HIGH);
188 | }
189 | }
190 |
191 | // ==============================================
192 | // Set to true when a code has been decode by the
193 | // OoK module
194 | bool RCSwitch::OokAvailable() {
195 | return RCSwitch::OokAvailableCode;
196 | }
197 |
198 | // ==============================================
199 | // Return the received code decoded by Ook engine
200 | // if available and true, otherwith return false
201 | // can be used w/o OokAvailable
202 | //
203 | // The decoded value is stored in the v this string
204 | // must have a size equal to RCSWITCH_MAX_MESS_SIZE
205 | //
206 | // Reset available flag (this allow new capture from
207 | // interrupt (locked otherwize to avoid reentrance
208 |
209 | bool RCSwitch::getOokCode(char * _dest) {
210 | if ( RCSwitch::OokAvailableCode ) {
211 | strcpy(_dest,RCSwitch::OokReceivedCode);
212 | RCSwitch::OokAvailableCode = false;
213 | return true;
214 | } else return false;
215 | }
216 |
217 | // =============================================
218 | // reset available (autorize new capture)
219 | void RCSwitch::OokResetAvailable() {
220 | RCSwitch::OokAvailableCode = false;
221 | }
222 |
223 |
224 | // ==============================================
225 | // Interrupt Handler to manage the different protocols
226 | void RCSwitch::handleInterrupt() {
227 |
228 | static unsigned int duration;
229 | static unsigned int changeCount;
230 | static unsigned long lastTime;
231 | static unsigned int repeatCount;
232 |
233 | long time = micros();
234 | duration = time - lastTime;
235 | lastTime = time;
236 | word p = (unsigned short int) duration;
237 |
238 | // Avoid re-entry
239 | if ( !OokAvailableCode ) { // avoid reentrance -- wait until data is read
240 | if (orscV2->nextPulse(p)) { RCSwitch::OokAvailableCode = true; orscV2->sprint("OSV2 ",RCSwitch::OokReceivedCode); orscV2->resetDecoder(); }
241 | if (orscV3->nextPulse(p)) { RCSwitch::OokAvailableCode = true; orscV3->sprint("OSV3 ",RCSwitch::OokReceivedCode); orscV3->resetDecoder(); }
242 | if (rcswp1->nextPulse(p)) { RCSwitch::OokAvailableCode = true; rcswp1->sprint("RCSW ",RCSwitch::OokReceivedCode); rcswp1->resetDecoder(); }
243 | if (dio->nextPulse(p)) { RCSwitch::OokAvailableCode = true; dio->sprint("DIO_ ",RCSwitch::OokReceivedCode); dio->resetDecoder(); }
244 |
245 | #ifdef __ALLDECODER
246 | if (rcswp2.nextPulse(p)) { RCSwitch::OokAvailableCode = true; rcswp2.sprint("ALR2 ",RCSwitch::OokReceivedCode); rcswp2.resetDecoder(); }
247 | if (cres.nextPulse(p)) { cres.print("CRES"); cres.resetDecoder(); }
248 | if (kaku.nextPulse(p)) { kaku.print("KAKU"); kaku.resetDecoder(); }
249 | if (xrf.nextPulse(p)) { xrf.print("XRF"); xrf.resetDecoder(); }
250 | if (hez.nextPulse(p)) { hez.print("HEZ"); hez.resetDecoder(); }
251 | if (viso.nextPulse(p)) { viso.print("VISO"); viso.resetDecoder(); }
252 | if (emx.nextPulse(p)) { emx.print("EMX"); emx.resetDecoder(); }
253 | if (ksx.nextPulse(p)) { ksx.print("KSX"); ksx.resetDecoder(); }
254 | if (fsx.nextPulse(p)) { fsx.print("FSX"); fsx.resetDecoder(); }
255 | #endif
256 | }
257 |
258 | }
259 |
260 |
261 |
262 |
263 | // =======================================================
264 | // Transmit pulse
265 | void RCSwitch::transmit(int nHighPulses, int nLowPulses) {
266 | boolean disabled_Receive = false;
267 | int nReceiverInterrupt_backup = nReceiverInterrupt;
268 | if (this->nTransmitterPin != -1) {
269 | if (this->nReceiverInterrupt != -1) {
270 | // XXX voir si on active ou non la reception lors de la tansmission ???
271 | // disk this->disableReceive();
272 | disabled_Receive = true;
273 | }
274 |
275 | digitalWrite(this->nTransmitterPin, HIGH);
276 | delayMicroseconds( nHighPulses);
277 | digitalWrite(this->nTransmitterPin, LOW);
278 | delayMicroseconds( nLowPulses);
279 |
280 | if(disabled_Receive){
281 | // XXX disk this->enableReceive(nReceiverInterrupt_backup);
282 | }
283 | }
284 | }
285 |
286 |
287 |
288 |
--------------------------------------------------------------------------------
/src/RCSwitch.d:
--------------------------------------------------------------------------------
1 | RCSwitch.o: RCSwitch.cpp RCSwitch.h RcOok.h
2 |
--------------------------------------------------------------------------------
/src/RCSwitch.h:
--------------------------------------------------------------------------------
1 | /* ==========================================================================
2 | * RCSwitch.h
3 | * --------------------------------------------------------------------------
4 | * RF433 demonstrator for rfrpi Raspberry PI shield
5 | * see : http://www.disk91.com/?p=1323
6 | * --------------------------------------------------------------------------
7 | * See License on next header
8 | * --------------------------------------------------------------------------
9 | *
10 | * Created on: 23 Feb. 2014
11 | * Author: disk91 - Paul Pinault (c) 2014
12 | * --------------------------------------------------------------------------
13 | * This Class is managing the RF433 reception, it is based on RCSwitch Arduino library
14 | * Adapted for the RFRPI card. Author and license are indicated in the next header
15 | * --------------------------------------------------------------------------
16 | */
17 | /* ---------------------------------------------------------------------------
18 | RCSwitch - Arduino libary for remote control outlet switches
19 | Copyright (c) 2011 Suat �zg�r. All right reserved.
20 |
21 | Contributors:
22 | - Andre Koehler / info(at)tomate-online(dot)de
23 | - Gordeev Andrey Vladimirovich / gordeev(at)openpyro(dot)com
24 |
25 | Project home: http://code.google.com/p/rc-switch/
26 |
27 | This library is free software; you can redistribute it and/or
28 | modify it under the terms of the GNU Lesser General Public
29 | License as published by the Free Software Foundation; either
30 | version 2.1 of the License, or (at your option) any later version.
31 |
32 | This library is distributed in the hope that it will be useful,
33 | but WITHOUT ANY WARRANTY; without even the implied warranty of
34 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
35 | Lesser General Public License for more details.
36 |
37 | You should have received a copy of the GNU Lesser General Public
38 | License along with this library; if not, write to the Free Software
39 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
40 | -------------------------------------------------------------------------------
41 | */
42 | #ifndef _RCSwitch_h
43 | #define _RCSwitch_h
44 |
45 | #if defined(ARDUINO) && ARDUINO >= 100
46 | #include "Arduino.h"
47 | #else
48 | #include
49 | #include
50 | // -- coment by disk91 #define NULL 0
51 | #define CHANGE 1
52 | #ifdef __cplusplus
53 | extern "C"{
54 | #endif
55 | typedef uint8_t boolean;
56 | typedef uint8_t byte;
57 |
58 | #if !defined(NULL)
59 | #endif
60 | #ifdef __cplusplus
61 | }
62 | #endif
63 | #endif
64 |
65 |
66 | // Number of maximum High/Low changes per packet.
67 | // We can handle up to (unsigned long) => 32 bit * 2 H/L changes per bit + 2 for sync
68 | //#define RCSWITCH_MAX_CHANGES 67
69 |
70 | // Taille max d'un message de type Oregon Scientific
71 | #define RCSWITCH_MAX_MESS_SIZE 128
72 |
73 | class RCSwitch {
74 |
75 | public:
76 | RCSwitch(int rxpin,int txpin,int _rxepin, int _txepin );
77 | ~RCSwitch();
78 |
79 |
80 | void sendTriState(char* Code);
81 | void send(unsigned long Code, unsigned int length);
82 | void send(char* Code);
83 |
84 | void enableReceive(int interrupt);
85 | void enableReceive();
86 | void disableReceive();
87 |
88 | void enableTransmit(int nTransmitterPin);
89 | void disableTransmit();
90 |
91 | static bool OokAvailable();
92 | static bool getOokCode(char * _dest);
93 | static void OokResetAvailable();
94 | void transmit(int nHighPulses, int nLowPulses);
95 | // -- antenna switch management
96 | void switch2transmit();
97 | void switch2receive();
98 |
99 | private:
100 |
101 | static void handleInterrupt();
102 | int nReceiverInterrupt;
103 | int nReceiverEnablePin;
104 | int nTransmitterPin;
105 | int nTransmitterEnablePin;
106 |
107 | static char OokReceivedCode[RCSWITCH_MAX_MESS_SIZE];
108 | static bool OokAvailableCode;
109 |
110 |
111 | };
112 |
113 | #endif
114 |
--------------------------------------------------------------------------------
/src/RcOok.cpp:
--------------------------------------------------------------------------------
1 | /* ==========================================================================
2 | * RcOoK.h
3 | * --------------------------------------------------------------------------
4 | * RF433 demonstrator for rfrpi Raspberry PI shield
5 | * see : http://www.disk91.com/?p=1323
6 | * --------------------------------------------------------------------------
7 | * This software is under GPLv3
8 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
9 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
10 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
11 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
12 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
13 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
14 | * THE SOFTWARE.
15 | * --------------------------------------------------------------------------
16 | * Modified on Jan 15 2015
17 | * Added OSV3 support for OWL CMR180 Energy sensor -- Onlinux (onlinux.fr)
18 | *
19 | * Created on: 23 Feb. 2014
20 | * Author: disk91 - Paul Pinault (c) 2014
21 | * --------------------------------------------------------------------------
22 | * 433 Mhz decoding OoK frame from Oregon Scientific
23 | *
24 | * Created on: 16 sept. 2013
25 | * Author: disk91 modified from
26 | * Oregon V2 decoder added - Dominique Pierre
27 | * Oregon V3 decoder revisited - Dominique Pierre
28 | * RwSwitch : Copyright (c) 2011 Suat Özgür. All right reserved.
29 | * Contributors:
30 | * - Andre Koehler / info(at)tomate-online(dot)de
31 | * - Gordeev Andrey Vladimirovich / gordeev(at)openpyro(dot)com
32 | * - Skineffect / http://forum.ardumote.com/viewtopic.php?f=2&t=48
33 | * Project home: http://code.google.com/p/rc-switch/
34 | *
35 | * New code to decode OOK signals from weather sensors, etc.
36 | * 2010-04-11 http://opensource.org/licenses/mit-license.php
37 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
38 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
39 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
40 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
41 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
42 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
43 | * THE SOFTWARE.
44 | * --------------------------------------------------------------------------
45 | */
46 | #include
47 |
48 | #include
49 | #include
50 | #include
51 |
52 | #include "RCSwitch.h"
53 | #include "RcOok.h"
54 | #include "tools.h"
55 |
56 | //#define TRACE_RCOOK
57 |
58 | /* ======================================================
59 | * Master class OOK
60 | * ------------------------------------------------------
61 | */
62 |
63 | DecodeOOK::DecodeOOK (RCSwitch * _rcs) {
64 | this->rcs = _rcs;
65 | this->nRepeatTransmit = 2;
66 | resetDecoder();
67 | }
68 |
69 | DecodeOOK::~DecodeOOK () {}
70 |
71 | bool DecodeOOK::nextPulse (word width) {
72 | if (state != DONE)
73 |
74 | switch (decode(width)) {
75 | case -1: resetDecoder(); break;
76 | case 1: done(); break;
77 | }
78 | return isDone();
79 | }
80 |
81 | bool DecodeOOK::isDone() const { return state == DONE; }
82 |
83 | const byte* DecodeOOK::getData (byte& count) const {
84 | count = pos;
85 | return data;
86 | }
87 |
88 | void DecodeOOK::resetDecoder () {
89 | total_bits = bits = pos = flip = 0;
90 | state = UNKNOWN;
91 | }
92 |
93 | // add one bit to the packet data buffer
94 |
95 | void DecodeOOK::gotBit (char value) {
96 | total_bits++;
97 | byte *ptr = data + pos;
98 | *ptr = (*ptr >> 1) | (value << 7);
99 |
100 | if (++bits >= 8) {
101 | bits = 0;
102 | if (++pos >= sizeof data) {
103 | resetDecoder();
104 | return;
105 | }
106 | }
107 | state = OK;
108 | }
109 |
110 | // store a bit using Manchester encoding
111 | void DecodeOOK::manchester (char value) {
112 | flip ^= value; // manchester code, long pulse flips the bit
113 | gotBit(flip);
114 | }
115 |
116 | // move bits to the front so that all the bits are aligned to the end
117 | void DecodeOOK::alignTail (byte max) {
118 | // align bits
119 | if (bits != 0) {
120 | data[pos] >>= 8 - bits;
121 | for (byte i = 0; i < pos; ++i)
122 | data[i] = (data[i] >> bits) | (data[i+1] << (8 - bits));
123 | bits = 0;
124 | }
125 | // optionally shift bytes down if there are too many of 'em
126 | if (max > 0 && pos > max) {
127 | byte n = pos - max;
128 | pos = max;
129 | for (byte i = 0; i < pos; ++i)
130 | data[i] = data[i+n];
131 | }
132 | }
133 |
134 | /* Reverse bits order last becoming first */
135 | void DecodeOOK::reverseData () {
136 | // reverse octets
137 | int start = 0;
138 | int end = pos-1;
139 | while ( start < end ) {
140 | byte b = data[start];
141 | data[start] = data[end];
142 | data[end] = b;
143 | start++;
144 | end--;
145 | }
146 | }
147 |
148 | void DecodeOOK::reverseBits () {
149 | for (byte i = 0; i < pos; i++) {
150 | byte b = data[i];
151 | for (byte j = 0; j < 8; ++j) {
152 | data[i] = (data[i] << 1) | (b & 1);
153 | b >>= 1;
154 | }
155 | }
156 | }
157 |
158 | void DecodeOOK::reverseNibbles () {
159 | for (byte i = 0; i < pos; i++)
160 | data[i] = (data[i] << 4) | (data[i] >> 4);
161 | }
162 |
163 | void DecodeOOK::done () {
164 | while (bits)
165 | gotBit(0); // padding
166 | state = DONE;
167 | }
168 |
169 | /**
170 | * Print in hex the received value into d string adding s string header
171 | * d minimal size is : OOK_MAX_STR_LEN
172 | * s recommended size : 3 char
173 | */
174 | void DecodeOOK::sprint(const char * s, char * d) {
175 | char v[] = { '0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F' };
176 | char * _d = d;
177 | byte pos;
178 | const byte* data = this->getData(pos);
179 | pos = pos + ((total_bits >> 2) & 0x1); // on ajoute 1 byte si le nombre de nibbles est impaire
180 | //printf("Pos: %d total_bits: %d %d", pos, total_bits, (total_bits >> 2) );
181 | sprintf(d,"%s ",s);
182 | d+=strlen(s);
183 | for (byte i = 0; i < pos ; ++i) {
184 | sprintf(d,"%c",v[ data[i] >> 4 ]);d++;
185 | sprintf(d,"%c",v[ data[i] & 0x0F]);d++;
186 | }
187 | if ((total_bits >> 2) & 0x1) {
188 | // Nombre de nibbles impaire
189 | // On code de dernier sur un byte inversé
190 | sprintf(d-2,"%c",'0');
191 | sprintf(d-1,"%c",v[ data[pos-1] >> 4 ]);
192 | }
193 |
194 | sprintf(d,'\0');
195 |
196 | #ifdef TRACE_RCOOK
197 | std::cout << " * DecodeOOK::sprint() - received [" << _d << "]" << std::endl;
198 | #endif
199 | }
200 |
201 | void DecodeOOK::print(const char* s) {
202 | char t[128];
203 | this->sprint(s,t);
204 | printf("%s\n",t);
205 | }
206 |
207 | // =====================================================================
208 | // Transmit
209 | // =====================================================================
210 |
211 | /* --------------------------------------------------------------
212 | * Convert a int value into as 0/1 binary string
213 | */
214 | char * DecodeOOK::dec2binWzerofill(unsigned int Dec, unsigned int bitLength){
215 | static char bin[65];
216 | for ( int i = bitLength-1 ; i >= 0 ; i-- ) {
217 | bin[i] = ((Dec & 1) > 0)? '1' : '0';
218 | Dec = Dec >> 1;
219 | }
220 | bin[bitLength] = '\0';
221 | return bin;
222 | }
223 |
224 |
225 | /* -------------------------------------------------------------
226 | * Send a code described in a String hex format like
227 | * 0x1D20304050 ; length is number of bit to send
228 | */
229 | #define MAXSZ 512
230 | void DecodeOOK::send(char * sHexStr, unsigned int length) {
231 | static char bin[MAXSZ];
232 | int expSz = length / 4 + 2;
233 | int len = strlen(sHexStr);
234 | bin[0]='\0';
235 | if ( expSz == len && length < MAXSZ && sHexStr[0]=='0' && sHexStr[1]=='x') {
236 | int k=0;
237 | sHexStr += 2;
238 | while ( *sHexStr != '\0' && k < MAXSZ ) {
239 | int v = getIntFromChar(*sHexStr);
240 | bin[k++]='0'+((v & 0x08) >> 3);
241 | bin[k++]='0'+((v & 0x04) >> 2);
242 | bin[k++]='0'+((v & 0x02) >> 1);
243 | bin[k++]='0'+((v & 0x01) >> 0);
244 | sHexStr++;
245 | }
246 | bin[k]='\0';
247 | } else {
248 | printf("pbm : %s, %d, %d, %d\n",sHexStr,len,length,expSz);
249 | }
250 | this->send( bin );
251 | }
252 |
253 | void DecodeOOK::send(unsigned int Code, unsigned int length) {
254 | this->send( this->dec2binWzerofill(Code, length) );
255 | }
256 |
257 | void DecodeOOK::send(char* sCodeWord) {
258 | this->rcs->switch2transmit();
259 | int len = strlen(sCodeWord);
260 | for (int nRepeat=0; nRepeatsendSync();
262 | int i = 0;
263 | while (i < len) {
264 | switch(sCodeWord[i]) {
265 | case '0':
266 | this->send0();
267 | break;
268 | case '1':
269 | this->send1();
270 | break;
271 | }
272 | i++;
273 | }
274 | }
275 | this->sendSync();
276 | this->rcs->switch2receive();
277 | }
278 |
279 | void DecodeOOK::send0() {printf("DecodeOOK::send0() - error - virtual!\n");}
280 | void DecodeOOK::send1() {printf("DecodeOOK::send1() - error - virtual!\n");}
281 | void DecodeOOK::sendSync() {printf("DecodeOOK::sendSync() - error - virtual!\n");}
282 |
283 |
284 | /* ======================================================
285 | * OregonDecoderV2
286 | * ------------------------------------------------------
287 | */
288 |
289 | OregonDecoderV2::OregonDecoderV2(RCSwitch * _rcs) : DecodeOOK(_rcs) {}
290 | OregonDecoderV2::~OregonDecoderV2() {}
291 |
292 | void OregonDecoderV2::gotBit (char value) {
293 | if(!(total_bits & 0x01))
294 | {
295 | data[pos] = (data[pos] >> 1) | (value ? 0x80 : 00);
296 | }
297 | total_bits++;
298 | pos = total_bits >> 4;
299 | if (pos >= sizeof data) {
300 | resetDecoder();
301 | return;
302 | }
303 | state = OK;
304 | }
305 |
306 | int OregonDecoderV2::decode (word width) {
307 | if (200 <= width && width < 1200) {
308 | byte w = width >= 700;
309 | switch (state) {
310 | case UNKNOWN:
311 | if (w != 0) {
312 | // Long pulse
313 | ++flip;
314 | } else if ( 24 <= flip ) {
315 | //BugFix : initialement on test 32b mais il semble que
316 | // tous n'en aient pas autant, en tout cas on constate
317 | // plus de message reçus avec 24 que 32 ; obligatoire pour THN132N
318 |
319 | // Short pulse, start bit
320 | flip = 0;
321 | state = T0;
322 | } else {
323 | // Reset decoder
324 | return -1;
325 | }
326 | break;
327 | case OK:
328 | if (w == 0) {
329 | // Short pulse
330 | state = T0;
331 | } else {
332 | // Long pulse
333 | manchester(1);
334 | }
335 | break;
336 | case T0:
337 | if (w == 0) {
338 | // Second short pulse
339 | manchester(0);
340 | } else {
341 | // Reset decoder
342 | return -1;
343 | }
344 | break;
345 | }
346 | } else {
347 | // Dans le cas du THN132N on a seulement 136b
348 | // donc si on depasse le timing et que l'on a 136b
349 | // c'est sans doute qu'il s'agit de celui-ci
350 | return ( total_bits == 136 )? 1 : -1;
351 | }
352 | return total_bits == 160 ? 1: 0 ;
353 | }
354 |
355 | /* ======================================================
356 | * OregonDecoderV3
357 | * ------------------------------------------------------
358 | */
359 |
360 | OregonDecoderV3::OregonDecoderV3(RCSwitch * _rcs) : DecodeOOK(_rcs) {}
361 | OregonDecoderV3::~OregonDecoderV3() {}
362 |
363 | // add one bit to the packet data buffer
364 | void OregonDecoderV3::gotBit (char value) {
365 | data[pos] = (data[pos] >> 1) | (value ? 0x80 : 00);
366 | total_bits++;
367 | pos = total_bits >> 3;
368 | if (pos >= sizeof data) {
369 | resetDecoder();
370 | return;
371 | }
372 | state = OK;
373 | }
374 |
375 | int OregonDecoderV3::decode (word width) {
376 | if (200 <= width && width < 1200) {
377 | byte w = width >= 700;
378 | switch (state) {
379 | case UNKNOWN:
380 | if (w == 0)
381 | ++flip;
382 | else if (24 <= flip) {
383 | flip = 1;
384 | manchester(1);
385 | } else
386 | return -1;
387 | break;
388 | case OK:
389 | if (w == 0)
390 | state = T0;
391 | else
392 | manchester(1);
393 | break;
394 | case T0:
395 | if (w == 0)
396 | manchester(0);
397 | else
398 | return -1;
399 | break;
400 | }
401 | } else {
402 | //return -1;
403 | // Trame intermédiaire 48bits ex: [OSV3 6281 3C 6801 70]
404 | return (total_bits <108 && total_bits>=40 ) ? 1: -1;
405 | }
406 | return (total_bits == 108) ? 1: 0;
407 |
408 | }
409 |
410 | /* ======================================================
411 | * CrestaDecoder
412 | * ------------------------------------------------------
413 | */
414 |
415 |
416 | CrestaDecoder::CrestaDecoder (RCSwitch * _rcs) : DecodeOOK(_rcs) {}
417 | CrestaDecoder::~CrestaDecoder() {}
418 |
419 | int CrestaDecoder::decode (word width) {
420 | if (200 <= width && width < 1300) {
421 | byte w = width >= 750;
422 | switch (state) {
423 | case UNKNOWN:
424 | if (w == 1)
425 | ++flip;
426 | else if (2 <= flip && flip <= 10)
427 | state = T0;
428 | else
429 | return -1;
430 | break;
431 | case OK:
432 | if (w == 0)
433 | state = T0;
434 | else
435 | gotBit(1);
436 | break;
437 | case T0:
438 | if (w == 0)
439 | gotBit(0);
440 | else
441 | return -1;
442 | break;
443 | }
444 | } else if (width >= 2500 && pos >= 7)
445 | return 1;
446 | else
447 | return -1;
448 | return 0;
449 | }
450 |
451 | /* ======================================================
452 | * KakuDecoder
453 | * ------------------------------------------------------
454 | */
455 |
456 |
457 | KakuDecoder::KakuDecoder (RCSwitch * _rcs) : DecodeOOK(_rcs) {}
458 | KakuDecoder::~KakuDecoder() {}
459 |
460 | int KakuDecoder::decode (word width) {
461 | if ( ( 180 <= width && width < 450) || (950 <= width && width < 1250) ) {
462 | byte w = width >= 700;
463 | switch (state) {
464 | case UNKNOWN:
465 | case OK:
466 | if (w == 0)
467 | state = T0;
468 | else
469 | return -1;
470 | break;
471 | case T0:
472 | if (w)
473 | state = T1;
474 | else
475 | return -1;
476 | break;
477 | case T1:
478 | state += w + 1;
479 | break;
480 | case T2:
481 | if (w)
482 | gotBit(0);
483 | else
484 | return -1;
485 | break;
486 | case T3:
487 | if (w == 0)
488 | gotBit(1);
489 | else
490 | return -1;
491 | break;
492 | }
493 | } else if (width >= 2500 && 8 * pos + bits == 12) {
494 | for (byte i = 0; i < 4; ++i)
495 | gotBit(0);
496 | alignTail(2);
497 | return 1;
498 | } else
499 | return -1;
500 | return 0;
501 | }
502 |
503 | /* ======================================================
504 | * XrfDecoder
505 | * ------------------------------------------------------
506 | */
507 |
508 | XrfDecoder::XrfDecoder (RCSwitch * _rcs) : DecodeOOK(_rcs) {}
509 | XrfDecoder::~XrfDecoder() {}
510 |
511 | // see also http://davehouston.net/rf.htm
512 | int XrfDecoder::decode (word width) {
513 | if (width > 2000 && pos >= 4)
514 | return 1;
515 | if (width > 5000)
516 | return -1;
517 | if (width > 4000 && state == UNKNOWN)
518 | state = OK;
519 | else if (350 <= width && width < 1800) {
520 | byte w = width >= 720;
521 | switch (state) {
522 | case OK:
523 | if (w == 0)
524 | state = T0;
525 | else
526 | return -1;
527 | break;
528 | case T0:
529 | gotBit(w);
530 | break;
531 | }
532 | } else
533 | return -1;
534 | return 0;
535 | }
536 |
537 | /* ======================================================
538 | * HezDecoder
539 | * ------------------------------------------------------
540 | */
541 |
542 | HezDecoder::HezDecoder (RCSwitch * _rcs) : DecodeOOK(_rcs) {}
543 | HezDecoder::~HezDecoder() {}
544 |
545 | // see also http://homeeasyhacking.wikia.com/wiki/Home_Easy_Hacking_Wiki
546 | int HezDecoder::decode (word width) {
547 | if (200 <= width && width < 1200) {
548 | byte w = width >= 600;
549 | gotBit(w);
550 | } else if (width >= 5000 && pos >= 5 /*&& 8 * pos + bits == 50*/) {
551 | for (byte i = 0; i < 6; ++i)
552 | gotBit(0);
553 | alignTail(7); // keep last 56 bits
554 | return 1;
555 | } else
556 | return -1;
557 | return 0;
558 | }
559 |
560 |
561 | /* ======================================================
562 | * VisonicDecoder
563 | * ------------------------------------------------------
564 | */
565 |
566 | // 868 MHz decoders
567 |
568 | VisonicDecoder::VisonicDecoder (RCSwitch * _rcs) : DecodeOOK(_rcs) {}
569 | VisonicDecoder::~VisonicDecoder() {}
570 |
571 | int VisonicDecoder::decode (word width) {
572 | if (200 <= width && width < 1000) {
573 | byte w = width >= 600;
574 | switch (state) {
575 | case UNKNOWN:
576 | case OK:
577 | state = w == 0 ? T0 : T1;
578 | break;
579 | case T0:
580 | gotBit(!w);
581 | if (w)
582 | return 0;
583 | break;
584 | case T1:
585 | gotBit(!w);
586 | if (!w)
587 | return 0;
588 | break;
589 | }
590 | // sync error, flip all the preceding bits to resync
591 | for (byte i = 0; i <= pos; ++i)
592 | data[i] ^= 0xFF;
593 | } else if (width >= 2500 && 8 * pos + bits >= 36 && state == OK) {
594 | for (byte i = 0; i < 4; ++i)
595 | gotBit(0);
596 | alignTail(5); // keep last 40 bits
597 | // only report valid packets
598 | byte b = data[0] ^ data[1] ^ data[2] ^ data[3] ^ data[4];
599 | if ((b & 0xF) == (b >> 4))
600 | return 1;
601 | } else
602 | return -1;
603 | return 0;
604 | }
605 |
606 | /* ======================================================
607 | * EMxDecoder
608 | * ------------------------------------------------------
609 | */
610 |
611 | EMxDecoder::EMxDecoder (RCSwitch * _rcs) : DecodeOOK(_rcs) {}
612 | EMxDecoder::~EMxDecoder() {}
613 |
614 | // see also http://fhz4linux.info/tiki-index.php?page=EM+Protocol
615 | int EMxDecoder::decode (word width) {
616 | if (200 <= width && width < 1000) {
617 | byte w = width >= 600;
618 | switch (state) {
619 | case UNKNOWN:
620 | if (w == 0)
621 | ++flip;
622 | else if (flip > 20)
623 | state = OK;
624 | else
625 | return -1;
626 | break;
627 | case OK:
628 | if (w == 0)
629 | state = T0;
630 | else
631 | return -1;
632 | break;
633 | case T0:
634 | gotBit(w);
635 | break;
636 | }
637 | } else if (width >= 1500 && pos >= 9)
638 | return 1;
639 | else
640 | return -1;
641 | return 0;
642 | }
643 |
644 | /* ======================================================
645 | * KSxDecoder
646 | * ------------------------------------------------------
647 | */
648 | KSxDecoder::KSxDecoder (RCSwitch * _rcs) : DecodeOOK(_rcs) {}
649 | KSxDecoder::~KSxDecoder() {}
650 |
651 | // see also http://www.dc3yc.homepage.t-online.de/protocol.htm
652 | int KSxDecoder::decode (word width) {
653 | if (200 <= width && width < 1000) {
654 | byte w = width >= 600;
655 | switch (state) {
656 | case UNKNOWN:
657 | gotBit(w);
658 | bits = pos = 0;
659 | if (data[0] != 0x95)
660 | state = UNKNOWN;
661 | break;
662 | case OK:
663 | state = w == 0 ? T0 : T1;
664 | break;
665 | case T0:
666 | gotBit(1);
667 | if (!w)
668 | return -1;
669 | break;
670 | case T1:
671 | gotBit(0);
672 | if (w)
673 | return -1;
674 | break;
675 | }
676 | } else if (width >= 1500 && pos >= 6)
677 | return 1;
678 | else
679 | return -1;
680 | return 0;
681 | }
682 |
683 | /* ======================================================
684 | * FSxDecoder
685 | * ------------------------------------------------------
686 | */
687 | FSxDecoder::FSxDecoder (RCSwitch * _rcs) : DecodeOOK(_rcs) {}
688 | FSxDecoder::~FSxDecoder() {}
689 |
690 | // see also http://fhz4linux.info/tiki-index.php?page=FS20%20Protocol
691 | int FSxDecoder::decode (word width) {
692 | if (300 <= width && width < 775) {
693 | byte w = width >= 500;
694 | switch (state) {
695 | case UNKNOWN:
696 | if (w == 0)
697 | ++flip;
698 | else if (flip > 20)
699 | state = T1;
700 | else
701 | return -1;
702 | break;
703 | case OK:
704 | state = w == 0 ? T0 : T1;
705 | break;
706 | case T0:
707 | gotBit(0);
708 | if (w)
709 | return -1;
710 | break;
711 | case T1:
712 | gotBit(1);
713 | if (!w)
714 | return -1;
715 | break;
716 | }
717 | } else if (width >= 1500 && pos >= 5)
718 | return 1;
719 | else
720 | return -1;
721 | return 0;
722 | }
723 |
724 | /* ======================================================
725 | * DI-O Decoder
726 | * ------------------------------------------------------
727 | *
728 | * DIO - Decode Data - 64 bits | in fact 32 elements of 2 bits 01 | 10
729 | * Bits 0-51 : identifiant télécommande
730 | * Bit 52/53 : bit de groupe (01 - no group)
731 | * Bit 54/55 : on/off (10 = on / 01 = off)
732 | * Bit 56/63 : button line (0101 0101 - ligne1)
733 | * (0101 0110 - ligne2)
734 | * (0101 1001 - ligne3)
735 | */
736 |
737 | DIO::DIO (RCSwitch * _rcs) : DecodeOOK(_rcs) {}
738 | DIO::~DIO() {}
739 |
740 |
741 | int DIO::decode (word width) {
742 |
743 | switch (state) {
744 | case UNKNOWN: // Start of frame
745 | if ( 2400 <= width && width <= 3000 )
746 | state = T0;
747 | break;
748 |
749 | case T0: // First half of pulse : HIGH around 260us
750 | if ( 150 <= width && width <= 400 )
751 | state = T1;
752 | else return -1; // error, reset
753 | break;
754 |
755 | case T1: // second half of pulse : LOW for around 310us / 1400us
756 | if ( 200 <= width && width <= 500) {
757 | gotBit(0);
758 | } else if ( 1200 <= width && width <= 1600 ) {
759 | gotBit(1);
760 | } else if ( width > 5000 && pos == 8) { // end of frame
761 | // got Bits reverse bit order
762 | this->reverseBits();
763 | for (byte i = 0; i < pos ; i++) {
764 | byte h = data[i] >> 4;
765 | byte l = data[i] & 0x0F;
766 |
767 | if ( ( h != 5 && h != 6 && h != 0x09 && h != 0x0A ) ||
768 | ( l != 5 && l != 6 && l != 0x09 && l != 0x0A ) ) {
769 | return -1;
770 | }
771 | }
772 | state = DONE;
773 | return 1;
774 | } else {
775 | return -1;
776 | }
777 | state = T0;
778 | break;
779 | }
780 | return 0;
781 | }
782 |
783 |
784 | /**
785 | * Sends a "0" Bit
786 | * en -YY : la correction entre ce qui est demandé et ce qui est mesuré
787 | * par experience ...
788 | * _
789 | * Waveform Protocol 1: | |___
790 | */
791 | void DIO::send0() {
792 | this->rcs->transmit(260-51,310-110);
793 | }
794 |
795 | /**
796 | * Sends a "1" Bit
797 | * _
798 | * Waveform Protocol 1: | |________________
799 | */
800 | void DIO::send1() {
801 | this->rcs->transmit(260-51,1400-110);
802 | }
803 |
804 |
805 | /**
806 | * Sends a "Sync" Bit
807 | * _
808 | * Waveform Protocol 1: | |_______________________________
809 | */
810 | void DIO::sendSync() {
811 | this->rcs->transmit(260-51,2800);
812 | }
813 |
814 | /**
815 | * Code format : 0x566A6A5659A69655
816 | * Change data stream to switch remote control ON
817 | */
818 | void DIO::getSwitchON(char * code) {
819 | if (code != NULL && strlen(code) == 18 ) {
820 | char c = code[15];
821 | if ( c == '5' ) code[15] = '6';
822 | else if ( c == '9' ) code[15] = 'A';
823 | } else {
824 | printf("DIO::getSwitchON - format problem(%d/18)\n",(code!=NULL)?strlen(code):-1);
825 | }
826 | }
827 |
828 | /**
829 | * Code format : 0x566A6A5659A69655
830 | * Change data stream to switch remote control OFF
831 | */
832 | void DIO::getSwitchOFF(char * code) {
833 | if (code != NULL && strlen(code) == 18 ) {
834 | char c = code[15];
835 | if ( c == '6' ) code[15] = '5';
836 | else if ( c == 'A' ) code[15] = '9';
837 | } else {
838 | printf("DIO::getSwitchOFF - format problem(%d/18)\n",(code!=NULL)?strlen(code):-1);
839 | }
840 | }
841 |
842 |
843 |
844 |
845 | /* ======================================================
846 | * RemoteControlProtocol1
847 | * ------------------------------------------------------
848 | */
849 | RCSwitch_::RCSwitch_(int protocol, RCSwitch * _rcs) : DecodeOOK(_rcs) {
850 | this->resetDecoder();
851 | this->setRepeatTransmit(5);
852 | this->setReceiveTolerance(60);
853 | this->setProtocol(protocol);
854 | this->changeCount = 0;
855 | }
856 | RCSwitch_::~RCSwitch_() {}
857 |
858 |
859 | int RCSwitch_::decode (word duration) {
860 |
861 | int _changeCount;
862 |
863 | // Signal starts with a Sync message, this one is between 6ms and 10ms
864 | // it finished with the same sync message.
865 | // if duration is > 5000 we can be in this sync period (begin or end)
866 | if ( changeCount > 6 /* this looks like end */
867 | && duration > 5000
868 | && duration > this->timings[0] - 200
869 | && duration < this->timings[0] + 200
870 | ) {
871 |
872 | _changeCount = changeCount-1;
873 | changeCount = 0;
874 | switch (this->nProtocol) {
875 | case 1: if (receiveProtocol1(_changeCount) == true) return 1; break;
876 | case 2: if (receiveProtocol2(_changeCount) == true) return 1; break;
877 | }
878 | return -1;
879 |
880 | } else if (duration > 5000) { /* otherwise it's just the begining */
881 | changeCount = 0;
882 | }
883 |
884 | // by default we are in the message itself
885 | if (changeCount >= RCSWITCH_MAX_CHANGES) {
886 | changeCount = 0;
887 | }
888 | this->timings[changeCount++] = duration;
889 | return 0;
890 | }
891 |
892 | /**
893 | * Sets the protocol to send.
894 | */
895 | void RCSwitch_::setProtocol(int nProtocol) {
896 | this->nProtocol = nProtocol;
897 | if (nProtocol == 1){
898 | this->setPulseLength(350);
899 | }
900 | else if (nProtocol == 2) {
901 | this->setPulseLength(650);
902 | }
903 | }
904 |
905 | /**
906 | * Sets the protocol to send with pulse length in microseconds.
907 | */
908 | void RCSwitch_::setProtocol(int nProtocol, int nPulseLength) {
909 | this->nProtocol = nProtocol;
910 | if (nProtocol == 1){
911 | this->setPulseLength(nPulseLength);
912 | }
913 | else if (nProtocol == 2) {
914 | this->setPulseLength(nPulseLength);
915 | }
916 | }
917 |
918 |
919 | /**
920 | * Sets pulse length in microseconds
921 | */
922 | void RCSwitch_::setPulseLength(int nPulseLength) {
923 | this->nPulseLength = nPulseLength;
924 | }
925 |
926 | /**
927 | * Sets Repeat Transmits
928 | */
929 | void RCSwitch_::setRepeatTransmit(int nRepeatTransmit) {
930 | this->nRepeatTransmit = nRepeatTransmit;
931 | }
932 |
933 | /**
934 | * Set Receiving Tolerance
935 | */
936 | void RCSwitch_::setReceiveTolerance(int nPercent) {
937 | this->nReceiveTolerance = nPercent;
938 | }
939 |
940 |
941 | bool RCSwitch_::receiveProtocol1(unsigned int _changeCount){
942 |
943 | unsigned long delay = this->timings[0] / 31;
944 | unsigned long delayTolerance = delay * this->nReceiveTolerance * 0.01;
945 |
946 | for (int i = 1; i<_changeCount ; i=i+2) {
947 |
948 | if ( this->timings[i] > delay-delayTolerance
949 | && this->timings[i] < delay+delayTolerance
950 | && this->timings[i+1] > delay*3-delayTolerance
951 | && this->timings[i+1] < delay*3+delayTolerance) {
952 | this->gotBit(0);
953 | } else if ( this->timings[i] > delay*3-delayTolerance
954 | && this->timings[i] < delay*3+delayTolerance
955 | && this->timings[i+1] > delay-delayTolerance
956 | && this->timings[i+1] < delay+delayTolerance) {
957 | this->gotBit(1);
958 | } else {
959 | // Failed
960 | return false;
961 | }
962 | }
963 | if (_changeCount > 6) { // ignore < 4bit values as there are no devices sending 4bit values => noise
964 | this->reverseBits();
965 | return true;
966 | }
967 | return false;
968 |
969 | }
970 |
971 | bool RCSwitch_::receiveProtocol2(unsigned int _changeCount){
972 |
973 | unsigned long delay = this->timings[0] / 10;
974 | unsigned long delayTolerance = delay * this->nReceiveTolerance * 0.01;
975 |
976 | for (int i = 1; i<_changeCount ; i=i+2) {
977 |
978 | if ( this->timings[i] > delay-delayTolerance
979 | && this->timings[i] < delay+delayTolerance
980 | && this->timings[i+1] > delay*2-delayTolerance
981 | && this->timings[i+1] < delay*2+delayTolerance) {
982 | this->gotBit(0);
983 | } else if ( this->timings[i] > delay*2-delayTolerance
984 | && this->timings[i] < delay*2+delayTolerance
985 | && this->timings[i+1] > delay-delayTolerance
986 | && this->timings[i+1] < delay+delayTolerance) {
987 | this->gotBit(1);
988 | } else {
989 | // Failed
990 | return false;
991 | }
992 | }
993 | if (_changeCount > 6) { // ignore < 4bit values as there are no devices sending 4bit values => noise
994 | this->reverseBits();
995 | return true;
996 | }
997 | return false;
998 | }
999 | // =====================================================================
1000 | // Transmit
1001 | // =====================================================================
1002 |
1003 |
1004 | /**
1005 | * Sends a "0" Bit
1006 | * _
1007 | * Waveform Protocol 1: | |___
1008 | * _
1009 | * Waveform Protocol 2: | |__
1010 | */
1011 | void RCSwitch_::send0() {
1012 | if (this->nProtocol == 1){
1013 | this->rcs->transmit(1*this->nPulseLength,3*this->nPulseLength);
1014 | }
1015 | else if (this->nProtocol == 2) {
1016 | this->rcs->transmit(1*this->nPulseLength,2*this->nPulseLength);
1017 | }
1018 | }
1019 |
1020 | /**
1021 | * Sends a "1" Bit
1022 | * ___
1023 | * Waveform Protocol 1: | |_
1024 | * __
1025 | * Waveform Protocol 2: | |_
1026 | */
1027 | void RCSwitch_::send1() {
1028 | if (this->nProtocol == 1){
1029 | this->rcs->transmit(3*this->nPulseLength,1*this->nPulseLength);
1030 | }
1031 | else if (this->nProtocol == 2) {
1032 | this->rcs->transmit(2*this->nPulseLength,1*this->nPulseLength);
1033 | }
1034 | }
1035 |
1036 |
1037 | /**
1038 | * Sends a Tri-State "0" Bit
1039 | * _ _
1040 | * Waveform: | |___| |___
1041 | */
1042 | void RCSwitch_::sendT0() {
1043 | this->rcs->transmit(1*this->nPulseLength,3*this->nPulseLength);
1044 | this->rcs->transmit(1*this->nPulseLength,3*this->nPulseLength);
1045 | }
1046 |
1047 | /**
1048 | * Sends a Tri-State "1" Bit
1049 | * ___ ___
1050 | * Waveform: | |_| |_
1051 | */
1052 | void RCSwitch_::sendT1() {
1053 | this->rcs->transmit(3*this->nPulseLength,1*this->nPulseLength);
1054 | this->rcs->transmit(3*this->nPulseLength,1*this->nPulseLength);
1055 | }
1056 |
1057 | /**
1058 | * Sends a Tri-State "F" Bit
1059 | * _ ___
1060 | * Waveform: | |___| |_
1061 | */
1062 | void RCSwitch_::sendTF() {
1063 | this->rcs->transmit(1*this->nPulseLength,3*this->nPulseLength);
1064 | this->rcs->transmit(3*this->nPulseLength,1*this->nPulseLength);
1065 | }
1066 |
1067 | /**
1068 | * Sends a "Sync" Bit
1069 | * _
1070 | * Waveform Protocol 1: | |_______________________________
1071 | * _
1072 | * Waveform Protocol 2: | |__________
1073 | */
1074 | void RCSwitch_::sendSync() {
1075 |
1076 | if (this->nProtocol == 1){
1077 | this->rcs->transmit(1*this->nPulseLength,31*this->nPulseLength);
1078 | }
1079 | else if (this->nProtocol == 2) {
1080 | this->rcs->transmit(1*this->nPulseLength,10*this->nPulseLength);
1081 | }
1082 | }
1083 |
1084 |
1085 | /* ========================================================================
1086 | * Phenix YC-4000S- Remote switch working with RCSWitch protcol
1087 | * --------------------------------------------------------------
1088 | * 24b data with
1089 | * First 10b are remote ID possible values for nibbles are 01 and 00 only
1090 | * Next 10b identify the outlet : nibble 00 select the outlet, other 01
1091 | * Last 4b 0001 => switch ON 0100 => switch OFF
1092 | * Tranmission sometime finish with FF => not clear if poor quality remote bug or feature
1093 | * example
1094 | */
1095 |
1096 | /**
1097 | * Code format : 0x000551 => mask last bit to have 0001
1098 | * Change data stream to switch remote control ON
1099 | */
1100 | void Phenix::getSwitchON(char * code) {
1101 | if (code != NULL && strlen(code) == 8 ) {
1102 | code[7] = '1';
1103 | } else {
1104 | printf("Phenix::getSwitchON - format problem(%d/8)\n",(code!=NULL)?strlen(code):-1);
1105 | }
1106 | }
1107 |
1108 | /**
1109 | * Code format : 0x000551 => mask last bit to have 0100
1110 | * Change data stream to switch remote control OFF
1111 | */
1112 | void Phenix::getSwitchOFF(char * code) {
1113 | if (code != NULL && strlen(code) == 8 ) {
1114 | code[7] = '4';
1115 | } else {
1116 | printf("Phenix::getSwitchOFF - format problem(%d/8)\n",(code!=NULL)?strlen(code):-1);
1117 | }
1118 | }
1119 |
1120 | /**
1121 | * Validate code format to be compared with Phenix type
1122 | * format 0x000551
1123 | */
1124 |
1125 | bool Phenix::checkCode(char * code) {
1126 | bool ret = true;
1127 | if (code != NULL && strlen(code) == 8 ) {
1128 | if ( code[0] != '0' ) ret = false;
1129 | if ( code[1] != 'x' ) ret = false;
1130 | for ( int i = 2 ; i < 8 ; i++ ) {
1131 | if ( code[i] != '0' && code[i] != '1' && code[i] != '4' && code[i] != '5' ) ret = false;
1132 | }
1133 | return ret;
1134 | } else {
1135 | printf("Phenix::getSwitchOFF - format problem(%d/8)\n",(code!=NULL)?strlen(code):-1);
1136 | }
1137 | return false;
1138 | }
1139 |
--------------------------------------------------------------------------------
/src/RcOok.d:
--------------------------------------------------------------------------------
1 | RcOok.o: RcOok.cpp RCSwitch.h RcOok.h tools.h
2 |
--------------------------------------------------------------------------------
/src/RcOok.h:
--------------------------------------------------------------------------------
1 | /* ==========================================================================
2 | * RcOoK.h
3 | * --------------------------------------------------------------------------
4 | * RF433 demonstrator for rfrpi Raspberry PI shield
5 | * see : http://www.disk91.com/?p=1323
6 | * --------------------------------------------------------------------------
7 | * This software is under GPLv3
8 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
9 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
10 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
11 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
12 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
13 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
14 | * THE SOFTWARE.
15 | * --------------------------------------------------------------------------
16 | * Modified on Jan 15 2015
17 | * Added OSV3 support for OWL CMR180 Energy sensor -- Onlinux (onlinux.fr)
18 | *
19 | * Created on: 23 Feb. 2014
20 | * Author: disk91 - Paul Pinault (c) 2014
21 | * --------------------------------------------------------------------------
22 | * 433 Mhz decoding OoK frame from Oregon Scientific
23 | *
24 | * Created on: 16 sept. 2013
25 | * Author: disk91 modified from
26 | * Oregon V2 decoder added - Dominique Pierre
27 | * Oregon V3 decoder revisited - Dominique Pierre
28 | * RwSwitch : Copyright (c) 2011 Suat Özgür. All right reserved.
29 | * Contributors:
30 | * - Andre Koehler / info(at)tomate-online(dot)de
31 | * - Gordeev Andrey Vladimirovich / gordeev(at)openpyro(dot)com
32 | * - Skineffect / http://forum.ardumote.com/viewtopic.php?f=2&t=48
33 | * Project home: http://code.google.com/p/rc-switch/
34 | *
35 | * New code to decode OOK signals from weather sensors, etc.
36 | * 2010-04-11 http://opensource.org/licenses/mit-license.php
37 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
38 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
39 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
40 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
41 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
42 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
43 | * THE SOFTWARE.
44 | * --------------------------------------------------------------------------
45 | */
46 |
47 | #ifndef RCOOK_H_
48 | #define RCOOK_H_
49 |
50 | typedef uint16_t word;
51 | typedef uint8_t byte;
52 |
53 | #define OOK_MAX_DATA_LEN 25
54 | #define OOK_MAX_STR_LEN 100
55 |
56 | class DecodeOOK {
57 | protected:
58 | byte total_bits, bits, flip, state, pos, data[OOK_MAX_DATA_LEN];
59 | int nRepeatTransmit;
60 | RCSwitch * rcs; // transmit function to call
61 |
62 | virtual int decode (word width) =0;
63 | virtual void send0();
64 | virtual void send1();
65 | virtual void sendSync();
66 |
67 | char * dec2binWzerofill(unsigned int Dec, unsigned int bitLength);
68 |
69 | public:
70 |
71 | enum { UNKNOWN, T0, T1, T2, T3, OK, DONE };
72 |
73 | DecodeOOK(RCSwitch * _rcs);
74 | virtual ~DecodeOOK();
75 |
76 | bool nextPulse (word width);
77 | bool isDone() const;
78 |
79 | const byte* getData (byte& count) const;
80 | void resetDecoder ();
81 |
82 | // store a bit using Manchester encoding
83 | void manchester (char value);
84 |
85 | // move bits to the front so that all the bits are aligned to the end
86 | void alignTail (byte max =0);
87 | void reverseBits ();
88 | void reverseNibbles ();
89 | void reverseData ();
90 | void done ();
91 | void print (const char* s);
92 | void sprint(const char * s, char * d);
93 |
94 | virtual void gotBit (char value);
95 |
96 | void send(unsigned int Code, unsigned int length);
97 | void send(char * sHexStr, unsigned int length);
98 | void send(char* sCodeWord);
99 |
100 |
101 | };
102 |
103 |
104 |
105 |
106 | class OregonDecoderV2 : public DecodeOOK {
107 | public:
108 | OregonDecoderV2(RCSwitch * _rcs);
109 | ~OregonDecoderV2();
110 | void gotBit (char value);
111 | int decode (word width);
112 | };
113 |
114 |
115 | class OregonDecoderV3 : public DecodeOOK {
116 | public:
117 | OregonDecoderV3(RCSwitch * _rcs);
118 | ~OregonDecoderV3();
119 | void gotBit (char value);
120 | int decode (word width);
121 | };
122 |
123 | class CrestaDecoder : public DecodeOOK {
124 | public:
125 | CrestaDecoder(RCSwitch * _rcs);
126 | ~CrestaDecoder();
127 | int decode (word width);
128 | };
129 |
130 | class KakuDecoder : public DecodeOOK {
131 | public:
132 | KakuDecoder(RCSwitch * _rcs);
133 | ~KakuDecoder();
134 | int decode (word width);
135 | };
136 |
137 | class XrfDecoder : public DecodeOOK {
138 | public:
139 | XrfDecoder(RCSwitch * _rcs);
140 | ~XrfDecoder();
141 | int decode (word width);
142 | };
143 |
144 | class HezDecoder : public DecodeOOK {
145 | public:
146 | HezDecoder(RCSwitch * _rcs);
147 | ~HezDecoder();
148 | int decode (word width);
149 | };
150 |
151 | class VisonicDecoder : public DecodeOOK {
152 | public:
153 | VisonicDecoder(RCSwitch * _rcs);
154 | ~VisonicDecoder();
155 | int decode (word width);
156 | };
157 |
158 | class EMxDecoder : public DecodeOOK {
159 | public:
160 | EMxDecoder(RCSwitch * _rcs);
161 | ~EMxDecoder();
162 | int decode (word width);
163 | };
164 |
165 | class KSxDecoder : public DecodeOOK {
166 | public:
167 | KSxDecoder(RCSwitch * _rcs);
168 | ~KSxDecoder();
169 | int decode (word width);
170 | };
171 |
172 | class FSxDecoder : public DecodeOOK {
173 | public:
174 | FSxDecoder(RCSwitch * _rcs);
175 | ~FSxDecoder();
176 | int decode (word width);
177 | };
178 |
179 |
180 | class DIO : public DecodeOOK {
181 | public:
182 | DIO(RCSwitch * _rcs);
183 | ~DIO();
184 | int decode (word width);
185 | void done ();
186 |
187 | static void getSwitchON(char * code);
188 | static void getSwitchOFF(char * code);
189 |
190 | protected:
191 | void send0();
192 | void send1();
193 | void sendSync();
194 |
195 | // unsigned int timings[256];
196 | // unsigned int changeCount;
197 |
198 | };
199 |
200 |
201 | // Number of maximum High/Low changes per packet.
202 | // We can handle up to (unsigned long) => 32 bit * 2 H/L changes per bit + 2 for sync
203 | // Disk91 - change to use 40 bit max
204 | // #define RCSWITCH_MAX_CHANGES 67
205 | #define RCSWITCH_MAX_CHANGES 83
206 |
207 | class RCSwitch_ : public DecodeOOK {
208 | public:
209 | RCSwitch_(int protocol, RCSwitch * _rcs);
210 | ~RCSwitch_();
211 | int decode (word duration);
212 |
213 | protected:
214 | int nPulseLength;
215 | char nProtocol;
216 | int nReceiveTolerance;
217 | unsigned int timings[RCSWITCH_MAX_CHANGES];
218 | unsigned int changeCount;
219 |
220 |
221 | void setPulseLength(int nPulseLength);
222 | void setRepeatTransmit(int nRepeatTransmit);
223 | void setReceiveTolerance(int nPercent);
224 | void setProtocol(int nProtocol);
225 | void setProtocol(int nProtocol, int nPulseLength);
226 |
227 | bool receiveProtocol1(unsigned int changeCount);
228 | bool receiveProtocol2(unsigned int changeCount);
229 |
230 | void sendT0();
231 | void sendT1();
232 | void sendTF();
233 | void send0();
234 | void send1();
235 | void sendSync();
236 |
237 | };
238 |
239 | class Phenix : public RCSwitch_ {
240 |
241 | public:
242 | static void getSwitchON(char * code);
243 | static void getSwitchOFF(char * code);
244 | static bool checkCode(char * code);
245 |
246 | };
247 |
248 | #endif /* RCOOK_H_ */
249 |
--------------------------------------------------------------------------------
/src/Sensor.cpp:
--------------------------------------------------------------------------------
1 | /* ==========================================================================
2 | * Sensor.cpp
3 | * --------------------------------------------------------------------------
4 | * RF433 demonstrator for rfrpi Raspberry PI shield
5 | * see : http://www.disk91.com/?p=1323
6 | * --------------------------------------------------------------------------
7 | * This software is under GPLv3
8 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
9 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
10 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
11 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
12 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
13 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
14 | * THE SOFTWARE.
15 | * --------------------------------------------------------------------------
16 | * Modified on Jan 15 2015
17 | * Added OSV3 support for OWL CMR180 Energy sensor -- Onlinux (onlinux.fr)
18 | *
19 | * Created on: 23 Feb. 2014
20 | * Author: disk91 - Paul Pinault (c) 2014
21 | *
22 | * Other code providers :
23 | * ericnosense AT hotmail PT com - added
24 | * --------------------------------------------------------------------------
25 | * This Class is decoding the different type of sensors data
26 | * --------------------------------------------------------------------------
27 | */
28 | #include
29 | #include
30 | #include
31 | #include
32 | #include "Sensor.h"
33 |
34 | //#define SENSORDEBUG // Large debug trace
35 | //#define SENSORTRACE // Small debug trace to verify error only
36 |
37 | char Sensor::_hexDecod[16] = { '0', '1', '2', '3', '4', '5', '6', '7',
38 | '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
39 |
40 | // ------------------------------------------------------
41 | // Construction - init variable then call decode function
42 | Sensor::Sensor( char * _strval ) {
43 |
44 | this->power = 0.0;
45 | this->powerTotal = 0.0;
46 | this->rawpower = 0.0;
47 | this->temperature = 0.0;
48 | this->humidity = 0.0;
49 | this->speed = 0.0;
50 | this->direction = -1.0;
51 | this->rain = 0.0;
52 | // this->train = 0.0;
53 | this->pressure = 0.0;
54 |
55 | this->channel = -1;
56 | this->battery = false;
57 |
58 | this->havePower = false;
59 | this->haveRawPower = false;
60 | this->haveTemperature = false;
61 | this->haveHumidity = false;
62 | this->haveBattery = false;
63 | this->isValid = false;
64 | this->haveChannel = false;
65 | this->haveSpeed = false;
66 | this->haveDirection = false;
67 | this->haveRain= false;
68 | // this->haveTrain = false;
69 | this->havePressure = false;
70 |
71 | this->sensorType = -1;
72 | this->sensorClass = SENS_CLASS_NONE;
73 | this->sensorName[0] = '\0';
74 | this->packet[0] = '\0';
75 |
76 | time(&this->creationTime);
77 |
78 | }
79 |
80 | // ---------------------------------------------------
81 | // availablePower() - return true if valid && have Power
82 | bool Sensor::availablePower(){
83 | return (this->isValid && this->havePower);
84 | }
85 | // ---------------------------------------------------
86 | // availableRawPower() - return true if valid && have Power
87 | bool Sensor::availableRawPower(){
88 | return (this->isValid && this->haveRawPower);
89 | }
90 | // ---------------------------------------------------
91 | // availableTemp() - return true if valid && have Temp
92 | bool Sensor::availableTemp(){
93 | return (this->isValid && this->haveTemperature);
94 | }
95 | // ---------------------------------------------------
96 | // availableHumidity() - return true if valid && have Humidity
97 | bool Sensor::availableHumidity() {
98 | return (this->isValid && this->haveHumidity);
99 | }
100 |
101 | // ---------------------------------------------------
102 | // availablePressure() – return true if valid && have wind speed
103 | bool Sensor::availablePressure(){
104 | return (this->isValid && this->havePressure);
105 | }
106 |
107 | // ---------------------------------------------------
108 | // availableSpeed() – return true if valid && have wind speed
109 | bool Sensor::availableSpeed(){
110 | return (this->isValid && this->haveSpeed);
111 | }
112 |
113 | // ---------------------------------------------------
114 | // availableDirection() – return true if valid && have wind direction
115 | bool Sensor::availableDirection(){
116 | return (this->isValid && this->haveDirection);
117 | }
118 |
119 | // ---------------------------------------------------
120 | // availableRain() – return true if valid && have Rain
121 | bool Sensor::availableRain(){
122 | return (this->isValid && this->haveRain);
123 | }
124 |
125 |
126 | // ---------------------------------------------------
127 | // isBatteryLow() - return true if valid && haveBattery && flag set
128 | bool Sensor::isBatteryLow(){
129 | return (this->isValid && this->haveBattery && this->battery);
130 | }
131 | // ---------------------------------------------------
132 | // getPower() - return Power in kW
133 | double Sensor::getPower(){
134 | return this->power;
135 | }
136 | // ---------------------------------------------------
137 | // getRawPower() - return raw power in kW
138 | double Sensor::getRawPower(){
139 | return this->rawpower;
140 | }
141 | // ---------------------------------------------------
142 | // getPowerTotal() - return Power in kW
143 | double Sensor::getPowerTotal(){
144 | return this->powerTotal;
145 | }
146 | // ---------------------------------------------------
147 | // getTemperature() - return temperature in C°
148 | double Sensor::getTemperature(){
149 | return this->temperature;
150 | }
151 | // ---------------------------------------------------
152 | // getHumidity() - return humidity in % (base 100)
153 | double Sensor::getHumidity() {
154 | return this->humidity;
155 | }
156 | // ---------------------------------------------------
157 | // getPressure() – return Wind speed in Mb
158 | double Sensor::getPressure(){
159 | return this->pressure;
160 | }
161 | // ---------------------------------------------------
162 | // getSpeed() – return Wind speed in Degre°
163 | double Sensor::getSpeed(){
164 | return this->speed;
165 | }
166 | // ---------------------------------------------------
167 | // getDirection() – return Wind direction in Degre°
168 | double Sensor::getDirection(){
169 | return this->direction;
170 | }
171 | // ---------------------------------------------------
172 | // getRain() – return Rain in mm/h
173 | double Sensor::getRain(){
174 | return this->rain;
175 | }
176 |
177 | // ---------------------------------------------------
178 | // haveChannel() - return true if valid && haveChannel
179 | bool Sensor::hasChannel() {
180 | return ( this->isValid && this->haveChannel );
181 | }
182 | // ---------------------------------------------------
183 | // isDecoded() - return true if valid
184 | bool Sensor::isDecoded() {
185 | return ( this->isValid );
186 | }
187 |
188 | // ---------------------------------------------------
189 | // getChannel() - return channel (1,2,3)
190 | int Sensor::getChannel() {
191 | return this->channel;
192 | }
193 |
194 | // ---------------------------------------------------
195 | // getCreationTime() - return object creation time
196 | time_t Sensor::getCreationTime() {
197 | return this->creationTime;
198 | }
199 |
200 | // ---------------------------------------------------
201 | // getSensClass() - return sensor class
202 | int Sensor::getSensClass() {
203 | return this->sensorClass;
204 | }
205 |
206 | // ---------------------------------------------------
207 | // getSensType() - return sensor type
208 | int Sensor::getSensType() {
209 | return this->sensorType;
210 | }
211 |
212 | // ---------------------------------------------------
213 | // getSensName() - return sensor name
214 | char * Sensor::getSensName() {
215 | return this->sensorName;
216 | }
217 | // ---------------------------------------------------
218 | // getPacket() - return packet string
219 | char * Sensor::getPacket() {
220 | return this->packet;
221 | }
222 | // ---------------------------------------------------
223 | // getIntFromChar() - (-1 if error)
224 | int Sensor::getIntFromChar(char c) {
225 | for ( int i=0 ; i < 16 ; i++ )
226 | if ( _hexDecod[i] == c ) return i;
227 | return -1;
228 | }
229 |
230 | // ---------------------------------------------------
231 | // getIntFromString() - get an unsigned int value from
232 | // the given string. -1 if error
233 | int Sensor::getIntFromString(char * s) {
234 | int r = 0;
235 | while ( *s != '\0' ) {
236 | r *= 16;
237 | int t = getIntFromChar(*s);
238 | if ( t == -1 ) return -1;
239 | r += t;
240 | s++;
241 | }
242 | return r;
243 | }
244 |
245 | // --------------------------------------------------
246 | // getDoubleFromString() - get double value related to
247 | // BCD encoded string XXX = XX.X
248 | double Sensor::getDoubleFromString(char * s) {
249 | int r = 0;
250 | while ( *s != '\0' ) {
251 | r *= 10;
252 | int t = getIntFromChar(*s);
253 | if ( t == -1 || t >= 10 ) return -1;
254 | r += t;
255 | s++;
256 | }
257 | return (double)( r / 10.0 );
258 | }
259 |
260 |
261 | // ==================================================================
262 |
263 | // ------------------------------------------------------
264 | // Build the right sensor type by identifying the
265 | // header code
266 | Sensor * Sensor::getRightSensor(char * s) {
267 | int len = strlen(s);
268 | if ( len > 4 ) {
269 | #ifdef SENSORDEBUG
270 | printf("Sensor::getRightSensor - create of (%s)\n",s);
271 | #endif
272 |
273 | if ( s[0] == 'O' && s[1] == 'S' && s[2] == 'V' && s[3] == '2') {
274 | #ifdef SENSORDEBUG
275 | printf("Sensor::getRightSensor - create OregonSensorV2\n");
276 | #endif
277 | OregonSensorV2 * r = new OregonSensorV2(s);
278 | return (Sensor *) r;
279 | } else if ( s[0] == 'O' && s[1] == 'S' && s[2] == 'V' && s[3] == '3'){
280 | #ifdef SENSORDEBUG
281 | printf("Sensor::getRightSensor - create OregonSensorV3\n");
282 | #endif
283 | OregonSensorV3 * r = new OregonSensorV3(s);
284 | return (Sensor *) r;
285 | }
286 | }
287 | return NULL;
288 | }
289 |
290 | // ==================================================================
291 |
292 | OregonSensorV3::OregonSensorV3(char * _strval) : Sensor(_strval) {
293 | this->sensorClass = SENS_CLASS_OS;
294 | this->isValid = this->decode(_strval);
295 | }
296 | bool OregonSensorV3::decode( char * _str ) {
297 | char * pt = & _str[5];
298 | int len = strlen(_str);
299 | char sensorId[3]; int isensorId;
300 |
301 | // Proceed the right sensor
302 | if ( len > 11 ) {
303 | sensorId[0] = pt[0];sensorId[1] = pt[1];
304 | //sensorId[2] = pt[2];
305 | //sensorId[3] = pt[3];
306 | sensorId[2]='\0';
307 | isensorId = getIntFromString(sensorId);
308 | #ifdef SENSORDEBUG
309 | printf("OSV3 - decode : id(%s)(0x%2X) length:%iB\n",sensorId, isensorId, len);
310 | #endif
311 |
312 | switch (isensorId) {
313 | case 0x62:
314 | strcpy(this->sensorName,"OWL micro+");
315 | this->sensorType=0x62;
316 | return decode_OWLCM180(pt); break;
317 |
318 | default:
319 | return false;
320 | }
321 | }
322 | return false;
323 | }
324 |
325 | bool OregonSensorV3::decode_OWLCM180(char * pt) {
326 | // OSV3 6284 3C 7801 D0
327 | // ^??
328 | // OSV3 6280 3C 2801 A0 A8 BA 05 00 00 ?? ?? ?? trame principale
329 | // pt^ p^ t^
330 | char * p = &pt[6];
331 | char * t = &pt[10];
332 | char power[5]; int ipower;
333 | int len = strlen(pt);
334 | char total[13];
335 | unsigned long long dtotal=0;
336 |
337 |
338 | power[0]=p[2]; power[1]=p[3]; power[2]=p[0]; power[3]=p[1]; power[4] ='\0';
339 | ipower= getIntFromString(power);
340 | if (len > 12 ) {
341 | // Trame principale avec cumul en W
342 |
343 | total[0]=t[10]; total[1]=t[11];
344 | total[2]=t[8]; total[3]=t[9];
345 | total[4]=t[6]; total[5]=t[7];
346 | total[6]=t[4]; total[7]=t[5];
347 | total[8]=t[2]; total[9]=t[3];
348 | total[10]=t[0]; total[11]=t[1];
349 | total[12]='\0';
350 |
351 | dtotal = strtoull(total, NULL, 16)/3600;// Watt heure Wh
352 | this->havePowerTotal = true;
353 | this->powerTotal = dtotal;
354 | }
355 | this->havePower = true;
356 | this->haveRawPower = true;
357 | this->rawpower = ipower;
358 |
359 | strncpy(this->packet, pt, 128);
360 |
361 | if (ipower > 2000)
362 | this->power = ipower -4;
363 | else
364 | this->power = ipower -8; // When main power is off (0 Watt) OWl sensor sends 8W (0800)-> (0x0008)
365 | //this->power = ipower | 0xFFF7;
366 | #ifdef SENSORDEBUG
367 | if (len > 12)
368 | printf("OSV3 - decode : id(%s) (%s) power(%s)(0x%04X)%iW %iW total(0x%X) %uW %uWh\n ",
369 | " 6280", this->packet, power, ipower, ipower, this->power, itotal, itotal, dtotal);
370 | else
371 | printf("OSV3 - decode : id(%s) power(%s)(0x%04X)%iW %iW\n ",
372 | " 6280", power, ipower, ipower, this->power);
373 | #endif
374 | return true;
375 | }
376 |
377 |
378 | // ==================================================================
379 |
380 | OregonSensorV2::OregonSensorV2(char * _strval) : Sensor(_strval) {
381 | this->sensorClass = SENS_CLASS_OS;
382 | this->isValid = this->decode(_strval);
383 | }
384 |
385 | // ---------------------------------------------------------------------------------------
386 | // []
387 |
388 | // ---------------------------------------------------------------------------------------
389 | // Decode OregonScientific V2 protocol for specific
390 | // Oregon Devices
391 | //
392 | // Support :
393 | // - THGR122NX : Temp + Humidity
394 | // OSV2 1A2D1002 502060552A4C
395 | // In correct order :
396 | // OSV2 A 1D20 1 20 0 502 0 655 2A 4C
397 | // OSV2 => decoding header
398 | // A => Sync header
399 | // 1D20 => THGR122NX ID
400 | // - THN132N : Temp
401 | // OSV2 EA4C20809822D013
402 | // In correct order :
403 | // OSV2 A EC40 2 08 8 922 D0 13
404 | //
405 | // - BTHG968 : type : 0x5D60
406 | // - THGRN228NX : type 0x1D30
407 | // - WGR918 : type 0x3D00
408 | // - RGR918 : type 0x2D10
409 | //
410 | // ------------------------------------------------------------------------------------------
411 | bool OregonSensorV2::decode( char * _str ) {
412 | char * pt = & _str[5];
413 | int len = strlen(_str);
414 | char sensorId[5]; int isensorId;
415 |
416 | // Proceed the right sensor
417 | if ( len > 11 ) {
418 | sensorId[0] = pt[0];sensorId[1] = pt[3];sensorId[2] = pt[2];sensorId[3] = pt[5];sensorId[4]='\0';
419 | isensorId = getIntFromString(sensorId);
420 | #ifdef SENSORDEBUG
421 | printf("OSV2 - decode : id(%s)(0x%4X)\n",sensorId, isensorId);
422 | #endif
423 |
424 | switch (isensorId) {
425 | case 0x1D20:
426 | strcpy(this->sensorName,"THGR122NX");
427 | this->sensorType=0x1D20;
428 | return decode_THGR122NX(pt); break;
429 | case 0xEC40:
430 | strcpy(this->sensorName,"THN132N");
431 | this->sensorType=0xEC40;
432 | return decode_THN132N(pt); break;
433 | case 0x5D60:
434 | strcpy(this->sensorName,"BTHG968");
435 | return decode_BTHG968(pt); break;
436 | case 0x1D30:
437 | strcpy(this->sensorName,"THGRN228NX");
438 | return decode_THGRN228NX(pt); break;
439 | case 0x3D00:
440 | strcpy(this->sensorName,"WGR918");
441 | return decode_WGR918(pt); break;
442 | case 0x2D10:
443 | strcpy(this->sensorName,"RGR918");
444 | return decode_RGR918(pt); break;
445 | default:
446 | return false;
447 |
448 | }
449 |
450 | }
451 | return false;
452 | }
453 |
454 | // --------------------------------------------------------------------------
455 | // Decode OregonScientific V2 protocol for specific
456 | // Oregon Devices
457 | // – BTHG968 temperature + hygro + pression atmospherique
458 | //
459 | // NOTE From disk91 - I have never tested this code
460 | // --------------------------------------------------------------------------
461 | bool OregonSensorV2::decode_BTHG968(char * pt) {
462 |
463 | char temp[4]; double dtemp; // Temp in BCD
464 | char tempS; int itempS; // Sign 0 = positif
465 | char humid[4]; double dhumid; // Humid in BCD
466 | char pressure[3]; double ipressure;
467 |
468 | char crc[3]; int icrc;
469 | int len = strlen(pt);
470 |
471 | if ( len == 20 ) {
472 | temp[0] = pt[10] ; temp[1] = pt[11] ; temp[2] = pt[8] ; temp[3] = '\0';
473 | tempS = pt[13];
474 | humid[0] = pt[15] ; humid[1] = pt[12]; humid[2] = '0' ; humid[3] = '\0';
475 | pressure[0]=pt[16];pressure[1]=pt[17];pressure[2]= '\0';
476 | crc[0] = pt[18] ; crc[1] = pt[19] ; crc[2] = '\0';
477 |
478 | #ifdef SENSORDEBUG
479 | printf(" OSV2 – decode : id(%s) temp(%s) sign(%c) humid(%s) pressure(%s) crc(%s)\n ",
480 | " 5D60",temp,tempS,humid, pressure, crc);
481 | #endif
482 |
483 | // Conversion to int value
484 | itempS = getIntFromChar(tempS) & 0x08;
485 | icrc = getIntFromString(crc);
486 | dtemp = getDoubleFromString(temp);
487 | dhumid = getDoubleFromString(humid);
488 | ipressure = getIntFromString(pressure);
489 |
490 | #ifdef SENSORDEBUG
491 | printf(" OSV2 – decode : id(0x%04X) temp(%f) sign(%d) humid(%d) pressure(%d) cksum(0x%02X) crc(0x%02X)\n ",
492 | 0x5D60,dtemp,itempS,dhumid, ipressure, icrc);
493 | #endif
494 |
495 | // Check SUM & CRC
496 | // if ( validate(pt,16,icrc,ichecksum) == true ) {
497 |
498 | // now we can decode the important flag and fill the object
499 | this->haveTemperature = true;
500 | this->temperature = (itempS == 0)?dtemp:-dtemp;
501 | this->haveHumidity = true;
502 | this->humidity = dhumid;
503 | this->havePressure = true;
504 | this->pressure = (856 + ipressure);
505 |
506 | return true;
507 | // } else return false;
508 |
509 | }
510 | return false;
511 | }
512 |
513 | // ---------------------------------------------------------------------
514 | // Decode OregonScientific V2 protocol for specific
515 | // Oregon Devices
516 | // - RGR918 PLUVIOMETRE
517 | //
518 | // NOTE From disk91 - I have never tested this code
519 | //
520 | // ---------------------------------------------------------------------
521 |
522 | bool OregonSensorV2::decode_RGR918(char * pt) {
523 |
524 | char rain[4]; double irain; // Temp
525 | char checksum[19]; int ichecksum;
526 | char crc[3]; int icrc;
527 | int len = strlen(pt);
528 |
529 | if ( len == 20 ) {
530 | //channel = pt[4];
531 | rain[0] = pt[10] ; rain[1] = pt[11] ; rain[2] = pt[8] ; rain[3] = '\0';
532 | checksum[0] = pt[18]; checksum[1] = pt[19]; checksum[2] = '\0';
533 | crc[0] = pt[18] ; crc[1] = pt[19] ; crc[2] = '\0';
534 |
535 | #ifdef SENSORDEBUG
536 |
537 | printf(" OSV2 – decode raw: id(%s) rain(%s) cksum(%s) crc(%s)\n ",
538 | " 2D10",rain,checksum,crc);
539 | #endif
540 | // Conversion to int value
541 | ichecksum = getIntFromString(checksum);
542 | irain = getDoubleFromString(rain);
543 | icrc = getIntFromString(crc);
544 | #ifdef SENSORDEBUG
545 | printf(" OSV2 – decode : id(0x%04X) rain(%f) cksum(0x%02X) crc(0x%02X) \n ",
546 | 0x2D10,irain,ichecksum,icrc);
547 | #endif
548 |
549 | // Check SUM & CRC
550 | // if ( validate(pt,16,icrc,ichecksum) == true ) {
551 |
552 | // now we can decode the important flag and fill the object
553 | this->haveRain = true;
554 | this->rain = (10 * irain);
555 | return true;
556 |
557 | // } else return false;
558 |
559 | }
560 | return false;
561 | }
562 |
563 | // ---------------------------------------------------------------------
564 | // Decode OregonScientific V2 protocol for specific
565 | // Oregon Devices
566 | // - WGR918 ANEMOMETER
567 | //
568 | // NOTE From disk91 - I have never tested this code
569 | //
570 | // ---------------------------------------------------------------------
571 | bool OregonSensorV2::decode_WGR918(char * pt) {
572 |
573 | char dir[4]; double idir; // direction en degres
574 | char speed[4]; double ispeed; // vitesse en km/h
575 | char checksum[3]; int ichecksum;
576 | char crc[3]; int icrc;
577 |
578 | int len = strlen(pt);
579 |
580 | if ( len == 20 ) {
581 | dir[0] = pt[10] ; dir[1] = pt[11] ; dir[2] = pt[8] ; dir[3] = '\0';
582 | speed[0] = pt[12] ; speed[1] = pt[13]; speed[2] = pt[14]; speed[3] = '\0';
583 | checksum[0] = pt[16]; checksum[1] = pt[17]; checksum[2] = '\0';
584 | crc[0] = pt[18] ; crc[1] = pt[19] ; crc[2] = '\0';
585 | #ifdef SENSORDEBUG
586 | printf(" OSV2 – decode : id(%s) dir(%s) speed(%s) cksum(%s) crc(%s)\n ",
587 | " 3D00",dir,speed, checksum, crc);
588 | #endif
589 |
590 | // Conversion to int value
591 | ichecksum = getIntFromString(checksum);
592 | icrc = getIntFromString(crc);
593 | idir = getDoubleFromString(dir);
594 | ispeed = getDoubleFromString(speed);
595 |
596 | #ifdef SENSORDEBUG
597 | printf(" OSV2 – decode : id(0x%04X) dir(%f) speed(%f) cksum(0x%02X) crc(0x%02X)\n ",
598 | 0x3D00,idir,ispeed, ichecksum,icrc);
599 | #endif
600 |
601 | // Check SUM & CRC
602 | // if ( validate(pt,16,icrc,ichecksum) == true ) {
603 |
604 | // now we can decode the important flag and fill the object
605 | this->haveDirection = true;
606 | this->direction = (10 * idir);
607 | this->haveSpeed = true;
608 | this->speed = (0.1 * ispeed)* 3.6;
609 | return true;
610 | // } else return false;
611 |
612 | }
613 | return false;
614 | }
615 |
616 | // ---------------------------------------------------------------------
617 | // Decode OregonScientific V2 protocol for specific
618 | // Oregon Devices
619 | // - THGRN228NX : Temp + Humidity
620 | //
621 | // NOTE From disk91 - I have never tested this code
622 | //
623 | // ---------------------------------------------------------------------
624 | bool OregonSensorV2::decode_THGRN228NX(char * pt) {
625 |
626 | char channel; int ichannel; // values 1,2,4
627 | char rolling[3]; int irolling;
628 | char battery; int ibattery; // value & 0×4
629 | char temp[4]; double dtemp; // Temp in BCD
630 | char tempS; int itempS; // Sign 0 = positif
631 | char humid[4]; double dhumid; // Humid in BCD
632 | char checksum[3]; int ichecksum;
633 | char crc[3]; int icrc;
634 | int len = strlen(pt);
635 |
636 | if ( len == 20 ) {
637 | channel = pt[4];
638 | rolling[0]=pt[7]; rolling[1]=pt[6]; rolling[2]= '\0';
639 | battery = pt[9];
640 | temp[0] = pt[10] ; temp[1] = pt[11] ; temp[2] = pt[8] ; temp[3] = '\0';
641 | tempS = pt[13];
642 | humid[0] = pt[15] ; humid[1] = pt[12]; humid[2] = '0' ; humid[3] = '\0';
643 | checksum[0] = pt[16]; checksum[1] = pt[17]; checksum[2] = '\0';
644 | crc[0] = pt[18] ; crc[1] = pt[19] ; crc[2] = '\0';
645 |
646 | #ifdef SENSORDEBUG
647 | printf(" OSV2 – decode : id(%s) ch(%c) bat(%c) temp(%s) sign(%c) humid(%s) cksum(%s) crc(%s)\n ",
648 | " 1D30",channel,battery,temp,tempS,humid, checksum, crc);
649 | #endif
650 |
651 | // Conversion to int value
652 | ichannel = getIntFromChar(channel);
653 | irolling = getIntFromString(rolling);
654 | ibattery = getIntFromChar(battery);
655 | itempS = getIntFromChar(tempS) & 0x08;
656 | ichecksum = getIntFromString(checksum);
657 | icrc = getIntFromString(crc);
658 | dtemp = getDoubleFromString(temp);
659 | dhumid = getDoubleFromString(humid);
660 |
661 | #ifdef SENSORDEBUG
662 | printf(" OSV2 – decode : id(0x%04X) ch(%d) bat(%d) temp(%f) sign(%d) humid(%f) cksum(0x%02X) crc(0x%02X)\n ",
663 | 0x1D30,ichannel,ibattery,dtemp,itempS,dhumid, ichecksum, icrc);
664 | #endif
665 |
666 | // Check SUM & CRC
667 | if ( validate(pt,16,icrc,ichecksum) == true ) {
668 |
669 | // now we can decode the important flag and fill the object
670 | this->haveChannel = true;
671 | this->channel = (ichannel != 4)?ichannel:3;
672 | this->haveBattery = true;
673 | this->battery = (ibattery & 0x4);
674 | this->haveTemperature = true;
675 | this->temperature = (itempS == 0)?dtemp:-dtemp;
676 | this->haveHumidity = true;
677 | this->humidity = dhumid;
678 | return true;
679 | } else return false;
680 |
681 | }
682 | return false;
683 | }
684 |
685 |
686 | // ---------------------------------------------------------------------------------------
687 | // Decode OregonScientific V2 protocol for specific
688 | // Oregon Devices
689 | // - THN132N : Temp
690 | // OSV2 EA4C2080 9822 D013 22.8°C
691 | // OSV2 EA4C2080 5208 F813 08.5°C
692 | // OSV2 EA4C2080 0201 B082
693 | // OSV2 EA4C2080 7202 3053
694 | // OSV2 EA4C2080 2204 0073
695 | // OSV2 EA4C2080 6206 6063
696 | // OSV2 EA4C2080 9210 40C3
697 | // OSV2 EA4C2080 4211 0033
698 | // OSV2 EA4C2080 1212 E002
699 | // OSV2 EA4C2080 6234 70C3 34.6°C
700 | // OSV2 EA4C2080 5231 3043 31.5°C
701 | // OSV2 EA4C2080 9207 28F4 // negatif autour de -7 ?
702 | // OSV2 EA4C2080 0213 68A3 // negatif -13
703 | // OSV2 EA4C2080 7019 9043
704 | // In correct order :
705 | // OSV2 A EC40 2 08 8 922 0 3D 1
706 | // A => Sync header
707 | // EC40 => THN132N ID
708 | // 2 => Channel ( 1 | 2 | 4 )
709 | // 08 => rolling (random value def @ reset)
710 | // 8 => battery flag & 0x4 other unknown
711 | // could be : 0x2 => rising
712 | // 0x8 => decreasing, to be analysed
713 | // XXX => temperature BCD to be read left from right
714 | // 0 => Sign 0 = + ; 8 = -
715 | // 3D => CheckSum ( from EC40 to Sign)
716 | // ------------------------------------------------------------------------------------------
717 | bool OregonSensorV2::decode_THN132N(char * pt) {
718 |
719 | char channel; int ichannel; // values 1,2,4
720 | char rolling[3]; int irolling;
721 | char battery; int ibattery; // value & 0x4
722 | char temp[4]; double dtemp; // Temp in BCD
723 | char tempS; int itempS; // Sign 0 = positif
724 | char checksum[3]; int ichecksum;
725 | int len = strlen(pt);
726 |
727 | if ( len == 16 ) {
728 | channel = pt[4];
729 | rolling[0]=pt[7]; rolling[1]=pt[6]; rolling[2]='\0';
730 | battery = pt[9];
731 | temp[0] = pt[10] ; temp[1] = pt[11] ; temp[2] = pt[8] ; temp[3] = '\0';
732 | tempS = pt[13];
733 | checksum[0] = pt[15]; checksum[1] = pt[12]; checksum[2] = '\0';
734 |
735 | #ifdef SENSORDEBUG
736 | printf("OSV2 - decode : id(%s) ch(%c) bat(%c) temp(%s) sign(%c) cksum(%s) \n",
737 | "EC040",channel,battery,temp,tempS, checksum);
738 | #endif
739 |
740 | // Conversion to int value
741 | ichannel = getIntFromChar(channel);
742 | irolling = getIntFromString(rolling);
743 | ibattery = getIntFromChar(battery) & 0x04;
744 | itempS = getIntFromChar(tempS) & 0x08;
745 | ichecksum = getIntFromString(checksum);
746 | dtemp = getDoubleFromString(temp);
747 |
748 | // Check SUM
749 | int _sum = getIntFromChar(pt[0]);
750 | for ( int i = 2 ; i <= 11 ; i++ ) _sum += getIntFromChar(pt[i]);
751 | _sum += getIntFromChar(pt[13]);
752 |
753 |
754 | #ifdef SENSORDEBUG
755 | printf("OSV2 - decode : id(0x%04X) ch(%d) bat(%d) temp(%f) sign(%d) cksum(0x%02X) _chksum(0x%02X)\n",
756 | 0xEC40,ichannel,ibattery,dtemp,itempS, ichecksum, _sum);
757 | #endif
758 |
759 | if ( _sum == ichecksum ){
760 |
761 | // now we can decode the important flag and fill the object
762 | this->haveChannel = true;
763 | this->channel = (ichannel != 4)?ichannel:3;
764 |
765 | this->haveBattery = true;
766 | this->battery = (ibattery != 0);
767 |
768 | this->haveTemperature = true;
769 | this->temperature = (itempS == 0)?dtemp:-dtemp;
770 |
771 | this->haveHumidity = false;
772 |
773 | return true;
774 | } else return false;
775 |
776 | }
777 | return false;
778 | }
779 |
780 |
781 | // ---------------------------------------------------------------------------------------
782 | // Decode OregonScientific V2 protocol for specific
783 | // Oregon Devices
784 | // - THGR122NX : Temp + Humidity
785 | // 1A2D1002502060552A4C
786 | // 1A2D1002300638042BB7 => -6°C
787 | // A 1D20 1 20 0 360 8 340 2BB7
788 | // In correct order :
789 | // A 1D20 1 20 0 502 0 655 2A 4C
790 | // A => Sync header
791 | // 1D20 => THGR122NX ID
792 | // 1 => Channel ( 1 | 2 | 4 )
793 | // 20 => rolling (random value def @ reset)
794 | // 0 => battery flag & 0x4
795 | // XXX => temperature BCD to be read left from right
796 | // 0 => temperature sign 0 = positive
797 | // XX (X) => hummidity BCD to be read L from R
798 | // last digit unclear as it is not a BCD when > 75%
799 | // 2A => checksum from ID to Humid (8b sum)
800 | // 4C => CRC8 from ID to Humid w/o rolling
801 | // init : 0x43 CRCPoly : 0x07
802 | // ------------------------------------------------------------------------------------------
803 | bool OregonSensorV2::decode_THGR122NX(char * pt) {
804 |
805 | char channel; int ichannel; // values 1,2,4
806 | char rolling[3]; int irolling;
807 | char battery; int ibattery; // value & 0x4
808 | char temp[4]; double dtemp; // Temp in BCD
809 | char tempS; int itempS; // Sign 0 = positif
810 | char humid[4]; double dhumid; // Humid in BCD
811 | char checksum[3]; int ichecksum;
812 | char crc[3]; int icrc;
813 | int len = strlen(pt);
814 |
815 | if ( len == 20 ) {
816 | channel = pt[4];
817 | rolling[0]=pt[7]; rolling[1]=pt[6]; rolling[2]='\0';
818 | battery = pt[9];
819 | temp[0] = pt[10] ; temp[1] = pt[11] ; temp[2] = pt[8] ; temp[3] = '\0';
820 | tempS = pt[13];
821 | // BugFix humid[0] = pt[15] ; humid[1] = pt[12]; humid[2] = pt[14] ; humid[3] = '\0'; /* when humid >75% pt[14] = 0xD ... */
822 | humid[0] = pt[15] ; humid[1] = pt[12]; humid[2] = '0' ; humid[3] = '\0';
823 | checksum[0] = pt[16]; checksum[1] = pt[17]; checksum[2] = '\0';
824 | crc[0] = pt[18] ; crc[1] = pt[19] ; crc[2] = '\0';
825 |
826 | #ifdef SENSORDEBUG
827 | printf("OSV2 - decode : id(%s) ch(%c) bat(%c) temp(%s) sign(%c) humid(%s) cksum(%s) crc(%s)\n",
828 | "1D20",channel,battery,temp,tempS,humid, checksum, crc);
829 | #endif
830 |
831 | // Conversion to int value
832 | ichannel = getIntFromChar(channel);
833 | irolling = getIntFromString(rolling);
834 | ibattery = getIntFromChar(battery);
835 | itempS = getIntFromChar(tempS) & 0x08;
836 | ichecksum = getIntFromString(checksum);
837 | icrc = getIntFromString(crc);
838 | dtemp = getDoubleFromString(temp);
839 | dhumid = getDoubleFromString(humid);
840 |
841 | #ifdef SENSORDEBUG
842 | printf("OSV2 - decode : id(0x%04X) ch(%d) bat(%d) temp(%f) sign(%d) humid(%f) cksum(0x%02X) crc(0x%02X)\n",
843 | 0x1D20,ichannel,ibattery,dtemp,itempS,dhumid, ichecksum, icrc);
844 | #endif
845 |
846 | // Check SUM & CRC
847 | if ( validate(pt,16,icrc,ichecksum) == true ) {
848 |
849 | // now we can decode the important flag and fill the object
850 | this->haveChannel = true;
851 | this->channel = (ichannel != 4)?ichannel:3;
852 |
853 | this->haveBattery = true;
854 | this->battery = (ibattery & 0x4);
855 |
856 | this->haveTemperature = true;
857 | this->temperature = (itempS == 0)?dtemp:-dtemp;
858 |
859 | this->haveHumidity = true;
860 | this->humidity = dhumid;
861 |
862 | return true;
863 | } else return false;
864 |
865 | }
866 | return false;
867 | }
868 |
869 |
870 | // -----------------------------------------------------
871 | // Validate CRC and Checksum value from the signal
872 | // Starts at the Sync header digit
873 | // return true if both are valid
874 | bool OregonSensorV2::validate(char * _str, int _len, int _CRC, int _SUM) {
875 |
876 | int i,j,c,CRC,SUM;
877 | CRC =0x43;
878 | int CCIT_POLY = 0x07;
879 | SUM = 0x00;
880 |
881 | // swap each 2 digit
882 | char __str[100];
883 | for (j=0 ; j < _len ; j+=2){
884 | __str[j] = _str[j+1];
885 | __str[j+1] = _str[j];
886 | }
887 | __str[_len]='\0'; // recopie de \0
888 |
889 |
890 | for (j=1; j< _len; j++)
891 | {
892 | c = getIntFromChar(__str[j]);
893 | SUM += c;
894 | CRC ^= c;
895 |
896 | // Because we have to skip the rolling value in the CRC computation
897 | if ( j != 6 && j != 7 ) {
898 | for(i = 0; i < 4; i++) {
899 | if( (CRC & 0x80) != 0 )
900 | CRC = ( (CRC << 1) ^ CCIT_POLY ) & 0xFF;
901 | else
902 | CRC = (CRC << 1 ) & 0xFF;
903 | }
904 | }
905 | }
906 |
907 | // CRC is 8b but the len is quartet based and we start are digit 1
908 | if ( ! (_len & 1) ) {
909 | for(i = 0; i<4; i++) {
910 | if( (CRC & 0x80) != 0 )
911 | CRC = ( (CRC << 1) ^ CCIT_POLY ) & 0xFF;
912 | else
913 | CRC = (CRC << 1 ) & 0xFF;
914 | }
915 | }
916 | #ifdef SENSORDEBUG
917 | printf("Validate OOK - SUM : 0x%02X(0x%02X) CRC : 0x%02X(0x%02X)\n",SUM,_SUM,CRC,_CRC);
918 | #endif
919 | // Do not check crc anymore as depend on sensor it is not working as expected
920 | if ( SUM == _SUM /* && CRC == _CRC */ ) return true;
921 | else {
922 | #ifdef SENSORTRACE
923 | printf("OSV2 - validate err (%s) SUM : 0x%02X(0x%02X) CRC : 0x%02X(0x%02X)\n",_str,SUM,_SUM,CRC,_CRC);
924 | #endif
925 | }
926 | return false;
927 | }
928 |
929 |
930 |
931 |
932 |
--------------------------------------------------------------------------------
/src/Sensor.d:
--------------------------------------------------------------------------------
1 | Sensor.o: Sensor.cpp Sensor.h
2 |
--------------------------------------------------------------------------------
/src/Sensor.h:
--------------------------------------------------------------------------------
1 | /* ===================================================
2 | * Sensor.h
3 | * ---------------------------------------------------
4 | * Sensor decoding from 433 Message
5 | *
6 | * Created on: 17 sept. 2013
7 | * Author: disk91 / (c) myteepi.com
8 | * ===================================================
9 | */
10 |
11 | #ifndef SENSOR_H_
12 | #define SENSOR_H_
13 |
14 | #define SENS_CLASS_NONE -1 // Not initialized
15 | #define SENS_CLASS_MTP 0 // MyTeePi virtual or physical sensor
16 | #define SENS_CLASS_OS 1 // Oregon Scientific
17 |
18 | #define SENS_TYP_MTP_CPU 0 // cpu temperature
19 | #define SENS_TYP_MTP_INT 1 // internal temperature
20 |
21 | #define SENS_TYP_OS_1D20 0x1D20 // THGR122NX
22 | #define SENS_TYP_OS_EC40 0xEC40 // THN132N
23 | #define SENS_TYP_OS_1D30 0x1D30 // THGRN228NX
24 | #define SENS_TYP_OS_3D00 0x3D00 // WGR9180
25 | #define SENS_TYP_OS_2D10 0x2D10 // STR928N
26 | #define SENS_TYP_OS_5D60 0x5D60 // BTHG968
27 |
28 | class Sensor {
29 |
30 | protected:
31 | int power;
32 | double powerTotal;
33 | double rawpower;
34 | double temperature;
35 | double humidity;
36 | double speed;
37 | double direction;
38 | double rain;
39 | // double train;
40 | double pressure;
41 |
42 | int channel;
43 | bool battery; // true if flag set (battery low)
44 |
45 | bool havePower; // true when power capacity decoded
46 | bool haveRawPower; // true when raw power capacity decoded
47 | bool havePowerTotal; // true when powerTotal capacity decoded
48 |
49 | bool haveTemperature; // true when temp capaciy decoded
50 | bool haveHumidity; // true when hum capacity decoded
51 | bool haveSpeed; // true when hum capacity decoded
52 | bool haveDirection; // true when hum capacity decoded
53 | bool haveRain; // true when hum capacity decoded
54 | // bool haveTrain; // true when hum capacity decoded
55 | bool havePressure; // true when hum capacity decoded
56 | bool haveBattery; // true when battery flag decoded
57 | bool haveChannel; // true when channel is present
58 | bool isValid; // true when chaecksum is valid and other value valid
59 |
60 | int sensorClass; // marque du sensor cf #define
61 | int sensorType; // model of sensor
62 | char sensorName[128]; // name of the sensor
63 |
64 | char packet[128]; // packet string
65 |
66 | time_t creationTime; // objectCreation time
67 |
68 | static char _hexDecod[];
69 | virtual bool decode ( char * _str) = 0 ; // decode the string and set the variable
70 |
71 | protected:
72 | int getIntFromChar(char c); // transform a Hex value in char into a number
73 | int getIntFromString(char *); // transform a Hex value in String into a number
74 | double getDoubleFromString(char *); // transform a BCD string into a double
75 |
76 |
77 |
78 | public:
79 |
80 | Sensor( char * _strval ); // construct and decode value
81 |
82 | bool availablePower(); // return true if valid && have Power
83 | bool availableRawPower(); // return true if valid && have rawPower
84 | bool availablePowerTotal(); // return true if valid && have PowerTotal
85 |
86 | bool availableTemp(); // return true if valid && have Temp
87 | bool availableHumidity(); // return true if valid && have Humidity
88 | bool availablePressure(); // return true if valid && have Pressure
89 | bool availableSpeed(); // return true if valid && have Wind Speed
90 | bool availableDirection(); // return true if valid && have Wind Direction
91 | bool availableRain(); // return true if valid && have Rain
92 | bool isBatteryLow(); // return true if valid && haveBattery && flag set.
93 | bool hasChannel(); // return true if valid && haveChannel
94 | bool isDecoded(); // return true if valide
95 |
96 | double getPower(); // return power in kW
97 | double getRawPower(); // return power in kW
98 | double getPowerTotal(); // return power in kWh
99 |
100 | double getTemperature(); // return temperature in C°
101 | double getHumidity(); // return humidity in % (base 100)
102 | double getPressure(); // return pressure
103 | double getSpeed(); // return wind speed
104 | double getDirection(); // return wind direction
105 | double getRain(); // return rain
106 | int getChannel(); // return channel value
107 |
108 | int getSensClass(); // return sensor class
109 | int getSensType(); // return sensor type
110 | char * getSensName(); // return sensor name string
111 |
112 | char * getPacket(); // return packet string
113 |
114 | time_t getCreationTime(); // return object creation time
115 |
116 | static Sensor * getRightSensor(char * s); // wrapper for child class
117 |
118 | };
119 |
120 |
121 | class OregonSensorV2 : public Sensor {
122 | public :
123 | OregonSensorV2( char * _strval );
124 |
125 | private:
126 | bool decode( char * _str ); // wrapper to right decode method
127 | bool decode_THGR122NX(char * pt); // decode sensor informations
128 | bool decode_THN132N(char * pt); // decode sensor informations
129 | bool decode_THGRN228NX(char * pt);
130 | bool decode_WGR918(char * pt);
131 | bool decode_BTHG968(char * pt);
132 | bool decode_RGR918(char * pt);
133 |
134 | bool validate(char * _str, int _len, int _CRC, int _SUM); // Verify CRC & CKSUM
135 |
136 | };
137 |
138 | class OregonSensorV3 : public Sensor {
139 | public :
140 | OregonSensorV3( char * _strval );
141 |
142 | private:
143 | bool decode( char * _str ); // wrapper to right decode method
144 | bool decode_OWLCM180(char * pt); // decode sensor informations
145 |
146 | bool validate(char * _str, int _len, int _CRC, int _SUM); // Verify CRC & CKSUM
147 |
148 | };
149 | #endif /* SENSOR_H_ */
150 |
--------------------------------------------------------------------------------
/src/core_433.cpp:
--------------------------------------------------------------------------------
1 | /* ==========================================================================
2 | * core_433.cpp
3 | * --------------------------------------------------------------------------
4 | * RF433 demonstrator for rfrpi Raspberry PI shield
5 | * see : http://www.disk91.com/?p=1323
6 | * --------------------------------------------------------------------------
7 | * This software is under GPLv3
8 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
9 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
10 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
11 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
12 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
13 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
14 | * THE SOFTWARE.
15 | * --------------------------------------------------------------------------
16 | *
17 | * Created on: 23 Feb. 2014
18 | * Author: disk91 - Paul Pinault (c) 2014
19 | * --------------------------------------------------------------------------
20 | * This Class is managing RF433 reception, it is working as a Thread listening
21 | * to RCSwitch results, analyze the data and create a message with the data for
22 | * the event manager.
23 | * The shield is able to send and receive and share one antenna for both RX & TX
24 | * as a consequence, a rfswitch is used to separate RX & TX. As this rf switch is not
25 | * stoping totally the signal, data send are also received (witch is a good way to debug)
26 | * --------------------------------------------------------------------------
27 | */
28 |
29 | #include
30 |
31 | #include
32 | #include
33 | #include
34 | #include
35 |
36 | #include "core_433.h"
37 | #include "eventManager.h"
38 | #include "singleton.h"
39 |
40 |
41 | //#define TRACECORE433
42 |
43 | /* ==========================================================
44 | * core_433 : initialize 433Mhz communication system
45 | * ----------------------------------------------------------
46 | * Before being called, wiringPiSetup() must be called and success
47 | */
48 | core_433::core_433(int _rxpin, int _txpin, int _rxepin, int _txepin) {
49 | mySwitch = NULL;
50 | mySwitch = new RCSwitch(_rxpin,_txpin,_rxepin,_txepin);
51 | pthread_create(&myThread,NULL, receptionLoop, this);
52 |
53 | }
54 |
55 | core_433::~core_433() {
56 | mySwitch = NULL;
57 | }
58 |
59 |
60 | /* =========================================================
61 | * Send message prototypes, remap to the RCSwitch_ interface
62 | * ---------------------------------------------------------
63 | */
64 | void core_433::sendMess(char * _m,int _len, int module) {
65 | DecodeOOK * m = NULL;
66 | switch ( module ) {
67 | case CORE433_MOD_PHENIX :
68 | case CORE433_MOD_RCS :
69 | m = new RCSwitch_(1,mySwitch);
70 | break;
71 |
72 | case CORE433_MOD_DIO :
73 | m = new DIO(mySwitch);
74 | break;
75 |
76 | }
77 | if ( m != NULL ) {
78 | m->send(_m,_len);
79 | delete m;
80 | }
81 | }
82 |
83 | void core_433::sendMess(char * _m, int module) {
84 | DecodeOOK * m = NULL;
85 | switch ( module ) {
86 | case CORE433_MOD_PHENIX :
87 | case CORE433_MOD_RCS :
88 | m = new RCSwitch_(1,mySwitch);
89 | break;
90 |
91 | case CORE433_MOD_DIO :
92 | m = new DIO(mySwitch);
93 | break;
94 |
95 | }
96 | if ( m != NULL ) {
97 | m->send(_m);
98 | delete m;
99 | }
100 |
101 | }
102 |
103 | void core_433::sendMess(unsigned long int _v, int module) {
104 | DecodeOOK * m = NULL;
105 | switch ( module ) {
106 | case CORE433_MOD_PHENIX :
107 | case CORE433_MOD_RCS :
108 | m = new RCSwitch_(1,mySwitch);
109 | break;
110 |
111 | case CORE433_MOD_DIO :
112 | m = new DIO(mySwitch);
113 | break;
114 |
115 | }
116 | if ( m != NULL ) {
117 | m->send(_v,24);
118 | delete m;
119 | }
120 | }
121 |
122 | void core_433::sendMess(unsigned long int _v,int _l, int module) {
123 | DecodeOOK * m = NULL;
124 | switch ( module ) {
125 | case CORE433_MOD_PHENIX :
126 | case CORE433_MOD_RCS :
127 | m = new RCSwitch_(1,mySwitch);
128 | break;
129 |
130 | case CORE433_MOD_DIO :
131 | m = new DIO(mySwitch);
132 | break;
133 |
134 | }
135 | if ( m != NULL ) {
136 | m->send(_v,_l);
137 | delete m;
138 | }
139 | }
140 |
141 |
142 | /* ========================================================
143 | * Thread to manage reception
144 | */
145 | void * core_433::receptionLoop( void * _param ) {
146 | core_433 * myCore = (core_433 *) _param;
147 | RCSwitch * mySwitch = myCore->mySwitch;
148 | char _tmpStr[RCSWITCH_MAX_MESS_SIZE];
149 |
150 | while(1) {
151 |
152 | // Scan for sensor code
153 | if ( mySwitch->getOokCode(_tmpStr) ) {
154 | #ifdef TRACECORE433
155 | std::cout << "* Core_433::receptionLoop() - received message [" << _tmpStr << "]" << std::endl;
156 | #endif
157 | if ( _tmpStr && strlen(_tmpStr) > 4 ) {
158 | if ( Singleton::get() != NULL ) {
159 | Singleton::get()->getEventManager()->enqueue(RFRPI_EVENT_GETSENSORDATA,_tmpStr);
160 | }
161 | }
162 | }
163 | usleep(5000); // 5 ms sleep before next pooling
164 | }
165 | }
166 |
167 | void core_433::loop( void ) {
168 |
169 | pthread_join(myThread,NULL);
170 |
171 | }
172 |
173 |
174 |
175 |
--------------------------------------------------------------------------------
/src/core_433.d:
--------------------------------------------------------------------------------
1 | core_433.o: core_433.cpp core_433.h RCSwitch.h RcOok.h ledManager.h \
2 | eventManager.h singleton.h
3 |
--------------------------------------------------------------------------------
/src/core_433.h:
--------------------------------------------------------------------------------
1 | /* ==========================================================================
2 | * core_433.h
3 | * --------------------------------------------------------------------------
4 | * RF433 demonstrator for rfrpi Raspberry PI shield
5 | * see : http://www.disk91.com/?p=1323
6 | * --------------------------------------------------------------------------
7 | * This software is under GPLv3
8 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
9 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
10 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
11 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
12 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
13 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
14 | * THE SOFTWARE.
15 | * --------------------------------------------------------------------------
16 | *
17 | * Created on: 23 Feb. 2014
18 | * Author: disk91 - Paul Pinault (c) 2014
19 | * --------------------------------------------------------------------------
20 | * This Class is managing RF433 reception, it is working as a Thread listening
21 | * to RCSwitch results, analyze the data and create a message with the data for
22 | * the event manager.
23 | * --------------------------------------------------------------------------
24 | */
25 |
26 | #ifndef CORE_433_H_
27 | #define CORE_433_H_
28 |
29 | #include
30 | #include
31 | #include "RCSwitch.h"
32 | #include "RcOok.h"
33 | #include "ledManager.h"
34 |
35 |
36 | #define CORE433_MOD_RCS 1
37 | #define CORE433_MOD_DIO 2
38 | #define CORE433_MOD_PHENIX 3
39 |
40 | class core_433
41 | {
42 | private:
43 | RCSwitch * mySwitch; // Used for RF433 communication
44 | pthread_t myThread; // Thread structure
45 |
46 | static void * receptionLoop(void *); // Thread function
47 |
48 | public:
49 | core_433(int _rxpin, int _txpin, int _rxepin, int _txepin);
50 |
51 | ~core_433();
52 |
53 | void sendMess(char * _m, int _len,int module); // send a message like sensors hexcode in string, len in bit <= 256
54 | void sendMess(char * _m, int module); // send a 24b message like sensors
55 | void sendMess(unsigned long int _v, int module); // send a 24b message like sensors
56 | void sendMess(unsigned long int _v, int _l, int module); // send a message like sensors with size _l <= 32
57 | void loop (void);
58 |
59 | };
60 |
61 |
62 | #endif /* CORE_433_H_ */
63 |
--------------------------------------------------------------------------------
/src/eventManager.cpp:
--------------------------------------------------------------------------------
1 | /* ==========================================================================
2 | * EventManager.cpp
3 | * --------------------------------------------------------------------------
4 | * RF433 demonstrator for rfrpi Raspberry PI shield
5 | * see : http://www.disk91.com/?p=1323
6 | * --------------------------------------------------------------------------
7 | * This software is under GPLv3
8 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
9 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
10 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
11 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
12 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
13 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
14 | * THE SOFTWARE.
15 | * --------------------------------------------------------------------------
16 | *
17 | * Created on: 23 Feb. 2014
18 | * Author: disk91 - Paul Pinault (c) 2014
19 | * --------------------------------------------------------------------------
20 | * This Class is managing event coming from the sensors. This is the main
21 | * entry point where you can modify behavior of the system by implementing
22 | * your logic
23 | * --------------------------------------------------------------------------
24 | */
25 | #include
26 | #include
27 | #include
28 | #include
29 | #include
30 | #include
31 | #include "eventManager.h"
32 | #include "tools.h"
33 | #include "singleton.h"
34 | #include "version.h"
35 | #include "Sensor.h"
36 |
37 | //#define TRACEEVENTMNG
38 |
39 | /* ==========================================================
40 | * Construction / Destruction
41 | */
42 | EventManager::EventManager(char * _piId) {
43 | this->eventListMutex = PTHREAD_MUTEX_INITIALIZER;
44 | this->piId = _piId;
45 | pthread_create(&this->myThread,NULL, eventLoop, this);
46 | }
47 |
48 | EventManager::~EventManager() {
49 | pthread_cancel(myThread);
50 | pthread_join(myThread, NULL);
51 | myThread = pthread_self();
52 | }
53 |
54 |
55 |
56 | /* ==========================================================
57 | * Event enqueuing
58 | */
59 | bool EventManager::enqueue(int newEvent, char * _strValue) {
60 | Event ev;
61 | ev.eventType = newEvent;
62 | if ( _strValue != NULL ) {
63 | if ( strlen(_strValue) > RFRPI_EVENT_MAX_SIZE-1 ) {
64 | #ifdef TRACEEVENTMNG
65 | printf("** ERROR ** EventManager::enqueue() - enqueue (%s) TOO LONG \n",_strValue);
66 | #endif
67 | }
68 | strncpy( ev.strValue, _strValue, RFRPI_EVENT_MAX_SIZE-1);
69 | } else ev.strValue[0] = '\0';
70 |
71 | pthread_mutex_lock( &this->eventListMutex );
72 | #ifdef TRACEEVENTMNG
73 | printf("EventManager::enqueue() - enqueue (%s)\n",_strValue);
74 | #endif
75 | this->eventList.push_back(ev);
76 | pthread_mutex_unlock( &this->eventListMutex );
77 | }
78 |
79 |
80 | /* ==========================================================
81 | * Thread to manage events ...
82 | */
83 | void * EventManager::eventLoop( void * _param ) {
84 | EventManager * myEventManager = (EventManager *) _param;
85 |
86 | while(1) {
87 | while ( ! myEventManager->eventList.empty() ) {
88 |
89 | pthread_mutex_lock( &myEventManager->eventListMutex );
90 | Event ev = myEventManager->eventList.front();
91 | myEventManager->eventList.pop_front();
92 | pthread_mutex_unlock( &myEventManager->eventListMutex );
93 |
94 | switch (ev.eventType) {
95 |
96 | // ------------------------------------------------------------
97 | // INIT OF MODULE - check leds & RX / TX
98 | case RFRPI_EVENT_INIT:
99 | #ifdef TRACEEVENTMNG
100 | printf("EventManager::eventLoop() - proceed INIT \n");
101 | #endif
102 | if ( Singleton::get() ) {
103 | // Singleton::get()->getLedManager1()->setMode(LM_BLINKM_TWICE);
104 | // Singleton::get()->getLedManager2()->setMode(LM_BLINKM_TWICE);
105 | // Singleton::get()->getLedManager3()->setMode(LM_BLINKM_TWICE);
106 | // usleep(2000000);
107 | // Send welcome message .. should be received right after !
108 | Singleton::get()->getCore433()->sendMess(0x123456,CORE433_MOD_RCS);
109 | }
110 | break;
111 |
112 | // ------------------------------------------------------------
113 | // Just received an event from the RF module
114 | case RFRPI_EVENT_GETSENSORDATA:
115 | #ifdef TRACEEVENTMNG
116 | printf("EventManager::eventLoop() - proceed GETSENSORDATA \n");
117 | #endif
118 | // if ( Singleton::get() ) {
119 | // //Singleton::get()->getLedManager1()->setMode(LM_BLINKM_TWICE);
120 | // }
121 | {
122 | Sensor * s = Sensor::getRightSensor(ev.strValue);
123 | if ( s != NULL && s->isDecoded() ) {
124 | time_t ltime;
125 | struct tm * curtime;
126 | char buffer[80];
127 |
128 | time( <ime );
129 | curtime = localtime( <ime );
130 | strftime(buffer,80,"%F %T", curtime);
131 | if ( s->availablePower() ) {
132 |
133 | printf("{\"datetime\": \"%s\", \"name\": \"%s\", \"power_unit\": \"W\",\"power\": \"%.0f\", \"total_unit\": \"Wh\",\"total\": \"%.0f\", \"packet\": \"%s\" }", buffer, s->getSensName(), s->getPower(), s->getPowerTotal(), s->getPacket());
134 | std::cout << std::endl;
135 | } else {
136 |
137 | printf("{\"datetime\": \"%s\", \"name\": \"%s\", \"temperature\": \"%.2f\", \"humidity\": \"%.0f\", \"channel\": \"%d\" }", buffer, s->getSensName(), s->getTemperature(), s->getHumidity(), s->getChannel());
138 | std::cout << std::endl;
139 | }
140 |
141 | } else {
142 | if ( s == NULL ) {
143 | // assuming it is the welcome message sent for test
144 | if ( ev.strValue != NULL && strlen(ev.strValue) >= 11 ) {
145 | if (strcmp(ev.strValue,"RCSW 123456") == 0) {
146 | printf("[INFO] Welcome message correctly received, Rx/Tx working fine\n");
147 | }
148 | }
149 | }
150 | }
151 | }
152 | break;
153 |
154 |
155 |
156 | default:
157 | #ifdef TRACEEVENTMNG
158 | printf("EventManager::eventLoop() - proceed UNKNOWN (%s) \n",ev.strValue);
159 | #endif
160 | break;
161 |
162 |
163 | }
164 |
165 | }
166 | usleep(300000); // 300 ms de repos c'est pas un mal
167 | }
168 | return NULL;
169 | }
170 |
--------------------------------------------------------------------------------
/src/eventManager.d:
--------------------------------------------------------------------------------
1 | eventManager.o: eventManager.cpp eventManager.h ledManager.h core_433.h \
2 | RCSwitch.h RcOok.h tools.h singleton.h version.h Sensor.h
3 |
--------------------------------------------------------------------------------
/src/eventManager.h:
--------------------------------------------------------------------------------
1 | /* ==========================================================================
2 | * EventManager.cpp
3 | * --------------------------------------------------------------------------
4 | * RF433 demonstrator for rfrpi Raspberry PI shield
5 | * see : http://www.disk91.com/?p=1323
6 | * --------------------------------------------------------------------------
7 | * This software is under GPLv3
8 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
9 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
10 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
11 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
12 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
13 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
14 | * THE SOFTWARE.
15 | * --------------------------------------------------------------------------
16 | *
17 | * Created on: 23 Feb. 2014
18 | * Author: disk91 - Paul Pinault (c) 2014
19 | * --------------------------------------------------------------------------
20 | * This Class is managing event coming from the sensors. This is the main
21 | * entry point where you can modify behavior of the system by implementing
22 | * your logic
23 | * --------------------------------------------------------------------------
24 | */
25 | #ifndef EVENTMANAGER_H_
26 | #define EVENTMANAGER_H_
27 |
28 | #include
29 | #include
30 |
31 | #include "ledManager.h"
32 | #include "core_433.h"
33 |
34 |
35 | #define RFRPI_EVENT_NONE 0
36 | // Receiving a sensor Data
37 | #define RFRPI_EVENT_INIT 1
38 | #define RFRPI_EVENT_GETSENSORDATA 2
39 |
40 |
41 |
42 | // Maximum size for an event message
43 | #define RFRPI_EVENT_MAX_SIZE 8192
44 |
45 | class EventManager
46 | {
47 | private:
48 |
49 | class Event {
50 | public:
51 | int eventType;
52 | char strValue[RFRPI_EVENT_MAX_SIZE];
53 | };
54 |
55 | std::list eventList; // event queue
56 | pthread_t myThread; // Thread structure
57 | pthread_mutex_t eventListMutex; // ensure queuing / dequeuing well managed
58 |
59 | char * piId;
60 |
61 | static void * eventLoop(void *); // Thread function
62 |
63 | public:
64 | EventManager(char * _piId);
65 | ~EventManager();
66 |
67 | bool enqueue(int newEvent, char * _strValue); // Add an event with eventQueue type
68 |
69 | };
70 |
71 |
72 |
73 |
74 | #endif /* EVENTMANAGER_H_ */
75 |
--------------------------------------------------------------------------------
/src/ledManager.cpp:
--------------------------------------------------------------------------------
1 | /* ==========================================================================
2 | * LedManager.cpp
3 | * --------------------------------------------------------------------------
4 | * RF433 demonstrator for rfrpi Raspberry PI shield
5 | * see : http://www.disk91.com/?p=1323
6 | * --------------------------------------------------------------------------
7 | * This software is under GPLv3
8 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
9 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
10 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
11 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
12 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
13 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
14 | * THE SOFTWARE.
15 | * --------------------------------------------------------------------------
16 | *
17 | * Created on: 23 Feb. 2014
18 | * Author: disk91 - Paul Pinault (c) 2014
19 | * --------------------------------------------------------------------------
20 | * This Class is managing Leds on the shield
21 | * Three leds are available on the shield. This management call allow Leds to be light on
22 | * light off, blinking, bliking twice of blinking fastly. The LedManager is an
23 | * independent Thread, so you just have to send it an order and the Led is executing it
24 | * before being back to the previous state (when applicable)
25 | * --------------------------------------------------------------------------
26 | */
27 | #include
28 | #include
29 | #include "ledManager.h"
30 |
31 |
32 | /* ==========================================================
33 | * LedManager : initialize LED manager
34 | * ----------------------------------------------------------
35 | * Before being called, wiringPiSetup() must be called and success
36 | */
37 | LedManager::LedManager(int _pin) {
38 | this->pin = _pin;
39 | pinMode (this->pin, OUTPUT);
40 | digitalWrite(this->pin, LOW);
41 |
42 | this->previMode = LM_BLINKM_ON;
43 | this->blinkMode = LM_BLINKM_ON;
44 | this->ledState = true;
45 | this->loopCount = 0;
46 | pthread_create(&myThread,NULL, ledLoop, this);
47 | }
48 |
49 | LedManager::~LedManager() {
50 | pthread_cancel(myThread);
51 | pthread_join(myThread, NULL);
52 | myThread = pthread_self();
53 | }
54 |
55 | /* ------------------------------------------------
56 | * Change the Led operation
57 | * Attention a ne pas boucler dans un mode FAST...
58 | */
59 | void LedManager::setMode(int _mode) {
60 | if ( this->blinkMode != LM_BLINKM_FAST
61 | && this->blinkMode != LM_BLINKM_TWICE ) {
62 | this->previMode = this->blinkMode;
63 | this->blinkMode = _mode;
64 | this->fastCount = 0;
65 | } else {
66 | if ( _mode != LM_BLINKM_FAST
67 | && _mode != LM_BLINKM_TWICE )
68 | this->previMode = _mode;
69 | /* reset is possible to get longer blink
70 | * but in fact not the best result...
71 | else
72 | this->fastCount = 0;
73 | */
74 | }
75 | }
76 |
77 | /* -------------------------------------------------
78 | * return pin (mostly for debuging purpose
79 | */
80 | int LedManager::getPin() {
81 | return this->pin;
82 | }
83 |
84 |
85 | void * LedManager::ledLoop( void * _param ) {
86 | LedManager * myLed = (LedManager *) _param;
87 |
88 | while(1) {
89 |
90 | switch ( myLed->blinkMode ) {
91 | // Always ON
92 | case LM_BLINKM_ON:
93 | digitalWrite(myLed->pin, HIGH);
94 | myLed->ledState = true;
95 | break;
96 | // Always OFF
97 | case LM_BLINKM_OFF:
98 | digitalWrite(myLed->pin, LOW);
99 | myLed->ledState = false;
100 | break;
101 | // 1000ms ON / 1000 ms OFF
102 | case LM_BLINKM_NORMAL:
103 | if ( (myLed->loopCount % 10) == 0 ) {
104 | if (myLed->ledState) {
105 | myLed->ledState = false;
106 | digitalWrite(myLed->pin, LOW);
107 | } else {
108 | myLed->ledState = true;
109 | digitalWrite(myLed->pin, HIGH);
110 | }
111 | }
112 | break;
113 |
114 | // 100ms ON / 100ms OFF - 10 x
115 | case LM_BLINKM_FAST:
116 | if (myLed->ledState) {
117 | myLed->ledState = false;
118 | digitalWrite(myLed->pin, LOW);
119 | } else {
120 | myLed->ledState = true;
121 | digitalWrite(myLed->pin, HIGH);
122 | }
123 | // Dans ce cas, on ne fera de clignotement rapide que durant
124 | // 1 seconde, puis on revient au mode normal
125 | if ( myLed->fastCount >= 10 ) {
126 | myLed->blinkMode = myLed->previMode;
127 | myLed->fastCount = 0;
128 | } else {
129 | myLed->fastCount++;
130 | }
131 | break;
132 |
133 | // 200ms ON / 200ms OFF - 2x
134 | case LM_BLINKM_TWICE:
135 | if ( (myLed->loopCount % 2) == 0 ) {
136 | if (myLed->ledState) {
137 | myLed->ledState = false;
138 | digitalWrite(myLed->pin, LOW);
139 | } else {
140 | myLed->ledState = true;
141 | digitalWrite(myLed->pin, HIGH);
142 | }
143 | }
144 | // Dans ce cas, on ne fera de clignotement rapide que durant
145 | // 600ms, puis on revient au mode normal
146 | if ( myLed->fastCount > 6 ) {
147 | myLed->blinkMode = myLed->previMode;
148 | myLed->fastCount = 0;
149 | } else {
150 | myLed->fastCount++;
151 | }
152 | break;
153 |
154 | default:
155 | break;
156 |
157 | }
158 |
159 | usleep(100000); // 100 ms de repos c'est pas un mal
160 | myLed->loopCount++;
161 | }
162 | return NULL;
163 | }
164 |
165 | void LedManager::loop( void ) {
166 |
167 | pthread_join(myThread,NULL);
168 |
169 | }
170 |
171 |
172 |
--------------------------------------------------------------------------------
/src/ledManager.d:
--------------------------------------------------------------------------------
1 | ledManager.o: ledManager.cpp ledManager.h
2 |
--------------------------------------------------------------------------------
/src/ledManager.h:
--------------------------------------------------------------------------------
1 | /* ==========================================================================
2 | * LedManager.h
3 | * --------------------------------------------------------------------------
4 | * RF433 demonstrator for rfrpi Raspberry PI shield
5 | * see : http://www.disk91.com/?p=1323
6 | * --------------------------------------------------------------------------
7 | * This software is under GPLv3
8 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
9 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
10 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
11 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
12 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
13 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
14 | * THE SOFTWARE.
15 | * --------------------------------------------------------------------------
16 | *
17 | * Created on: 23 Feb. 2014
18 | * Author: disk91 - Paul Pinault (c) 2014
19 | * --------------------------------------------------------------------------
20 | * This Class is managing Leds on the shield
21 | * Three leds are available on the shield. This management call allow Leds to be light on
22 | * light off, blinking, bliking twice of blinking fastly. The LedManager is an
23 | * independent Thread, so you just have to send it an order and the Led is executing it
24 | * before being back to the previous state (when applicable)
25 | * --------------------------------------------------------------------------
26 | */
27 | #ifndef LEDMANAGER_H_
28 | #define LEDMANAGER_H_
29 |
30 | #include
31 | #include
32 |
33 | #define LM_BLINKM_ON 0 /* light on */
34 | #define LM_BLINKM_OFF 1 /* light off */
35 | #define LM_BLINKM_NORMAL 2 /* 1s blink */
36 | #define LM_BLINKM_FAST 3 /* 100ms blink */
37 | #define LM_BLINKM_TWICE 4 /* 2 blink 1/2s */
38 |
39 |
40 | class LedManager
41 | {
42 | private:
43 | pthread_t myThread; // Thread structure
44 | int pin; // pin to be used for LED
45 | long loopCount; // 100ms loop count
46 | long fastCount; // count loop to automatically stop after 1s
47 | int blinkMode; // chosen mode
48 | int previMode; // previous mode
49 | bool ledState; // ON / OFF
50 |
51 | static void * ledLoop(void *); // Thread function
52 |
53 | public:
54 | LedManager(int _pin);
55 | ~LedManager();
56 |
57 | void loop (void);
58 | void setMode( int ); // change le mode de clignotement.
59 |
60 | int getPin(void);
61 | };
62 |
63 |
64 |
65 |
66 | #endif /* LEDMANAGER_H_ */
67 |
--------------------------------------------------------------------------------
/src/osv3_frame.txt:
--------------------------------------------------------------------------------
1 | OSV3 62 80 3C 7801 E072 8305 00
2 | OSV3 62 80 3C 7801 60C8 8305 00
3 | OSV3 62 80 3C 0801 002E 8705 00 257
4 | OSV3 62 80 3C 0801 006A 8705 00 257
5 | OSV3 62 80 3C E801 A0DF 8C05 00 483
6 |
7 | OSV3 62 80 3C D800 000E9805 00
8 | OSV3 62 80 3C E800 40409805 00
9 | OSV3 62 80 3C D800 40739805 00
10 | OSV3 62 80 3C E800 60A49805 00 power=0x00E8 Total=0x0598A460 W = 93889632 /60*60 /1000 = 26.08kWh
11 | OSV3 62 80 3C 480B 0095
12 | OSV3 62 83 3C 180B 30
13 | OSV3 62 82 3C 180B 10
14 | OSV3 62 80 3C 580B 00
15 | OSV3 62 84 3C 480B 60
16 |
17 | OSV3 62813C180B70
18 | OSV3 62803C180B00
19 | OSV3 62843C480B60]
20 | OSV3 62833C480BF0
21 | OSV3 62823C180B10
22 | 62813C 180B 70
23 | 62803C 180B E0
24 | 62843C 480B 60
25 | 62833C 580B 80
26 | 62823C 380C 40
27 | 62813C D80B A0
28 | 62803C A80B 60
29 |
30 | 62813C 980B 10
31 | 62833C 180B 30
32 |
33 | 6280 3C 480C 60F1E50A 0000 8 076
34 | 62803 C 480C 40CCE80A 0000 1 0C7
35 | 62803C380C40A5EB0A0000A056
36 | 62803C780280F3ED0A0000D066
37 | 62803C78024081EE0A00001066
38 | 62803C480CE039F00A00008016
39 | 62803C380CA016F30A00001006
40 | 62803C280C00EDF50A0000C066
41 |
--------------------------------------------------------------------------------
/src/rfrpi_test.cpp:
--------------------------------------------------------------------------------
1 | /* ==========================================================================
2 | * Rfrpi_test.cpp
3 | * --------------------------------------------------------------------------
4 | * RF433 demonstrator for rfrpi Raspberry PI shield
5 | * see : http://www.disk91.com/?p=1323
6 | * --------------------------------------------------------------------------
7 | * This software is under GPLv3
8 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
9 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
10 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
11 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
12 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
13 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
14 | * THE SOFTWARE.
15 | * --------------------------------------------------------------------------
16 | *
17 | * Created on: 23 Feb. 2014
18 | * Author: disk91 - Paul Pinault (c) 2014
19 | * --------------------------------------------------------------------------
20 | */
21 |
22 | #define DEBUG
23 |
24 | #include
25 | #include
26 | #include
27 | #include
28 |
29 | #include "singleton.h"
30 | #include "version.h"
31 |
32 |
33 |
34 | /* ======================================================
35 | * Main function
36 | * ------------------------------------------------------
37 | * Init processes and loop 4 ever
38 | * ======================================================
39 | */
40 | int main(int argc, char *argv[]) {
41 |
42 | // wiring Pi startup
43 | if(wiringPiSetup() == -1) {
44 | printf("Error during wiringPi Initialization\n");
45 | exit(1);
46 | }
47 |
48 | Singleton::init();
49 |
50 |
51 | // Create Register event right after initialization
52 | //printf("Version (%d.%d) Branch (%s) Build date(%u)\n",RFRPI_VERSION_MAJOR,RFRPI_VERSION_MINOR,RFRPI_VERSION_BRANCH,&RFRPI_BUILD_DATE);
53 |
54 | // Send init event to test the shield
55 | Singleton::get()->getEventManager()->enqueue(RFRPI_EVENT_INIT,NULL);
56 |
57 | // infinite loop
58 | Singleton::get()->getCore433()->loop();
59 | delete Singleton::get();
60 | exit(0);
61 |
62 | }
63 |
64 |
--------------------------------------------------------------------------------
/src/rfrpi_test.d:
--------------------------------------------------------------------------------
1 | rfrpi_test.o: rfrpi_test.cpp singleton.h core_433.h RCSwitch.h RcOok.h \
2 | ledManager.h eventManager.h version.h
3 |
--------------------------------------------------------------------------------
/src/singleton.cpp:
--------------------------------------------------------------------------------
1 | /* ==========================================================================
2 | * Singleton.h
3 | * --------------------------------------------------------------------------
4 | * RF433 demonstrator for rfrpi Raspberry PI shield
5 | * see : http://www.disk91.com/?p=1323
6 | * --------------------------------------------------------------------------
7 | * This software is under GPLv3
8 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
9 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
10 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
11 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
12 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
13 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
14 | * THE SOFTWARE.
15 | * --------------------------------------------------------------------------
16 | *
17 | * Created on: 23 Feb. 2014
18 | * Author: disk91 - Paul Pinault (c) 2014
19 | * --------------------------------------------------------------------------
20 | * This file creates and managed all the Thread used to control the shield
21 | * * Core433 object is used for RF433 communication
22 | * * LedManager{1..3} object is used to control the 3 led behavior
23 | * * EvenManager object is managing the events received from the RF433 object
24 | * made to be expendable
25 | * --------------------------------------------------------------------------
26 | */
27 |
28 | #include
29 | #include
30 | #include
31 |
32 |
33 | #include "singleton.h"
34 |
35 | Singleton * Singleton::m_instance = NULL;
36 |
37 | Singleton::Singleton() {
38 | this->core433 = NULL;
39 | this->eventManager = NULL;
40 | this->ledManager1 = NULL;
41 | this->ledManager2 = NULL;
42 | this->ledManager3 = NULL;
43 | }
44 |
45 | Singleton::~Singleton() {
46 | delete this->core433;
47 | delete this->eventManager;
48 | delete this->ledManager1;
49 | delete this->ledManager2;
50 | delete this->ledManager3;
51 | }
52 |
53 |
54 | core_433 * Singleton::getCore433() { return this->core433; }
55 | EventManager * Singleton::getEventManager() { return this->eventManager; }
56 | LedManager * Singleton::getLedManager1() { return this->ledManager1; }
57 | LedManager * Singleton::getLedManager2() { return this->ledManager2; }
58 | LedManager * Singleton::getLedManager3() { return this->ledManager3; }
59 |
60 | // ========================================================
61 | // Build instances
62 | // ========================================================
63 | void Singleton::init()
64 | {
65 | Singleton * s = new Singleton();
66 |
67 | #ifdef TRACESINGLETON
68 | printf("Singleton::init() - entering \n");
69 | #endif
70 |
71 | // This led is used fro RF433 communication
72 | // blink twice when a new data is received
73 | // fast blink during data transmission
74 | s->ledManager1 = new LedManager(LED);
75 | s->ledManager1->setMode(LM_BLINKM_OFF);
76 |
77 | // This led is not used for any special function
78 | s->ledManager2 = new LedManager(LED2);
79 | s->ledManager2->setMode(LM_BLINKM_OFF);
80 |
81 | // This led is not used for any special function
82 | s->ledManager3 = new LedManager(LED3);
83 | s->ledManager3->setMode(LM_BLINKM_OFF);
84 |
85 | #ifdef TRACESINGLETON
86 | printf("Singleton::init() - ledManager created \n");
87 | #endif
88 |
89 |
90 | // Lancement de la reception des signaux 433MHz
91 | s->core433 = new core_433(RX_PIN,TX_PIN,RX_ENA,TX_ENA);
92 |
93 | #ifdef TRACESINGLETON
94 | printf("Singleton::init() - core_433 created \n");
95 | #endif
96 |
97 |
98 | // Event Manager ... ici on a la plupart des fonctionnalités
99 | s->eventManager = new EventManager("rfrpi0"); // rpi Id
100 |
101 | #ifdef TRACESINGLETON
102 | printf("Singleton::init() - EventManager created \n");
103 | #endif
104 |
105 | Singleton::m_instance = s;
106 |
107 | #ifdef TRACESINGLETON
108 | printf("Singleton::init() - Singleton is ready \n");
109 | #endif
110 |
111 | }
112 |
113 | // ===========================================================================
114 | // Get instance
115 | // ===========================================================================
116 | Singleton * Singleton::get() {
117 | if( Singleton::m_instance != NULL ) {
118 | return Singleton::m_instance;
119 | } else {
120 | std::cout << "Singleton::get() - singleton not initialized" << std::endl;
121 | return NULL;
122 | }
123 | }
124 |
--------------------------------------------------------------------------------
/src/singleton.d:
--------------------------------------------------------------------------------
1 | singleton.o: singleton.cpp singleton.h core_433.h RCSwitch.h RcOok.h \
2 | ledManager.h eventManager.h
3 |
--------------------------------------------------------------------------------
/src/singleton.h:
--------------------------------------------------------------------------------
1 | /* ==========================================================================
2 | * Singleton.h
3 | * --------------------------------------------------------------------------
4 | * RF433 demonstrator for rfrpi Raspberry PI shield
5 | * see : http://www.disk91.com/?p=1323
6 | * --------------------------------------------------------------------------
7 | * This software is under GPLv3
8 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
9 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
10 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
11 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
12 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
13 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
14 | * THE SOFTWARE.
15 | * --------------------------------------------------------------------------
16 | *
17 | * Created on: 23 Feb. 2014
18 | * Author: disk91 - Paul Pinault (c) 2014
19 | * --------------------------------------------------------------------------
20 | * This file creates and managed all the Thread used to control the shiedl
21 | * --------------------------------------------------------------------------
22 | */
23 |
24 | #ifndef SINGLETON_H_
25 | #define SINGLETON_H_
26 |
27 | #include "core_433.h"
28 | #include "eventManager.h"
29 | #include "ledManager.h"
30 |
31 | // Hardware setting for RF433 RX PIN
32 | #define RX_PIN 0 // PIN 1.6 - GPIO0 - RF433 Receiver pin
33 | // This pin is not the first pin on the RPi GPIO header!
34 | // Consult https://projects.drogon.net/raspberry-pi/wiringpi/pins/
35 | // for more information.
36 | #define TX_PIN 7 // RF433 Transmitter pin - PIN 1.4 - GPIO7
37 | #define RX_ENA 5 // RX - Antenna switch to Rx
38 | #define TX_ENA 4 // TX - Antenna switch to Tx
39 |
40 | #define LED 1 // PIN12 / GPIO1
41 | #define LED2 2 // PIN1.7 / GPIO2
42 | #define LED3 3 // PIN1.8 / GPIO3
43 |
44 |
45 | class Singleton {
46 | protected:
47 | core_433 * core433;
48 | EventManager * eventManager;
49 | LedManager * ledManager1;
50 | LedManager * ledManager2;
51 | LedManager * ledManager3;
52 |
53 | static Singleton * m_instance;
54 |
55 | public:
56 | Singleton();
57 | ~Singleton();
58 |
59 | core_433 * getCore433();
60 | EventManager * getEventManager();
61 | LedManager * getLedManager1();
62 | LedManager * getLedManager2();
63 | LedManager * getLedManager3();
64 |
65 | static void init();
66 | static Singleton * get();
67 |
68 | };
69 |
70 |
71 |
72 |
73 | #endif /* SINGLETON_H_ */
74 |
--------------------------------------------------------------------------------
/src/tools.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | * tools.cpp
3 | *
4 | * Misc helper
5 | *
6 | * Created on: 10 sept. 2013
7 | * Author: Paul Pinault / disk_91
8 | */
9 |
10 |
11 | #include
12 | #include
13 | #include
14 | #include
15 | #include
16 |
17 | //#define DEBUG
18 |
19 | /* =======================================================
20 | * Retourne vrai si la chaine str commence par la sous-chaine
21 | * ref, sinon false;
22 | * -------------------------------------------------------
23 | */
24 | int startWith(char * str, char *ref ) {
25 | while ( *str != '\0' && *ref != '\0' && *str == *ref ) {
26 | str++;
27 | ref++;
28 | }
29 | if ( *ref == '\0' ) return 1;
30 | return 0;
31 | }
32 |
33 | /* ========================================================
34 | * Cut : type cut -d "seperator" -f field
35 | * Return in the destination string (allocated)
36 | * --------------------------------------------------------
37 | */
38 | char * cut(char * dst, char * str, char separator, int field ) {
39 | int cField=1;
40 | char * _dst = dst;
41 | *_dst='\0';
42 | while ( *str != '\0' && *str != '\n' ) {
43 | if ( *str == separator) cField++;
44 | else if ( cField == field ) { *_dst = *str; _dst++ ;}
45 | str++;
46 | }
47 | *_dst = '\0';
48 | return dst;
49 | }
50 |
51 |
52 | /* ==========================================================
53 | * trim : supression des espaces, devant et derriere
54 | * ----------------------------------------------------------
55 | */
56 | char * trim(char * str) {
57 | char * _s = str;
58 | char * _d = str;
59 |
60 | while ( *_s == ' ' ) _s++;
61 | while ( *_s != '\0' ) { *_d = *_s ; _s++ ; _d++; }
62 | do {
63 | _d--;
64 | } while ( *_d == ' ' );
65 | _d++;
66 | *_d='\0';
67 | return str;
68 | }
69 |
70 | /* ==========================================================
71 | * getIntFromChar : convert a Hex char into decimal number
72 | * ----------------------------------------------------------
73 | */
74 | int getIntFromChar(char c) {
75 | char _hexDecod[16] = { '0', '1', '2', '3', '4', '5', '6', '7',
76 | '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
77 | for ( int i=0 ; i < 16 ; i++ )
78 | if ( _hexDecod[i] == c ) return i;
79 | return -1;
80 | }
81 |
82 |
83 | /* ==========================================================
84 | * hexStr2int : convert a Hex string into decimal number
85 | * ----------------------------------------------------------
86 | */
87 | int hexStr2int(char * str) {
88 | int v=0;
89 | while (*str != '\0') {
90 | int w = getIntFromChar(*str);
91 | if ( w < 0 ) return -1;
92 | v = v << 4;
93 | v = v | w;
94 | str++;
95 | }
96 | return v;
97 | }
98 |
99 | /* ===========================================================
100 | * Convert a number into binary representation
101 | */
102 | char * dec2binWzerofill(unsigned long Dec, unsigned int bitLength){
103 | static char bin[64];
104 | unsigned int i=0;
105 |
106 | while (Dec > 0) {
107 | bin[32+i++] = ((Dec & 1) > 0) ? '1' : '0';
108 | Dec = Dec >> 1;
109 | }
110 |
111 | for (unsigned int j = 0; j< bitLength; j++) {
112 | if (j >= bitLength - i) {
113 | bin[j] = bin[ 31 + i - (j - (bitLength - i)) ];
114 | }else {
115 | bin[j] = '0';
116 | }
117 | }
118 | bin[bitLength] = '\0';
119 |
120 | return bin;
121 | }
122 |
123 |
124 | /* ====================================================
125 | * Extrait une donnée à partir d'une regex
126 | * On recupere 2 données dan la rex une fois executé
127 | * Ce qui map la totalité \0
128 | * Ce qui est dans la première parenthèse et qui est ce que
129 | * l'on extrait \1
130 | * ------------------------------------
131 | * rex type : "\"on\":\"([^\"]{12})\""
132 | */
133 | bool extractWithRex(const char * rex, char * dest, char * buf){
134 | regex_t reg;
135 | bool ret=false;
136 | int err = regcomp(®,rex,REG_EXTENDED );
137 | if (err == 0) {
138 | size_t nmatch = 2;
139 | regmatch_t pmatch[2];
140 | if ( regexec(®, buf, nmatch, pmatch, 0 ) == 0 ) {
141 | int start = pmatch[1].rm_so;
142 | int end = pmatch[1].rm_eo;
143 | strncpy(dest,&buf[start],end-start);
144 | dest[end-start]='\0';
145 | ret=true;
146 | }
147 | regfree(®);
148 | }
149 | return ret;
150 | }
151 |
152 | /* ====================================================
153 | * Get time from String
154 | * String format is : 2013-11-12_10:30:00
155 | * Return a time_t value
156 | */
157 | bool getTimeFromString(char * _str, time_t * _dst) {
158 |
159 | struct tm * _tm;
160 | time( _dst );
161 | _tm = localtime( _dst );
162 |
163 | char year[10], month[10], day[10], hour[10], min[10], sec[10];
164 | extractWithRex("([0-9]{4})-[0-9]{2}-[0-9]{2}_[0-9]{2}:[0-9]{2}:[0-9]{2}",year,_str);
165 | extractWithRex("[0-9]{4}-([0-9]{2})-[0-9]{2}_[0-9]{2}:[0-9]{2}:[0-9]{2}",month,_str);
166 | extractWithRex("[0-9]{4}-[0-9]{2}-([0-9]{2})_[0-9]{2}:[0-9]{2}:[0-9]{2}",day,_str);
167 | extractWithRex("[0-9]{4}-[0-9]{2}-[0-9]{2}_([0-9]{2}):[0-9]{2}:[0-9]{2}",hour,_str);
168 | extractWithRex("[0-9]{4}-[0-9]{2}-[0-9]{2}_[0-9]{2}:([0-9]{2}):[0-9]{2}",min,_str);
169 | extractWithRex("[0-9]{4}-[0-9]{2}-[0-9]{2}_[0-9]{2}:[0-9]{2}:([0-9]{2})",sec,_str);
170 |
171 | int iyear,imonth,iday,ihour,imin,isec;
172 | iyear = atoi(year);
173 | imonth = atoi(month);
174 | iday = atoi(day);
175 | ihour = atoi(hour);
176 | imin = atoi(min);
177 | isec = atoi(sec);
178 |
179 | #ifdef DEBUG
180 | printf("Tool:getTimeFromString() - read : %d/%d/%d %d:%d:%d \n",iyear,imonth,iday,ihour,imin,isec);
181 | #endif
182 |
183 | _tm->tm_year = iyear - 1900;
184 | _tm->tm_mon = imonth -1;
185 | _tm->tm_mday = iday;
186 | _tm->tm_hour = ihour;
187 | _tm->tm_min = imin;
188 | _tm->tm_sec = isec;
189 |
190 | *_dst = mktime( _tm );
191 |
192 | return true;
193 | }
194 |
--------------------------------------------------------------------------------
/src/tools.d:
--------------------------------------------------------------------------------
1 | tools.o: tools.cpp
2 |
--------------------------------------------------------------------------------
/src/tools.h:
--------------------------------------------------------------------------------
1 | /*
2 | * tools.h
3 | *
4 | * Created on: 10 sept. 2013
5 | * Author: paul
6 | */
7 |
8 | #ifndef TOOLS_H_
9 | #define TOOLS_H_
10 |
11 | int startWith(char * str, char *ref );
12 | char * cut(char * dst, char * str, char separator, int field );
13 | char * trim(char * str);
14 | int hexStr2int(char * str);
15 | int getIntFromChar(char c);
16 | char * dec2binWzerofill(unsigned long Dec, unsigned int bitLength);
17 |
18 | bool extractWithRex(const char * rex, char * dest, char * buf);
19 | bool getTimeFromString(char * _str, time_t * _dst);
20 |
21 | #endif /* TOOLS_H_ */
22 |
--------------------------------------------------------------------------------
/src/version.h:
--------------------------------------------------------------------------------
1 | /* ===================================================
2 | * version.h
3 | * ---------------------------------------------------
4 | * Version of the software
5 | *
6 | * Created on: 24 Nov. 2013
7 | * Author: disk91 (c) myteepi.fr
8 | * ===================================================
9 | */
10 | #ifndef VERSION_H_
11 | #define VERSION_H_
12 |
13 | #define RFRPI_VERSION_MAJOR 0
14 | #define RFRPI_VERSION_MINOR 1
15 | #define RFRPI_VERSION_BRANCH "beta"
16 |
17 | extern char RFRPI_BUILD_DATE;
18 |
19 | #endif /* VERSION_H_ */
20 |
--------------------------------------------------------------------------------