├── 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 | ![DIY CM180 Monitor ](https://raw.github.com/onlinux/OWL-CMR180/master/images/diy-owl-cm180.jpg) 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 | ![My RF433 receiver ](https://raw.github.com/onlinux/OWL-CMR180/master/images/rf433-receiver.jpg) 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 | ![DIY CM180 Monitor ](https://raw.github.com/onlinux/OWL-CMR180/master/images/oregon_owl_serial_output.png) 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 | --------------------------------------------------------------------------------