commit f83dd4f105cc1f87c8d9d796848207d43674a61f Author: Nigreon Date: Sat Feb 22 23:32:03 2025 +0100 Initial commit diff --git a/doc/arduino-nano.md b/doc/arduino-nano.md new file mode 100644 index 0000000..e5220fe --- /dev/null +++ b/doc/arduino-nano.md @@ -0,0 +1 @@ +https://docs.arduino.cc/hardware/nano/ diff --git a/doc/arduino-nano.pdf b/doc/arduino-nano.pdf new file mode 100644 index 0000000..3228b35 Binary files /dev/null and b/doc/arduino-nano.pdf differ diff --git a/lib/NexusTX/NexusTX.cpp b/lib/NexusTX/NexusTX.cpp new file mode 100644 index 0000000..9420af2 --- /dev/null +++ b/lib/NexusTX/NexusTX.cpp @@ -0,0 +1,101 @@ +#include "NexusTX.h" + +NexusTX::NexusTX(byte tr_pin) +{ + SendBuffer = new bool[buffer_size]; + TX_PIN = tr_pin; + pinMode(TX_PIN, OUTPUT); + digitalWrite(TX_PIN, LOW); + SendBuffer[9] = 0; + SendBuffer[24] = 1; + SendBuffer[25] = 1; + SendBuffer[26] = 1; + SendBuffer[27] = 1; + +} + + +void NexusTX::tx_bit(bool b) +{ + digitalWrite(TX_PIN, HIGH); + delayMicroseconds(PULSE_HIGH); + digitalWrite(TX_PIN, LOW); + if (b == true) + delayMicroseconds(PULSE_ONE); + else + delayMicroseconds(PULSE_ZERO); +} + +void NexusTX::setBatteryFlag(bool level) +{ + SendBuffer[8] = level; +} + +void NexusTX::setHumidity(int h) +{ +uint8_t h8 = (uint8_t)h; +for (idx = 0; idx <= 7; idx++) +{ + SendBuffer[35 - idx] = (h8 >> idx) & 0x1; +} + +} + +void NexusTX::setChannel(byte dev_ch) +{ + SendBuffer[10] = dev_ch & 0x2; + SendBuffer[11] = dev_ch & 0x1; +} + +void NexusTX::setId(byte dev_id) +{ + int array_idx = 0; + for (idx = 7; idx >= 0; idx--) + { + SendBuffer[array_idx++] = dev_id & (0x1 << idx); + } +} + +void NexusTX::setTemperature(float t) +{ +int16_t t12 = t * 10.0f; +for (idx = 0; idx <= 11; idx++) +{ + SendBuffer[23 - idx] = (t12 >> idx) & 0x1; +} +} + +void NexusTX::SendNexus() +{ + for (int i=0; i = time_marker_send && send_time) + { + time_marker_send = millis() + send_time; + SendPacket(); + return true; + } + else return false; +} + diff --git a/lib/NexusTX/NexusTX.h b/lib/NexusTX/NexusTX.h new file mode 100644 index 0000000..9882d09 --- /dev/null +++ b/lib/NexusTX/NexusTX.h @@ -0,0 +1,46 @@ +#include +#ifndef NexusTX_h +#define NexusTX_h + +#define PULSE_HIGH 500 /* 500 us */ +#define PULSE_ZERO 1000 /* 1000 us */ +#define PULSE_ONE 2000 /* 2000 us */ +#define PULSE_SYNC 4000 /* 4000 us */ + +// Time between two TX = 56.75 seconds +#define TX_INTERVAL 56750 + +// Bitstream repetition (this device sends the information 10 times) +#define repeat 12 + + + +class NexusTX +{ + public: + + bool* SendBuffer; + int buffer_size=36; + int tx_interval = TX_INTERVAL; + NexusTX(byte); + void setChannel(byte); + void setId(byte); + void setBatteryFlag(bool); + void setTemperature(float); + void setHumidity(int); + bool transmit(); + void SendPacket(); + + + private: + void tx_bit(bool); + byte TX_PIN; + int idx = 0; + void SendNexus(); + unsigned long time_marker_send = 0; + unsigned long send_time = TX_INTERVAL; + +}; + +#endif + diff --git a/lib/Oregon_TM/Oregon_TM.cpp b/lib/Oregon_TM/Oregon_TM.cpp new file mode 100644 index 0000000..14047a1 --- /dev/null +++ b/lib/Oregon_TM/Oregon_TM.cpp @@ -0,0 +1,901 @@ +#include "Oregon_TM.h" + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// This file is part of the Arduino OREGON_NR library. +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// The MIT License (MIT) +// +// Copyright (c) 2021 Sergey Zawislak +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Этот файл - часть библиотеки OREGON_NR +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2021 Сергей Зависляк +// +// Данная лицензия разрешает лицам, получившим копию данного программного обеспечения и сопутствующей документации +// (в дальнейшем именуемыми «Программное Обеспечение»), безвозмездно использовать Программное Обеспечение без ограничений, +// включая неограниченное право на использование, копирование, изменение, слияние, публикацию, распространение, сублицензирование +// и/или продажу копий Программного Обеспечения, а также лицам, которым предоставляется данное Программное Обеспечение, при соблюдении следующих условий: +// +// Указанное выше уведомление об авторском праве и данные условия должны быть включены во все копии или значимые части данного Программного Обеспечения. +// +// ДАННОЕ ПРОГРАММНОЕ ОБЕСПЕЧЕНИЕ ПРЕДОСТАВЛЯЕТСЯ «КАК ЕСТЬ», БЕЗ КАКИХ-ЛИБО ГАРАНТИЙ, ЯВНО ВЫРАЖЕННЫХ ИЛИ ПОДРАЗУМЕВАЕМЫХ, ВКЛЮЧАЯ ГАРАНТИИ ТОВАРНОЙ +// ПРИГОДНОСТИ, СООТВЕТСТВИЯ ПО ЕГО КОНКРЕТНОМУ НАЗНАЧЕНИЮ И ОТСУТСТВИЯ НАРУШЕНИЙ, НО НЕ ОГРАНИЧИВАЯСЬ ИМИ. НИ В КАКОМ СЛУЧАЕ АВТОРЫ ИЛИ ПРАВООБЛАДАТЕЛИ +// НЕ НЕСУТ ОТВЕТСТВЕННОСТИ ПО КАКИМ-ЛИБО ИСКАМ, ЗА УЩЕРБ ИЛИ ПО ИНЫМ ТРЕБОВАНИЯМ, В ТОМ ЧИСЛЕ, ПРИ ДЕЙСТВИИ КОНТРАКТА, ДЕЛИКТЕ ИЛИ ИНОЙ СИТУАЦИИ, +// ВОЗНИКШИМ ИЗ-ЗА ИСПОЛЬЗОВАНИЯ ПРОГРАММНОГО ОБЕСПЕЧЕНИЯ ИЛИ ИНЫХ ДЕЙСТВИЙ С ПРОГРАММНЫМ ОБЕСПЕЧЕНИЕМ. +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + + + +//Конструктор + +Oregon_TM::Oregon_TM(byte tr_pin, int buf_size) +{ + max_buffer_size = (int)(buf_size / 2) + 2; + SendBuffer = new byte[max_buffer_size + 2]; + TX_PIN = tr_pin; + pinMode(TX_PIN, OUTPUT); + digitalWrite(TX_PIN, LOW); + +} + +Oregon_TM::Oregon_TM(byte tr_pin) +{ + SendBuffer = new byte[max_buffer_size + 2]; + TX_PIN = tr_pin; + pinMode(TX_PIN, OUTPUT); + digitalWrite(TX_PIN, LOW); + +} + +Oregon_TM::Oregon_TM(void) +{ + SendBuffer = new byte[max_buffer_size + 2]; + pinMode(TX_PIN, OUTPUT); + digitalWrite(TX_PIN, LOW); +} + + +/////////////////////////////////////////////////////////////////////////////////////////////////// +// Функции передатчика//////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////////////////// +void Oregon_TM::sendZero(void) +{ + if (protocol == 2) { + while (time_marker + TR_TIME * 4 >= micros()); + time_marker += TR_TIME * 4; + digitalWrite(TX_PIN, HIGH); + delayMicroseconds(TR_TIME - PULSE_SHORTEN_2); + digitalWrite(TX_PIN, LOW); + delayMicroseconds(TWOTR_TIME + PULSE_SHORTEN_2); + digitalWrite(TX_PIN, HIGH); + } + if (protocol == 3) + { + if (prevstate) while (time_marker + TWOTR_TIME - PULSE_SHORTEN_3 >= micros()); + else while (time_marker + TWOTR_TIME >= micros()); + + time_marker += TWOTR_TIME; + + if (prevbit && prevstate) + { + digitalWrite(TX_PIN, LOW); + prevstate = 0; + prevbit = 0; + return; + } + if (prevbit && !prevstate) + { + digitalWrite(TX_PIN, HIGH); + delayMicroseconds(TWOTR_TIME); + prevstate = 1; + prevbit = 0; + return; + } + if (!prevbit && prevstate) + { + digitalWrite(TX_PIN, LOW); + delayMicroseconds(TR_TIME); + digitalWrite(TX_PIN, HIGH); + prevbit = 0; + return; + } + if (!prevbit && !prevstate) + { + digitalWrite(TX_PIN, HIGH); + delayMicroseconds(TR_TIME); + digitalWrite(TX_PIN, LOW); + prevbit = 0; + return; + } + } +} +/////////////////////////////////////////////////////////////////////////////////////////////////// + +void Oregon_TM::sendOne(void) +{ + if (protocol == 2) { + while (time_marker + TR_TIME * 4 - PULSE_SHORTEN_2 >= micros()); + time_marker += TR_TIME * 4; + digitalWrite(TX_PIN, LOW); + delayMicroseconds(TR_TIME + PULSE_SHORTEN_2); + digitalWrite(TX_PIN, HIGH); + delayMicroseconds(TWOTR_TIME - PULSE_SHORTEN_2); + digitalWrite(TX_PIN, LOW); + } + + if (protocol == 3) + { + if (prevstate) while (time_marker + TWOTR_TIME - PULSE_SHORTEN_3 >= micros()); + else while (time_marker + TWOTR_TIME >= micros()); + time_marker += TWOTR_TIME; + + if (!prevbit && prevstate) + { + digitalWrite(TX_PIN, LOW); + prevstate = 0; + prevbit = 1; + return; + } + if (!prevbit && !prevstate) + { + digitalWrite(TX_PIN, HIGH); + prevstate = 1; + prevbit = 1; + return; + } + if (prevbit && prevstate) + { + digitalWrite(TX_PIN, LOW); + delayMicroseconds(TR_TIME); + digitalWrite(TX_PIN, HIGH); + prevbit = 1; + return; + } + if (prevbit && !prevstate) + { + digitalWrite(TX_PIN, HIGH); + delayMicroseconds(TR_TIME); + digitalWrite(TX_PIN, LOW); + prevbit = 1; + return; + } + + } +} +/////////////////////////////////////////////////////////////////////////////////////////////////// + +void Oregon_TM::sendMSB(byte data) +{ + (bitRead(data, 4)) ? sendOne() : sendZero(); + (bitRead(data, 5)) ? sendOne() : sendZero(); + (bitRead(data, 6)) ? sendOne() : sendZero(); + (bitRead(data, 7)) ? sendOne() : sendZero(); + if (protocol == 2) time_marker += timing_corrector2; //Поправка на разницу тактовых частот 1024.07Гц и 1024.60Гц + if (protocol == 3) time_marker += timing_corrector3; + + +} +/////////////////////////////////////////////////////////////////////////////////////////////////// + +void Oregon_TM::sendLSB(byte data) +{ + (bitRead(data, 0)) ? sendOne() : sendZero(); + (bitRead(data, 1)) ? sendOne() : sendZero(); + (bitRead(data, 2)) ? sendOne() : sendZero(); + (bitRead(data, 3)) ? sendOne() : sendZero(); + if (protocol == 2) time_marker += timing_corrector2; //Поправка на разницу тактовых частот 1024.07Гц и 1024.60Гц + if (protocol == 3) time_marker += timing_corrector3; +} +/////////////////////////////////////////////////////////////////////////////////////////////////// + +void Oregon_TM::sendData() +{ + int q = 0; + for (byte i = 0; i < max_buffer_size; i++) + { + sendMSB(SendBuffer[i]); + q++; + if (q >= buffer_size) break; + sendLSB(SendBuffer[i]); + q++; + if (q >= buffer_size) break; + if (protocol == 2) time_marker += 4; //Поправка на разницу тактовых частот 1024.07Гц и 1024.60Гц + //if (protocol == 3) time_marker += 4; + //Поправка на разницу тактовых частот 1024.07Гц и 1024Гц + } +} +/////////////////////////////////////////////////////////////////////////////////////////////////// + +void Oregon_TM::sendOregon() +{ + time_marker = micros(); + sendPreamble(); + sendLSB(0xA); + sendData(); + sendZero(); +} +/////////////////////////////////////////////////////////////////////////////////////////////////// + +void Oregon_TM::sendPreamble(void) +{ + if (protocol == 2) { + sendLSB(0xF); + sendLSB(0xF); + time_marker += 9; + sendLSB(0xF); + sendLSB(0xF); + time_marker += 9; + } + if (protocol == 3) { + sendLSB(0xF); + sendLSB(0xF); + sendLSB(0xF); + sendLSB(0xF); + time_marker += 4; + sendLSB(0xF); + sendLSB(0xF); + time_marker += 3; + } +} +/////////////////////////////////////////////////////////////////////////////////////////////////// + +void Oregon_TM::calculateAndSetChecksum129(void) +{ + byte CCIT_POLY = 0x07; + SendBuffer[9] &= 0xF0; + SendBuffer[10] = 0x00; + SendBuffer[11] = 0x00; + byte summ = 0x00; + byte crc = 0x00; + byte cur_nible; + for (int i = 0; i < 10; i++) + { + cur_nible = (SendBuffer[i] & 0xF0) >> 4; + summ += cur_nible; + if (i != 3) + { + crc ^= cur_nible; + for (int j = 0; j < 4; j++) + if (crc & 0x80) crc = (crc << 1) ^ CCIT_POLY; + else crc <<= 1; + } + cur_nible = SendBuffer[i] & 0x0F; + summ += cur_nible; + if (i != 2) + { + crc ^= cur_nible; + for (int j = 0; j < 4; j++) + if (crc & 0x80) crc = (crc << 1) ^ CCIT_POLY; + else crc <<= 1; + } + } + SendBuffer[9] += summ & 0x0F; + SendBuffer[10] += summ & 0xF0; + SendBuffer[10] += crc & 0x0F; + SendBuffer[11] += crc & 0xF0; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// + +void Oregon_TM::calculateAndSetChecksum968(void) +{ + byte CCIT_POLY = 0x07; + SendBuffer[9] &= 0xF0; + SendBuffer[10] = 0x00; + SendBuffer[11] = 0x00; + byte summ = 0x00; + byte crc = 0xA1; + byte cur_nible; + for (int i = 0; i < 10; i++) + { + cur_nible = (SendBuffer[i] & 0xF0) >> 4; + summ += cur_nible; + if (i != 3) + { + crc ^= cur_nible; + for (int j = 0; j < 4; j++) + if (crc & 0x80) crc = (crc << 1) ^ CCIT_POLY; + else crc <<= 1; + } + cur_nible = SendBuffer[i] & 0x0F; + summ += cur_nible; + if (i != 2) + { + crc ^= cur_nible; + for (int j = 0; j < 4; j++) + if (crc & 0x80) crc = (crc << 1) ^ CCIT_POLY; + else crc <<= 1; + } + } + SendBuffer[9] += summ & 0x0F; + SendBuffer[10] += summ & 0xF0; + SendBuffer[10] += crc & 0x0F; + SendBuffer[11] += crc & 0xF0; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// + +void Oregon_TM::calculateAndSetChecksum132(void) +{ + byte CCIT_POLY = 0x07; + SendBuffer[7] &= 0xF0; + SendBuffer[8] = 0x00; + SendBuffer[9] = 0x00; + byte summ = 0x00; + byte crc = 0x3C; + byte cur_nible; + for (int i = 0; i < 8; i++) + { + cur_nible = (SendBuffer[i] & 0xF0) >> 4; + summ += cur_nible; + if (i != 3) + { + crc ^= cur_nible; + for (int j = 0; j < 4; j++) + if (crc & 0x80) crc = (crc << 1) ^ CCIT_POLY; + else crc <<= 1; + } + cur_nible = SendBuffer[i] & 0x0F; + summ += cur_nible; + if (i != 2) + { + crc ^= cur_nible; + for (int j = 0; j < 4; j++) + if (crc & 0x80) crc = (crc << 1) ^ CCIT_POLY; + else crc <<= 1; + } + } + SendBuffer[7] += summ & 0x0F; + SendBuffer[8] += summ & 0xF0; + SendBuffer[8] += crc & 0x0F; + SendBuffer[9] += crc & 0xF0; +} +/////////////////////////////////////////////////////////////////////////////////////////////////// + +void Oregon_TM::calculateAndSetChecksum132S(void) +{ + byte CCIT_POLY = 0x07; + byte summ = 0x00; + byte crc = 0xD6; + SendBuffer[6] = SendBuffer[7] = 0x00; + byte cur_nible; + for (int i = 0; i < 6; i++) + { + cur_nible = (SendBuffer[i] & 0xF0) >> 4; + summ += cur_nible; + if (i != 3) + { + crc ^= cur_nible; + for (int j = 0; j < 4; j++) + if (crc & 0x80) crc = (crc << 1) ^ CCIT_POLY; + else crc <<= 1; + } + cur_nible = SendBuffer[i] & 0x0F; + summ += cur_nible; + if (i != 2) + { + crc ^= cur_nible; + for (int j = 0; j < 4; j++) + if (crc & 0x80) crc = (crc << 1) ^ CCIT_POLY; + else crc <<= 1; + } + } + for (int j = 0; j < 4; j++) + if (crc & 0x80) crc = (crc << 1) ^ CCIT_POLY; + else crc <<= 1; + + SendBuffer[6] += (summ & 0x0F) << 4; + SendBuffer[6] += (summ & 0xF0) >> 4; + SendBuffer[7] += (crc & 0x0F) << 4; + SendBuffer[7] += (crc & 0xF0) >> 4; +} +/////////////////////////////////////////////////////////////////////////////////////////////////// + +void Oregon_TM::calculateAndSetChecksum318() +{ + byte CCIT_POLY = 0x07; + SendBuffer[7] = SendBuffer[7] & 0xF0; + SendBuffer[8] = 0x00; + SendBuffer[9] = 0x00; + byte summ = 0x00; + byte crc = 0x00; + byte cur_nible; + for (int i = 0; i < 8; i++) + { + cur_nible = (SendBuffer[i] & 0xF0) >> 4; + summ += cur_nible; + if (i != 3) + { + crc ^= cur_nible; + for (int j = 0; j < 4; j++) + if (crc & 0x80) crc = (crc << 1) ^ CCIT_POLY; + else crc <<= 1; + } + cur_nible = SendBuffer[i] & 0x0F; + summ += cur_nible; + if (i != 2) + { + crc ^= cur_nible; + for (int j = 0; j < 4; j++) + if (crc & 0x80) crc = (crc << 1) ^ CCIT_POLY; + else crc <<= 1; + } + } + SendBuffer[7] += summ & 0x0F; + SendBuffer[8] += summ & 0xF0; + SendBuffer[8] += crc & 0x0F; + SendBuffer[9] += crc & 0xF0; +} +/////////////////////////////////////////////////////////////////////////////////////////////////// + +void Oregon_TM::calculateAndSetChecksum810() +{ + byte CCIT_POLY = 0x07; + SendBuffer[7] = SendBuffer[7] & 0xF0; + SendBuffer[8] = 0x00; + SendBuffer[9] = 0x00; + byte summ = 0x00; + byte crc = 0x00; + byte cur_nible; + for (int i = 0; i < 8; i++) + { + cur_nible = (SendBuffer[i] & 0xF0) >> 4; + summ += cur_nible; + { + crc ^= cur_nible; + for (int j = 0; j < 4; j++) + if (crc & 0x80) crc = (crc << 1) ^ CCIT_POLY; + else crc <<= 1; + } + cur_nible = SendBuffer[i] & 0x0F; + summ += cur_nible; + { + crc ^= cur_nible; + for (int j = 0; j < 4; j++) + if (crc & 0x80) crc = (crc << 1) ^ CCIT_POLY; + else crc <<= 1; + } + } + SendBuffer[7] += summ & 0x0F; + SendBuffer[8] += summ & 0xF0; + SendBuffer[8] += crc & 0x0F; + SendBuffer[9] += crc & 0xF0; +} +/////////////////////////////////////////////////////////////////////////////////////////////////// + +void Oregon_TM::SendPacket() +{ + if (sens_type == BTHR968) + calculateAndSetChecksum968(); + if (sens_type == BTHGN129) + calculateAndSetChecksum129(); + if (sens_type == THGN132) + calculateAndSetChecksum132(); + if (sens_type == THN132) + calculateAndSetChecksum132S(); + if (sens_type == RTGN318) + calculateAndSetChecksum318(); + if (sens_type == THGR810) + calculateAndSetChecksum810(); + if (sens_type == THP) + calculateAndSetChecksumTHP(); + + sendOregon(); + digitalWrite(TX_PIN, LOW); + if (protocol == 2) { + delayMicroseconds(TWOTR_TIME * 15); + sendOregon(); + digitalWrite(TX_PIN, LOW); + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +// Функции кодирования данных////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////// +void Oregon_TM::setType(word type) +{ + sens_type = type; + if (type == THP) + { + SendBuffer[0] = 0x55; + return; + } + SendBuffer[0] = (type & 0xFF00) >> 8; + SendBuffer[1] = type & 0x00FF; +} +/////////////////////////////////////////////////////////////////////////////////////////////////// + +void Oregon_TM::setChannel(byte channel) +{ + byte channel_code; + + if (sens_type == BTHR968) + { + channel_code = 0x00; + setId(0xF0); + send_time = 40000; + } + + if (sens_type == THGN132) + { + if (channel <= 1) + { + channel_code = 0x10; + setId(0xE3); + send_time = 39000; + } + if (channel == 2) + { + channel_code = 0x20; + setId(0xE3); + send_time = 41000; + } + if (channel == 3) + { + channel_code = 0x40; + setId(0xBB); + send_time = 43000; + } + protocol = 2; + } + + if (sens_type == THN132) + { + if (channel <= 1) + { + channel_code = 0x10; + setId(0xE3); + send_time = 39000; + } + if (channel == 2) + { + channel_code = 0x20; + setId(0xE3); + send_time = 41000; + } + if (channel == 3) + { + channel_code = 0x40; + setId(0xBB); + send_time = 43000; + } + protocol = 2; + } + + + if (sens_type == RTGN318 || sens_type == BTHGN129) + { + + if (channel <= 1) + { + channel_code = 0x10; + setId(0xF1); + send_time = 53000; + } + if (channel == 2) + { + channel_code = 0x20; + setId(0x92); + send_time = 59000; + } + if (channel == 3) + { + channel_code = 0x30; + setId(0xAA); + send_time = 61000; + } + + if (channel == 4) + { + channel_code = 0x40; + setId(0x8A); + send_time = 67000; + } + + if (channel >= 5) + { + channel_code = 0x50; + setId(0xB1); + send_time = 71000; + } + protocol = 2; + } + + if (sens_type == THGR810) + { + if (channel <= 1) + { + channel_code = 0x10; + setId(0xCB); + send_time = 53000; + } + if (channel == 2) + { + channel_code = 0x20; + setId(0x69); + send_time = 59000; + } + if (channel == 3) + { + channel_code = 0x30; + setId(0xAA); + send_time = 61000; + } + if (channel == 4) + { + channel_code = 0x40; + setId(0x8A); + send_time = 67000; + } + if (channel == 5) + { + channel_code = 0x50; + setId(0xB1); + send_time = 71000; + } + if (channel == 6) + { + channel_code = 0x60; + send_time = 79000; + } + if (channel == 7) + { + channel_code = 0x70; + send_time = 83000; + } + if (channel == 8) + { + channel_code = 0x80; + send_time = 87000; + } + if (channel == 9) + { + channel_code = 0x90; + send_time = 91000; + } + if (channel >= 10) + { + channel_code = 0xA0; + send_time = 93000; + } + protocol = 3; + } + SendBuffer[2] &= 0x0F; + SendBuffer[2] += channel_code & 0xF0; +} +/////////////////////////////////////////////////////////////////////////////////////////////////// + +void Oregon_TM::setId(byte ID) +{ + SendBuffer[2] &= 0xF0; + SendBuffer[2] += (ID & 0xF0) >> 4; + SendBuffer[3] &= 0x0F; + SendBuffer[3] += (ID & 0x0F) << 4; +} +/////////////////////////////////////////////////////////////////////////////////////////////////// + +void Oregon_TM::setBatteryFlag(bool level) +{ + SendBuffer[3] &= 0xFB; + if (level) SendBuffer[3] |= 0x04; +} +/////////////////////////////////////////////////////////////////////////////////////////////////// + +void Oregon_TM::setStartCount(byte startcount) +{ + SendBuffer[3] &= 0xF4; + if (startcount == 8) SendBuffer[3] |= 0x08; + if (startcount == 2) SendBuffer[3] |= 0x02; + if (startcount == 1) SendBuffer[3] |= 0x01; +} + + +/////////////////////////////////////////////////////////////////////////////////////////////////// + +void Oregon_TM::setPressure(float mm_hg_pressure) +{ + //Ограничения датчика по даташиту + word pressure = (word)(mm_hg_pressure / 0.75); + if (mm_hg_pressure < 450) pressure = 600; + if (mm_hg_pressure > 790) pressure = 1054; + + if (sens_type == BTHR968) + { + pressure -= 600; + SendBuffer[7] &= 0xF0; + SendBuffer[7] += pressure & 0x0F; + SendBuffer[8] = (pressure & 0x0F0) + ((pressure & 0xF00) >> 8); + //прогноз - переменно + SendBuffer[9] &= 0x0F; + SendBuffer[9] += 0x60; + } + + if (sens_type == BTHGN129) + { + pressure -= 545; + SendBuffer[7] &= 0xF0; + SendBuffer[7] += pressure & 0x0F; + SendBuffer[8] = (pressure & 0x0F0) + ((pressure & 0xF00) >> 8); + SendBuffer[9] &= 0x0F; + SendBuffer[9] += 0x60; + } + +} +/////////////////////////////////////////////////////////////////////////////////////////////////// +void Oregon_TM::setTemperature(float temp) +{ + if (temp < 0) + { + SendBuffer[5] = 0x08; + temp *= -1; + } + else + { + SendBuffer[5] = 0x00; + } + byte tempInt = (byte) temp; + byte td = (tempInt / 10); + byte tf = tempInt - td * 10; + byte tempFloat = (temp - (float)tempInt) * 10; + + SendBuffer[5] += (td << 4); + SendBuffer[4] = tf; + SendBuffer[4] |= (tempFloat << 4); +} +/////////////////////////////////////////////////////////////////////////////////////////////////// + +void Oregon_TM::setHumidity(byte hum) +{ + if (sens_type != THN132) + { + SendBuffer[6] = (hum / 16); + SendBuffer[6] += (hum - (SendBuffer[6] * 16)) << 4; + } +} +/////////////////////////////////////////////////////////////////////////////////////////////////// + +void Oregon_TM::setComfort(float temp, byte hum) +{ + if (sens_type != THN132) + { + if (hum > 70) + { + SendBuffer[7] = 0xC0; + return; + } + if (hum < 40) + { + SendBuffer[7] = 0x80; + return; + } + if (temp > 20 && temp < 25) + { + SendBuffer[7] = 0x40; + return; + } + else SendBuffer[7] = 0x00; + return; + } +} +/////////////////////////////////////////////////////////////////////////////////////////////////// + +bool Oregon_TM::transmit() +{ + if (millis() >= time_marker_send && send_time) + { + SendPacket(); + time_marker_send = millis() + send_time; + return true; + } + else return false; +} + + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////// +//Поддержка датчика THP +/////////////////////////////////////////////////////////////////////////////////////////////////// + + +void Oregon_TM::setChannelTHP(byte channel) +{ + SendBuffer[1] &= 0x0F; + SendBuffer[1] += channel << 4; +} +/////////////////////////////////////////////////////////////////////////////////////////////////// +void Oregon_TM::setBatteryTHP( word bat_voltage) +{ + SendBuffer[6] = (bat_voltage & 0x0FF0) >> 4; + SendBuffer[7] &= 0x0F; + SendBuffer[7] += (bat_voltage & 0x000F) << 4; + +} +/////////////////////////////////////////////////////////////////////////////////////////////////// +void Oregon_TM::setTemperatureTHP(float bme_temperature) +{ + word temp_code; + if (bme_temperature < -100 || bme_temperature > 100) temp_code = 0x0FFF; + else temp_code = (word)((bme_temperature + 100) * 10); + SendBuffer[2] = temp_code & 0x00FF; + SendBuffer[1] &= 0xF0; + SendBuffer[1] += (temp_code & 0x0F00) >> 8; +} +/////////////////////////////////////////////////////////////////////////////////////////////////// +void Oregon_TM::setHumidityTHP(float bme_humidity) +{ + word hum_code; + if (bme_humidity > 100) hum_code = 0x0FFF; + else hum_code = (word)(bme_humidity * 10); + SendBuffer[3] = (hum_code & 0x0FF0) >> 4; + SendBuffer[4] &= 0x0F; + SendBuffer[4] += (hum_code & 0x000F) << 4; +} +/////////////////////////////////////////////////////////////////////////////////////////////////// +void Oregon_TM::setPressureTHP(float bme_pressure) +{ + word pres_code; + if (bme_pressure < 500) pres_code = 0x0000; + else pres_code = (word)((bme_pressure - 500) * 10); + SendBuffer[5] = pres_code & 0x00FF; + SendBuffer[4] &= 0xF0; + SendBuffer[4] += (pres_code & 0x0F00) >> 8; +} +/////////////////////////////////////////////////////////////////////////////////////////////////// +void Oregon_TM::setErrorTHP() +{ + SendBuffer[1] |= 0x0F; + SendBuffer[2] = 0xFF; + SendBuffer[3] = 0xFF; + SendBuffer[4] = 0xFF; + SendBuffer[5] = 0xFF; +} +/////////////////////////////////////////////////////////////////////////////////////////////////// +void Oregon_TM::calculateAndSetChecksumTHP() +{ + byte CCIT_POLY = 0x07; + SendBuffer[7] = SendBuffer[7] & 0xF0; + SendBuffer[8] = 0x00; + SendBuffer[9] = 0x00; + byte summ = 0x00; + byte crc = 0x00; + byte cur_nible; + for (int i = 0; i < 8; i++) + { + cur_nible = (SendBuffer[i] & 0xF0) >> 4; + summ += cur_nible; + crc ^= cur_nible; + for (int j = 0; j < 4; j++) + if (crc & 0x80) crc = (crc << 1) ^ CCIT_POLY; + else crc <<= 1; + + cur_nible = SendBuffer[i] & 0x0F; + summ += cur_nible; + crc ^= cur_nible; + for (int j = 0; j < 4; j++) + if (crc & 0x80) crc = (crc << 1) ^ CCIT_POLY; + else crc <<= 1; + } + SendBuffer[7] += summ & 0x0F; + SendBuffer[8] += summ & 0xF0; + SendBuffer[8] += crc & 0x0F; + SendBuffer[9] += crc & 0xF0; +} + diff --git a/lib/Oregon_TM/Oregon_TM.h b/lib/Oregon_TM/Oregon_TM.h new file mode 100644 index 0000000..89d03dc --- /dev/null +++ b/lib/Oregon_TM/Oregon_TM.h @@ -0,0 +1,127 @@ +#include +#ifndef Oregon_TM_h +#define Oregon_TM_h +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// This file is part of the Arduino OREGON_NR library. +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// The MIT License (MIT) +// +// Copyright (c) 2021 Sergey Zawislak +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Этот файл - часть библиотеки OREGON_NR +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2021 Сергей Зависляк +// +// Данная лицензия разрешает лицам, получившим копию данного программного обеспечения и сопутствующей документации +// (в дальнейшем именуемыми «Программное Обеспечение»), безвозмездно использовать Программное Обеспечение без ограничений, +// включая неограниченное право на использование, копирование, изменение, слияние, публикацию, распространение, сублицензирование +// и/или продажу копий Программного Обеспечения, а также лицам, которым предоставляется данное Программное Обеспечение, при соблюдении следующих условий: +// +// Указанное выше уведомление об авторском праве и данные условия должны быть включены во все копии или значимые части данного Программного Обеспечения. +// +// ДАННОЕ ПРОГРАММНОЕ ОБЕСПЕЧЕНИЕ ПРЕДОСТАВЛЯЕТСЯ «КАК ЕСТЬ», БЕЗ КАКИХ-ЛИБО ГАРАНТИЙ, ЯВНО ВЫРАЖЕННЫХ ИЛИ ПОДРАЗУМЕВАЕМЫХ, ВКЛЮЧАЯ ГАРАНТИИ ТОВАРНОЙ +// ПРИГОДНОСТИ, СООТВЕТСТВИЯ ПО ЕГО КОНКРЕТНОМУ НАЗНАЧЕНИЮ И ОТСУТСТВИЯ НАРУШЕНИЙ, НО НЕ ОГРАНИЧИВАЯСЬ ИМИ. НИ В КАКОМ СЛУЧАЕ АВТОРЫ ИЛИ ПРАВООБЛАДАТЕЛИ +// НЕ НЕСУТ ОТВЕТСТВЕННОСТИ ПО КАКИМ-ЛИБО ИСКАМ, ЗА УЩЕРБ ИЛИ ПО ИНЫМ ТРЕБОВАНИЯМ, В ТОМ ЧИСЛЕ, ПРИ ДЕЙСТВИИ КОНТРАКТА, ДЕЛИКТЕ ИЛИ ИНОЙ СИТУАЦИИ, +// ВОЗНИКШИМ ИЗ-ЗА ИСПОЛЬЗОВАНИЯ ПРОГРАММНОГО ОБЕСПЕЧЕНИЯ ИЛИ ИНЫХ ДЕЙСТВИЙ С ПРОГРАММНЫМ ОБЕСПЕЧЕНИЕМ. +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +#define TR_TIME 488 +#define TWOTR_TIME 976 +#define PULSE_SHORTEN_2 93 +#define PULSE_SHORTEN_3 138 + +#define THGN132 0x1D20 +#define THN132 0xEC40 +#define THGR810 0xF824 +#define RTGN318 0xDCC3 +#define THP 0x5500 +#define BTHGN129 0x5D53 +#define BTHR968 0x5D60 + + +#define OREGON_SEND_BUFFER_SIZE 12 + +//static byte TX_PIN = 4; +static byte TX_PIN; + + +class Oregon_TM +{ + public: + + int max_buffer_size = OREGON_SEND_BUFFER_SIZE; + int buffer_size = 24; + byte* SendBuffer; + byte protocol = 2; + word sens_type = 0x0000; + int timing_corrector2 = 4; + int timing_corrector3 = 2; + + Oregon_TM(byte, int); + Oregon_TM(byte); + Oregon_TM(); + void setType(word); + void setChannel( byte); + void setId(byte); + void setBatteryFlag(bool); + void setStartCount(byte); + void setTemperature(float); + void setHumidity(byte); + void setComfort(float, byte); + void setPressure(float); + bool transmit(); + void SendPacket(); + + void setErrorTHP(); + void setPressureTHP(float); + void setTemperatureTHP(float); + void setBatteryTHP(word); + void setChannelTHP(byte); + void setHumidityTHP(float); + + + + private: + + void sendZero(void); + void sendOne(void); + void sendMSB(const byte); + void sendLSB(const byte); + void sendData(); + void sendOregon(); + void sendPreamble(); + void calculateAndSetChecksum132(); + void calculateAndSetChecksum318(); + void calculateAndSetChecksum810(); + void calculateAndSetChecksum968(); + void calculateAndSetChecksum129(); + void calculateAndSetChecksum132S(); + + + void calculateAndSetChecksumTHP(); + + unsigned long time_marker = 0; + unsigned long time_marker_send = 0; + unsigned long send_time = 0; + bool prevbit = 1; + bool prevstate = 1; + + +}; + +#endif + diff --git a/lib/Oregon_TM/examples/send_oregon.ino b/lib/Oregon_TM/examples/send_oregon.ino new file mode 100644 index 0000000..18b1d2b --- /dev/null +++ b/lib/Oregon_TM/examples/send_oregon.ino @@ -0,0 +1,69 @@ +#include + +//Имитатор датчиков температуры и влажности Oregon Scientific + +//В данном примере иммитируются пакеты следующих типов датчиков: +//THGN132N (тип 1D20h, протокол v2, 3 канала) +//RTGN318 (тип ХСС3h, протокол v2, 5 каналов) +//THGR810 (тип F824h, протокол v3, 10 каналов) +//BTHR968 (тип 5D60h, протокол v2, 1 канал) +//BTHGN129 (тип 5D53h, протокол v2, 5 каналов) +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//Вывод передатчика один. Указывается только при создании первого объекта. В данном примере передатчик подключен к D4 + +Oregon_TM transmitter(4); + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void setup() +{ + //Serial.begin(9600); + + //THGN132 + //transmitter.setType(THGN132); + transmitter.setType(THGR810); + //transmitter.setType(RTGN318); + //transmitter.setType(BTHGN129); + //transmitter.setType(BTHR968); + transmitter.setChannel(3); // Номер канала для THGN132 - 1...3 + transmitter.setBatteryFlag(1); // Флаг разряженной батарейки + //transmitter.setComfort(24.2, 30); // Расчёт передаваемого индекса комфорта + +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void loop() +{ + //передача осуществляется по таймеру, который определяется типом датчика и номером канала + + //Serial.println("TX"); + // Displays temperatures from -10 degrees Celsius to +20, + // and humidity from 10% REL to 40% REL, with increments of 2 + for (int i = -10; i<=20; i+=2) { + // Temperatures are passed at 10 times the real value, + // to avoid using floating point math. + transmitter.setTemperature((float) i); // -49.9C...+69.9C + transmitter.setHumidity(i+20); // 2...98% + transmitter.SendPacket(); + + // Wait two seconds before sending next. + delay(2000); + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/*void PrintSentData(byte* buf, int buf_size) +{ + Serial.print(millis() / 1000); + Serial.print("s \t\t"); + for (byte i = 0; i < buf_size; i++) + { + byte trmbuf = *buf; + Serial.print(trmbuf >> 4, HEX); + i++; + if (i >= buf_size) break; + Serial.print(trmbuf & 0x0F, HEX); + buf++; + } + Serial.println(); + delay(1000); +}*/ diff --git a/lib/SensorTransmitter/SensorTransmitter.cpp b/lib/SensorTransmitter/SensorTransmitter.cpp new file mode 100644 index 0000000..dffc2d4 --- /dev/null +++ b/lib/SensorTransmitter/SensorTransmitter.cpp @@ -0,0 +1,183 @@ +/* + * RemoteSensor library v1.0.2 (20130601) for Arduino 1.0 + * + * This library encodes, encrypts en transmits data to + * remote weather stations made by Hideki Electronics.. + * + * Copyright 2011-2013 by Randy Simons http://randysimons.nl/ + * + * Parts of this code based on Oopsje's CrestaProtocol.pdf, for which + * I thank him very much! + * + * License: GPLv3. See license.txt + */ + +#include + + + +/******************* + * Sensor base class + ******************/ + +SensorTransmitter::SensorTransmitter(byte transmitterPin, byte randomId) { + _transmitterPin = transmitterPin; + _randomId = randomId; + + pinMode(_transmitterPin, OUTPUT); +} + +/* Encrypt data byte to send to station */ +byte SensorTransmitter::encryptByte(byte b) { + byte a; + for(a=0; b; b<<=1) { + a^=b; + } + return a; +} + +/* The second checksum. Input is OldChecksum^NewByte */ +byte SensorTransmitter::secondCheck(byte b) { + byte c; + if (b&0x80) { + b^=0x95; + } + c = b^(b>>1); + if (b&1) { + c^=0x5f; + } + if (c&1) { + b^=0x5f; + } + return b^(c>>1); +} + +/* Example to encrypt a package for sending, + Input: Buffer holds the unencrypted data. + Returns the number of bytes to send, + Buffer now holds data ready for sending. + */ +byte SensorTransmitter::encryptAndAddCheck(byte *buffer) { + byte cs1,cs2,count,i; + + count=(buffer[2]>>1) & 0x1f; + cs1=0; + cs2=0; + for(i=1; i>=1; + } + } +} + +/* Send bytes (prepared by “encryptAndAddCheck”) and pause at the end. */ +void SensorTransmitter::sendManchesterPackage(byte transmitterPin, byte *data, byte cnt) { + byte i; + + for (i=0; i=0x40; temp+=0x40) { /* Sends 3 packages */ + memcpy(buffer, data, ((data[2] >> 1) & 0x1f) + 1); + + buffer[3] = (buffer[3] & 0x1f) + temp; + + count = encryptAndAddCheck(buffer); /* Encrypt, add checksum bytes */ + sendManchesterPackage(transmitterPin, buffer,count); /* Send the package */ + + delay(30); + } +} + + +/************************************ + * Thermo / Hygro sensor transmitter + ***********************************/ + +ThermoHygroTransmitter::ThermoHygroTransmitter(byte transmitterPin, byte randomId, byte channel) : SensorTransmitter(transmitterPin, randomId) { + _channel = channel; +} + +void ThermoHygroTransmitter::sendTempHumi(int temperature, byte humidity) { + byte buffer[10]; + + // Note: temperature is 10x the actual temperature! So, 23.5 degrees is passed as 235. + + buffer[0] = 0x75; /* Header byte */ + buffer[1] = (_channel << 5) | _randomId ; /* Thermo-hygro at channel 1 (see table1)*/ + buffer[2] = 0xce; /* Package size byte for th-sensor */ + buffer[3] = 0x1e; /* Themo/Hygro */ + + if ( temperature < 0 ) { + buffer[5] = 0x4 << 4; // High nibble is 0x4 for sub zero temperatures... + temperature = -temperature; // Make temperature positive + } else { + buffer[5] = 0xc << 4; // ...0xc for positive + } + + // Note: temperature is now always positive! + buffer[4] = (((temperature % 100) / 10 ) << 4) | // the "3" from 23.5 + (temperature % 10); // the "5" from 23.5 + buffer[5] |= (temperature / 100); // the "2" from 23.5 + + buffer[6] = ((humidity / 10) << 4) | (humidity % 10); // BCD encoded + + buffer[7]=0xff; /* Comfort flag */ + + sendPackage(_transmitterPin, buffer); +} + +void ThermoHygroTransmitter::sendRainlevel(unsigned int level){ // in 0.7mm + byte buffer[10]; + + // Note: temperature is 10x the actual temperature! So, 23.5 degrees is passed as 235. + + buffer[0] = 0x75; /* Header byte */ + buffer[1] = (_channel << 5) | _randomId ; /* Thermo-hygro at channel 1 (see table1)*/ + buffer[2] = 0xcc; /* Package size byte for rain-sensor */ + buffer[3] = 0x0e; /* Rain level */ + + // Note: temperature is now always positive! + buffer[4] = level & 0xff; // Low byte + buffer[5] = (level >>8) & 0xff; // High byte + + sendPackage(_transmitterPin, buffer); +} diff --git a/lib/SensorTransmitter/SensorTransmitter.h b/lib/SensorTransmitter/SensorTransmitter.h new file mode 100644 index 0000000..11ef55d --- /dev/null +++ b/lib/SensorTransmitter/SensorTransmitter.h @@ -0,0 +1,112 @@ +/* + * RemoteSensor library v1.0.2 (20130601) for Arduino 1.0 + * + * This library encodes, encrypts en transmits data to + * remote weather stations made by Hideki Electronics.. + * + * Copyright 2011-2013 by Randy Simons http://randysimons.nl/ + * + * Parts of this code based on Oopsje's CrestaProtocol.pdf, for which + * I thank him very much! + * + * License: GPLv3. See license.txt + */ + +#ifndef SensorTransmitter_h +#define SensorTransmitter_h + +#include + +/** + * SensorTransmitter provides a generic class to simulate Cresta weather sensors, for use + * with Cresta weather stations. + * E.g. http://www.cresta.nl/index.php?Itemid=2&option=com_zoo&view=item&category_id=32&item_id=281&lang=en + * + * Cresta is just a brand name. The original OEM seems to be Hideki Electronics. There are + * other brands which use the same hardware and / or protocol. As far as I know these include + * Mebus, Irox, Honeywell, Cresta and RST. + * + * Hardware required for this library: a 433MHz/434MHz SAW oscillator transmitter, e.g. + * http://www.sparkfun.com/products/10534 + * http://www.conrad.nl/goto/?product=130428 + */ +class SensorTransmitter { + public: + /** + * Initializes the transmitter. About the random id: "A sensor selects a random value + * in the range of column 1 when it is reset. It keeps the same ID until it is reset again." + * You can leave it at 0 for most purposes + * The transmitter pin is set in OUTPUT mode; you don't have to do this yourself. + * + * @param transmitterPin Arduino-pin connected to the 433MHz transmitter + * @param randomId A "random" value in the range [0..31] + */ + SensorTransmitter(byte transmitterPin, byte randomId); + + /** + * Sends a raw sensor package. Before transmitting, the data is encrypted and checksums are + * added. The buffer of the data doesn't need to have room for the checksums, as the data is + * copied internally to a new buffer which is always large enough. + * However, the data must be valid! + * + * The data is transmitted 3 times. + * + * Note that this is a static method. + * + * @param transmitterPin Arduino-pin connected to the 433MHz transmitter + * @param data Pointer to data to transmit + */ + static void sendPackage(byte transmitterPin, byte *data); + + protected: + byte _transmitterPin; + byte _randomId; + + private: + /** + * Sends data as manchester encoded stream + */ + static void sendManchesterPackage(byte transmitterPin, byte *data, byte cnt); + + /** + * Sends a single byte as manchester encoded stream + */ + static void sendManchesterByte(byte transmitterPin, byte b); + + /** + * Encryption and checksum + */ + static byte encryptAndAddCheck(byte *buffer); + static byte secondCheck(byte b); + static byte encryptByte(byte b); +}; + +class ThermoHygroTransmitter : public SensorTransmitter { + public: + /** + * Mimics a Thermo / Hygro sensor. The channel of this device can be 1..5, but note + * that only the more expensive receivers can use channels 4 and 5. However, for use + * in conjunction with SensorReceiver this is of no concern. + * + * @param transmitterPin Arduino-pin connected to the 433MHz transmitter + * @param randomId A "random" value in the range [0..31] + * @channel The channel of this sensor, range [1..5] + * @see SensorTransmitter::SensorTransmitter (constructor) + */ + ThermoHygroTransmitter(byte transmitterPin, byte randomId, byte channel); + + /** + * Sends temperature and humidity. + * + * @param temperature 10x the actual temperature. You want to send 23,5 degrees, then temperature should be 235. + * @param humidty Humidity in percentage-points REL. Thus, for 34% REH humidity should be 34. + */ + void sendTempHumi(int temperature, byte humidity); + void sendRainlevel(unsigned int level); // in 0.7mm + + private: + byte _channel; // Note: internally, the channels for the thermo/hygro-sensor are mapped as follow: + // 1=>1, 2=>2, 3=>3, 4=>5, 5=>6. + // This because interally the rain sensor, UV sensor and anemometer are on channel 4. +}; +#endif \ No newline at end of file diff --git a/lib/SensorTransmitter/examples/ThermoHygroTransmitter.ino b/lib/SensorTransmitter/examples/ThermoHygroTransmitter.ino new file mode 100644 index 0000000..a507357 --- /dev/null +++ b/lib/SensorTransmitter/examples/ThermoHygroTransmitter.ino @@ -0,0 +1,28 @@ +/* + * This sketch sends (bogus) thermo / hygro data to a remote weather sensors made by Cresta. + * + * Setup: + * - connect transmitter input of a 433MHz transmitter to digital pin 11 + * - On the weather station, activate the "scan" function for channel 1. + */ + + #include + + // Initializes a ThermoHygroTransmitter on pin 11, with "random" ID 0, on channel 1. + ThermoHygroTransmitter transmitter(4, 0, 1); + + void setup() { + } + + void loop() { + // Displays temperatures from -10 degrees Celsius to +20, + // and humidity from 10% REL to 40% REL, with increments of 2 + for (int i = -10; i<=20; i+=2) { + // Temperatures are passed at 10 times the real value, + // to avoid using floating point math. + transmitter.sendTempHumi(i * 10, i + 20); + + // Wait two seconds before sending next. + delay(2000); + } + } diff --git a/lib/inFactory/inFactoryReceive.ino b/lib/inFactory/inFactoryReceive.ino new file mode 100644 index 0000000..56f7a93 --- /dev/null +++ b/lib/inFactory/inFactoryReceive.ino @@ -0,0 +1,150 @@ +/* + + Transmissions includes an id. Every 60 seconds the sensor transmits 6 packets: + + 0000 1111 | 0011 0000 | 0101 1100 | 1110 0111 | 0110 0001 + iiii iiii | cccc ub?? | tttt tttt | tttt hhhh | hhhh ??nn + + - i: identification // changes on battery switch + - c: CRC-4 // CCITT checksum, see below for computation specifics + - u: unknown // (sometimes set at power-on, but not always) + - b: battery low // flag to indicate low battery voltage + - h: Humidity // BCD-encoded, each nibble is one digit, 'A0' means 100%rH + - t: Temperature // in °F as binary number with one decimal place + 90 °F offset + - n: Channel // Channel number 1 - 3 +*/ + +int old_duration = 0; +int new_duration = 0; + +unsigned long old_time = 0; +unsigned long new_time = 0; + +boolean old_status = LOW; +boolean new_status = LOW; +boolean started = false; +byte input = 2; + +uint8_t msg[5]; +byte indexBit = 0; + + + +void setup() { + // put your setup code here, to run once: + pinMode(input, INPUT_PULLUP); + Serial.begin(500000); +} + +void loop() { + // put your main code here, to run repeatedly: + receive(); + +} + +void receive() { + new_status = digitalRead(input); + if (new_status != old_status) { + new_time = micros(); + new_duration = new_time - old_time; + + if (!new_status && !started) { + if (durationEquals(old_duration, 7850, 100) && (durationEquals(new_duration, 600, 100)) ) {// __________|"| //that's a start + started = true; + } + } + else if (new_status && started) {//right after rising edge + + if (durationEquals(old_duration, 600, 100)) { + if (new_duration < 2700) {// it's a 0 + bitWrite(msg[indexBit / 8], 7 - (indexBit % 8), 0); + } + else {// it's a 1 + bitWrite(msg[indexBit / 8], 7 - (indexBit % 8), 1); + } + indexBit++; + } + else { // abort if high level duration is not correct + started = false; + indexBit = 0; + } + if (indexBit >= 40) {// stop when 40 bits are received + started = false; + indexBit = 0; + + if (infactory_crc_check()) { + decode(); + } + else { + Serial.println("CRC Error"); + } + } + } + + old_time = new_time; + old_duration = new_duration; + } + old_status = new_status; +} + +void decode() { + byte iD = msg[0]; + bool battery_low = (msg[1] >> 2) & 1; + int temp_raw = (msg[2] << 4) | (msg[3] >> 4); + byte humidity = (msg[3] & 0x0F) * 10 + (msg[4] >> 4); // BCD, 'A0'=100%rH + byte channel = msg[4] & 0x03; + float temp_f = ((float)temp_raw * 0.1 - 122) * 5 / 9.; + Serial.print("iD="); + Serial.print(iD, HEX); + Serial.print("; lo_bat="); + Serial.print(battery_low); + Serial.print("; hum="); + Serial.print(humidity); + Serial.print("; chn="); + Serial.print(channel); + Serial.print("; tmp="); + Serial.println(temp_f); +} + +bool durationEquals(int duration1, int duration2, int tolerance) { + if (abs(duration1 - duration2) <= tolerance) return true; + else return false; +} + +void printHex(byte b) { + if (b < 0x10) Serial.print("0"); + Serial.print(b, HEX); +} + + + +uint8_t crc4(uint8_t const message[], unsigned nBytes, uint8_t polynomial, uint8_t init) +{ + unsigned remainder = init << 4; // LSBs are unused + unsigned poly = polynomial << 4; + unsigned bit; + + while (nBytes--) { + remainder ^= *message++; + for (bit = 0; bit < 8; bit++) { + if (remainder & 0x80) { + remainder = (remainder << 1) ^ poly; + } else { + remainder = (remainder << 1); + } + } + } + return remainder >> 4 & 0x0f; // discard the LSBs +} + + +bool infactory_crc_check() { + uint8_t msg_crc, crc; + msg_crc = msg[1] >> 4; + // for CRC computation, channel bits are at the CRC position(!) + msg[1] = (msg[1] & 0x0F) | (msg[4] & 0x0F) << 4; + // crc4() only works with full bytes + crc = crc4(msg, 4, 0x13, 0); // Koopmann 0x9, CCITT-4; FP-4; ITU-T G.704 + crc ^= msg[4] >> 4; // last nibble is only XORed + return (crc == msg_crc); +} diff --git a/lib/inFactory/inFactoryTransmit.ino b/lib/inFactory/inFactoryTransmit.ino new file mode 100644 index 0000000..0facafa --- /dev/null +++ b/lib/inFactory/inFactoryTransmit.ino @@ -0,0 +1,116 @@ + +#define PIN_TX 4 + +uint8_t msg[5]; + +uint8_t id = 0x2C; +uint8_t channel = 0x01; +bool batteryOK = 0;// 0 means OK +uint8_t humidity = 69; +float temperature = 1.; + +void setup() { + Serial.begin(9600); + pinMode(PIN_TX, OUTPUT); + + +} + +void loop() { + // put your main code here, to run repeatedly: + temperature += 1.; + if (temperature >= 35.) temperature = 1.; + + + buildMsg(); + sendRF(); + + delay(10000); + +} + +void buildMsg(){ + + // put your setup code here, to run once: + msg[0] = id; + msg[4] = channel + ((humidity%10) << 4 ); + msg[3] = humidity/10; + + int binTemp = ((temperature * 9./5) + 32 + 90)*10; + msg[3] += (binTemp & 0x0F) << 4; + msg[2] = binTemp >> 4; + + msg[1] = batteryOK << 2; + + msg[1] = (msg[1] & 0x0F) | (msg[4] & 0x0F) << 4; + // crc4() only works with full bytes + uint8_t crc = crc4(msg, 4, 0x13, 0); // Koopmann 0x9, CCITT-4; FP-4; ITU-T G.704 + crc ^= msg[4] >> 4; // last nibble is only XORed + + msg[1] = (msg[1] & 0x0F) | (crc & 0x0F) << 4; + + for (int i=0; i<=4; i++){ + + Serial.print(msg[i],HEX); + Serial.print(" "); + + } +} + +uint8_t crc4(uint8_t const message[], unsigned nBytes, uint8_t polynomial, uint8_t init) +{ + unsigned remainder = init << 4; // LSBs are unused + unsigned poly = polynomial << 4; + unsigned bit; + + while (nBytes--) { + remainder ^= *message++; + for (bit = 0; bit < 8; bit++) { + if (remainder & 0x80) { + remainder = (remainder << 1) ^ poly; + } else { + remainder = (remainder << 1); + } + } + } + return remainder >> 4 & 0x0f; // discard the LSBs +} + +void sendRF() +{ +/*low long 4000 +low short 1800 +high short 700 +*/ + + for (int n=0; n<6; n++) + { + for (int i=0; i<10; i++){ + digitalWrite(PIN_TX, !(i%2)); + delayMicroseconds(1000); + } + delayMicroseconds(7000); + + for (int i = 0; i<5; i++) { + for (int b = 7; b>=0; b--) { + digitalWrite(PIN_TX, HIGH); + delayMicroseconds(700); + digitalWrite(PIN_TX, LOW); + if (bitRead(msg[i], b)){ + delayMicroseconds(4000); + } + else{ + delayMicroseconds(1800); + } + + } + + + } + digitalWrite(PIN_TX, HIGH); + delayMicroseconds(1000); + digitalWrite(PIN_TX, LOW); + + delayMicroseconds(16000); + } +} diff --git a/lib/rc-switch/.gitignore b/lib/rc-switch/.gitignore new file mode 100644 index 0000000..d0972bb --- /dev/null +++ b/lib/rc-switch/.gitignore @@ -0,0 +1,17 @@ +# Mac stuff +.DS_Store + +# Compiled Object files +*.slo +*.lo +*.o + +# Compiled Dynamic libraries +*.so +*.dylib + +# Compiled Static libraries +*.lai +*.la +*.a + diff --git a/lib/rc-switch/.piopm b/lib/rc-switch/.piopm new file mode 100644 index 0000000..8ad06c7 --- /dev/null +++ b/lib/rc-switch/.piopm @@ -0,0 +1 @@ +{"type": "library", "name": "rc-switch", "version": "2.6.4", "spec": {"owner": "sui77", "id": 246, "name": "rc-switch", "requirements": null, "uri": null}} \ No newline at end of file diff --git a/lib/rc-switch/.travis.yml b/lib/rc-switch/.travis.yml new file mode 100644 index 0000000..d8de894 --- /dev/null +++ b/lib/rc-switch/.travis.yml @@ -0,0 +1,70 @@ +language: c +python: + - "2.7" + +# Cache PlatformIO packages using Travis CI container-based infrastructure +cache: + pip: true + directories: + - "~/.platformio" + +env: + - > + PLATFORMIO_CI_SRC=$PWD/examples/Webserver + ARDUINOIDE_CI_SRC=$PLATFORMIO_CI_SRC/Webserver.ino + BOARDS="--board=diecimilaatmega328 --board=uno --board=esp01" + - > + PLATFORMIO_CI_SRC=$PWD/examples/ReceiveDemo_Simple + ARDUINOIDE_CI_SRC=$PLATFORMIO_CI_SRC/ReceiveDemo_Simple.ino + BOARDS="--board=diecimilaatmega328 --board=uno --board=esp01" + - > + PLATFORMIO_CI_SRC=$PWD/examples/TypeC_Intertechno + ARDUINOIDE_CI_SRC=$PLATFORMIO_CI_SRC/TypeC_Intertechno.ino + BOARDS="--board=diecimilaatmega328 --board=uno --board=esp01 --board=attiny25 --board=attiny24" + - > + PLATFORMIO_CI_SRC=$PWD/examples/TypeD_REV + ARDUINOIDE_CI_SRC=$PLATFORMIO_CI_SRC/TypeD_REV.ino + BOARDS="--board=diecimilaatmega328 --board=uno --board=esp01 --board=attiny25 --board=attiny24" + - > + PLATFORMIO_CI_SRC=$PWD/examples/TypeA_WithDIPSwitches + ARDUINOIDE_CI_SRC=$PLATFORMIO_CI_SRC/TypeA_WithDIPSwitches.ino + BOARDS="--board=diecimilaatmega328 --board=uno --board=esp01 --board=attiny25 --board=attiny24" + - > + PLATFORMIO_CI_SRC=$PWD/examples/TypeA_WithDIPSwitches_Lightweight + ARDUINOIDE_CI_SRC=$PLATFORMIO_CI_SRC/TypeA_WithDIPSwitches_Lightweight.ino + BOARDS="--board=diecimilaatmega328 --board=uno --board=esp01 --board=attiny25 --board=attiny24" + - > + PLATFORMIO_CI_SRC=$PWD/examples/TypeB_WithRotaryOrSlidingSwitches + ARDUINOIDE_CI_SRC=$PLATFORMIO_CI_SRC/TypeB_WithRotaryOrSlidingSwitches.ino + BOARDS="--board=diecimilaatmega328 --board=uno --board=esp01 --board=attiny25 --board=attiny24" + - > + PLATFORMIO_CI_SRC=$PWD/examples/SendDemo + ARDUINOIDE_CI_SRC=$PLATFORMIO_CI_SRC/SendDemo.ino + BOARDS="--board=diecimilaatmega328 --board=uno --board=esp01" + - > + PLATFORMIO_CI_SRC=$PWD/examples/ReceiveDemo_Advanced + ARDUINOIDE_CI_SRC=$PLATFORMIO_CI_SRC/ReceiveDemo_Advanced.ino + BOARDS="--board=diecimilaatmega328 --board=uno --board=esp01" + +before_install: + # Arduino IDE + - "/sbin/start-stop-daemon --start --quiet --pidfile /tmp/custom_xvfb_1.pid --make-pidfile --background --exec /usr/bin/Xvfb -- :1 -ac -screen 0 1280x1024x16" + - sleep 3 + - export DISPLAY=:1.0 + - wget http://downloads.arduino.cc/arduino-1.8.10-linux64.tar.xz + - tar xf arduino-1.8.10-linux64.tar.xz + - sudo mv arduino-1.8.10 /usr/local/share/arduino + - sudo ln -s /usr/local/share/arduino/arduino /usr/local/bin/arduino + +install: + # Arduino IDE + - ln -s $PWD /usr/local/share/arduino/libraries/rc-switch + # PlatformIO + # - pip install -U platformio + # - platformio update + +script: + # Arduino IDE + - arduino --verify --board arduino:avr:uno ${ARDUINOIDE_CI_SRC} + # PlatformIO + # - platformio run --lib="." ${BOARDS} diff --git a/lib/rc-switch/RCSwitch.cpp b/lib/rc-switch/RCSwitch.cpp new file mode 100644 index 0000000..6c821cb --- /dev/null +++ b/lib/rc-switch/RCSwitch.cpp @@ -0,0 +1,712 @@ +/* + RCSwitch - Arduino libary for remote control outlet switches + Copyright (c) 2011 Suat Özgür. All right reserved. + + Contributors: + - Andre Koehler / info(at)tomate-online(dot)de + - Gordeev Andrey Vladimirovich / gordeev(at)openpyro(dot)com + - Skineffect / http://forum.ardumote.com/viewtopic.php?f=2&t=46 + - Dominik Fischer / dom_fischer(at)web(dot)de + - Frank Oltmanns / .(at)gmail(dot)com + - Andreas Steinel / A.(at)gmail(dot)com + - Max Horn / max(at)quendi(dot)de + - Robert ter Vehn / .(at)gmail(dot)com + - Johann Richard / .(at)gmail(dot)com + - Vlad Gheorghe / .(at)gmail(dot)com https://github.com/vgheo + - Matias Cuenca-Acuna + + Project home: https://github.com/sui77/rc-switch/ + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "RCSwitch.h" + +#ifdef RaspberryPi + // PROGMEM and _P functions are for AVR based microprocessors, + // so we must normalize these for the ARM processor: + #define PROGMEM + #define memcpy_P(dest, src, num) memcpy((dest), (src), (num)) +#endif + +#if defined(ESP8266) + // interrupt handler and related code must be in RAM on ESP8266, + // according to issue #46. + #define RECEIVE_ATTR ICACHE_RAM_ATTR + #define VAR_ISR_ATTR +#elif defined(ESP32) + #define RECEIVE_ATTR IRAM_ATTR + #define VAR_ISR_ATTR DRAM_ATTR +#else + #define RECEIVE_ATTR + #define VAR_ISR_ATTR +#endif + + +/* Format for protocol definitions: + * {pulselength, Sync bit, "0" bit, "1" bit, invertedSignal} + * + * pulselength: pulse length in microseconds, e.g. 350 + * Sync bit: {1, 31} means 1 high pulse and 31 low pulses + * (perceived as a 31*pulselength long pulse, total length of sync bit is + * 32*pulselength microseconds), i.e: + * _ + * | |_______________________________ (don't count the vertical bars) + * "0" bit: waveform for a data bit of value "0", {1, 3} means 1 high pulse + * and 3 low pulses, total length (1+3)*pulselength, i.e: + * _ + * | |___ + * "1" bit: waveform for a data bit of value "1", e.g. {3,1}: + * ___ + * | |_ + * + * These are combined to form Tri-State bits when sending or receiving codes. + */ +#if defined(ESP8266) || defined(ESP32) +static const VAR_ISR_ATTR RCSwitch::Protocol proto[] = { +#else +static const RCSwitch::Protocol PROGMEM proto[] = { +#endif + { 350, { 1, 31 }, { 1, 3 }, { 3, 1 }, false }, // protocol 1 + { 650, { 1, 10 }, { 1, 2 }, { 2, 1 }, false }, // protocol 2 + { 100, { 30, 71 }, { 4, 11 }, { 9, 6 }, false }, // protocol 3 + { 380, { 1, 6 }, { 1, 3 }, { 3, 1 }, false }, // protocol 4 + { 500, { 6, 14 }, { 1, 2 }, { 2, 1 }, false }, // protocol 5 + { 450, { 23, 1 }, { 1, 2 }, { 2, 1 }, true }, // protocol 6 (HT6P20B) + { 150, { 2, 62 }, { 1, 6 }, { 6, 1 }, false }, // protocol 7 (HS2303-PT, i. e. used in AUKEY Remote) + { 200, { 3, 130}, { 7, 16 }, { 3, 16}, false}, // protocol 8 Conrad RS-200 RX + { 200, { 130, 7 }, { 16, 7 }, { 16, 3 }, true}, // protocol 9 Conrad RS-200 TX + { 365, { 18, 1 }, { 3, 1 }, { 1, 3 }, true }, // protocol 10 (1ByOne Doorbell) + { 270, { 36, 1 }, { 1, 2 }, { 2, 1 }, true }, // protocol 11 (HT12E) + { 320, { 36, 1 }, { 1, 2 }, { 2, 1 }, true } // protocol 12 (SM5212) +}; + +enum { + numProto = sizeof(proto) / sizeof(proto[0]) +}; + +#if not defined( RCSwitchDisableReceiving ) +volatile unsigned long RCSwitch::nReceivedValue = 0; +volatile unsigned int RCSwitch::nReceivedBitlength = 0; +volatile unsigned int RCSwitch::nReceivedDelay = 0; +volatile unsigned int RCSwitch::nReceivedProtocol = 0; +int RCSwitch::nReceiveTolerance = 60; +const unsigned int RCSwitch::nSeparationLimit = 4300; +// separationLimit: minimum microseconds between received codes, closer codes are ignored. +// according to discussion on issue #14 it might be more suitable to set the separation +// limit to the same time as the 'low' part of the sync signal for the current protocol. +unsigned int RCSwitch::timings[RCSWITCH_MAX_CHANGES]; +#endif + +RCSwitch::RCSwitch() { + this->nTransmitterPin = -1; + this->setRepeatTransmit(10); + this->setProtocol(1); + #if not defined( RCSwitchDisableReceiving ) + this->nReceiverInterrupt = -1; + this->setReceiveTolerance(60); + RCSwitch::nReceivedValue = 0; + #endif +} + +/** + * Sets the protocol to send. + */ +void RCSwitch::setProtocol(Protocol protocol) { + this->protocol = protocol; +} + +/** + * Sets the protocol to send, from a list of predefined protocols + */ +void RCSwitch::setProtocol(int nProtocol) { + if (nProtocol < 1 || nProtocol > numProto) { + nProtocol = 1; // TODO: trigger an error, e.g. "bad protocol" ??? + } +#if defined(ESP8266) || defined(ESP32) + this->protocol = proto[nProtocol-1]; +#else + memcpy_P(&this->protocol, &proto[nProtocol-1], sizeof(Protocol)); +#endif +} + +/** + * Sets the protocol to send with pulse length in microseconds. + */ +void RCSwitch::setProtocol(int nProtocol, int nPulseLength) { + setProtocol(nProtocol); + this->setPulseLength(nPulseLength); +} + + +/** + * Sets pulse length in microseconds + */ +void RCSwitch::setPulseLength(int nPulseLength) { + this->protocol.pulseLength = nPulseLength; +} + +/** + * Sets Repeat Transmits + */ +void RCSwitch::setRepeatTransmit(int nRepeatTransmit) { + this->nRepeatTransmit = nRepeatTransmit; +} + +/** + * Set Receiving Tolerance + */ +#if not defined( RCSwitchDisableReceiving ) +void RCSwitch::setReceiveTolerance(int nPercent) { + RCSwitch::nReceiveTolerance = nPercent; +} +#endif + + +/** + * Enable transmissions + * + * @param nTransmitterPin Arduino Pin to which the sender is connected to + */ +void RCSwitch::enableTransmit(int nTransmitterPin) { + this->nTransmitterPin = nTransmitterPin; + pinMode(this->nTransmitterPin, OUTPUT); +} + +/** + * Disable transmissions + */ +void RCSwitch::disableTransmit() { + this->nTransmitterPin = -1; +} + +/** + * Switch a remote switch on (Type D REV) + * + * @param sGroup Code of the switch group (A,B,C,D) + * @param nDevice Number of the switch itself (1..3) + */ +void RCSwitch::switchOn(char sGroup, int nDevice) { + this->sendTriState( this->getCodeWordD(sGroup, nDevice, true) ); +} + +/** + * Switch a remote switch off (Type D REV) + * + * @param sGroup Code of the switch group (A,B,C,D) + * @param nDevice Number of the switch itself (1..3) + */ +void RCSwitch::switchOff(char sGroup, int nDevice) { + this->sendTriState( this->getCodeWordD(sGroup, nDevice, false) ); +} + +/** + * Switch a remote switch on (Type C Intertechno) + * + * @param sFamily Familycode (a..f) + * @param nGroup Number of group (1..4) + * @param nDevice Number of device (1..4) + */ +void RCSwitch::switchOn(char sFamily, int nGroup, int nDevice) { + this->sendTriState( this->getCodeWordC(sFamily, nGroup, nDevice, true) ); +} + +/** + * Switch a remote switch off (Type C Intertechno) + * + * @param sFamily Familycode (a..f) + * @param nGroup Number of group (1..4) + * @param nDevice Number of device (1..4) + */ +void RCSwitch::switchOff(char sFamily, int nGroup, int nDevice) { + this->sendTriState( this->getCodeWordC(sFamily, nGroup, nDevice, false) ); +} + +/** + * Switch a remote switch on (Type B with two rotary/sliding switches) + * + * @param nAddressCode Number of the switch group (1..4) + * @param nChannelCode Number of the switch itself (1..4) + */ +void RCSwitch::switchOn(int nAddressCode, int nChannelCode) { + this->sendTriState( this->getCodeWordB(nAddressCode, nChannelCode, true) ); +} + +/** + * Switch a remote switch off (Type B with two rotary/sliding switches) + * + * @param nAddressCode Number of the switch group (1..4) + * @param nChannelCode Number of the switch itself (1..4) + */ +void RCSwitch::switchOff(int nAddressCode, int nChannelCode) { + this->sendTriState( this->getCodeWordB(nAddressCode, nChannelCode, false) ); +} + +/** + * Deprecated, use switchOn(const char* sGroup, const char* sDevice) instead! + * Switch a remote switch on (Type A with 10 pole DIP switches) + * + * @param sGroup Code of the switch group (refers to DIP switches 1..5 where "1" = on and "0" = off, if all DIP switches are on it's "11111") + * @param nChannelCode Number of the switch itself (1..5) + */ +void RCSwitch::switchOn(const char* sGroup, int nChannel) { + const char* code[6] = { "00000", "10000", "01000", "00100", "00010", "00001" }; + this->switchOn(sGroup, code[nChannel]); +} + +/** + * Deprecated, use switchOff(const char* sGroup, const char* sDevice) instead! + * Switch a remote switch off (Type A with 10 pole DIP switches) + * + * @param sGroup Code of the switch group (refers to DIP switches 1..5 where "1" = on and "0" = off, if all DIP switches are on it's "11111") + * @param nChannelCode Number of the switch itself (1..5) + */ +void RCSwitch::switchOff(const char* sGroup, int nChannel) { + const char* code[6] = { "00000", "10000", "01000", "00100", "00010", "00001" }; + this->switchOff(sGroup, code[nChannel]); +} + +/** + * Switch a remote switch on (Type A with 10 pole DIP switches) + * + * @param sGroup Code of the switch group (refers to DIP switches 1..5 where "1" = on and "0" = off, if all DIP switches are on it's "11111") + * @param sDevice Code of the switch device (refers to DIP switches 6..10 (A..E) where "1" = on and "0" = off, if all DIP switches are on it's "11111") + */ +void RCSwitch::switchOn(const char* sGroup, const char* sDevice) { + this->sendTriState( this->getCodeWordA(sGroup, sDevice, true) ); +} + +/** + * Switch a remote switch off (Type A with 10 pole DIP switches) + * + * @param sGroup Code of the switch group (refers to DIP switches 1..5 where "1" = on and "0" = off, if all DIP switches are on it's "11111") + * @param sDevice Code of the switch device (refers to DIP switches 6..10 (A..E) where "1" = on and "0" = off, if all DIP switches are on it's "11111") + */ +void RCSwitch::switchOff(const char* sGroup, const char* sDevice) { + this->sendTriState( this->getCodeWordA(sGroup, sDevice, false) ); +} + + +/** + * Returns a char[13], representing the code word to be send. + * + */ +char* RCSwitch::getCodeWordA(const char* sGroup, const char* sDevice, bool bStatus) { + static char sReturn[13]; + int nReturnPos = 0; + + for (int i = 0; i < 5; i++) { + sReturn[nReturnPos++] = (sGroup[i] == '0') ? 'F' : '0'; + } + + for (int i = 0; i < 5; i++) { + sReturn[nReturnPos++] = (sDevice[i] == '0') ? 'F' : '0'; + } + + sReturn[nReturnPos++] = bStatus ? '0' : 'F'; + sReturn[nReturnPos++] = bStatus ? 'F' : '0'; + + sReturn[nReturnPos] = '\0'; + return sReturn; +} + +/** + * Encoding for type B switches with two rotary/sliding switches. + * + * The code word is a tristate word and with following bit pattern: + * + * +-----------------------------+-----------------------------+----------+------------+ + * | 4 bits address | 4 bits address | 3 bits | 1 bit | + * | switch group | switch number | not used | on / off | + * | 1=0FFF 2=F0FF 3=FF0F 4=FFF0 | 1=0FFF 2=F0FF 3=FF0F 4=FFF0 | FFF | on=F off=0 | + * +-----------------------------+-----------------------------+----------+------------+ + * + * @param nAddressCode Number of the switch group (1..4) + * @param nChannelCode Number of the switch itself (1..4) + * @param bStatus Whether to switch on (true) or off (false) + * + * @return char[13], representing a tristate code word of length 12 + */ +char* RCSwitch::getCodeWordB(int nAddressCode, int nChannelCode, bool bStatus) { + static char sReturn[13]; + int nReturnPos = 0; + + if (nAddressCode < 1 || nAddressCode > 4 || nChannelCode < 1 || nChannelCode > 4) { + return 0; + } + + for (int i = 1; i <= 4; i++) { + sReturn[nReturnPos++] = (nAddressCode == i) ? '0' : 'F'; + } + + for (int i = 1; i <= 4; i++) { + sReturn[nReturnPos++] = (nChannelCode == i) ? '0' : 'F'; + } + + sReturn[nReturnPos++] = 'F'; + sReturn[nReturnPos++] = 'F'; + sReturn[nReturnPos++] = 'F'; + + sReturn[nReturnPos++] = bStatus ? 'F' : '0'; + + sReturn[nReturnPos] = '\0'; + return sReturn; +} + +/** + * Like getCodeWord (Type C = Intertechno) + */ +char* RCSwitch::getCodeWordC(char sFamily, int nGroup, int nDevice, bool bStatus) { + static char sReturn[13]; + int nReturnPos = 0; + + int nFamily = (int)sFamily - 'a'; + if ( nFamily < 0 || nFamily > 15 || nGroup < 1 || nGroup > 4 || nDevice < 1 || nDevice > 4) { + return 0; + } + + // encode the family into four bits + sReturn[nReturnPos++] = (nFamily & 1) ? 'F' : '0'; + sReturn[nReturnPos++] = (nFamily & 2) ? 'F' : '0'; + sReturn[nReturnPos++] = (nFamily & 4) ? 'F' : '0'; + sReturn[nReturnPos++] = (nFamily & 8) ? 'F' : '0'; + + // encode the device and group + sReturn[nReturnPos++] = ((nDevice-1) & 1) ? 'F' : '0'; + sReturn[nReturnPos++] = ((nDevice-1) & 2) ? 'F' : '0'; + sReturn[nReturnPos++] = ((nGroup-1) & 1) ? 'F' : '0'; + sReturn[nReturnPos++] = ((nGroup-1) & 2) ? 'F' : '0'; + + // encode the status code + sReturn[nReturnPos++] = '0'; + sReturn[nReturnPos++] = 'F'; + sReturn[nReturnPos++] = 'F'; + sReturn[nReturnPos++] = bStatus ? 'F' : '0'; + + sReturn[nReturnPos] = '\0'; + return sReturn; +} + +/** + * Encoding for the REV Switch Type + * + * The code word is a tristate word and with following bit pattern: + * + * +-----------------------------+-------------------+----------+--------------+ + * | 4 bits address | 3 bits address | 3 bits | 2 bits | + * | switch group | device number | not used | on / off | + * | A=1FFF B=F1FF C=FF1F D=FFF1 | 1=0FF 2=F0F 3=FF0 | 000 | on=10 off=01 | + * +-----------------------------+-------------------+----------+--------------+ + * + * Source: http://www.the-intruder.net/funksteckdosen-von-rev-uber-arduino-ansteuern/ + * + * @param sGroup Name of the switch group (A..D, resp. a..d) + * @param nDevice Number of the switch itself (1..3) + * @param bStatus Whether to switch on (true) or off (false) + * + * @return char[13], representing a tristate code word of length 12 + */ +char* RCSwitch::getCodeWordD(char sGroup, int nDevice, bool bStatus) { + static char sReturn[13]; + int nReturnPos = 0; + + // sGroup must be one of the letters in "abcdABCD" + int nGroup = (sGroup >= 'a') ? (int)sGroup - 'a' : (int)sGroup - 'A'; + if ( nGroup < 0 || nGroup > 3 || nDevice < 1 || nDevice > 3) { + return 0; + } + + for (int i = 0; i < 4; i++) { + sReturn[nReturnPos++] = (nGroup == i) ? '1' : 'F'; + } + + for (int i = 1; i <= 3; i++) { + sReturn[nReturnPos++] = (nDevice == i) ? '1' : 'F'; + } + + sReturn[nReturnPos++] = '0'; + sReturn[nReturnPos++] = '0'; + sReturn[nReturnPos++] = '0'; + + sReturn[nReturnPos++] = bStatus ? '1' : '0'; + sReturn[nReturnPos++] = bStatus ? '0' : '1'; + + sReturn[nReturnPos] = '\0'; + return sReturn; +} + +/** + * @param sCodeWord a tristate code word consisting of the letter 0, 1, F + */ +void RCSwitch::sendTriState(const char* sCodeWord) { + // turn the tristate code word into the corresponding bit pattern, then send it + unsigned long code = 0; + unsigned int length = 0; + for (const char* p = sCodeWord; *p; p++) { + code <<= 2L; + switch (*p) { + case '0': + // bit pattern 00 + break; + case 'F': + // bit pattern 01 + code |= 1L; + break; + case '1': + // bit pattern 11 + code |= 3L; + break; + } + length += 2; + } + this->send(code, length); +} + +/** + * @param sCodeWord a binary code word consisting of the letter 0, 1 + */ +void RCSwitch::send(const char* sCodeWord) { + // turn the tristate code word into the corresponding bit pattern, then send it + unsigned long code = 0; + unsigned int length = 0; + for (const char* p = sCodeWord; *p; p++) { + code <<= 1L; + if (*p != '0') + code |= 1L; + length++; + } + this->send(code, length); +} + +/** + * Transmit the first 'length' bits of the integer 'code'. The + * bits are sent from MSB to LSB, i.e., first the bit at position length-1, + * then the bit at position length-2, and so on, till finally the bit at position 0. + */ +void RCSwitch::send(unsigned long code, unsigned int length) { + if (this->nTransmitterPin == -1) + return; + +#if not defined( RCSwitchDisableReceiving ) + // make sure the receiver is disabled while we transmit + int nReceiverInterrupt_backup = nReceiverInterrupt; + if (nReceiverInterrupt_backup != -1) { + this->disableReceive(); + } +#endif + + for (int nRepeat = 0; nRepeat < nRepeatTransmit; nRepeat++) { + for (int i = length-1; i >= 0; i--) { + if (code & (1L << i)) + this->transmit(protocol.one); + else + this->transmit(protocol.zero); + } + this->transmit(protocol.syncFactor); + } + + // Disable transmit after sending (i.e., for inverted protocols) + digitalWrite(this->nTransmitterPin, LOW); + +#if not defined( RCSwitchDisableReceiving ) + // enable receiver again if we just disabled it + if (nReceiverInterrupt_backup != -1) { + this->enableReceive(nReceiverInterrupt_backup); + } +#endif +} + +/** + * Transmit a single high-low pulse. + */ +void RCSwitch::transmit(HighLow pulses) { + uint8_t firstLogicLevel = (this->protocol.invertedSignal) ? LOW : HIGH; + uint8_t secondLogicLevel = (this->protocol.invertedSignal) ? HIGH : LOW; + + digitalWrite(this->nTransmitterPin, firstLogicLevel); + delayMicroseconds( this->protocol.pulseLength * pulses.high); + digitalWrite(this->nTransmitterPin, secondLogicLevel); + delayMicroseconds( this->protocol.pulseLength * pulses.low); +} + + +#if not defined( RCSwitchDisableReceiving ) +/** + * Enable receiving data + */ +void RCSwitch::enableReceive(int interrupt) { + this->nReceiverInterrupt = interrupt; + this->enableReceive(); +} + +void RCSwitch::enableReceive() { + if (this->nReceiverInterrupt != -1) { + RCSwitch::nReceivedValue = 0; + RCSwitch::nReceivedBitlength = 0; +#if defined(RaspberryPi) // Raspberry Pi + wiringPiISR(this->nReceiverInterrupt, INT_EDGE_BOTH, &handleInterrupt); +#else // Arduino + attachInterrupt(this->nReceiverInterrupt, handleInterrupt, CHANGE); +#endif + } +} + +/** + * Disable receiving data + */ +void RCSwitch::disableReceive() { +#if not defined(RaspberryPi) // Arduino + detachInterrupt(this->nReceiverInterrupt); +#endif // For Raspberry Pi (wiringPi) you can't unregister the ISR + this->nReceiverInterrupt = -1; +} + +bool RCSwitch::available() { + return RCSwitch::nReceivedValue != 0; +} + +void RCSwitch::resetAvailable() { + RCSwitch::nReceivedValue = 0; +} + +unsigned long RCSwitch::getReceivedValue() { + return RCSwitch::nReceivedValue; +} + +unsigned int RCSwitch::getReceivedBitlength() { + return RCSwitch::nReceivedBitlength; +} + +unsigned int RCSwitch::getReceivedDelay() { + return RCSwitch::nReceivedDelay; +} + +unsigned int RCSwitch::getReceivedProtocol() { + return RCSwitch::nReceivedProtocol; +} + +unsigned int* RCSwitch::getReceivedRawdata() { + return RCSwitch::timings; +} + +/* helper function for the receiveProtocol method */ +static inline unsigned int diff(int A, int B) { + return abs(A - B); +} + +/** + * + */ +bool RECEIVE_ATTR RCSwitch::receiveProtocol(const int p, unsigned int changeCount) { +#if defined(ESP8266) || defined(ESP32) + const Protocol &pro = proto[p-1]; +#else + Protocol pro; + memcpy_P(&pro, &proto[p-1], sizeof(Protocol)); +#endif + + unsigned long code = 0; + //Assuming the longer pulse length is the pulse captured in timings[0] + const unsigned int syncLengthInPulses = ((pro.syncFactor.low) > (pro.syncFactor.high)) ? (pro.syncFactor.low) : (pro.syncFactor.high); + const unsigned int delay = RCSwitch::timings[0] / syncLengthInPulses; + const unsigned int delayTolerance = delay * RCSwitch::nReceiveTolerance / 100; + + /* For protocols that start low, the sync period looks like + * _________ + * _____________| |XXXXXXXXXXXX| + * + * |--1st dur--|-2nd dur-|-Start data-| + * + * The 3rd saved duration starts the data. + * + * For protocols that start high, the sync period looks like + * + * ______________ + * | |____________|XXXXXXXXXXXXX| + * + * |-filtered out-|--1st dur--|--Start data--| + * + * The 2nd saved duration starts the data + */ + const unsigned int firstDataTiming = (pro.invertedSignal) ? (2) : (1); + + for (unsigned int i = firstDataTiming; i < changeCount - 1; i += 2) { + code <<= 1; + if (diff(RCSwitch::timings[i], delay * pro.zero.high) < delayTolerance && + diff(RCSwitch::timings[i + 1], delay * pro.zero.low) < delayTolerance) { + // zero + } else if (diff(RCSwitch::timings[i], delay * pro.one.high) < delayTolerance && + diff(RCSwitch::timings[i + 1], delay * pro.one.low) < delayTolerance) { + // one + code |= 1; + } else { + // Failed + return false; + } + } + + if (changeCount > 7) { // ignore very short transmissions: no device sends them, so this must be noise + RCSwitch::nReceivedValue = code; + RCSwitch::nReceivedBitlength = (changeCount - 1) / 2; + RCSwitch::nReceivedDelay = delay; + RCSwitch::nReceivedProtocol = p; + return true; + } + + return false; +} + +void RECEIVE_ATTR RCSwitch::handleInterrupt() { + + static unsigned int changeCount = 0; + static unsigned long lastTime = 0; + static unsigned int repeatCount = 0; + + const long time = micros(); + const unsigned int duration = time - lastTime; + + if (duration > RCSwitch::nSeparationLimit) { + // A long stretch without signal level change occurred. This could + // be the gap between two transmission. + if ((repeatCount==0) || (diff(duration, RCSwitch::timings[0]) < 200)) { + // This long signal is close in length to the long signal which + // started the previously recorded timings; this suggests that + // it may indeed by a a gap between two transmissions (we assume + // here that a sender will send the signal multiple times, + // with roughly the same gap between them). + repeatCount++; + if (repeatCount == 2) { + for(unsigned int i = 1; i <= numProto; i++) { + if (receiveProtocol(i, changeCount)) { + // receive succeeded for protocol i + break; + } + } + repeatCount = 0; + } + } + changeCount = 0; + } + + // detect overflow + if (changeCount >= RCSWITCH_MAX_CHANGES) { + changeCount = 0; + repeatCount = 0; + } + + RCSwitch::timings[changeCount++] = duration; + lastTime = time; +} +#endif diff --git a/lib/rc-switch/RCSwitch.h b/lib/rc-switch/RCSwitch.h new file mode 100644 index 0000000..1a4b141 --- /dev/null +++ b/lib/rc-switch/RCSwitch.h @@ -0,0 +1,184 @@ +/* + RCSwitch - Arduino libary for remote control outlet switches + Copyright (c) 2011 Suat Özgür. All right reserved. + + Contributors: + - Andre Koehler / info(at)tomate-online(dot)de + - Gordeev Andrey Vladimirovich / gordeev(at)openpyro(dot)com + - Skineffect / http://forum.ardumote.com/viewtopic.php?f=2&t=46 + - Dominik Fischer / dom_fischer(at)web(dot)de + - Frank Oltmanns / .(at)gmail(dot)com + - Max Horn / max(at)quendi(dot)de + - Robert ter Vehn / .(at)gmail(dot)com + + Project home: https://github.com/sui77/rc-switch/ + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ +#ifndef _RCSwitch_h +#define _RCSwitch_h + +#if defined(ARDUINO) && ARDUINO >= 100 + #include "Arduino.h" +#elif defined(ENERGIA) // LaunchPad, FraunchPad and StellarPad specific + #include "Energia.h" +#elif defined(RPI) // Raspberry Pi + #define RaspberryPi + + // Include libraries for RPi: + #include /* memcpy */ + #include /* abs */ + #include +#elif defined(SPARK) + #include "application.h" +#else + #include "WProgram.h" +#endif + +#include + + +// At least for the ATTiny X4/X5, receiving has to be disabled due to +// missing libm depencies (udivmodhi4) +//#if defined( __AVR_ATtinyX5__ ) or defined ( __AVR_ATtinyX4__ ) +//#define RCSwitchDisableReceiving +//#endif + +// Number of maximum high/Low changes per packet. +// We can handle up to (unsigned long) => 32 bit * 2 H/L changes per bit + 2 for sync +#define RCSWITCH_MAX_CHANGES 67 + +class RCSwitch { + + public: + RCSwitch(); + + void switchOn(int nGroupNumber, int nSwitchNumber); + void switchOff(int nGroupNumber, int nSwitchNumber); + void switchOn(const char* sGroup, int nSwitchNumber); + void switchOff(const char* sGroup, int nSwitchNumber); + void switchOn(char sFamily, int nGroup, int nDevice); + void switchOff(char sFamily, int nGroup, int nDevice); + void switchOn(const char* sGroup, const char* sDevice); + void switchOff(const char* sGroup, const char* sDevice); + void switchOn(char sGroup, int nDevice); + void switchOff(char sGroup, int nDevice); + + void sendTriState(const char* sCodeWord); + void send(unsigned long code, unsigned int length); + void send(const char* sCodeWord); + + #if not defined( RCSwitchDisableReceiving ) + void enableReceive(int interrupt); + void enableReceive(); + void disableReceive(); + bool available(); + void resetAvailable(); + + unsigned long getReceivedValue(); + unsigned int getReceivedBitlength(); + unsigned int getReceivedDelay(); + unsigned int getReceivedProtocol(); + unsigned int* getReceivedRawdata(); + #endif + + void enableTransmit(int nTransmitterPin); + void disableTransmit(); + void setPulseLength(int nPulseLength); + void setRepeatTransmit(int nRepeatTransmit); + #if not defined( RCSwitchDisableReceiving ) + void setReceiveTolerance(int nPercent); + #endif + + /** + * Description of a single pule, which consists of a high signal + * whose duration is "high" times the base pulse length, followed + * by a low signal lasting "low" times the base pulse length. + * Thus, the pulse overall lasts (high+low)*pulseLength + */ + struct HighLow { + uint8_t high; + uint8_t low; + }; + + /** + * A "protocol" describes how zero and one bits are encoded into high/low + * pulses. + */ + struct Protocol { + /** base pulse length in microseconds, e.g. 350 */ + uint16_t pulseLength; + + HighLow syncFactor; + HighLow zero; + HighLow one; + + /** + * If true, interchange high and low logic levels in all transmissions. + * + * By default, RCSwitch assumes that any signals it sends or receives + * can be broken down into pulses which start with a high signal level, + * followed by a a low signal level. This is e.g. the case for the + * popular PT 2260 encoder chip, and thus many switches out there. + * + * But some devices do it the other way around, and start with a low + * signal level, followed by a high signal level, e.g. the HT6P20B. To + * accommodate this, one can set invertedSignal to true, which causes + * RCSwitch to change how it interprets any HighLow struct FOO: It will + * then assume transmissions start with a low signal lasting + * FOO.high*pulseLength microseconds, followed by a high signal lasting + * FOO.low*pulseLength microseconds. + */ + bool invertedSignal; + }; + + void setProtocol(Protocol protocol); + void setProtocol(int nProtocol); + void setProtocol(int nProtocol, int nPulseLength); + + private: + char* getCodeWordA(const char* sGroup, const char* sDevice, bool bStatus); + char* getCodeWordB(int nGroupNumber, int nSwitchNumber, bool bStatus); + char* getCodeWordC(char sFamily, int nGroup, int nDevice, bool bStatus); + char* getCodeWordD(char group, int nDevice, bool bStatus); + void transmit(HighLow pulses); + + #if not defined( RCSwitchDisableReceiving ) + static void handleInterrupt(); + static bool receiveProtocol(const int p, unsigned int changeCount); + int nReceiverInterrupt; + #endif + int nTransmitterPin; + int nRepeatTransmit; + + Protocol protocol; + + #if not defined( RCSwitchDisableReceiving ) + static int nReceiveTolerance; + volatile static unsigned long nReceivedValue; + volatile static unsigned int nReceivedBitlength; + volatile static unsigned int nReceivedDelay; + volatile static unsigned int nReceivedProtocol; + const static unsigned int nSeparationLimit; + /* + * timings[0] contains sync timing, followed by a number of bits + */ + static unsigned int timings[RCSWITCH_MAX_CHANGES]; + #endif + + +}; + +#endif diff --git a/lib/rc-switch/README.md b/lib/rc-switch/README.md new file mode 100644 index 0000000..f6a679c --- /dev/null +++ b/lib/rc-switch/README.md @@ -0,0 +1,42 @@ +# rc-switch +[![arduino-library-badge](https://www.ardu-badge.com/badge/rc-switch.svg?)](https://www.ardu-badge.com/rc-switch) +[![Build Status](https://travis-ci.org/sui77/rc-switch.svg?branch=master)](https://travis-ci.org/sui77/rc-switch) + +Use your Arduino or [Raspberry Pi](https://github.com/r10r/rcswitch-pi) to operate remote radio controlled devices + +## Download +https://github.com/sui77/rc-switch/releases/latest + +rc-switch is also listed in the arduino library manager. + +## Wiki +https://github.com/sui77/rc-switch/wiki + +## Info +### Send RC codes + +Use your Arduino or Raspberry Pi to operate remote radio controlled devices. +This will most likely work with all popular low cost power outlet sockets. If +yours doesn't work, you might need to adjust the pulse length. + +All you need is a Arduino or Raspberry Pi, a 315/433MHz AM transmitter and one +or more devices with one of the supported chipsets: + + - SC5262 / SC5272 + - HX2262 / HX2272 + - PT2262 / PT2272 + - EV1527 / RT1527 / FP1527 / HS1527 + - Intertechno outlets + - HT6P20X + +### Receive and decode RC codes + +Find out what codes your remote is sending. Use your remote to control your +Arduino. + +All you need is an Arduino, a 315/433MHz AM receiver (altough there is no +instruction yet, yes it is possible to hack an existing device) and a remote +hand set. + +For the Raspberry Pi, clone the https://github.com/ninjablocks/433Utils project to +compile a sniffer tool and transmission commands. diff --git a/lib/rc-switch/examples/ReceiveDemo_Advanced/ReceiveDemo_Advanced.ino b/lib/rc-switch/examples/ReceiveDemo_Advanced/ReceiveDemo_Advanced.ino new file mode 100644 index 0000000..18380d3 --- /dev/null +++ b/lib/rc-switch/examples/ReceiveDemo_Advanced/ReceiveDemo_Advanced.ino @@ -0,0 +1,24 @@ +/* + Example for receiving + + https://github.com/sui77/rc-switch/ + + If you want to visualize a telegram copy the raw data and + paste it into http://test.sui.li/oszi/ +*/ + +#include + +RCSwitch mySwitch = RCSwitch(); + +void setup() { + Serial.begin(9600); + mySwitch.enableReceive(0); // Receiver on interrupt 0 => that is pin #2 +} + +void loop() { + if (mySwitch.available()) { + output(mySwitch.getReceivedValue(), mySwitch.getReceivedBitlength(), mySwitch.getReceivedDelay(), mySwitch.getReceivedRawdata(),mySwitch.getReceivedProtocol()); + mySwitch.resetAvailable(); + } +} diff --git a/lib/rc-switch/examples/ReceiveDemo_Advanced/output.ino b/lib/rc-switch/examples/ReceiveDemo_Advanced/output.ino new file mode 100644 index 0000000..31e61ba --- /dev/null +++ b/lib/rc-switch/examples/ReceiveDemo_Advanced/output.ino @@ -0,0 +1,70 @@ +static const char* bin2tristate(const char* bin); +static char * dec2binWzerofill(unsigned long Dec, unsigned int bitLength); + +void output(unsigned long decimal, unsigned int length, unsigned int delay, unsigned int* raw, unsigned int protocol) { + + const char* b = dec2binWzerofill(decimal, length); + Serial.print("Decimal: "); + Serial.print(decimal); + Serial.print(" ("); + Serial.print( length ); + Serial.print("Bit) Binary: "); + Serial.print( b ); + Serial.print(" Tri-State: "); + Serial.print( bin2tristate( b) ); + Serial.print(" PulseLength: "); + Serial.print(delay); + Serial.print(" microseconds"); + Serial.print(" Protocol: "); + Serial.println(protocol); + + Serial.print("Raw data: "); + for (unsigned int i=0; i<= length*2; i++) { + Serial.print(raw[i]); + Serial.print(","); + } + Serial.println(); + Serial.println(); +} + +static const char* bin2tristate(const char* bin) { + static char returnValue[50]; + int pos = 0; + int pos2 = 0; + while (bin[pos]!='\0' && bin[pos+1]!='\0') { + if (bin[pos]=='0' && bin[pos+1]=='0') { + returnValue[pos2] = '0'; + } else if (bin[pos]=='1' && bin[pos+1]=='1') { + returnValue[pos2] = '1'; + } else if (bin[pos]=='0' && bin[pos+1]=='1') { + returnValue[pos2] = 'F'; + } else { + return "not applicable"; + } + pos = pos+2; + pos2++; + } + returnValue[pos2] = '\0'; + return returnValue; +} + +static char * dec2binWzerofill(unsigned long Dec, unsigned int bitLength) { + static char bin[64]; + unsigned int i=0; + + while (Dec > 0) { + bin[32+i++] = ((Dec & 1) > 0) ? '1' : '0'; + Dec = Dec >> 1; + } + + for (unsigned int j = 0; j< bitLength; j++) { + if (j >= bitLength - i) { + bin[j] = bin[ 31 + i - (j - (bitLength - i)) ]; + } else { + bin[j] = '0'; + } + } + bin[bitLength] = '\0'; + + return bin; +} diff --git a/lib/rc-switch/examples/ReceiveDemo_Simple/ReceiveDemo_Simple.ino b/lib/rc-switch/examples/ReceiveDemo_Simple/ReceiveDemo_Simple.ino new file mode 100644 index 0000000..bb10763 --- /dev/null +++ b/lib/rc-switch/examples/ReceiveDemo_Simple/ReceiveDemo_Simple.ino @@ -0,0 +1,29 @@ +/* + Simple example for receiving + + https://github.com/sui77/rc-switch/ +*/ + +#include + +RCSwitch mySwitch = RCSwitch(); + +void setup() { + Serial.begin(9600); + mySwitch.enableReceive(0); // Receiver on interrupt 0 => that is pin #2 +} + +void loop() { + if (mySwitch.available()) { + + Serial.print("Received "); + Serial.print( mySwitch.getReceivedValue() ); + Serial.print(" / "); + Serial.print( mySwitch.getReceivedBitlength() ); + Serial.print("bit "); + Serial.print("Protocol: "); + Serial.println( mySwitch.getReceivedProtocol() ); + + mySwitch.resetAvailable(); + } +} diff --git a/lib/rc-switch/examples/SendDemo/SendDemo.ino b/lib/rc-switch/examples/SendDemo/SendDemo.ino new file mode 100644 index 0000000..5acb495 --- /dev/null +++ b/lib/rc-switch/examples/SendDemo/SendDemo.ino @@ -0,0 +1,57 @@ +/* + Example for different sending methods + + https://github.com/sui77/rc-switch/ + +*/ + +#include + +RCSwitch mySwitch = RCSwitch(); + +void setup() { + + Serial.begin(9600); + + // Transmitter is connected to Arduino Pin #10 + mySwitch.enableTransmit(10); + + // Optional set protocol (default is 1, will work for most outlets) + // mySwitch.setProtocol(2); + + // Optional set pulse length. + // mySwitch.setPulseLength(320); + + // Optional set number of transmission repetitions. + // mySwitch.setRepeatTransmit(15); + +} + +void loop() { + + /* See Example: TypeA_WithDIPSwitches */ + mySwitch.switchOn("11111", "00010"); + delay(1000); + mySwitch.switchOff("11111", "00010"); + delay(1000); + + /* Same switch as above, but using decimal code */ + mySwitch.send(5393, 24); + delay(1000); + mySwitch.send(5396, 24); + delay(1000); + + /* Same switch as above, but using binary code */ + mySwitch.send("000000000001010100010001"); + delay(1000); + mySwitch.send("000000000001010100010100"); + delay(1000); + + /* Same switch as above, but tri-state code */ + mySwitch.sendTriState("00000FFF0F0F"); + delay(1000); + mySwitch.sendTriState("00000FFF0FF0"); + delay(1000); + + delay(20000); +} diff --git a/lib/rc-switch/examples/TypeA_WithDIPSwitches/TypeA_WithDIPSwitches.ino b/lib/rc-switch/examples/TypeA_WithDIPSwitches/TypeA_WithDIPSwitches.ino new file mode 100644 index 0000000..14f7d2a --- /dev/null +++ b/lib/rc-switch/examples/TypeA_WithDIPSwitches/TypeA_WithDIPSwitches.ino @@ -0,0 +1,40 @@ +/* + Example for outlets which are configured with a 10 pole DIP switch. + + https://github.com/sui77/rc-switch/ +*/ + +#include + +RCSwitch mySwitch = RCSwitch(); + +void setup() { + + // Transmitter is connected to Arduino Pin #10 + mySwitch.enableTransmit(10); + + // Optional set pulse length. + // mySwitch.setPulseLength(320); + +} + +void loop() { + + // Switch on: + // The first parameter represents the setting of the first 5 DIP switches. + // In this example it's ON-ON-OFF-OFF-ON. + // + // The second parameter represents the setting of the last 5 DIP switches. + // In this example the last 5 DIP switches are OFF-ON-OFF-ON-OFF. + mySwitch.switchOn("11001", "01010"); + + // Wait a second + delay(1000); + + // Switch off + mySwitch.switchOff("11001", "01010"); + + // Wait another second + delay(1000); + +} diff --git a/lib/rc-switch/examples/TypeA_WithDIPSwitches_Lightweight/TypeA_WithDIPSwitches_Lightweight.ino b/lib/rc-switch/examples/TypeA_WithDIPSwitches_Lightweight/TypeA_WithDIPSwitches_Lightweight.ino new file mode 100644 index 0000000..214daf4 --- /dev/null +++ b/lib/rc-switch/examples/TypeA_WithDIPSwitches_Lightweight/TypeA_WithDIPSwitches_Lightweight.ino @@ -0,0 +1,43 @@ +/* + This is a minimal sketch without using the library at all but only works for + the 10 pole dip switch sockets. It saves a lot of memory and thus might be + very useful to use with ATTinys :) + + https://github.com/sui77/rc-switch/ +*/ + +int RCLpin = 7; + +void setup() { + pinMode(RCLpin, OUTPUT); +} + +void loop() { + RCLswitch(0b010001000001); // DIPs an Steckdose: 0100010000 An:01 + delay(2000); + + RCLswitch(0b010001000010); // DIPs an Steckdose: 0100010000 Aus:10 + delay(2000); +} + +void RCLswitch(uint16_t code) { + for (int nRepeat=0; nRepeat<6; nRepeat++) { + for (int i=4; i<16; i++) { + RCLtransmit(1,3); + if (((code << (i-4)) & 2048) > 0) { + RCLtransmit(1,3); + } else { + RCLtransmit(3,1); + } + } + RCLtransmit(1,31); + } +} + +void RCLtransmit(int nHighPulses, int nLowPulses) { + digitalWrite(RCLpin, HIGH); + delayMicroseconds( 350 * nHighPulses); + digitalWrite(RCLpin, LOW); + delayMicroseconds( 350 * nLowPulses); +} + diff --git a/lib/rc-switch/examples/TypeB_WithRotaryOrSlidingSwitches/TypeB_WithRotaryOrSlidingSwitches.ino b/lib/rc-switch/examples/TypeB_WithRotaryOrSlidingSwitches/TypeB_WithRotaryOrSlidingSwitches.ino new file mode 100644 index 0000000..e8568e2 --- /dev/null +++ b/lib/rc-switch/examples/TypeB_WithRotaryOrSlidingSwitches/TypeB_WithRotaryOrSlidingSwitches.ino @@ -0,0 +1,40 @@ +/* + Example for outlets which are configured with two rotary/sliding switches. + + https://github.com/sui77/rc-switch/ +*/ + +#include + +RCSwitch mySwitch = RCSwitch(); + +void setup() { + + // Transmitter is connected to Arduino Pin #10 + mySwitch.enableTransmit(10); + + // Optional set pulse length. + // mySwitch.setPulseLength(320); + +} + +void loop() { + + // Switch on: + // The first parameter represents the setting of the first rotary switch. + // In this example it's switched to "1" or "A" or "I". + // + // The second parameter represents the setting of the second rotary switch. + // In this example it's switched to "4" or "D" or "IV". + mySwitch.switchOn(1, 4); + + // Wait a second + delay(1000); + + // Switch off + mySwitch.switchOff(1, 4); + + // Wait another second + delay(1000); + +} diff --git a/lib/rc-switch/examples/TypeC_Intertechno/TypeC_Intertechno.ino b/lib/rc-switch/examples/TypeC_Intertechno/TypeC_Intertechno.ino new file mode 100644 index 0000000..0fc69c7 --- /dev/null +++ b/lib/rc-switch/examples/TypeC_Intertechno/TypeC_Intertechno.ino @@ -0,0 +1,40 @@ +/* + Example for Intertechno outlets + + https://github.com/sui77/rc-switch/ +*/ + +#include + +RCSwitch mySwitch = RCSwitch(); + +void setup() { + + // Transmitter is connected to Arduino Pin #10 + mySwitch.enableTransmit(10); + + // Optional set pulse length. + // mySwitch.setPulseLength(320); + +} + +void loop() { + + // Switch on: + // The first parameter represents the familycode (a, b, c, ... f) + // The second parameter represents the group number + // The third parameter represents the device number + // + // In this example it's family 'b', group #3, device #2 + mySwitch.switchOn('b', 3, 2); + + // Wait a second + delay(1000); + + // Switch off + mySwitch.switchOff('b', 3, 2); + + // Wait another second + delay(1000); + +} \ No newline at end of file diff --git a/lib/rc-switch/examples/TypeD_REV/TypeD_REV.ino b/lib/rc-switch/examples/TypeD_REV/TypeD_REV.ino new file mode 100644 index 0000000..8782729 --- /dev/null +++ b/lib/rc-switch/examples/TypeD_REV/TypeD_REV.ino @@ -0,0 +1,41 @@ +/* + Example for REV outlets (e.g. 8342L) + + https://github.com/sui77/rc-switch/ + + Need help? http://forum.ardumote.com +*/ + +#include + +RCSwitch mySwitch = RCSwitch(); + +void setup() { + + // Transmitter is connected to Arduino Pin #10 + mySwitch.enableTransmit(10); + + // set pulse length. + mySwitch.setPulseLength(360); + +} + +void loop() { + + // Switch on: + // The first parameter represents the channel (a, b, c, d) + // The second parameter represents the device number + // + // In this example it's family 'd', device #2 + mySwitch.switchOn('d', 2); + + // Wait a second + delay(1000); + + // Switch off + mySwitch.switchOff('d', 2); + + // Wait another second + delay(1000); + +} diff --git a/lib/rc-switch/examples/Webserver/Webserver.ino b/lib/rc-switch/examples/Webserver/Webserver.ino new file mode 100644 index 0000000..66668e7 --- /dev/null +++ b/lib/rc-switch/examples/Webserver/Webserver.ino @@ -0,0 +1,154 @@ +/* + A simple RCSwitch/Ethernet/Webserver demo + + https://github.com/sui77/rc-switch/ +*/ + +#include +#include +#include + +// Ethernet configuration +uint8_t mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; // MAC Address +uint8_t ip[] = { 192,168,0, 2 }; // IP Address +EthernetServer server(80); // Server Port 80 + +// RCSwitch configuration +RCSwitch mySwitch = RCSwitch(); +int RCTransmissionPin = 7; + +// More to do... +// You should also modify the processCommand() and +// httpResponseHome() functions to fit your needs. + + + +/** + * Setup + */ +void setup() { + Ethernet.begin(mac, ip); + server.begin(); + mySwitch.enableTransmit( RCTransmissionPin ); +} + +/** + * Loop + */ +void loop() { + char* command = httpServer(); +} + +/** + * Command dispatcher + */ +void processCommand(char* command) { + if (strcmp(command, "1-on") == 0) { + mySwitch.switchOn(1,1); + } else if (strcmp(command, "1-off") == 0) { + mySwitch.switchOff(1,1); + } else if (strcmp(command, "2-on") == 0) { + mySwitch.switchOn(1,2); + } else if (strcmp(command, "2-off") == 0) { + mySwitch.switchOff(1,2); + } +} + +/** + * HTTP Response with homepage + */ +void httpResponseHome(EthernetClient c) { + c.println("HTTP/1.1 200 OK"); + c.println("Content-Type: text/html"); + c.println(); + c.println(""); + c.println(""); + c.println( "RCSwitch Webserver Demo"); + c.println( ""); + c.println(""); + c.println(""); + c.println( "

RCSwitch Webserver Demo

"); + c.println( ""); + c.println( ""); + c.println( "
"); + c.println( "https://github.com/sui77/rc-switch/"); + c.println(""); + c.println(""); +} + +/** + * HTTP Redirect to homepage + */ +void httpResponseRedirect(EthernetClient c) { + c.println("HTTP/1.1 301 Found"); + c.println("Location: /"); + c.println(); +} + +/** + * HTTP Response 414 error + * Command must not be longer than 30 characters + **/ +void httpResponse414(EthernetClient c) { + c.println("HTTP/1.1 414 Request URI too long"); + c.println("Content-Type: text/plain"); + c.println(); + c.println("414 Request URI too long"); +} + +/** + * Process HTTP requests, parse first request header line and + * call processCommand with GET query string (everything after + * the ? question mark in the URL). + */ +char* httpServer() { + EthernetClient client = server.available(); + if (client) { + char sReturnCommand[32]; + int nCommandPos=-1; + sReturnCommand[0] = '\0'; + while (client.connected()) { + if (client.available()) { + char c = client.read(); + if ((c == '\n') || (c == ' ' && nCommandPos>-1)) { + sReturnCommand[nCommandPos] = '\0'; + if (strcmp(sReturnCommand, "\0") == 0) { + httpResponseHome(client); + } else { + processCommand(sReturnCommand); + httpResponseRedirect(client); + } + break; + } + if (nCommandPos>-1) { + sReturnCommand[nCommandPos++] = c; + } + if (c == '?' && nCommandPos == -1) { + nCommandPos = 0; + } + } + if (nCommandPos > 30) { + httpResponse414(client); + sReturnCommand[0] = '\0'; + break; + } + } + if (nCommandPos!=-1) { + sReturnCommand[nCommandPos] = '\0'; + } + // give the web browser time to receive the data + delay(1); + client.stop(); + + return sReturnCommand; + } + return '\0'; +} \ No newline at end of file diff --git a/lib/rc-switch/keywords.txt b/lib/rc-switch/keywords.txt new file mode 100644 index 0000000..2474367 --- /dev/null +++ b/lib/rc-switch/keywords.txt @@ -0,0 +1,57 @@ +####################################### +# Syntax Coloring Map For RCSwitch +####################################### + +####################################### +# Datatypes (KEYWORD1) +####################################### + +RCSwitch KEYWORD1 + +####################################### +# Methods and Functions (KEYWORD2) +####################################### + +########## +#SENDS Begin +########## +switchOn KEYWORD2 +switchOff KEYWORD2 +sendTriState KEYWORD2 +send KEYWORD2 +########## +#SENDS End +########## + +########## +#RECEIVE Begin +########## +enableReceive KEYWORD2 +disableReceive KEYWORD2 +available KEYWORD2 +resetAvailable KEYWORD2 +setReceiveTolerance KEYWORD2 +getReceivedValue KEYWORD2 +getReceivedBitlength KEYWORD2 +getReceivedDelay KEYWORD2 +getReceivedProtocol KEYWORD2 +getReceivedRawdata KEYWORD2 +########## +#RECEIVE End +########## + +########## +#OTHERS Begin +########## +enableTransmit KEYWORD2 +disableTransmit KEYWORD2 +setPulseLength KEYWORD2 +setProtocol KEYWORD2 +setRepeatTransmit KEYWORD2 +########## +#OTHERS End +########## + +####################################### +# Constants (LITERAL1) +####################################### diff --git a/lib/rc-switch/library.json b/lib/rc-switch/library.json new file mode 100644 index 0000000..b98e28f --- /dev/null +++ b/lib/rc-switch/library.json @@ -0,0 +1,21 @@ +{ + "name": "rc-switch", + "description": "Use your Arduino or Raspberry Pi to operate remote radio controlled devices", + "keywords": "rf, radio, wireless", + "authors": + { + "name": "Suat Ozgur" + }, + "repository": + { + "type": "git", + "url": "https://github.com/sui77/rc-switch.git" + }, + "version": "2.6.4", + "frameworks": [ + "arduino", + "energia", + "wiringpi" + ], + "platforms": "*" +} diff --git a/lib/rc-switch/library.properties b/lib/rc-switch/library.properties new file mode 100644 index 0000000..f7b8723 --- /dev/null +++ b/lib/rc-switch/library.properties @@ -0,0 +1,10 @@ +name=rc-switch +version=2.6.4 +author=sui77 +maintainer=sui77,fingolfin +sentence=Operate 433/315Mhz devices. +paragraph=Use your Arduino, ESP8266/ESP32 or Raspberry Pi to operate remote radio controlled devices. This will most likely work with all popular low cost power outlet sockets. +category=Device Control +url=https://github.com/sui77/rc-switch +architectures=avr,esp8266,esp32,stm32 +includes=RCSwitch.h diff --git a/platformio.ini b/platformio.ini new file mode 100644 index 0000000..c3687e0 --- /dev/null +++ b/platformio.ini @@ -0,0 +1,42 @@ +[platformio] +default_envs = nanoatmega328 + +[env:nanoatmega328] +platform = atmelavr +board = nanoatmega328 +framework = arduino +board_build.f_cpu = 16000000L +lib_deps = + robtillaart/DHTStable +targets = upload, monitor +upload_port = /dev/ttyUSB0 +monitor_port = /dev/ttyUSB0 +monitor_speed = 9600 +monitor_filters = + default ; Remove typical terminal control codes from input + time ; Add timestamp with milliseconds for each new line +; log2file ; Log data to a file “platformio-device-monitor-*.log” located in the current working directory + + +[env:tiny85] +platform = atmelavr +framework = arduino +#board = digispark-tiny +board = attiny85 +board_build.f_cpu = 8000000L +lib_deps = + robtillaart/DHTStable +upload_protocol = stk500v1 +upload_flags = + -P$UPLOAD_PORT + -b$UPLOAD_SPEED +targets = upload, monitor +upload_speed = 19200 +upload_port = /dev/ttyUSB0 +monitor_port = /dev/ttyUSB1 +monitor_speed = 9600 +monitor_filters = + default ; Remove typical terminal control codes from input + time ; Add timestamp with milliseconds for each new line +; log2file ; Log data to a file “platformio-device-monitor-*.log” located in the current working directory + diff --git a/src/config.h b/src/config.h new file mode 100644 index 0000000..610429b --- /dev/null +++ b/src/config.h @@ -0,0 +1,2 @@ +#define LOGGER 1 // Enable logger on Serial 1 : enable 2 : disable +#define SEND_DELAY 120L // Delay between each sensor transmitting / "L" is for the long type (mandatory) diff --git a/src/main.ino b/src/main.ino new file mode 100644 index 0000000..a545904 --- /dev/null +++ b/src/main.ino @@ -0,0 +1,160 @@ +#include +//#include +#include +#include +#include +#include + +#if defined(ARDUINO_AVR_NANO) +#include "pins-nano.h" +#elif defined(ARDUINO_AVR_ATTINYX5) +#include "pins-attiny85.h" +#else +#error "Pins undef for this core" +#endif + +#include "config.h" + +RCSwitch mySwitch = RCSwitch(); +NexusTX transmitter(PIN_SEND); +//Oregon_TM transmitter(PIN_SEND); +//ThermoHygroTransmitter transmitter(PIN_SEND, SENSORID, 2); + +DHTStable DHT; + +bool sensor_enabled = false; +//bool output_enabled = false; +bool output_state = false; +unsigned long lastsend = 0; + +void setup() { + pinMode(PIN_OUTPUT, OUTPUT); + digitalWrite(PIN_OUTPUT, LOW); + //delay(5000); + if(LOGGER) { + Serial.begin(9600); + #if defined(ARDUINO_AVR_ATTINYX5) + // Disable RX (free port PB1) + ACSR &=~(1< (lastsend + (SEND_DELAY*1000))) || (curmillis < lastsend)) { + lastsend = curmillis; + mySwitch.disableReceive(); + if(DHT.read22(PIN_DHT) == DHTLIB_OK) + { + LogS("Send "); + LogS("Temperature: "); + LogF(DHT.getTemperature()); + LogS(" Humidity: "); + LogF(DHT.getHumidity()); + Logln(); + transmitter.setTemperature(DHT.getTemperature()); // -49.9C...+69.9C + transmitter.setHumidity(lround(DHT.getHumidity())); // 2...98% + transmitter.SendPacket(); + //transmitter.sendTempHumi(lround(DHT.getTemperature()*10), lround(DHT.getHumidity())); + } else { + LogS("DHT Error"); + Logln(); + } + mySwitch.enableReceive(0); + } + } +} diff --git a/src/pins-attiny85.h b/src/pins-attiny85.h new file mode 100644 index 0000000..2700b17 --- /dev/null +++ b/src/pins-attiny85.h @@ -0,0 +1,7 @@ +#define PIN_SEND 3 +#define PIN_OUTPUT 1 +#define SENSORID 12 +#define PIN_DHT 4 + +#define OUTPUTCODE_OFF 5312788 +#define OUTPUTCODE_ON 5312789 diff --git a/src/pins-nano.h b/src/pins-nano.h new file mode 100644 index 0000000..5f5c3f9 --- /dev/null +++ b/src/pins-nano.h @@ -0,0 +1,7 @@ +#define PIN_SEND 4 // Transmitter PIN +#define PIN_OUTPUT 3 // Output PIN +#define SENSORID 11 // Sensor ID in the 433Mhz protocol +#define PIN_DHT 5 // Sensor (DHT22) pin + +#define OUTPUTCODE_OFF 1118484 // Code receive for put the output off / {'cmd':'rcsend', 'value': 1118484} +#define OUTPUTCODE_ON 1118485 // Code receive for put the output on / {'cmd':'rcsend', 'value': 1118485}