Initial commit

This commit is contained in:
Nigreon 2025-02-22 23:32:03 +01:00
commit f83dd4f105
36 changed files with 3704 additions and 0 deletions

1
doc/arduino-nano.md Normal file
View File

@ -0,0 +1 @@
https://docs.arduino.cc/hardware/nano/

BIN
doc/arduino-nano.pdf Normal file

Binary file not shown.

101
lib/NexusTX/NexusTX.cpp Normal file
View File

@ -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 <buffer_size; i++)
{
tx_bit(SendBuffer[i]);
}
}
void NexusTX::SendPacket()
{
for (idx = 1; idx < repeat; idx++)
{
SendNexus();
if (idx + 1 == repeat) {break;} // do not send sync after last TX
// sync bit
digitalWrite(TX_PIN, HIGH);
delayMicroseconds(PULSE_HIGH);
digitalWrite(TX_PIN, LOW);
delayMicroseconds(PULSE_SYNC);
}
}
bool NexusTX::transmit()
{
if (millis() >= time_marker_send && send_time)
{
time_marker_send = millis() + send_time;
SendPacket();
return true;
}
else return false;
}

46
lib/NexusTX/NexusTX.h Normal file
View File

@ -0,0 +1,46 @@
#include <Arduino.h>
#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

901
lib/Oregon_TM/Oregon_TM.cpp Normal file
View File

@ -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;
}

127
lib/Oregon_TM/Oregon_TM.h Normal file
View File

@ -0,0 +1,127 @@
#include <Arduino.h>
#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

View File

@ -0,0 +1,69 @@
#include <Oregon_TM.h>
//Имитатор датчиков температуры и влажности 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);
}*/

View File

@ -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 <SensorTransmitter.h>
/*******************
* 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<count+1; i++) {
buffer[i]=encryptByte(buffer[i]);
cs1^=buffer[i];
cs2 =secondCheck(buffer[i]^cs2);
}
buffer[count+1]=cs1;
buffer[count+2]=secondCheck(cs1^cs2);
return count+3;
}
/* Send one byte and keep the transmitter ready to send the next */
void SensorTransmitter::sendManchesterByte(byte transmitterPin, byte b) {
byte i;
// Send start-bit 0.
digitalWrite(transmitterPin, LOW);
delayMicroseconds(500);
digitalWrite(transmitterPin, HIGH);
delayMicroseconds(500);
for (i = 0; i < 16; i++) {
if (b&1) {
digitalWrite(transmitterPin, HIGH);
} else {
digitalWrite(transmitterPin, LOW);
}
delayMicroseconds(500); /* 500uS delay */
b=~b;
if (i&1) {
b>>=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<cnt; i++) {
sendManchesterByte(transmitterPin, data[i]);
}
digitalWrite(transmitterPin, LOW); /* Drop the transmitter line */
}
/**
* Encrypts, adds checksums and transmits the data. The value of byte 3 in the data is ignored.
*/
void SensorTransmitter::sendPackage(byte transmitterPin, byte *data) {
byte buffer[14], temp, count;
for (temp=0x40; temp>=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);
}

View File

@ -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 <Arduino.h>
/**
* 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

View File

@ -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 <SensorTransmitter.h>
// 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);
}
}

View File

@ -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);
}

View File

@ -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);
}
}

17
lib/rc-switch/.gitignore vendored Normal file
View File

@ -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

1
lib/rc-switch/.piopm Normal file
View File

@ -0,0 +1 @@
{"type": "library", "name": "rc-switch", "version": "2.6.4", "spec": {"owner": "sui77", "id": 246, "name": "rc-switch", "requirements": null, "uri": null}}

70
lib/rc-switch/.travis.yml Normal file
View File

@ -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}

712
lib/rc-switch/RCSwitch.cpp Normal file
View File

@ -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 / <first name>.<last name>(at)gmail(dot)com
- Andreas Steinel / A.<lastname>(at)gmail(dot)com
- Max Horn / max(at)quendi(dot)de
- Robert ter Vehn / <first name>.<last name>(at)gmail(dot)com
- Johann Richard / <first name>.<last name>(at)gmail(dot)com
- Vlad Gheorghe / <first name>.<last name>(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

184
lib/rc-switch/RCSwitch.h Normal file
View File

@ -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 / <first name>.<last name>(at)gmail(dot)com
- Max Horn / max(at)quendi(dot)de
- Robert ter Vehn / <first name>.<last name>(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 <string.h> /* memcpy */
#include <stdlib.h> /* abs */
#include <wiringPi.h>
#elif defined(SPARK)
#include "application.h"
#else
#include "WProgram.h"
#endif
#include <stdint.h>
// 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

42
lib/rc-switch/README.md Normal file
View File

@ -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.

View File

@ -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.h>
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();
}
}

View File

@ -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;
}

View File

@ -0,0 +1,29 @@
/*
Simple example for receiving
https://github.com/sui77/rc-switch/
*/
#include <RCSwitch.h>
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();
}
}

View File

@ -0,0 +1,57 @@
/*
Example for different sending methods
https://github.com/sui77/rc-switch/
*/
#include <RCSwitch.h>
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);
}

View File

@ -0,0 +1,40 @@
/*
Example for outlets which are configured with a 10 pole DIP switch.
https://github.com/sui77/rc-switch/
*/
#include <RCSwitch.h>
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);
}

View File

@ -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);
}

View File

@ -0,0 +1,40 @@
/*
Example for outlets which are configured with two rotary/sliding switches.
https://github.com/sui77/rc-switch/
*/
#include <RCSwitch.h>
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);
}

View File

@ -0,0 +1,40 @@
/*
Example for Intertechno outlets
https://github.com/sui77/rc-switch/
*/
#include <RCSwitch.h>
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);
}

View File

@ -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.h>
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);
}

View File

@ -0,0 +1,154 @@
/*
A simple RCSwitch/Ethernet/Webserver demo
https://github.com/sui77/rc-switch/
*/
#include <SPI.h>
#include <Ethernet.h>
#include <RCSwitch.h>
// 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("<html>");
c.println("<head>");
c.println( "<title>RCSwitch Webserver Demo</title>");
c.println( "<style>");
c.println( "body { font-family: Arial, sans-serif; font-size:12px; }");
c.println( "</style>");
c.println("</head>");
c.println("<body>");
c.println( "<h1>RCSwitch Webserver Demo</h1>");
c.println( "<ul>");
c.println( "<li><a href=\"./?1-on\">Switch #1 on</a></li>");
c.println( "<li><a href=\"./?1-off\">Switch #1 off</a></li>");
c.println( "</ul>");
c.println( "<ul>");
c.println( "<li><a href=\"./?2-on\">Switch #2 on</a></li>");
c.println( "<li><a href=\"./?2-off\">Switch #2 off</a></li>");
c.println( "</ul>");
c.println( "<hr>");
c.println( "<a href=\"https://github.com/sui77/rc-switch/\">https://github.com/sui77/rc-switch/</a>");
c.println("</body>");
c.println("</html>");
}
/**
* 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';
}

View File

@ -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)
#######################################

View File

@ -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": "*"
}

View File

@ -0,0 +1,10 @@
name=rc-switch
version=2.6.4
author=sui77
maintainer=sui77,fingolfin <noreply@sui.li>
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

42
platformio.ini Normal file
View File

@ -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

2
src/config.h Normal file
View File

@ -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)

160
src/main.ino Normal file
View File

@ -0,0 +1,160 @@
#include <RCSwitch.h>
//#include <Oregon_TM.h>
#include <NexusTX.h>
#include <SensorTransmitter.h>
#include <DHTStable.h>
#include <math.h>
#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<<ACIE);
ACSR |=~(1<<ACD);
#endif
}
delay(5000);
LogS("Hello");
Logln();
//sensor_enabled = true;
if(DHT.read22(PIN_DHT) == DHTLIB_OK) { sensor_enabled = true; LogS("DHT detected"); Logln(); } else { LogS("No DHT"); Logln(); }
delay(1000);
//THGN132
// Model for Oregon_TM only
//transmitter.setType(THGN132);
//transmitter.setType(THGR810); //preferred
//transmitter.setType(RTGN318);
//transmitter.setType(BTHGN129);
//transmitter.setType(BTHR968);
transmitter.setChannel(0);
transmitter.setId(SENSORID);
transmitter.setBatteryFlag(1);
//transmitter.setComfort(24.2, 30);
mySwitch.enableReceive(0); // Receiver on pin INT0
delay(1000);
}
void LogS(const char* logstr)
{
if(LOGGER) {
Serial.print(logstr);
}
}
void LogI(long unsigned int logint)
{
if(LOGGER) {
Serial.print(logint);
}
}
void LogF(float logfloat)
{
if(LOGGER) {
Serial.print(logfloat);
}
}
void Logln()
{
if(LOGGER) {
Serial.println();
}
}
void loop() {
if (mySwitch.available()) {
unsigned long recvvalue = mySwitch.getReceivedValue();
/*
LogS("Received ");
LogI(recvvalue);
LogS(" / ");
LogI(mySwitch.getReceivedBitlength());
LogS("bit ");
LogS("Protocol: ");
LogI(mySwitch.getReceivedProtocol());
Logln();
*/
switch (recvvalue) {
case OUTPUTCODE_ON:
if(output_state == false)
{
output_state = true;
digitalWrite(PIN_OUTPUT, HIGH);
LogS("Output ON");
Logln();
}
break;
case OUTPUTCODE_OFF:
if(output_state == true)
{
output_state = false;
digitalWrite(PIN_OUTPUT, LOW);
LogS("Output OFF");
Logln();
}
break;
}
mySwitch.resetAvailable();
}
if(sensor_enabled == true) {
unsigned long curmillis = millis();
if((lastsend == 0) || (curmillis > (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);
}
}
}

7
src/pins-attiny85.h Normal file
View File

@ -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

7
src/pins-nano.h Normal file
View File

@ -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}