From 0c3af5d9c7ac43f01675334eae7ff282cbf76c10 Mon Sep 17 00:00:00 2001 From: Jan-Willem de Bruyn Date: Fri, 22 Jun 2018 10:29:56 +0200 Subject: [PATCH] initial --- Base.ino | 228 +++++ Config/Config_01.c | 112 +++ Doc/RFLink Protocol Reference.txt | 161 +++ Doc/RFLink Schematic.jpg | Bin 0 -> 41087 bytes Doc/Readme_Loader.txt | 23 + Doc/Readme_RFLink.txt | 273 +++++ Doc/Supported Device List.txt | 233 +++++ License.txt | 65 ++ Misc.ino | 78 ++ Plugin.ino | 1532 +++++++++++++++++++++++++++++ Plugins/Plugin_001.c | 617 ++++++++++++ Plugins/Plugin_002.c | 220 +++++ Plugins/Plugin_003.c | 936 ++++++++++++++++++ Plugins/Plugin_004.c | 341 +++++++ Plugins/Plugin_005.c | 204 ++++ Plugins/Plugin_006.c | 287 ++++++ Plugins/Plugin_007.c | 320 ++++++ Plugins/Plugin_008.c | 250 +++++ Plugins/Plugin_009.c | 347 +++++++ Plugins/Plugin_010.c | 242 +++++ Plugins/Plugin_011.c | 444 +++++++++ Plugins/Plugin_012.c | 309 ++++++ Plugins/Plugin_013.c | 276 ++++++ Plugins/Plugin_014.c | 147 +++ Plugins/Plugin_015.c | 270 +++++ Plugins/Plugin_030.c | 261 +++++ Plugins/Plugin_031.c | 181 ++++ Plugins/Plugin_032.c | 121 +++ Plugins/Plugin_033.c | 95 ++ Plugins/Plugin_034.c | 313 ++++++ Plugins/Plugin_035.c | 110 +++ Plugins/Plugin_040.c | 117 +++ Plugins/Plugin_041.c | 250 +++++ Plugins/Plugin_042.c | 293 ++++++ Plugins/Plugin_043.c | 211 ++++ Plugins/Plugin_044.c | 111 +++ Plugins/Plugin_045.c | 105 ++ Plugins/Plugin_046.c | 141 +++ Plugins/Plugin_048.c | 974 ++++++++++++++++++ Plugins/Plugin_060.c | 78 ++ Plugins/Plugin_061.c | 93 ++ Plugins/Plugin_062.c | 76 ++ Plugins/Plugin_063.c | 83 ++ Plugins/Plugin_070.c | 163 +++ Plugins/Plugin_071.c | 95 ++ Plugins/Plugin_072.c | 139 +++ Plugins/Plugin_073.c | 166 ++++ Plugins/Plugin_074.c | 192 ++++ Plugins/Plugin_075.c | 143 +++ Plugins/Plugin_080.c | 119 +++ Plugins/Plugin_081.c | 177 ++++ Plugins/Plugin_082.c | 177 ++++ Plugins/Plugin_090.c | 217 ++++ Plugins/Plugin_100.c | 166 ++++ Plugins/Plugin_101.c | 912 +++++++++++++++++ Plugins/Plugin_254.c | 58 ++ RFLink.ino | 28 + RawSignal.ino | 119 +++ Readme_RFLink.txt | 40 + 59 files changed, 14439 insertions(+) create mode 100644 Base.ino create mode 100644 Config/Config_01.c create mode 100644 Doc/RFLink Protocol Reference.txt create mode 100644 Doc/RFLink Schematic.jpg create mode 100644 Doc/Readme_Loader.txt create mode 100644 Doc/Readme_RFLink.txt create mode 100644 Doc/Supported Device List.txt create mode 100644 License.txt create mode 100644 Misc.ino create mode 100644 Plugin.ino create mode 100644 Plugins/Plugin_001.c create mode 100644 Plugins/Plugin_002.c create mode 100644 Plugins/Plugin_003.c create mode 100644 Plugins/Plugin_004.c create mode 100644 Plugins/Plugin_005.c create mode 100644 Plugins/Plugin_006.c create mode 100644 Plugins/Plugin_007.c create mode 100644 Plugins/Plugin_008.c create mode 100644 Plugins/Plugin_009.c create mode 100644 Plugins/Plugin_010.c create mode 100644 Plugins/Plugin_011.c create mode 100644 Plugins/Plugin_012.c create mode 100644 Plugins/Plugin_013.c create mode 100644 Plugins/Plugin_014.c create mode 100644 Plugins/Plugin_015.c create mode 100644 Plugins/Plugin_030.c create mode 100644 Plugins/Plugin_031.c create mode 100644 Plugins/Plugin_032.c create mode 100644 Plugins/Plugin_033.c create mode 100644 Plugins/Plugin_034.c create mode 100644 Plugins/Plugin_035.c create mode 100644 Plugins/Plugin_040.c create mode 100644 Plugins/Plugin_041.c create mode 100644 Plugins/Plugin_042.c create mode 100644 Plugins/Plugin_043.c create mode 100644 Plugins/Plugin_044.c create mode 100644 Plugins/Plugin_045.c create mode 100644 Plugins/Plugin_046.c create mode 100644 Plugins/Plugin_048.c create mode 100644 Plugins/Plugin_060.c create mode 100644 Plugins/Plugin_061.c create mode 100644 Plugins/Plugin_062.c create mode 100644 Plugins/Plugin_063.c create mode 100644 Plugins/Plugin_070.c create mode 100644 Plugins/Plugin_071.c create mode 100644 Plugins/Plugin_072.c create mode 100644 Plugins/Plugin_073.c create mode 100644 Plugins/Plugin_074.c create mode 100644 Plugins/Plugin_075.c create mode 100644 Plugins/Plugin_080.c create mode 100644 Plugins/Plugin_081.c create mode 100644 Plugins/Plugin_082.c create mode 100644 Plugins/Plugin_090.c create mode 100644 Plugins/Plugin_100.c create mode 100644 Plugins/Plugin_101.c create mode 100644 Plugins/Plugin_254.c create mode 100644 RFLink.ino create mode 100644 RawSignal.ino create mode 100644 Readme_RFLink.txt diff --git a/Base.ino b/Base.ino new file mode 100644 index 0000000..1bba7b4 --- /dev/null +++ b/Base.ino @@ -0,0 +1,228 @@ +#define BUILDNR 0x07 // shown in version +#define REVNR 0x33 // shown in version and startup string +#define MIN_RAW_PULSES 20 // =8 bits. Minimal number of bits*2 that need to have been received before we spend CPU time on decoding the signal. +#define RAWSIGNAL_SAMPLE_RATE 30 // Sample width / resolution in uSec for raw RF pulses. +#define MIN_PULSE_LENGTH 25 // Pulses shorter than this value in uSec. will be seen as garbage and not taken as actual pulses. +#define SIGNAL_TIMEOUT 7 // Timeout, after this time in mSec. the RF signal will be considered to have stopped. +#define SIGNAL_REPEAT_TIME 500 // Time in mSec. in which the same RF signal should not be accepted again. Filters out retransmits. +#define BAUD 57600 // Baudrate for serial communication. +#define TRANSMITTER_STABLE_DELAY 500 // delay to let the transmitter become stable (Note: Aurel RTX MID needs 500µS/0,5ms). +#define RAW_BUFFER_SIZE 512 // Maximum number of pulses that is received in one go. +#define PLUGIN_MAX 55 // Maximum number of Receive plugins +#define PLUGIN_TX_MAX 26 // Maximum number of Transmit plugins +#define SCAN_HIGH_TIME 50 // tijdsinterval in ms. voor achtergrondtaken snelle verwerking +#define FOCUS_TIME 50 // Duration in mSec. that, after receiving serial data from USB only the serial port is checked. +#define INPUT_COMMAND_SIZE 60 // Maximum number of characters that a command via serial can be. +#define PRINT_BUFFER_SIZE 60 // Maximum number of characters that a command should print in one go via the print buffer. + +// to include Config_xx.c files: +#define stringify(x) #x +#define CONFIGFILE2(a, b) stringify(a/Config/b) +#define CONFIGFILE(a, b) CONFIGFILE2(a, b) +#define CONFIG_FILE Config_01.c +#include CONFIGFILE(SKETCH_PATH,CONFIG_FILE) + +#define VALUE_PAIR 44 +#define VALUE_ALLOFF 55 +#define VALUE_OFF 74 +#define VALUE_ON 75 +#define VALUE_DIM 76 +#define VALUE_BRIGHT 77 +#define VALUE_UP 78 +#define VALUE_DOWN 79 +#define VALUE_STOP 80 +#define VALUE_CONFIRM 81 +#define VALUE_LIMIT 82 +#define VALUE_ALLON 141 + +// PIN Definition +#define PIN_BSF_0 22 // Board Specific Function lijn-0 +#define PIN_BSF_1 23 // Board Specific Function lijn-1 +#define PIN_BSF_2 24 // Board Specific Function lijn-2 +#define PIN_BSF_3 25 // Board Specific Function lijn-3 +#define PIN_RF_TX_VCC 15 // +5 volt / Vcc power to the transmitter on this pin +#define PIN_RF_TX_DATA 14 // Data to the 433Mhz transmitter on this pin +#define PIN_RF_RX_VCC 16 // Power to the receiver on this pin +#define PIN_RF_RX_DATA 19 // On this input, the 433Mhz-RF signal is received. LOW when no signal. + +//**************************************************************************************************************************************** +byte dummy=1; // get the linker going. Bug in Arduino. (old versions?) + +void(*Reboot)(void)=0; // reset function on adres 0. +byte PKSequenceNumber=0; // 1 byte packet counter +boolean RFDebug=false; // debug RF signals with plugin 001 +boolean RFUDebug=false; // debug RF signals with plugin 254 +boolean QRFDebug=false; // debug RF signals with plugin 254 but no multiplication + +uint8_t RFbit,RFport; // for processing RF signals. + +char pbuffer[PRINT_BUFFER_SIZE]; // Buffer for printing data +char InputBuffer_Serial[INPUT_COMMAND_SIZE]; // Buffer for Seriel data + +// Van alle devices die worden mee gecompileerd, worden in een tabel de adressen opgeslagen zodat hier naar toe gesprongen kan worden +void PluginInit(void); +void PluginTXInit(void); +boolean (*Plugin_ptr[PLUGIN_MAX])(byte, char*); // Receive plugins +byte Plugin_id[PLUGIN_MAX]; +boolean (*PluginTX_ptr[PLUGIN_TX_MAX])(byte, char*); // Transmit plugins +byte PluginTX_id[PLUGIN_TX_MAX]; + +void PrintHex8(uint8_t *data, uint8_t length); // prototype +void PrintHexByte(uint8_t data); // prototype +byte reverseBits(byte data); // prototype +void RFLinkHW( void ); // prototype + +struct RawSignalStruct // Raw signal variabelen places in a struct + { + int Number; // Number of pulses, times two as every pulse has a mark and a space. + byte Repeats; // Number of re-transmits on transmit actions. + byte Delay; // Delay in ms. after transmit of a single RF pulse packet + byte Multiply; // Pulses[] * Multiply is the real pulse time in microseconds + unsigned long Time; // Timestamp indicating when the signal was received (millis()) + byte Pulses[RAW_BUFFER_SIZE+2]; // Table with the measured pulses in microseconds divided by RawSignal.Multiply. (halves RAM usage) + // First pulse is located in element 1. Element 0 is used for special purposes, like signalling the use of a specific plugin +} RawSignal={0,0,0,0,0,0L}; +// =============================================================================== +unsigned long RepeatingTimer=0L; +unsigned long SignalCRC=0L; // holds the bitstream value for some plugins to identify RF repeats +unsigned long SignalHash=0L; // holds the processed plugin number +unsigned long SignalHashPrevious=0L; // holds the last processed plugin number + +void setup() { + Serial.begin(BAUD); // Initialise the serial port + pinMode(PIN_RF_RX_DATA, INPUT); // Initialise in/output ports + pinMode(PIN_RF_TX_DATA, OUTPUT); // Initialise in/output ports + pinMode(PIN_RF_TX_VCC, OUTPUT); // Initialise in/output ports + pinMode(PIN_RF_RX_VCC, OUTPUT); // Initialise in/output ports + digitalWrite(PIN_RF_RX_VCC,HIGH); // turn VCC to RF receiver ON + digitalWrite(PIN_RF_RX_DATA,INPUT_PULLUP); // pull-up resister on (to prevent garbage) + + pinMode(PIN_BSF_0,OUTPUT); // rflink board switch signal + digitalWrite(PIN_BSF_0,HIGH); // rflink board switch signal + + RFbit=digitalPinToBitMask(PIN_RF_RX_DATA); + RFport=digitalPinToPort(PIN_RF_RX_DATA); + Serial.print(F("20;00;Nodo RadioFrequencyLink - RFLink Gateway V1.1 - ")); + sprintf(InputBuffer_Serial,"R%02x;",REVNR); + Serial.println(InputBuffer_Serial); + + PKSequenceNumber++; + PluginInit(); + PluginTXInit(); +} + +void loop() { + byte SerialInByte=0; // incoming character value + int SerialInByteCounter=0; // number of bytes counter + + byte ValidCommand=0; + unsigned long FocusTimer=0L; // Timer to keep focus on the task during communication + InputBuffer_Serial[0]=0; // erase serial buffer string + + while(true) { + ScanEvent(); // Scan for RF events + // SERIAL: *************** kijk of er data klaar staat op de seriele poort ********************** + if(Serial.available()) { + FocusTimer=millis()+FOCUS_TIME; + + while(FocusTimer>millis()) { // standby + if(Serial.available()) { + SerialInByte=Serial.read(); + + if(isprint(SerialInByte)) + if(SerialInByteCounter<(INPUT_COMMAND_SIZE-1)) + InputBuffer_Serial[SerialInByteCounter++]=SerialInByte; + + if(SerialInByte=='\n') { // new line character + InputBuffer_Serial[SerialInByteCounter]=0; // serieel data is complete + //Serial.print("20;incoming;"); + //Serial.println(InputBuffer_Serial); + if (strlen(InputBuffer_Serial) > 7){ // need to see minimal 8 characters on the serial port + // 10;....;..;ON; + if (strncmp (InputBuffer_Serial,"10;",3) == 0) { // Command from Master to RFLink + // ------------------------------------------------------- + // Handle Device Management Commands + // ------------------------------------------------------- + if (strcasecmp(InputBuffer_Serial+3,"PING;")==0) { + sprintf(InputBuffer_Serial,"20;%02X;PONG;",PKSequenceNumber++); + Serial.println(InputBuffer_Serial); + } else + if (strcasecmp(InputBuffer_Serial+3,"REBOOT;")==0) { + strcpy(InputBuffer_Serial,"reboot"); + Reboot(); + } else + if (strncasecmp(InputBuffer_Serial+3,"RFDEBUG=O",9) == 0) { + if (InputBuffer_Serial[12] == 'N' || InputBuffer_Serial[12] == 'n' ) { + RFDebug=true; // full debug on + RFUDebug=false; // undecoded debug off + QRFDebug=false; // undecoded debug off + sprintf(InputBuffer_Serial,"20;%02X;RFDEBUG=ON;",PKSequenceNumber++); + } else { + RFDebug=false; // full debug off + sprintf(InputBuffer_Serial,"20;%02X;RFDEBUG=OFF;",PKSequenceNumber++); + } + Serial.println(InputBuffer_Serial); + } else + if (strncasecmp(InputBuffer_Serial+3,"RFUDEBUG=O",10) == 0) { + if (InputBuffer_Serial[13] == 'N' || InputBuffer_Serial[13] == 'n') { + RFUDebug=true; // undecoded debug on + QRFDebug=false; // undecoded debug off + RFDebug=false; // full debug off + sprintf(InputBuffer_Serial,"20;%02X;RFUDEBUG=ON;",PKSequenceNumber++); + } else { + RFUDebug=false; // undecoded debug off + sprintf(InputBuffer_Serial,"20;%02X;RFUDEBUG=OFF;",PKSequenceNumber++); + } + Serial.println(InputBuffer_Serial); + } else + if (strncasecmp(InputBuffer_Serial+3,"QRFDEBUG=O",10) == 0) { + if (InputBuffer_Serial[13] == 'N' || InputBuffer_Serial[13] == 'n') { + QRFDebug=true; // undecoded debug on + RFUDebug=false; // undecoded debug off + RFDebug=false; // full debug off + sprintf(InputBuffer_Serial,"20;%02X;QRFDEBUG=ON;",PKSequenceNumber++); + } else { + QRFDebug=false; // undecoded debug off + sprintf(InputBuffer_Serial,"20;%02X;QRFDEBUG=OFF;",PKSequenceNumber++); + } + Serial.println(InputBuffer_Serial); + } else + if (strncasecmp(InputBuffer_Serial+3,"VERSION",7) == 0) { + sprintf(InputBuffer_Serial,"20;%02X;VER=1.1;REV=%02x;BUILD=%02x;",PKSequenceNumber++,REVNR, BUILDNR); + Serial.println(InputBuffer_Serial); + } else { + // ------------------------------------------------------- + // Handle Generic Commands / Translate protocol data into Nodo text commands + // ------------------------------------------------------- + // check plugins + if (InputBuffer_Serial[SerialInByteCounter-1]==';') InputBuffer_Serial[SerialInByteCounter-1]=0; // remove last ";" char + if(PluginTXCall(0, InputBuffer_Serial)) { + ValidCommand=1; + } else { + // Answer that an invalid command was received? + ValidCommand=2; + } + } + } + } // if > 7 + if (ValidCommand != 0) { + if (ValidCommand==1) { + sprintf(InputBuffer_Serial,"20;%02X;OK;",PKSequenceNumber++); + Serial.println( InputBuffer_Serial ); + } else { + sprintf(InputBuffer_Serial, "20;%02X;CMD UNKNOWN;", PKSequenceNumber++); // Node and packet number + Serial.println( InputBuffer_Serial ); + } + } + SerialInByteCounter=0; + InputBuffer_Serial[0]=0; // serial data has been processed. + ValidCommand=0; + FocusTimer=millis()+FOCUS_TIME; + }// if(SerialInByte + }// if(Serial.available()) + }// while + }// if(Serial.available()) + }// while +} // void +/*********************************************************************************************/ + diff --git a/Config/Config_01.c b/Config/Config_01.c new file mode 100644 index 0000000..b07ebfc --- /dev/null +++ b/Config/Config_01.c @@ -0,0 +1,112 @@ +// **************************************************************************************************************************************** +// RFLink List of Plugins +// **************************************************************************************************************************************** +// Here are all plugins listed that are supported and used after compilation. +// When needed additional plugins can be added or selected plugins can be enabled/disabled. +// +// BEWARE OF THE PLUGIN_MAX setting!! TX: 51 RX: 24 +// **************************************************************************************************************************************** +// Translation Plugin for oversized packets due to their breaks/pause being too short between packets +// Used for Flamingo FA500R and various others, do NOT exclude this plugin. +#define PLUGIN_001 // DO NOT CHANGE OR EXCLUDE!! +// ------------------------------------------------------------------------ +// -- Any of the following protocols can be excluded whenever not needed -- +// ------------------------------------------------------------------------ +#define PLUGIN_002 // Lacrosse v2 2300/3600 +#define PLUGIN_003 // Kaku : Klik-Aan-Klik-Uit (with code wheel) aka ARC +#define PLUGIN_004 // NewKAKU : Klik-Aan-Klik-Uit with automatic coding aka Intertechno. +#define PLUGIN_005 // Eurodomest +#define PLUGIN_006 // Blyss +#define PLUGIN_007 // Conrad RSL2 +#define PLUGIN_008 // Kambrook +#define PLUGIN_009 // X10 RF +#define PLUGIN_010 // TRC02 RGB Switch +#define PLUGIN_011 // Home Confort +#define PLUGIN_012 // Flamingo FA500R +#define PLUGIN_013 // Powerfix/Quigg +#define PLUGIN_014 // Ikea Koppla +#define PLUGIN_015 // Home Easy EU +// ------------------- +// Weather sensors +// ------------------- +#define PLUGIN_030 // Alecto V1 (WS3500) 434 MHz. +#define PLUGIN_031 // Alecto V3 (WS1100/WS1200/WSD-19) 433.9 MHz. +#define PLUGIN_032 // Alecto V4 +#define PLUGIN_033 // Conrad Pool Thermometer +#define PLUGIN_034 // Cresta +#define PLUGIN_035 // Imagintronix +#define PLUGIN_040 // Mebus +#define PLUGIN_041 // LaCrosse v3 ws7000 +#define PLUGIN_042 // UPM/Esic +#define PLUGIN_043 // LaCrosse v1 +#define PLUGIN_044 // Auriol v3 +#define PLUGIN_045 // Auriol +#define PLUGIN_046 // Auriol v2 / Xiron +#define PLUGIN_048 // Oregon V1/2/3 +// ------------------- +// Motion Sensors, include when needed +// ------------------- +#define PLUGIN_060 // Ajax Chubb Varel 433 MHz. motion sensors +#define PLUGIN_061 // Chinese PIR motion door and window sensors +#define PLUGIN_062 // Chuango Alarm Devices +#define PLUGIN_063 // Oregon PIR/ALARM/LIGHT +// ------------------- +// Doorbells +// ------------------- +#define PLUGIN_070 // Select Plus (Action - Quhwa) +#define PLUGIN_071 // Plieger York +#define PLUGIN_072 // Byron SX doorbell +#define PLUGIN_073 // Deltronic doorbell +#define PLUGIN_074 // RL02 +#define PLUGIN_075 // Silvercrest +// ------------------- +// Smoke detectors / Fire Places +// ------------------- +#define PLUGIN_080 // Flamingo FA20 / KD101 smoke detector +#define PLUGIN_082 // Mertik Maxitrol / Dru fireplace +//#define PLUGIN_083 // Alecto SA33 +// ------------------- +// 868 MHZ +// ------------------- +#define PLUGIN_100 // Alecto V2 (DKW2012/ACH2010) 868 MHz. => PLANNED +// ------------------- +// Housekeeping +// ------------------- +#define PLUGIN_090 // Nodo Slave conversion plugin +#define PLUGIN_254 // Debug to show unsupported packets +// **************************************************************************************************************************************** +// RFLink List of Plugins that have TRANSMIT functionality +// **************************************************************************************************************************************** +// Here are all plugins listed that are supported and used after compilation. +// When needed additional plugins can be added or selected plugins can be enabled/disabled. +// **************************************************************************************************************************************** +// ------------------------------------------------------------------------ +// -- Any of the following protocols can be excluded whenever not needed -- +// ------------------------------------------------------------------------ +#define PLUGIN_TX_003 // Kaku : Klik-Aan-Klik-Uit (with code wheel) aka ARC +#define PLUGIN_TX_004 // NewKAKU : Klik-Aan-Klik-Uit with automatic coding aka Intertechno. +#define PLUGIN_TX_005 // Eurodomest +#define PLUGIN_TX_006 // Blyss +#define PLUGIN_TX_007 // Conrad RSL2 +#define PLUGIN_TX_008 // Kambrook +#define PLUGIN_TX_009 // X10 RF +#define PLUGIN_TX_010 // TRC02 RGB switch +#define PLUGIN_TX_011 // Home Confort +#define PLUGIN_TX_012 // Flamingo FA500R (11) +#define PLUGIN_TX_013 // Powerfix/Quigg +#define PLUGIN_TX_015 // Home Easy EU (14) +// ------------------- +// Doorbells +// ------------------- +#define PLUGIN_TX_070 // Select Plus (Action - Quhwa) +#define PLUGIN_TX_072 // Byron SX doorbell +#define PLUGIN_TX_073 // Deltronic doorbell +#define PLUGIN_TX_074 // RL02 +// ------------------- +// Smoke detectors +// ------------------- +#define PLUGIN_TX_080 // Flamingo FA20 / KD101 smoke detector +#define PLUGIN_TX_082 // Mertik Maxitrol / Dru fireplace +// ------------------- +// -=#=- +// ------------------- diff --git a/Doc/RFLink Protocol Reference.txt b/Doc/RFLink Protocol Reference.txt new file mode 100644 index 0000000..177ab57 --- /dev/null +++ b/Doc/RFLink Protocol Reference.txt @@ -0,0 +1,161 @@ +RFLink Gateway Commands and Data fields + +The use of this protocol is free for both server and client sides of the implementation +Data is send serial (via USB) as "text" at a speed of 57600 baud (N,8,1) +It is sufficient to open a port and listen to the data. +Each text line contains information that has been received via RF signals. + + +Packet structure - Received data from RF: +----------------------------------------- +20;ID=9999;Name;LABEL=data; + +20 => Node number 20 means from the RFLink Gateway to the master, 10 means from the master to the RFLink Gateway +; => field separator +NAME => Device name (can be used to display in applications etc.) +LABEL=data => contains the field type and data for that field, can be present multiple times per device + + +Data Fields: (LABEL=data) +------------ +ID=9999 => device ID (often a rolling code and/or device channel number) +SWITCH=A16 => House/Unit code like A1, P2, B16 or a button number etc. +CMD=ON => Command (ON/OFF/ALLON/ALLOFF) +SET_LEVEL=15 => Direct dimming level setting value +TEMP=9999 => Temperature (hexadecimal), high bit contains negative sign, needs devision by 10 +HUM=99 => Humidity (hexadecimal) +BARO=9999 => Barometric pressure (hexadecimal) +HSTATUS=99 => 0=Normal, 1=Comfortable, 2=Dry, 3=Wet +BFORECAST=99 => 0=No Info/Unknown, 1=Sunny, 2=Partly Cloudy, 3=Cloudy, 4=Rain +UV=9999 => UV intensity (hexadecimal) +LUX=9999 => Light intensity (hexadecimal) +BAT=OK => Battery status indicator (OK/LOW) +RAIN=1234 => rain rate in mm. (hexadecimal) +RAINTOT=1234 => total rain in mm. (hexadecimal) +WINSP=9999 => Wind speed in km. p/h (hexadecimal) needs devision by 10 +AWINSP=9999 => Average Wind speed in km. p/h (hexadecimal) needs devision by 10 +WINGS=9999 => Wind Gust in km. p/h (hexadecimal) +WINDIR=123 => Wind direction 0-15 reflecting 0-360 degrees in 22.5 degree steps (decimal) +WINCHL => wind chill (hexadecimal) +WINTMP=1234 => Wind meter temperature reading (hexadecimal) +CHIME=123 => Chime/Doorbell melody number +SMOKEALERT=ON => ON/OFF +PIR=ON => ON/OFF +CO2=1234 => CO2 air quality +SOUND=1234 => Noise level +KWATT=1234 => KWatt +WATT=1234 => Watt +DIST=1234 => Distance +METER=1234 => Water flow meter +VOLT=1234 => Voltage +CURRENT=1234 => Current + + +Sample data of received RF packets: +----------------------------------- +20;2D;UPM/Esic;ID=0001;TEMP=00cf;HUM=16;BAT=OK; +20;6A;UPM/Esic;ID=1002;WINSP=0041;WINDIR=5A;BAT=OK; +20;08;UPM/Esic;ID=1003;RAIN=0010;BAT=OK; +20;31;Mebus;ID=c201;TEMP=00cf; +20;32;Auriol;ID=008f;TEMP=00d3;BAT=OK; +20;A2;Auriol V2;ID=008f;TEMP=00a3;BAT=LOW; +20;33;Cresta;ID=3001;TEMP=00b0;HUM=50; +20;0C;Cresta;ID=8001;RAIN=001c; +20;47;Cresta;ID=8001;WINDIR=0002;WINSP=0060;WINGS=0088;WINCHL=b0; +20;47;Cresta;ID=8001;TEMP=00b0;UV=00d0; +20;36;Alecto V1;ID=ec02;TEMP=00d1;HUM=14; +20;07;Mebus;ID=ea01;TEMP=0017; +20;3D;Alecto V1;ID=2000;TEMP=0011;HUM=61; +20;26;Alecto V1;ID=0086;RAIN=02ac; +20;30;Alecto V1;ID=0020;WINSP=0068; +20;16;Alecto V1;ID=0020;WINSP=0020; +20;17;Alecto V1;ID=0020;WINDIR=0002;WINGS=0088; +20;36;Alecto V1;ID=0020;WINDIR=0002;WINGS=0040; +20;74;Oregon TempHygro;ID=0ACC;TEMP=00be;HUM=40;BAT=OK; +20;b3;Oregon TempHygro;ID=1a2d;TEMP=00dd;HUM=43;BAT=OK; +20;e5;Oregon BTHR;ID=5a6d;TEMP=00be;HUM=40;BARO=03d7;BAT=OK; +20;46;Oregon Rain;ID=2a1d;RAIN=0012;RAINTOT=0012;BAT=OK; +20;83;Oregon Rain2;ID=2a19;RAIN=002a;RAINTOT=0054;BAT=OK; +20;32;Oregon Wind;ID=1a89;WDIR=0045;WINSP=0068;AWINSP=0050;BAT=OK; +20;4a;Oregon Wind2;ID=3a0d;WDIR=0021;WINSP=0040;AWINSP=005a;BAT=OK; +20;ba;Oregon UVN128/138;ID=ea7c;UV=0030;BAT=OK; +20;AF;SelectPlus;ID=1bb4;CHIME=01; +20;FC;Plieger York;ID=dd01;CHIME=02; +20;47;Byron SX;ID=a66a;CHIME=A9; +20;12;Pir;ID=aa66;PIR=ON; +20;63;SmokeAlert;ID=123456;SMOKEALERT=ON; +20;06;Kaku;ID=41;SWITCH=1;CMD=ON; +20;0C;Kaku;ID=41;SWITCH=2;CMD=OFF; +20;0D;Kaku;ID=41;SWITCH=2;CMD=ON; +20;46;Kaku;ID=44;SWITCH=4;CMD=OFF; +20;E0;NewKaku;ID=cac142;SWITCH=1;CMD=ALLOFF; +20;3B;NewKaku;ID=cac142;SWITCH=3;CMD=OFF; +20;0B;NewKaku;ID=000005;SWITCH=2;CMD=ON; +20;0E;NewKaku;ID=000005;SWITCH=2;CMD=OFF; +20;12;NewKaku;ID=000002;SWITCH=2;CMD=OFF; +20;1E;NewKaku;ID=00000a;SWITCH=2;CMD=OFF; +20;1F;NewKaku;ID=00000a;SWITCH=2;CMD=ON; +20;01;NewKaku;ID=000007;SWITCH=2;CMD=OFF; +20;04;NewKaku;ID=000007;SWITCH=2;CMD=ON; +20;04;NewKaku;ID=000007;SWITCH=2;CMD=SET_LEVEL=14; +20;0C;HomeEasy;ID=7900b200;SWITCH=0b;CMD=ALLON; +20;AD;FA500;ID=0d00b900;SWITCH=0001;CMD=UNKOWN; +20;AE;FA500;ID=0a01;SWITCH=0a01;CMD=OFF; +20;03;Eurodomest;ID=03696b;SWITCH=00;CMD=OFF; +20;04;Eurodomest;ID=03696b;SWITCH=07;CMD=ALLOFF; +20;41;Conrad RSL2;ID=010002;SWITCH=03;CMD=ON; +20;47;Blyss;ID=ff98;SWITCH=A1;CMD=ON; +20;73;Kambrook;ID=010203;SWITCH=A1;CMD=OFF; + +Note that for sensors that only report values like temperature, only the data and the ID are required. +Name labels can be thrown away or used for cosmetic purposes. + +For switches, the protocol name has to be stored and re-used on the transmission side. +Thus, when a remote control is used to control a device data like below will be send from the RFLink Gateway over USB: +20;3B;NewKaku;ID=cac142;SWITCH=3;CMD=OFF; +When the state of this switch needs to be changed the following command has to be send: +10;NewKaku;0cac142;3;ON; +The name label (here "NewKaku") is used to tell the RFLink Gateway what protocol it has to use for the RF broadcast. + + + +Special Control Commands - Send: +-------------------------------- +10;REBOOT; => Reboot RFLink Gateway hardware +10;PING; => a "keep alive" function. Is replied with: 20;99;PONG; +10;VERSION; => Version and build indicator. Is replied with: 20;99;"RFLink Gateway software version"; +10;RFDEBUG=ON; => ON/OFF to Enable/Disable showing of RF packets. Is replied with: 20;99;RFDEBUG="state"; +10;RFUDEBUG=ON; => ON/OFF to Enable/Disable showing of undecoded RF packets. Is replied with: 20;99;RFUDEBUG="state"; +10;QRFDEBUG=ON; => ON/OFF to Enable/Disable showing of undecoded RF packets. Is replied with: 20;99;QRFDEBUG="state"; + QRFDEBUG is a faster version of RFUDEBUG but all pulse times are shown in hexadecimal and need to be multiplied by 30 + + +Packet structure - To Send data via RF: +--------------------------------------- +10;Protocol Name;device address,button number;action; + +Sample data of transmitted RF packets: +-------------------------------------- +10;Kaku;00004d;1;OFF; => Kaku/ARC protocol;address;action (ON/OFF) +10;AB400D;00004d;1;OFF; => Sartano protocol;address;action (ON/OFF) +10;Impuls;00004d;1;OFF; => Impuls protocol;address;action (ON/OFF) +10;NewKaku;00c142;1;ON; => Newkaku/AC protocol;address (24 bits);button number (hexadecimal 0x0-0x0f);action (ON/OFF/ALLON/ALLOFF/15 - 1 to 15 for direct dim level) +10;NewKaku;128ac4d;1;OFF; => Newkaku/AC protocol;address (28 bits);button number (hexadecimal 0x0-0x0f);action (ON/OFF/ALLON/ALLOFF/15 - 1 to 15 for direct dim level) +10;Eurodomest;123456;01;ON; => Eurodomest protocol;address;button number;action (ON/OFF/ALLON/ALLOFF) +10;Blyss;ff98;A1;OFF; => Blyss protocol;address;button;action (ON/OFF/ALLON/ALLOFF) +10;Conrad;ff0607;1;OFF; => Conrad RSL protocol, address, button number, action (ON/OFF/ALLON/ALLOFF) +10;Kambrook;050325;a1;ON; => Kambrook protocol, address, unit/button number, action (ON/OFF) +10;X10;000041;1;OFF; => X10 protocol;address;action (ON/OFF) +10;HomeConfort;01b523;D3;ON;=> HomeConfort protocol;address;action (ON/OFF) +10;FA500;001b523;D3;ON; => Flamingo protocol;address;action (ON/OFF) +10;Powerfix;000080;0;ON; => Powerfix/Quigg/Chacon protocol;address;action (ON/OFF) +10;Ikea Koppla;000080;0;ON; => Koppla protocol;address;action (ON/OFF) +10;HomeEasy;7900b100;3;ON; => Home Easy protocol;address;action (ON/OFF/ALLON/ALLOFF) +10;EV1527;000080;0;ON; => EV1527 protocol;address;device 0x00-0x0f,action ON/OFF +10;Chuango;000080;2;ON; => Chuango Protocol;address;action (ON/OFF/ALLON/ALLOFF) +10;Selectplus;001c33; => SelectPlus protocol;address +10;Byron;112233;01;OFF; => Dyron SX protocol;address;ringtone +10;DELTRONIC;001c33; => Deltronic protocol;address +10;BYRON;00009F;01;ON; => Byron protocol;address;chime number,command +10;FA20RF;67f570;1;ON; => Flamingo FA20RF / FA21 / KD101 protocol, address, button number, action (ON/OFF/ALLON/ALLOFF) +10;MERTIK;64;UP; => Mertik protocol, address, command diff --git a/Doc/RFLink Schematic.jpg b/Doc/RFLink Schematic.jpg new file mode 100644 index 0000000000000000000000000000000000000000..fa40c547a5707f27ca6916aef31d525b0fdc63d1 GIT binary patch literal 41087 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 CGnDTD literal 0 HcmV?d00001 diff --git a/Doc/Readme_Loader.txt b/Doc/Readme_Loader.txt new file mode 100644 index 0000000..3b91a89 --- /dev/null +++ b/Doc/Readme_Loader.txt @@ -0,0 +1,23 @@ +RFLink Loader Version 1.02 + + +The RFLink Loader program runs on Windows and can program an Arduino Mega 2560 board +with the RFLink software. +You do not need any Arduino IDE/Compiler etc. + + +Steps: +------ +- Launch the program +- Select the file you want to program (rflink.cpp.hex) +- Select the serial port to which the Arduino is connected. +- Hit the "program" button and wait for the process to finish. + + + +History: +-------- +1.02 Fixed: could not use serial ports > 9 +1.01 Fixed: could not load the hex file if the path name contained a space + Added: test serial port availability before trying to program the Arduino +1.00 Initial Release diff --git a/Doc/Readme_RFLink.txt b/Doc/Readme_RFLink.txt new file mode 100644 index 0000000..8b3e200 --- /dev/null +++ b/Doc/Readme_RFLink.txt @@ -0,0 +1,273 @@ +! ============================================================================ +! Only for educational purposes, the source might not be synchronized with the +! latest release! +! For normal operation, use the RFLink Loader that includes the latest release +! ============================================================================ + +Please note that the RFLink Gateway is a freeware project. +Stuntteam is not making money in any way. +This means that there are no unlimited funds to purchase test devices, +it also means the project has to rely on you, the user, to send debug data. + +If you want to contribute to this project, you can send a donation which is more than welcome (see www.nemcon.nl/blog2 donation button), +or help with sending debug data of unsupported devices (you can even write and contribute plugins and/or code fixes), +or donate the device that you would like to see supported. + +Right now we are looking for some older remotes and/or switches. +Like for example: Impuls, Ikea Koppla, Powerfix, Blyss, Home Confort, Conrad, Kambrook, Everflourish +For the implementation of the planned 2.4Ghz support we could use some simple MySensor devices. +For the implementation of the planned 868Mhz support we could use some devices as well. +If you have anything that you do not use, send a mail to frankzirrone@gmail.com +Thanks in advance! + +------------------------ +Synology NAS: +If you want to use RFLink with a Synology NAS you can use: +- an Arduino Mega clone based on CH340 USB/serial chip +In all other cases: +- connect a 10 uF capacitor between reset and ground on the Arduino. + Simply stick the pins of the capacitor in the power connector socket. + When you want to update the firmware of the Arduino, remove the capacitor and reconnect it when done. + For details about the Domoticz Synology package check out: http://www.jadahl.com +------------------------ +RFlink via Network Connection: +It is possible to use RFlink via a network connection using ser2net. +------------------------ +You can now use the RFLink Gateway with the following home automation software: +Domoticz +Jeedom +------------------------ +R35: (Work-In-Progress) Build 05 +- Added: Brel motor support +- Fixed: Oregon OWL CM119, CM160, CM180 +- Fixed: Corrected WH440 temperature values +- Fixed: Improved Philips SBC +- Fixed: Improved Chacon EMW200 +- Fixed: Removed a slash in the UPM/Esic name +- Tested: tested and working: Lidl / Libra TR502MSV switches + +R34: +- Added: Heidemann HX Silverline 70290 +- Added: Eurochron EAS 301Z / EAS 302 +- Added: Znane-01 switch set sold at Biedronka (Impuls clone) +- Added: HomeEasy HE800 protocol support +- Added: Fine Offset Electronics WH2, Agimex Rosenborg 66796, ClimeMET CM9088 +- Added: Somfy Smoove Origin RTS (433mhz) (receive) +- Tested: Eurodomest 972086 (Sold at Action in Belgium) +- Added: Eurodomest revised protocol (full autodetection) +- Added: Prologue temperature sensor support +- Tested: tested and working: Home Confort, Smart Home PRF-100 switch set +- Fixed: Auto detection of "Emil Lux"/LUX-Tools remote control/switch set (Sold at Obi.de Art.Nr. 2087971) (Impuls clone) +- Fixed: Alecto WS1100 working properly again (Adjusted pulse range and displayed humidity value) +- Fixed: Byron SX receive and send commands +- Fixed: Ikea Koppla Send routines +- Fixed: Improved the Impuls remote detection +- Fixed: Impuls transmit +- Changed: added checks for valid temperatures in various plugins + +R33: +- Updated RFlink loader to version 1.03 to include a serial log option with command sending ability! + +- Added: Full automatic 'Flamingo FA500/SilverCrest 91210/60494 RCS AAA3680/Mumbi M-FS300/Toom 1919384' protocol support! (send & receive!) + Note: re-learn your FA500 Remote in Domoticz +- Added: Unitec 48111/48112 (receive) +- Added: Avidsen +- Added: Somfy Telis (433mhz) (receive) +- Fixed: Extreme temperature value rejection for various sensor types (TFA/LaCrosse) +- Fixed: Improved Blyss send routines +- Added: Support for old Xiron temperature sensor in Cresta plugin (Temperature only sensor was not handled) +- Added: Biowin meteo sensor +- Fixed: Imagintronix humidity and temperature values were sometimes incorrect +- Fixed: AB4400/Sartano/Phenix detection corrected +- Fixed: Modification to allow EMW200/203 to work better +- Changed: ARC (and compatible) remote and switch handling improved +- Fixed: Improved Impuls handling +- Fixed: Auriol V3 minus temperature handling +- Fixed: TRC02RGB send +- Fixed: Oregon OWL180 data +- Changed: Aster signal detection so that L^Home model 32311T is recognized as well +- Changed: ID for Nodo Slave 'Wind direction/Wind gust' combined so that Domoticz can handle the data +- Changed: Protocol handling order for 'multi-protocol' transmitting devices + +R32: +- Added: Europe RS-200, Conrad TR-200 +- Added: Bofu motor transmit +- Added: ARC group command support +- Added: support for ARC based tri-state protocol +- Tested and working: Hormann 868mhz receive +- Changed: Bofu motor signal repetition detection improved +- Fixed: Aster transmit routines +- Fixed: plugin 003 output was not processed correctly by Domoticz +- Fixed: ARC higher address numbers did not work correctly in combination with Domoticz +- Changed: Chacon/Powerfix/Mandolyn/Quigg transmit routine and optimized timing +- Changed: Increased the number of re-transmits in the Home Easy protocol to improve signal reception and distance +- Changed: Sensor plugins now suppressing ARC derived protocols a bit better + +R31: +- New Device: Forrinx Wireless Doorbell +- New Device: TRC02 RGB controller +- New Device: OWL CM180 +- New Device: ELMES CTX3H and CTX4H contact sensor +- New Device: Bofu Motor (receive) +- New Device: Aster / GEMINI EMC/99/STI/037 +- Changed: EV1527 based sensors were reported as X10, now they are reported as EV1527. Note that it might be needed to re-add the devices to Domoticz +- Changed: increased number of retransmits for ARC and AC protocols +- Fixed: Koppla switch number was incorrect +- Fixed: Powerfix/Mandolyn/Chacon Parity calculation in send routines +- Fixed: Powerfix/Mandolyn/Chacon timing +- Fixed Windspeed value for WS2300 +- Fixed: Home Easy HE300 ON/OFF signal was reversed +- Changed: HomeEasy suppressing additional protocol data to avoid reporting the same event multiple times under different protocols +- Fixed: More fixes to avoid duplicate reporting of the same event (various protocols) + +R30: +- New Device: Conrad 9771 Pool Thermometer +- New Device: SilverCrest Z31370-TX Doorbell +- New Device: Smartwares remote controls (among others: SH5-TDR-K 10.037.17) +- New Device: Chuango Alarm devices Motion/Door/Window etc. (among others: CG-105S) +- New Device: Oregon Scientific NR868 PIR/night light +- New Device: Oregon Scientific MSR939 PIR +- New Device: Imagintronix Temperature/Soil humidity sensor +- New Device: Ikea Koppla (receive) +- New Device: Chacon (TR-502MSV, NR.RC402) +- Fixed: Arc protocol send +- Fixed: Impuls. Note: pair devices with the KAKU protocol, the remote is recognized separately. (Needs more tests!) +- Changed: Plugin 3 send method, combined routines +- Changed: HomeConfort was recognized as Impuls, now using GDR2 name +- Changed: HomeEasy remotes can deliver various signals, now skipping KAKU compatible signals and just reporting the HomeEasy code when both codes are transmitted +- Fixed: HomeEasy group on/off command was reversed for HE8xx devices, now correctly detects differences between HE3xx and HE8xx +- Fixed: HomeEasy was not able to control HE87x switches, changed the entire transmit routine +- Changed: stretched Xiron timing checks +- Changed: Various timing modifications (NewKaku/AC, Blyss) due to the new timing introduced at version R26 +- Changed: Plugin 61, Chinese Alarm devices, reversed bits as it seemed to correspond better to bit settings, increased address range +- Fixed: Flamingo Smokedetector packet detection tightened up to prevent false positives +- Fixed: Corrected Conrad RSL command interpretation +- Added: Extended Nodo Slave support to support separate and combined sensors +- Added: Extended Nodo Slave support to support pulse meters + +R29: +- Fixed: AC/NewKaku high unit numbers were incorrect. + If you already have devices with high unit numbers in Domoticz, just throw them away and let them be recognized again + +R28: +- Fixed: FA20RF smoke detector transmit from Domoticz + +R27: +- Added: OSV1 battery status +- Fixed: OSV1 boundaries and removed some debug info +- Fixed: Some plugins set an incorrect sampling rate divider value +- Changed: AlectoV1 false positives filter was too agressive + +R26: +- Added: QRFDEBUG command to do faster logging of undecoded data +- Added: VERSION command +- Added: Powerfix/Quigg switches +- Added: proper Lacrosse V3 WS7000 sensor support +- Changed: config file and plugin integration +- Changed: timeout and divider value +- Changed: Lacrosse V2 WS2300/WS3600 plugin number to get faster processing, changed various other parts as well +- Changed: Lacrosse V1 pulse duration checks +- Changed: various parts to improve speed +- Changed: Flamingo Smoke detector signal re-transmits from 8 to 10 times +- Added: Additional tests on Alecto V1 and Alecto V4 to filter out false positives +- Fixed: AC (NewKaku) protocol send for some device numbers +- Fixed: little bug in UPM code +- Fixed: Oregon wind speed reporting +- Fixed: Wind speed calculations +- Fixed: Wind direction reporting in all plugins +- Fixed: AlectoV3 humidity value displaying out of range values +- Fixed: OregonV1 decoding + +R25: +- Fixed: Eurodomest address range check +- Fixed: Alecto V1 and V3 humidity handling +- Fixed: Lacrosse WS2300/WS3600 and labelled as LacrosseV2 + +R24: +- Fixed: Flamingo Smoke Detector timings and device address usage +- Fixed: Timing for Nexa/Jula Anslut + +R23: +- Changed: Alecto V1 temperature data filtering +- Added: Alecto V1 battery status now shown for temperature sensors + +R22: +- Various additional tests and fixes after intensive tests +- Added: Home Confort send and recognition by Domoticz + +R21: +- Re-Activated PIR & Door/Window sensors (plugin 60/61) + +R20: +- Switched to Arduino 1.6.5 + +R19: +- Complete rewrite +- Added: Home Confort Smart Home - TEL-010 +- Added: RGB LED Controller +- Added: RL-02 Digital Doorbell +- Added: Deltronic Doorbell +- Added: Sartano 2606 remote & switch + +r18: +- Added Banggood SKU174397, Sako CH113, Homemart/Onemall FD030 and Blokker (Dake) 1730796 outdoor temperature sensor +- Tested Okay: Promax RSL366T, Profile PR-44N & PR-47N +- Fixed: LaCrosse humidity values are correctly displayed +- Fixed: Humidity values that originate from slave Nodos are correctly displayed +- Fixed: UPM/Esic insane temperature values are skipped +- Removed Xiron & Auriol debug data +- Tightened pulse range on various protocols to prevent false positives + +r17: +- Modified Oregon THGR228N code, +- Modified Newkaku(AC) dim values, +- Corrected support for KAKU door switches, +- Fixed Nodo Slave sensors, +- Improved speed and priorities so that group commands are properly transmitting + +r16: +- Fixed Aleco V1 temperature ID to match wind sensors +- Fixed HomeEasy transmit +- Added AC(NewKaku) dimmer support + +r15: +- Improved large packet translation + +r14: +- Changed Motion sensors (60/61) + +r13: +- Flamingo Smoke detector fix +- Added Xiron sensor support + +r11/12: +- Mertik / Dru Send added + +r10: +- Added Auriol Z32171A + +r9: +- Fixed Kaku send with high device id's (P1 M1 etc) + +r8: +- Improved descriptions + +r7: +- Fixed Oregon RTGR328N ID and humidity format +- Fixed UPM/Esic humidity format +- Fixed Alecto humidity format + +r6: +- Fixed Auriol V2 plugin +- Updated Auriol plugin +- Fixed Lacrosse Humidity + +r1/2/3/4/5: +- Added X10 receive/transmit plugin +- Minor changes & improvements + + +Special thanks to: +Alex, Benoit, Bert, Christophe, Deennoo, Emmanuel, Gerrit, Goran, Graeme, Jelle, John, Jonas, Marek, Mark, Martinus, Maurice, +Paul, Pim, Remco, Richard, Rob, Sebastien, Thibaut, William +and everyone who contributed with feedback, suggestions, debug data, tests etc. diff --git a/Doc/Supported Device List.txt b/Doc/Supported Device List.txt new file mode 100644 index 0000000..5b3f446 --- /dev/null +++ b/Doc/Supported Device List.txt @@ -0,0 +1,233 @@ +============================================================================ +Nodo - RFLink supported devices list +============================================================================ +Remote Controls and Electrical Switches / Dimmers etc.: +------------------------------------------------------- +ARC Protocol: (Send + Receive) + - Klik Aan Klik Uit (Code Wheel) + - Princeton PT2262 + - MOSDESIGN M3EB + - Domia Lite + - Intertechno + - CoCo Technologies + - Cogex + - D-IO (Chacon) + - Nexa + - Düwi Terminal + - Promax RSL366T + - Conrad 640472, 640473, 640475, 640476 + - ME FLS 100 + - Profile PR-44N, PR-47N + - Sartano 2606 + - Impuls + - Elro AB400 + - Waveman + - Elro Home Comfort AB600 + - (Elro) Home Control + - Smarthome YC-4000B + - Phenix YC-4000S + - Pollin Funk-Dimmer HouseLight FD-UP001 + - Pollin Funk-Steckdosen-Set 2605 + - Conrad 646443 outlet sockets / RSL366R + - Proteam HO1853 + - Velleman WRS3B + - "Emil Lux"/LUX-Tools (Obi.de Art.Nr. 2087971) + - Znane-01 switch set sold at Biedronka (Impuls clone) + - Renkforce + +Automatic Code Learning Protocols: (Send + Receive) +- Klik Aan Klik Uit + (All products, among others: AWMR210, AWMT230, AWMD250, AWST8800) +- CoCo Technologies +- Trust SmartHome +- Nexa Jula Anslut + (among others: 408-063, 408-064) +- Home Easy EU (among others HE100, HE200, HE300, AB600/HE600, HE800) +- Smartwares (SH5-TDR-K 10.037.17) + +Others: (Send + Receive) +- Home Easy (Code Wheel) +- Unitec 48110 EIM 826 / 48111 EIM 821 +- Quigg GT-1000 RC / Quigg GT-FSI-08 +- Intertek Eurodomest 972080 (Protocol revision 1 & 2 - full autodetection) +- Intertek Eurodomest 972086 (Protocol revision 1 & 2 - full autodetection) +- Blyss +- Conrad RSL2, Conrad RSL88R/B +- Kambrook RF3399/RF3405/RF3672/RF3689/RF4471R +- X10 RF switches +- Home Confort Smart Home - Among others: TEL-010, PRF-100 +- Quigg GT-7008BS, Quigg GT-FSI-04 +- DMV-7008S +- Mandolyn RC-710 +- Powerfix RCB-I 3600 +- Quigg GT-8000 +- Chacon (TR-502MSV, NR.RC402) +- Rev Ritter REV 8342L +- Ikea Koppla +- TRC02 RGB Controller +- Aster / GEMINI EMC/99/STI/037 +- Silvercrest l36-001 +- Brennenstuhl RCS 1000 N Comfort +- Lidl / Libra TR502MSV + +- Flamingo FA500R/S/DSS/WD +- SilverCrest 91210/60494 RCS AAA3680 +- Mumbi M-FS300 +- Toom 1919384 +============================================================================ +Weather Sensors: +---------------- +- Alecto V1: + Alecto WS3500, SilverCrest, Otio SH-10, Otio SH-20 + Auriol H13726, Ventus WS155, Hama EWS 1500, Meteoscan W155/W160 + Alecto WS4500, Ventus W044, Balance RF-WS105 +- Alecto V2: (868 Mhz!) + ACH2010, DKW2012 +- Alecto V3: + WS1100, WS1200 +- Alecto V4: + Banggood SKU174397, Sako CH113, Homemart/Onemall FD030 and Blokker (Dake) 1730796 outdoor temperature sensor +- Cresta/Hideki: + Hideki, TFA Nexus, Mebus, Irox, Irox-Pro X, Honeywell, Cresta TE923, TE923W, TE821W, + WXR810, DV928, Ventus W906, HomeWizard Rain meter +- Mebus: + Mebus Outdoor Sensor + Stacja Pogody WS-9941-M +- UPM/Esic: + UPM, Esic, Emos, DVM, Clas Ohlson, Dickson + WT260,WT260H,WT440H,WT450,WT450H,WDS500,RG700 +- LaCrosse: + Lacrosse TX3-TH Thermo/Humidity, Lacrosse TX4 + WS7000-15: Anemometer, WS7000-16: Rain precipitation, WS2500-19: Brightness Luxmeter, + WS7000-20: Thermo/Humidity/Barometer, TFA 30.3125 (temperature + humidity), TFA 30.3120.90 (temperature) +- LaCrosseV2: + WS2300 (Temperature/Humidity/Wind/Rain), WS2310, WS2355, WS3600 +- Auriol: + Z31743, Rubicson +- Auriol V2: + Z31055A-TX, Xiron, Watshome YT6018-2, Technoline +- Auriol V3: + Z32171A +- FineOffset: + Fine Offset Electronics WH2, Agimex Rosenborg 66796, ClimeMET CM9088 +- Eurochron: + EAS 301Z / EAS 302 +- Prologue: + Clas Ohlson 36-4741, Clas Ohlson 36-5087 +- Oregon V1/2/3: + THC238, THC268, THN132N, THWR288A, THRN122N, THN122N, AW129, AW131, THGR268, THGR122X, + THGN122N, THGN123N, THGR122NX, THGR228N, THGR238, WTGR800, THGR918, THGRN228NX, THGN500, + THGR810, RTGR328N, THGR328N, Huger BTHR918, BTHR918N, BTHR968, RGR126, RGR682, RGR918, PCR122, + THWR800, THR128, THR138, THC138, OWL CM119, cent-a-meter, OWL CM113, Electrisave, RGR928, + UVN128, UV138, UVN800, Huger-STR918, WGR918, WGR800, PCR800, WGTR800, BTHG968, OWL CM180, BTHGN129 +============================================================================ +Pool/Water Thermometer + Conrad 9771 Pool Thermometer +============================================================================ +Soil : Temperature & Moisture/Humidity Sensors + Imagintronix XH300 sensor + TH12 Opus TX300 +============================================================================ +Dusk Sensors (Light On/Off detection): +----------------------------------- +KAKU ABST-604 +Home Easy HE863 +Intertechno ITDS-5 +============================================================================ +Window blinds/shutters/sun screens: +----------------------------------- +Kaku ASUN650 +Intertechno CMR-500, ITL-500, ITL-1000 +============================================================================ +Motion Detectors: +-------------------------------- +Low Budget Chinese PIR (only signals motion detection) +Aritech PIR (only signals motion detection) +Ajax Chub Varel PIR (only signals motion detection) +Chuango: among others PIR-900 PIR sensor +Oregon Scientific NR868 PIR/night light +Oregon Scientific MSR939 PIR +============================================================================ +Door/Window sensors: +-------------------------------- +Low Budget Chinese Door/Window detectors +Intertechno ITM-100 +KAKU AMST-606 door/window sensor (separate signals for open and close) +Home Easy HE852 (separate signals for open and close) +CHACON DIO 54781 (separate signals for open and close) +COCO (separate signals for open and close) +Evology door/window contact sensor +ELMES CTX3H, CTX4H +Chuango: among others DWC-100 Door sensor, DWC-102 Door sensor +============================================================================ +Fireplace: +---------- +Mertik G6R H4T1 / Dru +============================================================================ +Smoke Detectors: +---------------- +KD101 (Send + Receive) +Diamant Smoke alarm (Send + Receive) +Flamingo FA20RF (Send + Receive) +Chacon 34126 (Send + Receive) +============================================================================ +Doorbells: +---------- +SelectPlus (200689103 - Black - Date code:0614), 1 by One, Delta (O00 QH-0031) (Send + Receive) +SelectPlus (200689101 - White - Date code:0914) (Send + Receive) +KAKU ACDB 6500BC (Send + Receive) +Deltronic Wireless Doorbell (Send + Receive) +RL-02 Wireless Doorbell (Send + Receive) +Byron SX (Send + Receive) +SilverCrest Z31370-TX (Receive) +Forrinx (Receive) +Heidemann HX Silverline 70290 (Receive) +L^Home model 32311T (Receive) +Plieger York (Receive) + Note that when the Plieger button is detected as Conrad RSL2, you need to use the button + on the inside of the Plieger doorbell to change the address code of the Plieger. +============================================================================ +Nodo Slave Sensors: +------------------- +Various Nodo sensor types are supported. +Make sure you use Nodo Slave ID 1,2, 4 till 9 to make the sensor a separate sensor. +If you want to have combined sensors (eg temperature + humidity in one device) make sure +you use Nodo Slave ID 10 till 16 + +The following variable / sensor types are currently supported + +Variabele 5 : Temperature +Variabele 6 : Humidity +Variabele 7 : Rain +Variabele 8 : Wind speed +Variabele 9 : Wind direction +Variabele 10 : Wind gust +Variable 11, 12 en 13 : Temperature +Variable 14 : Humidity +Variable 15 : UV +Variable 16 : Barometric pressure + + +Electricity/Water/Gas pulse meters are supported when using the following Nodo variable numbers: + +Make sure the slave devices are using Nodo Slave ID 3 !!! + +Variabele 1 : Pulse value 1 +Variabele 2 : Pulse value 2 +Variabele 3 : Pulse value 3 +Variabele 4 : Pulse value 4 +Variabele 5 : Pulse value 5 +Variabele 6 : Pulse value 6 + +If you require support for other sensors just let us know. +============================================================================ + +Chuango + * G5 GSM/SMS/RFID Touch Alarm System + * DWC-100 Door sensor + * DWC-102 Door sensor + * KP-700 Wireless Keypad + * PIR-900 PIR sensor + * RC-80 Remote Control + * SMK-500 Smoke sensor + \ No newline at end of file diff --git a/License.txt b/License.txt new file mode 100644 index 0000000..3ad2949 --- /dev/null +++ b/License.txt @@ -0,0 +1,65 @@ +===================================================================================================================================== + RFLINK GATEWAY LICENSE v1.0 +===================================================================================================================================== +Arduino project "RFLink Gateway" + +© Copyright 2015..2016 Stuntteam +(Portions © Copyright 2010..2015 Paul Tonkes (some Nodo 3.7 code parts)) + +Documentation : http://sourceforge.net/projects/rflink/ +Questions/Suggestions etc. mail to : frankzirrone@gmail.com +Compiler : http://arduino.cc +===================================================================================================================================== +LICENSE TERMS AND CONDITIONS + +The RFLink distributed and pre-compiled code is made available free of charge and without any warranties. +This License explicitly affirms your unlimited permission to run the unmodified Program. +If someone made you pay for this program, contact us! + + +The RFLink Gateway Source Code is made available so that third parties can develop their own plugins for the RFLink Gateway. +Availability of the core code and functional sample plugins does not mean that each and every plugin will be open source. +The fact that the code is published does not mean you obtain any kind of ownership or get any rights to the code. +This License explicitly affirms that you have the right to use the source code as a development environment to create your +own plugins which you can share with the community and project maintainers. +When you publish your plugin with the community it automatically falls under this license. However, it is also possible to share your +plugin with the project maintainers and keep your plugin closed source. In that case, the compiled portions of your code will +automatically fall under this license. All contributors hold the copyright for their own work. +The source code is free for use in any open source project when the original copyright notices, headers and this license file are included. +This program is free software: you can redistribute it and/or modify it when the original copyright notices and headers are included. + +You are not allowed to sell the pre-compiled code or the source code or distribute it without this license file. + +Usage of any parts of this code in a commercial work is prohibited unless express written approval is granted! +Some parts of the code might contain watermarking to identify our work. +You are not allowed to decompile, disassemble or reverse engineer any parts of the pre-compiled program. + +The license does not permit incorporating this program into proprietary programs. + +Disclaimer of Warranty. +This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +The copyright holders provide the program "AS IS" without warranty of any kind, either expressed or implied, including, but not limited to, +the implied warranties of merchantability or fitness for a particular purpose. The entire risk as to the quality and performance of the program +is with you. Should the program prove defective, you assume the cost of all necessary servicing, repair or correction. + +Limitation of Liability. +In no event unless required by applicable law or agreed to in writing will any copyright holder, or any other party who modifies and/or conveys +the program as permitted above, be liable to you for damages, including any general, special, incidental or consequential damages arising out of the +ues or inability to use the program (including but not limited to loss of data being rendered inaccurate or losses sustained by you or third +parties or a failure of the program to operate with any other programs), even if such holder or other party has been advised of the possibility of +such damages. +------------------------------------------------------------------------------------------------------------------------------------- +Warning: +The RFLink Gateway should not be used in any life-threatening situations. + +The RFLink Gateway is NOT justified to be used as a main part of a primary alarm system. +RFLink may only be used to listen to alarm sensors or activate a siren to complement a home automation system. +Note that if you own an alarm system which RFLink could disarm by broadcasting a recorded signal, it is time to upgrade your alarm system. +------------------------------------------------------------------------------------------------------------------------------------- +Installation information: + +All files should be located in a directory named "RFLink". +Install the Arduino compiler. You can download the compiler at www.arduino.cc +Preffered is 1.6.5 or similar +Open from the Arduino IDE the file RFlink.ino +------------------------------------------------------------------------------------------------------------------------------------- diff --git a/Misc.ino b/Misc.ino new file mode 100644 index 0000000..3afbbcc --- /dev/null +++ b/Misc.ino @@ -0,0 +1,78 @@ +/*********************************************************************************************\ + * Convert HEX or DEC tring to unsigned long HEX, DEC +\*********************************************************************************************/ +unsigned long str2int(char *string) { + return(strtoul(string,NULL,0)); +} +/*********************************************************************************************\ + * Convert string to command code +\*********************************************************************************************/ +int str2cmd(char *command) { + if(strcasecmp(command,"ON") == 0) return VALUE_ON; + if(strcasecmp(command,"OFF") == 0) return VALUE_OFF; + if(strcasecmp(command,"ALLON") == 0) return VALUE_ALLON; + if(strcasecmp(command,"ALLOFF") == 0) return VALUE_ALLOFF; + if(strcasecmp(command,"PAIR") == 0) return VALUE_PAIR; + if(strcasecmp(command,"DIM") == 0) return VALUE_DIM; + if(strcasecmp(command,"BRIGHT") == 0) return VALUE_BRIGHT; + if(strcasecmp(command,"UP") == 0) return VALUE_UP; + if(strcasecmp(command,"DOWN") == 0) return VALUE_DOWN; + if(strcasecmp(command,"STOP") == 0) return VALUE_STOP; + if(strcasecmp(command,"CONFIRM") == 0) return VALUE_CONFIRM; + if(strcasecmp(command,"LIMIT") == 0) return VALUE_LIMIT; + return false; +} +/********************************************************************************************\ + * Convert unsigned long to float long through memory +\*********************************************************************************************/ +float ul2float(unsigned long ul) { + float f; + memcpy(&f, &ul,4); + return f; +} +/*********************************************************************************************/ +void PrintHex8(uint8_t *data, uint8_t length) { // prints 8-bit data in hex (lowercase) + char tmp[length*2+1]; + byte first ; + int j=0; + for (uint8_t i=0; i> 4) | 48; + if (first > 57) tmp[j] = first + (byte)39; + else tmp[j] = first ; + j++; + + first = (data[i] & 0x0F) | 48; + if (first > 57) tmp[j] = first + (byte)39; + else tmp[j] = first; + j++; + } + tmp[length*2] = 0; + Serial.print(tmp); +} +/*********************************************************************************************/ +// todo: make uppercase? 3a = 3 or 48 (0x30) = 0x33 >57 (0x39) a>3a >39 > +27 +void PrintHexByte(uint8_t data) { // prints 8-bit value in hex (single byte) + char tmp[3]; + byte first ; + first = (data >> 4) | 48; // or with 0x30 + if (first > 57) tmp[0] = first + (byte)7; // 39; // if > 0x39 add 0x27 + else tmp[0] = first ; + + first = (data & 0x0F) | 48; + if (first > 57) tmp[1] = first + (byte)7; // 39; + else tmp[1] = first; + tmp[2] = 0; + Serial.print(tmp); +} +/*********************************************************************************************/ +// Reverse all bits in a byte +byte reverseBits(byte data) { + byte b = data; + for (byte i = 0; i < 8; ++i) { + data = (data << 1) | (b & 1); + b >>= 1; + } + return data; +} +/*********************************************************************************************/ + diff --git a/Plugin.ino b/Plugin.ino new file mode 100644 index 0000000..9b0de18 --- /dev/null +++ b/Plugin.ino @@ -0,0 +1,1532 @@ +/**********************************************************************************************\ + * Load plugins +\*********************************************************************************************/ +#define PLUGINFILE2(a, b) stringify(a/Plugins/b) +#define PLUGINFILE(a, b) PLUGINFILE2(a, b) + +#ifdef PLUGIN_001 +#include PLUGINFILE(SKETCH_PATH,Plugin_001.c) +#endif + +#ifdef PLUGIN_002 +#include PLUGINFILE(SKETCH_PATH,Plugin_002.c) +#endif + +#ifdef PLUGIN_003 +#include PLUGINFILE(SKETCH_PATH,Plugin_003.c) +#endif + +#ifdef PLUGIN_004 +#include PLUGINFILE(SKETCH_PATH,Plugin_004.c) +#endif + +#ifdef PLUGIN_005 +#include PLUGINFILE(SKETCH_PATH,Plugin_005.c) +#endif + +#ifdef PLUGIN_006 +#include PLUGINFILE(SKETCH_PATH,Plugin_006.c) +#endif + +#ifdef PLUGIN_007 +#include PLUGINFILE(SKETCH_PATH,Plugin_007.c) +#endif + +#ifdef PLUGIN_008 +#include PLUGINFILE(SKETCH_PATH,Plugin_008.c) +#endif + +#ifdef PLUGIN_009 +#include PLUGINFILE(SKETCH_PATH,Plugin_009.c) +#endif + +#ifdef PLUGIN_010 +#include PLUGINFILE(SKETCH_PATH,Plugin_010.c) +#endif + +#ifdef PLUGIN_011 +#include PLUGINFILE(SKETCH_PATH,Plugin_011.c) +#endif + +#ifdef PLUGIN_012 +#include PLUGINFILE(SKETCH_PATH,Plugin_012.c) +#endif + +#ifdef PLUGIN_013 +#include PLUGINFILE(SKETCH_PATH,Plugin_013.c) +#endif + +#ifdef PLUGIN_014 +#include PLUGINFILE(SKETCH_PATH,Plugin_014.c) +#endif + +#ifdef PLUGIN_015 +#include PLUGINFILE(SKETCH_PATH,Plugin_015.c) +#endif + +#ifdef PLUGIN_016 +#include PLUGINFILE(SKETCH_PATH,Plugin_016.c) +#endif + +#ifdef PLUGIN_017 +#include PLUGINFILE(SKETCH_PATH,Plugin_017.c) +#endif + +#ifdef PLUGIN_018 +#include PLUGINFILE(SKETCH_PATH,Plugin_018.c) +#endif + +#ifdef PLUGIN_019 +#include PLUGINFILE(SKETCH_PATH,Plugin_019.c) +#endif + +#ifdef PLUGIN_020 +#include PLUGINFILE(SKETCH_PATH,Plugin_020.c) +#endif + +#ifdef PLUGIN_021 +#include PLUGINFILE(SKETCH_PATH,Plugin_021.c) +#endif + +#ifdef PLUGIN_022 +#include PLUGINFILE(SKETCH_PATH,Plugin_022.c) +#endif + +#ifdef PLUGIN_023 +#include PLUGINFILE(SKETCH_PATH,Plugin_023.c) +#endif + +#ifdef PLUGIN_024 +#include PLUGINFILE(SKETCH_PATH,Plugin_024.c) +#endif + +#ifdef PLUGIN_025 +#include PLUGINFILE(SKETCH_PATH,Plugin_025.c) +#endif + +#ifdef PLUGIN_026 +#include PLUGINFILE(SKETCH_PATH,Plugin_026.c) +#endif + +#ifdef PLUGIN_027 +#include PLUGINFILE(SKETCH_PATH,Plugin_027.c) +#endif + +#ifdef PLUGIN_028 +#include PLUGINFILE(SKETCH_PATH,Plugin_028.c) +#endif + +#ifdef PLUGIN_029 +#include PLUGINFILE(SKETCH_PATH,Plugin_029.c) +#endif + +#ifdef PLUGIN_030 +#include PLUGINFILE(SKETCH_PATH,Plugin_030.c) +#endif + +#ifdef PLUGIN_031 +#include PLUGINFILE(SKETCH_PATH,Plugin_031.c) +#endif + +#ifdef PLUGIN_032 +#include PLUGINFILE(SKETCH_PATH,Plugin_032.c) +#endif + +#ifdef PLUGIN_033 +#include PLUGINFILE(SKETCH_PATH,Plugin_033.c) +#endif + +#ifdef PLUGIN_034 +#include PLUGINFILE(SKETCH_PATH,Plugin_034.c) +#endif + +#ifdef PLUGIN_035 +#include PLUGINFILE(SKETCH_PATH,Plugin_035.c) +#endif + +#ifdef PLUGIN_036 +#include PLUGINFILE(SKETCH_PATH,Plugin_036.c) +#endif + +#ifdef PLUGIN_037 +#include PLUGINFILE(SKETCH_PATH,Plugin_037.c) +#endif + +#ifdef PLUGIN_038 +#include PLUGINFILE(SKETCH_PATH,Plugin_038.c) +#endif + +#ifdef PLUGIN_039 +#include PLUGINFILE(SKETCH_PATH,Plugin_039.c) +#endif + +#ifdef PLUGIN_040 +#include PLUGINFILE(SKETCH_PATH,Plugin_040.c) +#endif + +#ifdef PLUGIN_041 +#include PLUGINFILE(SKETCH_PATH,Plugin_041.c) +#endif + +#ifdef PLUGIN_042 +#include PLUGINFILE(SKETCH_PATH,Plugin_042.c) +#endif + +#ifdef PLUGIN_043 +#include PLUGINFILE(SKETCH_PATH,Plugin_043.c) +#endif + +#ifdef PLUGIN_044 +#include PLUGINFILE(SKETCH_PATH,Plugin_044.c) +#endif + +#ifdef PLUGIN_045 +#include PLUGINFILE(SKETCH_PATH,Plugin_045.c) +#endif + +#ifdef PLUGIN_046 +#include PLUGINFILE(SKETCH_PATH,Plugin_046.c) +#endif + +#ifdef PLUGIN_047 +#include PLUGINFILE(SKETCH_PATH,Plugin_047.c) +#endif + +#ifdef PLUGIN_048 +#include PLUGINFILE(SKETCH_PATH,Plugin_048.c) +#endif + +#ifdef PLUGIN_049 +#include PLUGINFILE(SKETCH_PATH,Plugin_049.c) +#endif + +#ifdef PLUGIN_050 +#include PLUGINFILE(SKETCH_PATH,Plugin_050.c) +#endif + +#ifdef PLUGIN_051 +#include PLUGINFILE(SKETCH_PATH,Plugin_051.c) +#endif + +#ifdef PLUGIN_052 +#include PLUGINFILE(SKETCH_PATH,Plugin_052.c) +#endif + +#ifdef PLUGIN_053 +#include PLUGINFILE(SKETCH_PATH,Plugin_053.c) +#endif + +#ifdef PLUGIN_054 +#include PLUGINFILE(SKETCH_PATH,Plugin_054.c) +#endif + +#ifdef PLUGIN_055 +#include PLUGINFILE(SKETCH_PATH,Plugin_055.c) +#endif + +#ifdef PLUGIN_056 +#include PLUGINFILE(SKETCH_PATH,Plugin_056.c) +#endif + +#ifdef PLUGIN_057 +#include PLUGINFILE(SKETCH_PATH,Plugin_057.c) +#endif + +#ifdef PLUGIN_058 +#include PLUGINFILE(SKETCH_PATH,Plugin_058.c) +#endif + +#ifdef PLUGIN_059 +#include PLUGINFILE(SKETCH_PATH,Plugin_059.c) +#endif + +#ifdef PLUGIN_060 +#include PLUGINFILE(SKETCH_PATH,Plugin_060.c) +#endif + +#ifdef PLUGIN_061 +#include PLUGINFILE(SKETCH_PATH,Plugin_061.c) +#endif + +#ifdef PLUGIN_062 +#include PLUGINFILE(SKETCH_PATH,Plugin_062.c) +#endif + +#ifdef PLUGIN_063 +#include PLUGINFILE(SKETCH_PATH,Plugin_063.c) +#endif + +#ifdef PLUGIN_064 +#include PLUGINFILE(SKETCH_PATH,Plugin_064.c) +#endif + +#ifdef PLUGIN_065 +#include PLUGINFILE(SKETCH_PATH,Plugin_065.c) +#endif + +#ifdef PLUGIN_066 +#include PLUGINFILE(SKETCH_PATH,Plugin_066.c) +#endif + +#ifdef PLUGIN_067 +#include PLUGINFILE(SKETCH_PATH,Plugin_067.c) +#endif + +#ifdef PLUGIN_068 +#include PLUGINFILE(SKETCH_PATH,Plugin_068.c) +#endif + +#ifdef PLUGIN_069 +#include PLUGINFILE(SKETCH_PATH,Plugin_069.c) +#endif + +#ifdef PLUGIN_070 +#include PLUGINFILE(SKETCH_PATH,Plugin_070.c) +#endif + +#ifdef PLUGIN_071 +#include PLUGINFILE(SKETCH_PATH,Plugin_071.c) +#endif + +#ifdef PLUGIN_072 +#include PLUGINFILE(SKETCH_PATH,Plugin_072.c) +#endif + +#ifdef PLUGIN_073 +#include PLUGINFILE(SKETCH_PATH,Plugin_073.c) +#endif + +#ifdef PLUGIN_074 +#include PLUGINFILE(SKETCH_PATH,Plugin_074.c) +#endif + +#ifdef PLUGIN_075 +#include PLUGINFILE(SKETCH_PATH,Plugin_075.c) +#endif + +#ifdef PLUGIN_076 +#include PLUGINFILE(SKETCH_PATH,Plugin_076.c) +#endif + +#ifdef PLUGIN_077 +#include PLUGINFILE(SKETCH_PATH,Plugin_077.c) +#endif + +#ifdef PLUGIN_078 +#include PLUGINFILE(SKETCH_PATH,Plugin_078.c) +#endif + +#ifdef PLUGIN_079 +#include PLUGINFILE(SKETCH_PATH,Plugin_079.c) +#endif + +#ifdef PLUGIN_080 +#include PLUGINFILE(SKETCH_PATH,Plugin_080.c) +#endif + +#ifdef PLUGIN_081 +#include PLUGINFILE(SKETCH_PATH,Plugin_081.c) +#endif + +#ifdef PLUGIN_082 +#include PLUGINFILE(SKETCH_PATH,Plugin_082.c) +#endif + +#ifdef PLUGIN_083 +#include PLUGINFILE(SKETCH_PATH,Plugin_083.c) +#endif + +#ifdef PLUGIN_084 +#include PLUGINFILE(SKETCH_PATH,Plugin_084.c) +#endif + +#ifdef PLUGIN_085 +#include PLUGINFILE(SKETCH_PATH,Plugin_085.c) +#endif + +#ifdef PLUGIN_086 +#include PLUGINFILE(SKETCH_PATH,Plugin_086.c) +#endif + +#ifdef PLUGIN_087 +#include PLUGINFILE(SKETCH_PATH,Plugin_087.c) +#endif + +#ifdef PLUGIN_088 +#include PLUGINFILE(SKETCH_PATH,Plugin_088.c) +#endif + +#ifdef PLUGIN_089 +#include PLUGINFILE(SKETCH_PATH,Plugin_089.c) +#endif + +#ifdef PLUGIN_090 +#include PLUGINFILE(SKETCH_PATH,Plugin_090.c) +#endif + +#ifdef PLUGIN_091 +#include PLUGINFILE(SKETCH_PATH,Plugin_091.c) +#endif + +#ifdef PLUGIN_092 +#include PLUGINFILE(SKETCH_PATH,Plugin_092.c) +#endif + +#ifdef PLUGIN_093 +#include PLUGINFILE(SKETCH_PATH,Plugin_093.c) +#endif + +#ifdef PLUGIN_094 +#include PLUGINFILE(SKETCH_PATH,Plugin_094.c) +#endif + +#ifdef PLUGIN_095 +#include PLUGINFILE(SKETCH_PATH,Plugin_095.c) +#endif + +#ifdef PLUGIN_096 +#include PLUGINFILE(SKETCH_PATH,Plugin_096.c) +#endif + +#ifdef PLUGIN_097 +#include PLUGINFILE(SKETCH_PATH,Plugin_097.c) +#endif + +#ifdef PLUGIN_098 +#include PLUGINFILE(SKETCH_PATH,Plugin_098.c) +#endif + +#ifdef PLUGIN_099 +#include PLUGINFILE(SKETCH_PATH,Plugin_099.c) +#endif + +#ifdef PLUGIN_100 +#include PLUGINFILE(SKETCH_PATH,Plugin_100.c) +#endif + +#ifdef PLUGIN_101 +#include PLUGINFILE(SKETCH_PATH,Plugin_101.c) +#endif + +#ifdef PLUGIN_102 +#include PLUGINFILE(SKETCH_PATH,Plugin_102.c) +#endif + +#ifdef PLUGIN_103 +#include PLUGINFILE(SKETCH_PATH,Plugin_103.c) +#endif + +#ifdef PLUGIN_104 +#include PLUGINFILE(SKETCH_PATH,Plugin_104.c) +#endif + +#ifdef PLUGIN_105 +#include PLUGINFILE(SKETCH_PATH,Plugin_105.c) +#endif + +#ifdef PLUGIN_106 +#include PLUGINFILE(SKETCH_PATH,Plugin_106.c) +#endif + +#ifdef PLUGIN_107 +#include PLUGINFILE(SKETCH_PATH,Plugin_107.c) +#endif + +#ifdef PLUGIN_108 +#include PLUGINFILE(SKETCH_PATH,Plugin_108.c) +#endif + +#ifdef PLUGIN_109 +#include PLUGINFILE(SKETCH_PATH,Plugin_109.c) +#endif + +#ifdef PLUGIN_110 +#include PLUGINFILE(SKETCH_PATH,Plugin_110.c) +#endif + +#ifdef PLUGIN_111 +#include PLUGINFILE(SKETCH_PATH,Plugin_111.c) +#endif + +#ifdef PLUGIN_112 +#include PLUGINFILE(SKETCH_PATH,Plugin_112.c) +#endif + +#ifdef PLUGIN_113 +#include PLUGINFILE(SKETCH_PATH,Plugin_113.c) +#endif + +#ifdef PLUGIN_114 +#include PLUGINFILE(SKETCH_PATH,Plugin_114.c) +#endif + +#ifdef PLUGIN_115 +#include PLUGINFILE(SKETCH_PATH,Plugin_115.c) +#endif + +#ifdef PLUGIN_116 +#include PLUGINFILE(SKETCH_PATH,Plugin_116.c) +#endif + +#ifdef PLUGIN_117 +#include PLUGINFILE(SKETCH_PATH,Plugin_117.c) +#endif + +#ifdef PLUGIN_118 +#include PLUGINFILE(SKETCH_PATH,Plugin_118.c) +#endif + +#ifdef PLUGIN_119 +#include PLUGINFILE(SKETCH_PATH,Plugin_119.c) +#endif + +#ifdef PLUGIN_120 +#include PLUGINFILE(SKETCH_PATH,Plugin_120.c) +#endif + +#ifdef PLUGIN_250 +#include PLUGINFILE(SKETCH_PATH,Plugin_250.c) +#endif + +#ifdef PLUGIN_251 +#include PLUGINFILE(SKETCH_PATH,Plugin_251.c) +#endif + +#ifdef PLUGIN_252 +#include PLUGINFILE(SKETCH_PATH,Plugin_252.c) +#endif + +#ifdef PLUGIN_253 +#include PLUGINFILE(SKETCH_PATH,Plugin_253.c) +#endif + +#ifdef PLUGIN_254 +#include PLUGINFILE(SKETCH_PATH,Plugin_254.c) +#endif + +#ifdef PLUGIN_255 +#include PLUGINFILE(SKETCH_PATH,Plugin_255.c) +#endif +/*********************************************************************************************/ +void PluginInit(void) + { + byte x; + + // Wis de pointertabel voor de plugins. + for(x=0;x PULSE4200) { + if (RawSignal.Pulses[100] > PULSE4200) { + if (RawSignal.Pulses[150] > PULSE4200) { + RawSignal.Number=50; // New packet length + RawSignal.Pulses[0]=33; // signal the plugin number that should process this packet + return false; // packet detected, conversion done + } + } + } + } + // ========================================================================== + // End of Signal translation + // ========================================================================== + // ========================================================================== + // Beginning of Signal translation for Home Confort Switches/Remotes + // ========================================================================== + if (RawSignal.Number == 200) { + if (RawSignal.Pulses[1] > PULSE2000) { + if (RawSignal.Pulses[100] > PULSE4000) { + if (RawSignal.Pulses[101] > PULSE2000) { + RawSignal.Number=100; // New packet length + RawSignal.Pulses[0]=11; // signal the plugin number that should process this packet + return false; // packet detected, conversion done + } + } + } + } + // ========================================================================== + // End of Signal translation + // ========================================================================== + // ========================================================================== + // Beginning of Signal translation for Intertek Unitec Switches/Remotes + // ========================================================================== + if (RawSignal.Number == 202) { + if (RawSignal.Pulses[2] > PULSE2000) { + if (RawSignal.Pulses[52] > PULSE2000) { + if (RawSignal.Pulses[102] > PULSE2000) { + if (RawSignal.Pulses[1] < PULSE500) { + RawSignal.Number=50; // New packet length + RawSignal.Pulses[0]=19; // signal the plugin number that should process this packet + return false; // packet detected, conversion done + } + } + } + } + } + // ========================================================================== + // End of Signal translation + // ========================================================================== + // ========================================================================== + // Beginning of Signal translation for HomeEasy HE844 mode 4 - compatibility mode + // ========================================================================== + //if (RawSignal.Number == 234) { + // if (HEconversiontype==0) { // Reject the entire packet + // if ((RawSignal.Pulses[2] > PULSE4000) && (RawSignal.Pulses[2+58] > PULSE4000) && (RawSignal.Pulses[2+58+58] > PULSE4000) ){ + // RawSignal.Pulses[0]=15; // Instruct plugin 3 to skip any packets it might see after this + // RawSignal.Number=0; // Kill packet + // return true; // abort processing + // } + // } + //} + // ========================================================================== + // End of Signal translation HomeEasy HE842 + // ========================================================================== + + // ========================================================================== + // END plugin 001 if the incoming packet is not oversized and resume normal processing of plugins + // there is no need to do all the checks if there never will be a match + if (RawSignal.Number < OVERSIZED_LIMIT) return false; + // ========================================================================== + // ########################################################################## + // ========================================================================== + // Beginning of Signal translation for oversized packets (more pulses than handled by any plugin) + // ========================================================================== + + // ========================================================================== + // Beginning of Signal translation for Flamingo FA500R + // ========================================================================== + if (RawSignal.Number > 330 && RawSignal.Number < 378) { + int pos1=RawSignal.Number - 130; + int pos2=RawSignal.Number - 130 - 58; + if (RawSignal.Pulses[pos1] > PULSE2000 && RawSignal.Pulses[pos2] > PULSE4000) { + for (i=0;i<58;i++){ + RawSignal.Pulses[1+i]=RawSignal.Pulses[pos2+1+i]; + } + RawSignal.Pulses[0]=12; // Data will be processed by plugin 12 + RawSignal.Number=58; // New packet length + return false; // Conversion done, stop plugin 1 and continue with regular plugins + } + } + // ========================================================================== + // End of Signal Translation + // ========================================================================== + // ========================================================================== + // Beginning of Signal translation for Conrad RSL + // ========================================================================== + //Conrad RSL + //20;87;DEBUG;Pulses=462;Pulses(uSec)=1260,420,510,1140,1230,420,1230,420,510,1140,1230,420,1230,420,510,1140,510,1140,510,1140,510,1140,510,1140,510,1140,510,1140,510,1140,510,1140,510,1140,510,1140,510,1140,510,1140,1230,420,1230,420,1230,420,1230,420,1230,420,510,1140,1230,420,510,1140,510,1140,510,1140,510,1140,510,1140,510,6930,1230,420,510,1140,1230,420,1230,420,510,1140,1230,420,1230,420,510,1140,510,1140,510,1140,510,1140,510,1140,510,1140,510,1140,510,1140,510,1140,510,1140,510,1140,510,1140,510,1140,1230,420,1230,420,1230,420,1230,420,1230,420,510,1140,1230,420,510,1140,510,1140,510,1140,510,1140,510,1140,510,6930,1230,420,510,1140,1230,420,1230,420,510,1140,1230,420,1230,420,510,1140,510,1140,510,1140,510,1140,510,1140,510,1140,510,1140,510,1140,510,1140,510,1140,510,1140,510,1140,510,1140,1230,420,1230,420,1230,420,1230,420,1230,420,510,1140,1230,420,510,1140,510,1140,510,1140,510,1140,510,1140,510,6930,1230,420,510,1140,1230,420,1230,420,510,1140,1230,420,1230,420,510,1140,510,1140,510,1140,510,1140,510,1140,510,1140,510,1140,510,1140,510,1140,510,1140,510,1140,510,1140,510,1140,1230,420,1230,420,1230,420,1230,420,1230,420,510,1140,1230,420,510,1140,510,1140,510,1140,510,1140,510,1140,510,6930,1230,420,510,1140,1230,420,1230,420,510,1140,1230,420,1230,420,510,1140,510,1140,510,1140,510,1140,510,1140,510,1140,510,1140,510,1140,510,1140,510,1140,510,1140,510,1140,510,1140,1230,420,1230,420,1230,420,1230,420,1230,420,510,1140,1230,420,510,1140,510,1140,510,1140,510,1140,510,1140,510,6930,1230,420,510,1140,1230,420,1230,420,510,1140,1230,420,1230,420,510,1140,510,1140,510,1140,510,1140,510,1140,510,1140,510,1140,510,1140,510,1140,510,1140,510,1140,510,1140,510,1140,1230,420,1230,420,1230,420,1230,420,1230,420,510,1140,1230,420,510,1140,510,1140,510,1140,510,1140,510,1140,510,6930,1230,420,510,1140,1230,420,1230,420,510,1140,1230,420,1230,420,510,1140,510,1140,510,1140,510,1140,510,1140,510,1140,510,1140,510,1140,510,1140,510,1140,510,1140,510,1140,510,1140,1230,420,1230,420,1230,420,1230,420,1230,420,510,1140,1230,420,510,1140,510,1140,510,1140,510,1140,510,1140,510,6990; + if (RawSignal.Number == 462) { + if ( (RawSignal.Pulses[66] > PULSE6500) && (RawSignal.Pulses[66+66] > PULSE6500) && (RawSignal.Pulses[66+66+66] > PULSE6500) ) { + RawSignal.Number=66; // New packet length + return false; // Conversion done, terminate plugin 1 and continue with regular plugins + } + } + // ========================================================================== + // End of Signal Translation + // ========================================================================== + // ========================================================================== + // Beginning of Signal translation for HomeEasy HE842/HE852/HE863 + // ========================================================================== + if (RawSignal.Number > 460) { // && RawSignal.Number < 470) { + if (HEconversiontype==0) { // Reject the entire packet + if ((RawSignal.Pulses[2] > PULSE4000) && (RawSignal.Pulses[2+58] > PULSE4000) && (RawSignal.Pulses[2+58+58] > PULSE4000) ){ + RawSignal.Pulses[0]=15; // Instruct plugin 3 to skip any packets it might see after this + RawSignal.Number=0; // Kill packet + return true; // abort processing + } + } else { // Convert to Elro Method 1 (same as FA500 Method 1) + int pos1=RawSignal.Number - 58; + //if (RawSignal.Pulses[pos1]*RawSignal.Multiply > 4000) { + if (RawSignal.Pulses[pos1] > PULSE4000) { + for (i=0;i<58;i++){ + RawSignal.Pulses[1+i]=RawSignal.Pulses[pos1+1+i]; + } + RawSignal.Number=58; // New packet length + return false; // Conversion done, stop plugin 1 and continue with regular plugins + } + } + } + // ========================================================================== + // End of Signal translation HomeEasy HE842 + // ========================================================================== + + // ************************************************************************** + // Full buffer size checks, >>>>>> STATIC checks <<<<< + // ************************************************************************** + + // ========================================================================== + // Beginning of Signal translation for Forrinx + // ========================================================================== + if (RawSignal.Number == RAW_BUFFER_SIZE-1) { + if ((RawSignal.Pulses[2] > PULSE6000) && (RawSignal.Pulses[2+50] > PULSE6000) && (RawSignal.Pulses[2+50+50] > PULSE6000)) { + for (i=0;i<50;i++){ + RawSignal.Pulses[1+i]=RawSignal.Pulses[3+i]; // reorder pulse array + } + RawSignal.Number=51; // New packet length (report 51 and not 50 to avoid handling by other plugins + RawSignal.Pulses[0]=76; // signal the plugin number that should process this packet + return false; // packet detected, conversion done + } + } + // ========================================================================== + // ========================================================================== + // Beginning of Signal translation for bofu + // ========================================================================== + if (RawSignal.Number == RAW_BUFFER_SIZE-1) { + if ( (RawSignal.Pulses[1] > PULSE4200) && (RawSignal.Pulses[2] > PULSE2000) && (RawSignal.Pulses[3] > PULSE1100) ) { + if ( (RawSignal.Pulses[1+86] > PULSE4200) && (RawSignal.Pulses[2+86] > PULSE2000) && (RawSignal.Pulses[3+86] > PULSE1100) ) { + RawSignal.Number=85; // New packet length + return false; // Conversion done, stop plugin 1 and continue with regular plugins + } + } + } + // ========================================================================== + + // ************************************************************************** + // Full buffer size checks, >>>> SCANNING checks <<<<< sorted by packet size + // ************************************************************************** + + // ========================================================================== + // Beginning of Signal translation for Silvercrest Doorbell + // ========================================================================== + if (RawSignal.Number == RAW_BUFFER_SIZE-1) { + for (j=2;j<114;j++) { // Only check twice the total RF packet length we are looking for + if (RawSignal.Pulses[j] > PULSE1100) { // input is going to fast skip to where new part is going to start + if (j+114 > RAW_BUFFER_SIZE-1) break; // cant be the packet we look for + if ( (RawSignal.Pulses[j+114] > PULSE1100) && (RawSignal.Pulses[j+114+114] > PULSE1100) && (RawSignal.Pulses[j+114+114+114] > PULSE1100) ) { // first long delay found, make sure we have another at the right position + for (i=0;i<114;i++){ + RawSignal.Pulses[1+i]=RawSignal.Pulses[j+1+i]; // reorder pulse array + } + RawSignal.Number=114; // New packet length + RawSignal.Pulses[0]=75; // signal the plugin number that should process this packet + return false; // Conversion done, stop plugin 1 and continue with regular plugins + } + } + } + } + // ========================================================================== + // ========================================================================== + // Beginning of Signal translation for Home Confort Switches/Remotes + // ========================================================================== + if (RawSignal.Number > 299) { // Scan for corrupted/chained Home Confort RF packets + if (RawSignal.Pulses[1] > PULSE2000) { + if (RawSignal.Pulses[100] > PULSE4000) { + if (RawSignal.Pulses[101] > PULSE2000) { + RawSignal.Number=100; // New packet length + RawSignal.Pulses[0]=11; // signal the plugin number that should process this packet + return false; // packet detected, conversion done + } + } + } + for (j=1;j<104;j++) { + //if (RawSignal.Pulses[j]*RawSignal.Multiply > 4000) { + if (RawSignal.Pulses[j] > PULSE4000) { + //if (RawSignal.Pulses[j+1]*RawSignal.Multiply > 2000) { + if (RawSignal.Pulses[j+1] > PULSE2000) { + if ( (j+98) > RawSignal.Number) break; // cant be the packet we are looking for + //if ( (RawSignal.Pulses[j+100]*RawSignal.Multiply > 4000) && (RawSignal.Pulses[j+101]*RawSignal.Multiply > 2000) ) { // This could be a Home Confort packet + if ( (RawSignal.Pulses[j+100] > PULSE4000) && (RawSignal.Pulses[j+101] > PULSE2000) ) { // This could be a Home Confort packet + for (i=0;i<100;i++){ + RawSignal.Pulses[1+i]=RawSignal.Pulses[j+i]; // relocate/reorder pulse array + } + RawSignal.Number=100; // New packet length + RawSignal.Pulses[0]=11; // signal the plugin number that should process this packet + } + return false; // Conversion done, stop plugin 1 and continue with regular plugins + } + } + } + } + // ========================================================================== + // ========================================================================== + // Beginning of Signal translation for Auriol & Xiron + // ========================================================================== + if (RawSignal.Number == RAW_BUFFER_SIZE-1) { + for (int offset=0; offset < 74; offset++) { + //if (RawSignal.Pulses[offset]*RawSignal.Multiply > 3300) { + if (RawSignal.Pulses[offset+74] > PULSE3300 + && RawSignal.Pulses[offset+74*2] > PULSE3300 + && ((offset > 0 && RawSignal.Pulses[offset] > PULSE3300) + || RawSignal.Pulses[offset+74*3] > PULSE3300)) { + for (i=0;i<74;i++){ + RawSignal.Pulses[1+i]=RawSignal.Pulses[offset+i+1]; // reorder pulse array + } + RawSignal.Number=74; // New packet length + RawSignal.Pulses[0]=46; // signal the plugin number that should process this packet + return false; // Conversion done, stop plugin 1 and continue with regular plugins + } + } + } + // ========================================================================== + // ========================================================================== + // Beginning of Signal translation for Oregon + // ========================================================================== + if (RawSignal.Number == RAW_BUFFER_SIZE-1) { + for (j=50;j<104;j++) { // Only check the total RF packet length we are looking for + //if (RawSignal.Pulses[j]*RawSignal.Multiply > 2500) { // input is going to fast skip to where new part is going to start + if (RawSignal.Pulses[j] > PULSE1600) { // input is going to fast skip to where new part is going to start + if (j+52 > RAW_BUFFER_SIZE-1) break; // check for overflow, cant be the packet we look for + byte x=0; + if ( (RawSignal.Pulses[j+52] > PULSE1600) && (RawSignal.Pulses[j+52+52] > PULSE1600) && (RawSignal.Pulses[j+52+52+52] > PULSE1600) ) x=2; + if ( (RawSignal.Pulses[j+50] > PULSE1600) && (RawSignal.Pulses[j+50+50] > PULSE1600) && (RawSignal.Pulses[j+50+50+50] > PULSE1600) ) x=1; + if (x !=0) { + for (i=0;i<52;i++){ + RawSignal.Pulses[1+i]=RawSignal.Pulses[j+1+i]; // reorder pulse array + } + RawSignal.Number=52; // New packet length + RawSignal.Pulses[0]=63; // signal the plugin number that should process this packet + return false; // Conversion done, stop plugin 1 and continue with regular plugins + } + } + } + } + // ========================================================================== + // ========================================================================== + // Beginning of Signal translation for SelectPlus + // ========================================================================== + if (RawSignal.Number == RAW_BUFFER_SIZE-1) { + for (j=2;j<37;j++) { // Only check the total RF packet length we are looking for + //if (RawSignal.Pulses[j]*RawSignal.Multiply > 2500) { // input is going to fast skip to where new part is going to start + if (RawSignal.Pulses[j] > PULSE5000) { // input is going to fast skip to where new part is going to start + if (j+36 > RAW_BUFFER_SIZE-1) break; // cant be the packet we look for + //if ( (RawSignal.Pulses[j+26]*RawSignal.Multiply > 2500) && (RawSignal.Pulses[j+26]*RawSignal.Multiply < 3000) && (RawSignal.Pulses[j+26+26]*RawSignal.Multiply > 2500) ) { // first long delay found, make sure we have another at the right position + if ( (RawSignal.Pulses[j+36] > PULSE5000) && (RawSignal.Pulses[j+36+36] > PULSE5000) ) { // first long delay found, make sure we have another at the right position + if (j != 36) { + for (i=0;i<36;i++){ + RawSignal.Pulses[1+i]=RawSignal.Pulses[j+1+i]; // reorder pulse array + } + } + RawSignal.Number=36; // New packet length + RawSignal.Pulses[0]=70; // signal the plugin number that should process this packet + return false; // Conversion done, stop plugin 1 and continue with regular plugins + } + } + } + } + // ========================================================================== + // ========================================================================== + // Beginning of Signal translation for Byron Doorbell + // ========================================================================== + if (RawSignal.Number == RAW_BUFFER_SIZE-1) { + for (j=2;j<90 /*RawSignal.Number*/;j++) { // Only check twice the total RF packet length we are looking for + // Byron SX + //if (RawSignal.Pulses[j]*RawSignal.Multiply > 2500) { // input is going to fast skip to where new part is going to start + if (RawSignal.Pulses[j] > PULSE2500) { // input is going to fast skip to where new part is going to start + if (j+26 > RAW_BUFFER_SIZE-1) break; // cant be the packet we look for + //if ( (RawSignal.Pulses[j+26]*RawSignal.Multiply > 2500) && (RawSignal.Pulses[j+26]*RawSignal.Multiply < 3000) && (RawSignal.Pulses[j+26+26]*RawSignal.Multiply > 2500) ) { // first long delay found, make sure we have another at the right position + if ( (RawSignal.Pulses[j+26] > PULSE2500) && (RawSignal.Pulses[j+26] < PULSE3000) && (RawSignal.Pulses[j+26+26] > PULSE2500) ) { // first long delay found, make sure we have another at the right position + for (i=0;i<26;i++){ + RawSignal.Pulses[1+i]=RawSignal.Pulses[j+1+i]; // reorder pulse array + } + RawSignal.Number=26; // New packet length + RawSignal.Pulses[0]=72; // signal the plugin number that should process this packet + return false; // Conversion done, stop plugin 1 and continue with regular plugins + } + } + } + } + // ========================================================================== + + // ========================================================================== + // End of Signal translation + // ========================================================================== + if (RawSignal.Number > OVERSIZED_LIMIT) { // unknown and unsupported long packet (290 is the max. pulse length used at the Oregon plugin) + RawSignal.Number=0; // no need to show this to any of the other plugins for processing + return true; // abort processing completely + } // as there is no support for it anyway + return false; +} +#endif //PLUGIN_001 +/*********************************************************************************************\ +167 5010 +77 2310 +52 1560 + +161 +77 +52 + +0137 +77 +52 +161 +77 +52 +169 +78 +52 +161 +77 +52 +0137 +77 +52 +161 +77 +52 + + +20;08;DEBUG;Pulses=511;Pulses(uSec)= + +5010,2340,1560,210,270,510,270,510,270,540, 9 +270,510,270,510,270,510,270,510,270,510, 20 +270,510,270,510,270,510,270,510,270,510, 30 +270,510,270,510,270,510,600,210,270,510, 4 +270,510,270,510,270,540,270,540,600,210, 5 +600,210,600,210,270,510,270,540,270,540, 6 +270,510,270,510,270,510,270,510,600,210, 69 +600,210,600,210,600,210,600,210,600,210, 79 +270,510,270,510,600,210,4830,2310,1560,210,270,510,270,510,270,510,270,510,270,510,270,510,270,510,270,540,270,540,270,510,270,510,270,510,270,540,270,540,270,510,270,510,600,210,270,510,270,510,270,510,270,510,270,510,600,210,600,210,600,210,270,510,270,510,270,510,270,510,270,510,270,510,270,510,600,210,600,210,600,210,600,210,600,210,600,210,270,510,270,540,600,210,4830,2310,1560,210,270,510,270,510,270,510,270,540,270,510,270,510,270,510,270,510,270,510,270,510,270,510,270,510,270,510,270,510,270,510,270,510,600,210,270,510,270,510,270,510,270,510,270,510,600,210,600,210,600,210,270,540,270,540,270,510,270,510,270,510,270,510,270,510,600,210,600,210,600,210,600,210,600,210,600,210,270,510,270,510,600,210,4830,2310,1560,210,270,510,270,510,270,510,270,510,270,510,270,510,270,510,270,540,270,540,270,540,270,510,270,510,270,510,270,510,270,540,270,540,600,210,270,510,270,510,270,510,270,510,270,510,600,210,600,210,600,210,270,510,270,510,270,510,270,510,270,510,270,510,270,510,600,210,600,210,600,210,600,210,600,210,600,210,270,510,270,510,600,210,4830,2310,1560,210,270,540,270,510,270,510,270,510,270,510,270,510,270,510,270,510,270,510,270,510,270,510,270,510,270,510,270,510,270,510,270,510,600,210,270,510,270,510,270,510,270,510,270,540,600,210,600,210,600,210,270,510,270,510,270,510,270,540,270,540,270,510,270,510,600,210,600,210,600,210,600,210,600,210,600,210,270,510,270,510,600,210,4830,2310,1560,210,270,510,270,510,270,510,270,510,270,510,270,510,270,510,270,510,270,540,270,540,270,510,270,510,270,510,270,540,270,540,270,510,600,210,270,510,270,510,270,510,270,510,270,510,600,210,600,210,600,210,270,510,270,510,270,510,270,510,270,510,270,510,270,510,600,210,600,210,600,210,600,210,600,210,600,210,270; + + +Plugin Pulselength +------ ----------- +002 102-104 +003 50 +004 132 148 +005 50 +006 106 +007 66 +008 96 +009 66 68 +010 134-164 +011 100 +012 (26) 58 +013 42 +014 36-52 +015 116 +030 74 +031 94 & 126 +032 74-78 +034 124-284 +035 96 +040 58 +041 92/162/122/132 +042 48-56 +043 88 +044 82 +045 66 +046 74 +048 126-290 +060 26 +061 50 +070 36 +071 66 +072 26 +073 26 +074 50 +080 52 +081 46 +082 26 +090 194 +100 160 & 164-176 +\*********************************************************************************************/ diff --git a/Plugins/Plugin_002.c b/Plugins/Plugin_002.c new file mode 100644 index 0000000..dea3816 --- /dev/null +++ b/Plugins/Plugin_002.c @@ -0,0 +1,220 @@ +//####################################################################################################### +//## This Plugin is only for use with the RFLink software package ## +//## Plugin-41 LaCrosse ## +//####################################################################################################### +/*********************************************************************************************\ + * This plugin takes care of decoding LaCrosse weatherstation outdoor sensors + * It also works for all non LaCrosse sensors that follow this protocol. + * Lacrosse WS2355, WS3600 and compatibles + * + * Author : StuntTeam + * Support : http://sourceforge.net/projects/rflink/ + * License : This code is free for use in any open source project when this header is included. + * Usage of any parts of this code in a commercial application is prohibited! + ********************************************************************************************* + * Changelog: v1.0 initial release + ********************************************************************************************* + * Technical information: + * Partially based on http://makin-things.com/articles/decoding-lacrosse-weather-sensor-rf-transmissions/ + * + * WS2355 + * Each packet is 52 bits long. 4 different packet formats are transmitted. They are composed of: + * + * data 0 1 2 3 4 5 6 7 8 9 10 + * 1) 0000 1001 01 00 00100010 01111000 01010011 0011 10101100 0001 0+9+4+2+2+7+8+5+3+3+A+C=41 = 1 + * 2) 0000 1001 00 01 00100010 01111000 01010000 1101 10101111 1000 0+9+1+2+2+7+8+5+0+D+A+F=48 = 8 + * 3) 0000 1001 00 10 00100010 01111000 00001000 1100 11110111 1000 + * 4) 0000 1001 01 11 00100010 01111000 00000000 1100 11111111 1101 + * SSSS PPPP QR AA BBBBBBBB CCCCCCCC DDDDDDDD dddd EEEEEEEE FFFF + * 0000 1001 01 01 01100110 01011110 10001000 1001 01110111 0100 + * 0000 1001 00 11 01100110 01011110 00000000 1101 11111111 0110 + * 0000 1001 01 00 01100110 01011110 01000110 1000 10111001 0010 + * 0000 1001 00 01 01100110 01011110 01100111 1001 10011000 000 9+1+6+6+5+E+6+7+9+9+8=50 missing bit is 0 + 0000 1001 01 11 01100110 01011110 00000000 1001 11111111 011 9+7+6+6+5+E+0+0+9+F+F=56 missing bit is 0 + * S = Sync + * P = Preamble (1001 for WS2300, 0110 for WS3600?) + * A = packet type 00=TEMP, 01=HUM, 10=RAIN, 11=WIND + * B = Rolling Code + * C = Flags + * D = 12 bit value depending on the device/packet type + * E = Inverted data + * F = Checksum + * Q = 0/1 1=windpacket reports windgust 0=windpacket reports windspeed + * R = Error checking bit + * + * TEMP Dd = temperature - 30 degrees offset, 12 bits, 0x533 - 0x300 = 0x233 = 23.3 degrees + * HUM D = humidity value, 8 bits, 0x50 = RH of 50 + * RAIN D = number of tips * 0.508mm (range=0-4095, Once the count reaches 4095 it wraps back to 0. Powerloss results in a reset of the count) + * WIND d = Wind direction (0-15 in 22.5 degrees steps) D= wind speed + * + * Sample: + * 20;D3;DEBUG;Pulses=104;Pulses(uSec)=1400,1300,1325,1300,1325,1275,1350,1150,225,1300,1325,1275,1325,1275,225,1300,1325,1275,225,1275,1350,1275,225,1300,1325,1275,225,1300,225,1275,1350,1275,1350,1275,250,1275,225,1275,1350,1275,1350,1300,225,1300,1350,1275,225,1275,225,1275,225,1275,225,1275,1325,1275,225,1300,1325,1275,1325,1275,1325,1275,250,1275,1350,1275,1325,1300,1325,1275,250,1275,1350,1275,1325,1275,250,1275,1325,1275,250,1275,225,1275,225,1275,1350,1275,225,1275,250,1275,225,1275,1325,1275,250,1275,1350,1300,1325; + * 20;D4;DEBUG;Pulses=104;Pulses(uSec)=1400,1275,1350,1275,1350,1275,1325,1150,250,1275,1350,1275,1325,1275,250,1275,1325,1275,1350,1275,225,1275,225,1275,1350,1300,225,1275,225,1275,1350,1275,1325,1275,225,1275,225,1275,1325,1275,1325,1275,250,1275,1350,1300,225,1275,225,1275,225,1275,225,1275,1350,1275,1325,1275,1350,1275,1325,1275,1350,1275,1325,1275,1350,1275,1325,1300,1325,1275,225,1275,225,1275,1350,1275,225,1275,225,1300,225,1275,250,1275,225,1275,225,1275,250,1275,225,1275,225,1275,1350,1275,250,1275,225,1275,1325; + * 20;D5;DEBUG;Pulses=104;Pulses(uSec)=1400,1275,1350,1275,1350,1275,1325,1150,225,1275,1350,1275,1325,1275,225,1300,1325,1275,225,1300,1325,1275,1325,1275,1350,1275,225,1300,225,1275,1350,1275,1350,1300,225,1300,225,1275,1350,1275,1325,1275,250,1275,1350,1275,250,1275,225,1275,225,1275,225,1275,1325,1275,1350,1275,250,1275,1325,1275,1350,1275,1350,1275,225,1275,225,1275,1350,1275,225,1300,1325,1275,1325,1275,1350,1275,250,1275,1325,1275,250,1275,250,1275,225,1275,1350,1275,1350,1275,225,1275,1350,1275,1350,1275,225,1275,1325; + 20;C3;DEBUG;Pulses=102;Pulses(uSec)=1400,1275,1325,1275,1325,1275,1325,1175,225,1300,1350,1275,1350,1275,225,1300,1325,1300,1325,1275,1325,1300,225,1300,1325,1275,225,1275,225,1300,1325,1275,1325,1275,250,1275,225,1275,1325,1275,1350,1275,225,1275,1325,1275,225,1225,300,1275,250,1275,225,1275,1325,1275,1325,1300,225,1275,225,1275,1325,1300,1325,1275,225,1275,225,1275,225,1275,225,1275,1325,1275,1325,1275,250,1275,250,1275,1325,1275,1350,1275,225,1275,225,1300,1325,1275,1350,1275,1325,1300,1325,1275,1350,1275,1325; + 20;E1;DEBUG;Pulses=102;Pulses(uSec)=1425,1275,1325,1275,1325,1275,1350,1150,225,1275,1350,1275,1350,1275,250,1275,1350,1275,225,1275,225,1275,250,1275,1350,1275,225,1300,225,1275,1325,1275,1350,1300,225,1275,225,1275,1350,1275,1325,1300,225,1275,1350,1275,250,1275,225,1275,225,1275,250,1275,1325,1275,1350,1275,1325,1275,1325,1275,1325,1275,1350,1275,1350,1275,1325,1300,1325,1275,250,1275,1325,1275,1325,1275,225,1275,250,1275,225,1275,250,1275,225,1300,225,1275,225,1275,225,1300,225,1275,1350,1275,250,1275,225; + \*********************************************************************************************/ +#define LACROSSE_PULSECOUNT 104 // also handles 102 pulses! +#define LACROSSE_PULSEMID 1000/RAWSIGNAL_SAMPLE_RATE +#define LACROSSE_MIDLO 1100/RAWSIGNAL_SAMPLE_RATE +#define LACROSSE_MIDHI 1400/RAWSIGNAL_SAMPLE_RATE + +#ifdef PLUGIN_002 +boolean Plugin_002(byte function, char *string) { + if ((RawSignal.Number != LACROSSE_PULSECOUNT) && (RawSignal.Number != (LACROSSE_PULSECOUNT-2))) return false; + unsigned long bitstream1=0L; // holds first 16 bits + unsigned long bitstream2=0L; // holds last 28 bits + + unsigned int sensordata=0; + byte checksum=0; + byte bitcounter=0; // counts number of received bits (converted from pulses) + byte sensortype=0; + byte data[12]; + //================================================================================== + // get bytes + for(int x=1;x < RawSignal.Number;x+=2) { + if ((RawSignal.Pulses[x+1] < LACROSSE_MIDLO) || (RawSignal.Pulses[x+1] > LACROSSE_MIDHI)) { + if (x+1 < RawSignal.Number) return false; // in between pulse check + } + if (RawSignal.Pulses[x] > LACROSSE_PULSEMID ){ + if (bitcounter < 20) { + bitstream1 = (bitstream1 << 1); + bitcounter++; // only need to count the first 20 bits + } else { + bitstream2 = (bitstream2 << 1); + } + } else { + if (bitcounter < 20) { + bitstream1 = (bitstream1 << 1) | 0x1; + bitcounter++; // only need to count the first 20 bits + } else { + bitstream2 = (bitstream2 << 1) | 0x1; + } + } + } + if (RawSignal.Number == (LACROSSE_PULSECOUNT-2)) bitstream2 = (bitstream2 << 1); // add missing zero bit + //================================================================================== + // all bytes received, sort data, do sanity checks and make sure checksum is okay + //================================================================================== + if (bitstream1 == 0) return false; + //if ((bitstream1 == 0) && (bitstream2 == 0)) return false; + //data[0] = (bitstream1 >> 16) & 0x0f; // prepare nibbles from bit stream + //if (data[0] != 0) return false; // just a quick check to make sure the sync bits are correct. they are not needed for the checksum + data[0] = (bitstream1 >> 12) & 0x0f; // First nibble + if ( (data[0] != 0x09) && (data[0] != 0x06) ) { // type verification + return false; // 1001 for WS2300, 0110 for WS3600 + } + data[1] = (bitstream1 >> 8) & 0x0f; // Various other checks are possible + data[2] = (bitstream1 >> 4) & 0x0f; // Like parity checks and bit tests + data[3] = (bitstream1 >> 0) & 0x0f; // but false positives do not seem to be a problem + + data[4] = (bitstream2 >> 28) & 0x0f; + data[5] = (bitstream2 >> 24) & 0x0f; + data[6] = (bitstream2 >> 20) & 0x0f; + data[7] = (bitstream2 >> 16) & 0x0f; + data[8] = (bitstream2 >> 12) & 0x0f; + data[9] = (bitstream2 >> 8) & 0x0f; + data[10]= (bitstream2 >> 4) & 0x0f; + //================================================================================== + for (byte i=0;i<11;i++){ + checksum=checksum + data[i]; // max. value = A5 + } + checksum=checksum & 0x0f; + if (checksum != ((bitstream2)&0x0f)) return false; + //================================================================================== + // Prevent repeating signals from showing up + //================================================================================== + sensortype=(data[1])&0x03; // get sensor type from bitstream + unsigned long tempval=((checksum)<<8)+sensortype; // sensor type + checksum + if( (SignalHash!=SignalHashPrevious) || (RepeatingTimer j) return false; // Last pulse has to be low! Otherwise we are not dealing with an ARC signal + // ========================================================================== + // TIMING MEASUREMENT, this will find the shortest and longest pulse within the RF packet + // ========================================================================== + for (i=2;i PTHigh) { // longest pulse? + PTHigh=RawSignal.Pulses[i]; // new value + } + } + // ------------------------------------------- + // TIMING MEASUREMENT to devicetype + if (devicetype != 5) { // Dont do the timing check for Impuls, it is already identified at this point + if( ((PTLow==7)||(PTLow==8)) && ((PTHigh ==30) || (PTHigh ==31) ) ) devicetype=4; // ELRO AB400 + if( ((PTLow==9)||(PTLow==10)) && ((PTHigh==36)||(PTHigh==37)||((PTHigh >=40)&&(PTHigh <=42))) ) devicetype=1; // ELRO AB600 + else + if( ((PTLow==10)||(PTLow == 11)) && ((PTHigh >=40)&&(PTHigh <=42)) ) devicetype=2; // Profile PR44N / Promax rsl366t + else + if( (PTLow==13) && ((PTHigh >=32)&&(PTHigh <=34)) ) devicetype=3; // Profile PR47N + else + if( ((PTLow >=11)&&(PTLow <=12)) && ((PTHigh >=31)&&(PTHigh <=37)) ) devicetype=4; // Sartano + else + if( ((PTLow==12)||(PTLow==13)) && ((PTHigh==45)||(PTHigh==46)) ) devicetype=4; // Philips SBC + else + if( (PTLow <=3) && ((PTHigh==22)||(PTHigh==23)) ) devicetype=5; // Philips SBC + //else + //if( (PTLow == 8 || PTLow == 9) && (PTHigh == 33 || PTHigh == 34) ) devicetype=7; // Perel st=0,dt=7 + } + //sprintf(pbuffer, "ST=%d DT=%d %d/%d",signaltype,devicetype,PTLow,PTHigh); + //Serial.println( pbuffer ); + // ========================================================================== + // Turn pulses into bits + // ------------------------------------------- + for (i=0; ij && RawSignal.Pulses[4*i+3]j) { // 0101 + bitstream=(bitstream >> 1); // bit '0' + bitstream2=(bitstream2 << 2); // bit '0' written as '00' + signaltype=signaltype|1; // bit '0' present in signal '0001' + } else + if (RawSignal.Pulses[4*i+1]j && RawSignal.Pulses[4*i+3]>j && RawSignal.Pulses[4*i+4]> 1 | (1 << (KAKU_CodeLength-1))); // bit f (1) + bitstream2=(bitstream2 << 2) | 2; // bit 'f' written as '10' + // DONT CHANGE signal type to tri-state to keep EMW200 in KAKU mode + } else { + bitstream=(bitstream >> 1 | (1 << (KAKU_CodeLength-1))); // bit f (1) + bitstream2=(bitstream2 << 2) | 2; // bit 'f' written as '10' + signaltype=signaltype|2; // bit 'f' present in signal '0010' + } + } else + if (RawSignal.Pulses[4*i+1]j && RawSignal.Pulses[4*i+3]> 1); // Short 0, Group command on 2nd bit. (NOT USED?!) + bitstream2=(bitstream2 << 2) | 3; // bit 'short' written as '11' + group=1; + } else + if (RawSignal.Pulses[4*i+1]>j && RawSignal.Pulses[4*i+2]j && RawSignal.Pulses[4*i+4]> 1); // bit 1 (stored as 0) (IMPULS REMOTE) + } else { + if (i==11) { // last bit seems to cause trouble every now and then..?! + bitstream=(bitstream >> 1); // bit 1 (stored as 0) (IMPULS REMOTE) + } else { + devicetype=6; + bitstream=(bitstream >> 1 | (1 << (KAKU_CodeLength-1))); // bit f (1) + } + } + } else { + // ------------------------------------------- + // following are signal patches to fix bad transmission/receptions + // ------------------------------------------- + if (i==0) { // are we dealing with a RTK/AB600 device? then the first bit is sometimes mistakenly seen as 1101 + bitstream2=(bitstream2 << 2); // bit 0 + if (RawSignal.Pulses[4*i+1]>j && RawSignal.Pulses[4*i+2]>j && RawSignal.Pulses[4*i+3]j) { // 1101 + bitstream=(bitstream >> 1); // 0, treat as 0101 eg 0 bit + } else { + error=true; + signaltype=signaltype|8; + } + } else { + error=true; + signaltype=signaltype|8; + } + } // bad signal + } + //================================================================================== + // Sort out devices based on signal type and timing measurements + // ------------------------------------------- + //sprintf(pbuffer, "ST=%d DT=%d [%d]",signaltype,devicetype, error); + //Serial.println( pbuffer ); + //Serial.println(bitstream,BIN); + //Serial.println(bitstream2,BIN); + // ------------------------------------------- + // END OF TIMING MEASUREMENTS + // ========================================================================== + if (error==true) { // Error means that a pattern other than 0101/0110 was found + return false; // This usually means we are dealing with a semi-compatible device + } // that might have more states than used by ARC + if ((signaltype != 0x03) && (signaltype != 0x05) && (signaltype != 0x07)) return false; + //================================================================================== + // Prevent repeating signals from showing up + //================================================================================== + //if( (SignalHash!=SignalHashPrevious) || ((RepeatingTimer+500)millis())&&(SignalCRC != bitstream2)) ) { + // not seen the RF packet recently + if (signaltype == 0x07) { + if (((RepeatingTimer+1000)>millis())&&(SignalCRC != bitstream2)) { + return true; // skip tristate after normal arc + } + } + //Serial.print("KAKU PREV:"); + //Serial.println(SignalHashPrevious); + if ((SignalHashPrevious==14) && ((RepeatingTimer+2000)>millis()) ) { + SignalHash=14; + return true; // SignalHash 14 = HomeEasy, eg. cant switch KAKU after HE for 2 seconds + } + if ((SignalHashPrevious==11) && ((RepeatingTimer+2000)>millis()) ) { + SignalHash=11; + return true; // SignalHash 11 = FA500, eg. cant switch KAKU after FA500 for 2 seconds + } + SignalCRC=bitstream2; // store RF packet identifier + } else { + // already seen the RF packet recently + return true; + } + //================================================================================== + // Determine signal type to sort out the various houdecode/unitcode/button bits and on/off command bits + //================================================================================== + if (signaltype != 0x07) { + if ((bitstream&0x700)!=0x600) { // valid but not real KAKU + devicetype=4; + } + // ------------------------------------------- + if (devicetype == 4) { // Sartano + // ---------------------------------- // Sartano + housecode = ((bitstream) & 0x0000001FL); // .......11111b + unitcode = (((bitstream) & 0x000003E0L) >> 5); // ..1111100000b + housecode = ~housecode; // Sartano housecode is 5 bit ('A' - '`') + housecode &= 0x0000001FL; // Translate housecode so that all jumpers off = 'A' and all jumpers on = '`' + housecode += 0x41; + switch(unitcode) { // Translate unit code into button number 1 - 5 + case 0x1E: // E=1110 + unitcode = 1; + break; + case 0x1D: // D=1101 + unitcode = 2; + break; + case 0x1B: // B=1011 + unitcode = 3; + break; + case 0x17: // 7=0111 + unitcode = 4; + break; + case 0x0F: // f=1111 + unitcode = 5; + break; + default: + //Serial.print("Sartano:"); + devicetype=3; // invalid for Sartano, fall back + break; + } + if (devicetype == 4) { // Sartano + if ( ((bitstream >> 10) & 0x03) == 2) { + command = 1; // On + } else if ( ((bitstream >> 10) & 0x03) == 1){ + command = 0;// Off + } + } + } else + // ------------------------------------------- + if (devicetype == 5) { // IMPULS + housecode = ((bitstream) & 0x0000000FL); // ........1111b + unitcode = (((bitstream) & 0x000003F0L) >> 4); // ..1111110000b + housecode = ~housecode; // Impuls housecode is 4 bit ('A' - 'P') + housecode &= 0x0000000FL; // Translate housecode so that all jumpers off = 'A' and all jumpers on = 'P' + housecode += 0x41; + unitcode = ~unitcode; // Impuls unitcode is 5 bit + unitcode &= 0x0000001FL; // Translate unitcode so that all jumpers off = '1' and all jumpers on = '64' + if ( ((bitstream >> 10) & 0x03) == 2) { + command = 0; // Off + } else + if ( ((bitstream >> 10) & 0x03) == 1){ + command = 1;// On + } + } else + // ------------------------------------------- + if (devicetype == 6) { // Blokker/SelectRemote + // ---------------------------------- + if ( ((bitstream)&0xef1) != 0) return false; // Not a valid bitstream + + housecode = ((bitstream) & 0x0000000EL); // Isolate housecode + housecode = housecode >> 1; // shift right 1 bit + housecode = (~housecode)&0x07; // invert bits + housecode += 0x41; // add 'A' + unitcode = 0; + + if ( ((bitstream >> 8) & 0x01) == 1) { + command = 1; // On + } else { + command = 0;// Off + } + // ---------------------------------- // Sartano + } else + // ------------------------------------------- + if (devicetype != 4) { // KAKU (and some compatibles for now) + // ---------------------------------- + if ((bitstream&0x700)!=0x600) { // valid but not real KAKU + housecode=(((bitstream) &0x0f) +0x41); + unitcode=((((bitstream) &0xf0) >> 4)+1); + devicetype=1; + } else { + if ((bitstream&0x600)!=0x600) { + //Serial.println("Kaku 0/1 error"); + return false; // use two static bits as checksum + } + //bitstream=(bitstream)&0xffe; // kill bit to ensure Perel works -> actually kills code wheel letter B + housecode=(((bitstream) &0x0f) +0x41); + unitcode=((((bitstream) &0xf0) >> 4)+1); + // ---------------------------------- + } + if ( ((bitstream >> 11) & 0x01) == 1) { + command=1; // ON + } else { + command=0; // OFF + } + } + } + // ========================================================================== + // Output + // ---------------------------------- + sprintf(pbuffer, "20;%02X;", PKSequenceNumber++); // Node and packet number + Serial.print( pbuffer ); + // ---------------------------------- + if (signaltype == 0x03) { // '0011' bits indicate bits 0 and f are used in the signal + if (devicetype < 4) { // KAKU (and some compatibles for now) + Serial.print(F("Kaku;")); // Label + } else { + if (devicetype == 4) { // AB440R and Sartano + Serial.print(F("AB400D;")); // Label + } else + if (devicetype == 5) { // Impuls + Serial.print(F("Impuls;")); // Label + } else { + Serial.print(F("Sartano;")); // Others + } + } + } else + if (signaltype == 0x05) { // '0101' bits indicate bits 0 and 1 are used in the signal + if (devicetype == 5) { // KAKU (and some compatibles for now) + Serial.print(F("Impuls;")); // Label + } else { + Serial.print(F("PT2262;")); // Label + } + } else + if (signaltype == 0x07) { // '0111' bits indicate bits 0, f and 1 are used in the signal (tri-state) + Serial.print(F("TriState;")); // Label + } + // ---------------------------------- + if (signaltype == 0x07) { // '0111' bits indicate bits 0, f and 1 are used in the signal (tri-state) + sprintf(pbuffer, "ID=%06lx;",((bitstream2)>>4)&0xffffff) ; // ID + Serial.print( pbuffer ); + housecode=(bitstream2)&0x03; + unitcode=((((bitstream2)>>2)&0x03)^0x03)^housecode; + //command=((bitstream2)&1)^1; << ^1 reverses lidl light + command=((bitstream2)&3); // 00 01 10 0 1 f + if (command > 1) command=1; // 0 stays 0 (OFF), 1 and f become 1 (ON) + } else { + sprintf(pbuffer, "ID=%02x;", housecode); // ID + Serial.print( pbuffer ); + } + sprintf(pbuffer, "SWITCH=%d;", unitcode); + Serial.print( pbuffer ); + Serial.print(F("CMD=")); + if (group==1) Serial.print(F("ALL")); + if ( command == 1 ) { + Serial.print(F("ON;")); + } else { + Serial.print(F("OFF;")); + } + Serial.println(); + // ---------------------------------- + RawSignal.Repeats=true; // suppress repeats of the same RF packet + RawSignal.Number=0; + return true; +} +#endif //PLUGIN_003 + +#ifdef PLUGIN_TX_003 +void Arc_Send(unsigned long address); // sends 0 and float +void NArc_Send(unsigned long bitstream); // sends 0 and 1 +void TriState_Send(unsigned long bitstream); // sends 0, 1 and float + +boolean PluginTX_003(byte function, char *string) { + boolean success=false; + unsigned long bitstream=0L; + byte command=0; + uint32_t housecode = 0; + uint32_t unitcode = 0; + byte Home=0; // KAKU home A..P + byte Address=0; // KAKU Address 1..16 + byte c=0; + byte x=0; + // ========================================================================== + //10;Kaku;00004d;1;OFF; + //10;Kaku;00004f;e;ON; + //10;Kaku;000050;10;ON; + //10;Kaku;000049;b;ON; + //012345678901234567890 + // ========================================================================== + if (strncasecmp(InputBuffer_Serial+3,"KAKU;",5) == 0) { // KAKU Command eg. Kaku;A1;On + if (InputBuffer_Serial[14] != ';') return false; + + x=15; // character pointer + InputBuffer_Serial[10]=0x30; + InputBuffer_Serial[11]=0x78; // Get home from hexadecimal value + InputBuffer_Serial[14]=0x00; // Get home from hexadecimal value + Home=str2int(InputBuffer_Serial+10); // KAKU home A is intern 0 + if (Home < 0x51) // take care of upper/lower case + Home=Home - 'A'; + else + if (Home < 0x71) // take care of upper/lower case + Home=Home - 'a'; + else { + return false; // invalid value + } + + while((c=InputBuffer_Serial[x++])!=';'){ // Address: 1 to 16/32 + if(c>='0' && c<='9'){Address=Address*10;Address=Address+c-'0';} + if(c>='a' && c<='f'){Address=Address+(c-'a'+10);} // 31? + if(c>='A' && c<='F'){Address=Address+(c-'A'+10);} // 51? + } + //if (Address==0) { // group command is given: 0=all + // command=2; // Set 2nd bit for group. + // bitstream=Home; + //} else { + // bitstream= Home | ((Address-1)<<4); + //} + + bitstream= Home | ((Address-1)<<4); + command |= str2cmd(InputBuffer_Serial+x)==VALUE_ON; // ON/OFF command + bitstream = bitstream | (0x600 | ((command & 1) << 11)); // create the bitstream + //Serial.println(bitstream); + Arc_Send(bitstream); + success=true; + // --------------- END KAKU SEND ------------ + } else + // ========================================================================== + //10;AB400D;00004d;1;OFF; + //012345678901234567890 + // ========================================================================== + if (strncasecmp(InputBuffer_Serial+3,"AB400D;",7) == 0) { // KAKU Command eg. Kaku;A1;On + if (InputBuffer_Serial[16] != ';') return false; + x=17; // character pointer + InputBuffer_Serial[12]=0x30; + InputBuffer_Serial[13]=0x78; // Get home from hexadecimal value + InputBuffer_Serial[16]=0x00; // Get home from hexadecimal value + Home=str2int(InputBuffer_Serial+12); // KAKU home A is intern 0 + if (Home < 0x61) // take care of upper/lower case + Home=Home - 'A'; + else + if (Home < 0x81) // take care of upper/lower case + Home=Home - 'a'; + else { + return false; // invalid value + } + while((c=InputBuffer_Serial[x++])!=';'){ // Address: 1 to 16/32 + if(c>='0' && c<='9'){Address=Address*10;Address=Address+c-'0';} + } + command = str2cmd(InputBuffer_Serial+x)==VALUE_ON; // ON/OFF command + housecode = ~Home; + housecode &= 0x0000001FL; + unitcode=Address; + if ((unitcode >= 1) && (unitcode <= 5) ) { + bitstream = housecode & 0x0000001FL; + if (unitcode == 1) bitstream |= 0x000003C0L; + else if (unitcode == 2) bitstream |= 0x000003A0L; + else if (unitcode == 3) bitstream |= 0x00000360L; + else if (unitcode == 4) bitstream |= 0x000002E0L; + else if (unitcode == 5) bitstream |= 0x000001E0L; + + if (command) bitstream |= 0x00000800L; + else bitstream |= 0x00000400L; + } + //Serial.println(bitstream); + Arc_Send(bitstream); + success=true; + } else + // --------------- END SARTANO SEND ------------ + // ========================================================================== + //10;PT2262;000041;1;OFF; + //012345678901234567890 + // ========================================================================== + if (strncasecmp(InputBuffer_Serial+3,"PT2262;",7) == 0) { // KAKU Command eg. Kaku;A1;On + if (InputBuffer_Serial[16] != ';') return false; + x=17; // character pointer + InputBuffer_Serial[12]=0x30; + InputBuffer_Serial[13]=0x78; // Get home from hexadecimal value + InputBuffer_Serial[16]=0x00; // Get home from hexadecimal value + Home=str2int(InputBuffer_Serial+12); // KAKU home A is intern 0 + if (Home < 0x61) // take care of upper/lower case + Home=Home - 'A'; + else + if (Home < 0x81) // take care of upper/lower case + Home=Home - 'a'; + else { + return false; // invalid value + } + while((c=InputBuffer_Serial[x++])!=';'){ // Address: 1 to 16/32 + if(c>='0' && c<='9'){Address=Address*10;Address=Address+c-'0';} + } + // reconstruct bitstream reversed order so that most right bit can be send first + command = str2cmd(InputBuffer_Serial+x)==VALUE_ON; // ON/OFF command + housecode = ~Home; + housecode &= 0x00000007L; + housecode=(housecode)<<1; + if (command) bitstream |= 0x00000100L; + NArc_Send(bitstream); // send 24 bits tristate signal (0/1/f) + success=true; + } else + // --------------- END Select Remote SEND ------------ + // ========================================================================== + //10;TriState;00004d;1;OFF; + //10;TriState;08000a;2;OFF; 20;1B;TriState;ID=08000a;SWITCH=2;CMD=OFF; + //10;TriState;0a6980;2;OFF; + //01234567890123456789012 + // ========================================================================== + if (strncasecmp(InputBuffer_Serial+3,"TriState;",9) == 0) { // KAKU Command eg. Kaku;A1;On + if (InputBuffer_Serial[18] != ';') return false; + x=19; // character pointer + InputBuffer_Serial[10]=0x30; + InputBuffer_Serial[11]=0x78; // Get home from hexadecimal value + InputBuffer_Serial[18]=0x00; // Get home from hexadecimal value + bitstream=str2int(InputBuffer_Serial+10); // KAKU home A is intern 0 + bitstream=(bitstream<<4); + + // 11^00^01=10 11^10^11=01 11^11^00=00 + while((c=InputBuffer_Serial[x++])!=';'){ // Address: 0/1/2 + if(c>='0' && c<='9'){Address=Address*10;Address=Address+c-'0';} + } + Address=(Address)&0x03; // only use 3 bits + command = str2cmd(InputBuffer_Serial+x); // ON/OFF command + if (command==VALUE_ON) { // on + if (Address==0x0) bitstream |= 0x0000000bL; // 0011 + if (Address==0x1) bitstream |= 0x0000000cL; // 1011 + if (Address==0x2) bitstream |= 0x00000001L; // 0001 + } else { // off + if (Address==0x0) bitstream |= 0x0000000cL; // 1100 + if (Address==0x1) bitstream |= 0x0000000eL; // 1110 + if (Address==0x2) bitstream |= 0x00000004L; // 0100 + } + TriState_Send(bitstream); + success=true; + } else + // --------------- END TRISTATE SEND ------------ + // ========================================================================== + //10;Impuls;00004d;1;OFF; + //012345678901234567890 + // ========================================================================== + if (strncasecmp(InputBuffer_Serial+3,"Impuls;",7) == 0) { // KAKU Command eg. Kaku;A1;On + if (InputBuffer_Serial[16] != ';') return false; + x=17; // character pointer + InputBuffer_Serial[12]=0x30; + InputBuffer_Serial[13]=0x78; // Get home from hexadecimal value + InputBuffer_Serial[16]=0x00; // Get home from hexadecimal value + Home=str2int(InputBuffer_Serial+12); // KAKU home A is intern 0 + if (Home < 0x61) // take care of upper/lower case + Home=Home - 'A'; + else + if (Home < 0x81) // take care of upper/lower case + Home=Home - 'a'; + else { + return false; // invalid value + } + while((c=InputBuffer_Serial[x++])!=';'){ // Address: 1 to 16/32 + if(c>='0' && c<='9'){Address=Address*10;Address=Address+c-'0';} + } + command = str2cmd(InputBuffer_Serial+x)==VALUE_ON; // ON/OFF command + housecode = ~Home; + housecode &= 0x0000001FL; + unitcode=Address; + if ((unitcode >= 1) && (unitcode <= 5) ) { + bitstream = housecode & 0x0000001FL; + if (unitcode == 1) bitstream |= 0x000003C0L; + else if (unitcode == 2) bitstream |= 0x000003A0L; + else if (unitcode == 3) bitstream |= 0x00000360L; + else if (unitcode == 4) bitstream |= 0x000002E0L; + else if (unitcode == 5) bitstream |= 0x000001E0L; + + if (command) bitstream |= 0x00000800L; + else bitstream |= 0x00000400L; + } + TriState_Send(bitstream); + success=true; + } + return success; +} + +//#define KAKU_T 390 //420 // 370 // 370? 350 us +//#define Sartano_T 300 //360 // 300 // 300 uS + +void Arc_Send(unsigned long bitstream) { + int fpulse = 360; // Pulse width in microseconds + int fretrans = 8; // Number of code retransmissions + uint32_t fdatabit; + uint32_t fdatamask = 0x00000001; + uint32_t fsendbuff; + + digitalWrite(PIN_RF_RX_VCC,LOW); // Turn off power to the RF receiver + digitalWrite(PIN_RF_TX_VCC,HIGH); // Enable the 433Mhz transmitter + delayMicroseconds(TRANSMITTER_STABLE_DELAY); // short delay to let the transmitter become stable (Note: Aurel RTX MID needs 500µS/0,5ms) + + for (int nRepeat = 0; nRepeat <= fretrans; nRepeat++) { + fsendbuff = bitstream; + // Send command + + for (int i = 0; i < 12; i++) { // Arc packet is 12 bits + // read data bit + fdatabit = fsendbuff & fdatamask; // Get most right bit + fsendbuff = (fsendbuff >> 1); // Shift right + + // PT2262 data can be 0, 1 or float. Only 0 and float is used by regular ARC + if (fdatabit != fdatamask) { // Write 0 + digitalWrite(PIN_RF_TX_DATA, HIGH); + delayMicroseconds(fpulse * 1); + digitalWrite(PIN_RF_TX_DATA, LOW); + delayMicroseconds(fpulse * 3); + digitalWrite(PIN_RF_TX_DATA, HIGH); + delayMicroseconds(fpulse * 1); + digitalWrite(PIN_RF_TX_DATA, LOW); + delayMicroseconds(fpulse * 3); + } else { // Write float + digitalWrite(PIN_RF_TX_DATA, HIGH); + delayMicroseconds(fpulse * 1); + digitalWrite(PIN_RF_TX_DATA, LOW); + delayMicroseconds(fpulse * 3); + digitalWrite(PIN_RF_TX_DATA, HIGH); + delayMicroseconds(fpulse * 3); + digitalWrite(PIN_RF_TX_DATA, LOW); + delayMicroseconds(fpulse * 1); + } + } + // Send sync bit + digitalWrite(PIN_RF_TX_DATA, HIGH); + delayMicroseconds(fpulse * 1); + digitalWrite(PIN_RF_TX_DATA, LOW); // and lower the signal + delayMicroseconds(fpulse * 31); + } + delayMicroseconds(TRANSMITTER_STABLE_DELAY); // short delay to let the transmitter become stable (Note: Aurel RTX MID needs 500µS/0,5ms) + digitalWrite(PIN_RF_TX_VCC,LOW); // Turn the 433Mhz transmitter off + digitalWrite(PIN_RF_RX_VCC,HIGH); // Turn the 433Mhz receiver on + RFLinkHW(); +} + +void NArc_Send(unsigned long bitstream) { + int fpulse = 190; // Pulse width in microseconds + int fretrans = 7; // Number of code retransmissions + uint32_t fdatabit; + uint32_t fdatamask = 0x00000001; + uint32_t fsendbuff; + + digitalWrite(PIN_RF_RX_VCC,LOW); // Turn off power to the RF receiver + digitalWrite(PIN_RF_TX_VCC,HIGH); // Enable the 433Mhz transmitter + delayMicroseconds(TRANSMITTER_STABLE_DELAY); // short delay to let the transmitter become stable (Note: Aurel RTX MID needs 500µS/0,5ms) + + for (int nRepeat = 0; nRepeat <= fretrans; nRepeat++) { + fsendbuff = bitstream; + // Send command + + for (int i = 0; i < 12; i++) { // Arc packet is 12 bits + // read data bit + fdatabit = fsendbuff & fdatamask; // Get most right bit + fsendbuff = (fsendbuff >> 1); // Shift right + + // PT2262 data can be 0, 1 or float. Only 0 and float is used by regular ARC + if (fdatabit != fdatamask) { // Write 0 + digitalWrite(PIN_RF_TX_DATA, HIGH); + delayMicroseconds(fpulse * 1); + digitalWrite(PIN_RF_TX_DATA, LOW); + delayMicroseconds(fpulse * 3); + digitalWrite(PIN_RF_TX_DATA, HIGH); + delayMicroseconds(fpulse * 1); + digitalWrite(PIN_RF_TX_DATA, LOW); + delayMicroseconds(fpulse * 3); + } else { // Write 1 + digitalWrite(PIN_RF_TX_DATA, HIGH); + delayMicroseconds(fpulse * 3); + digitalWrite(PIN_RF_TX_DATA, LOW); + delayMicroseconds(fpulse * 1); + digitalWrite(PIN_RF_TX_DATA, HIGH); + delayMicroseconds(fpulse * 3); + digitalWrite(PIN_RF_TX_DATA, LOW); + delayMicroseconds(fpulse * 1); + } + } + // Send sync bit + digitalWrite(PIN_RF_TX_DATA, HIGH); + delayMicroseconds(fpulse * 1); + digitalWrite(PIN_RF_TX_DATA, LOW); // and lower the signal + delayMicroseconds(fpulse * 31); + } + delayMicroseconds(TRANSMITTER_STABLE_DELAY); // short delay to let the transmitter become stable (Note: Aurel RTX MID needs 500µS/0,5ms) + digitalWrite(PIN_RF_TX_VCC,LOW); // Turn thew 433Mhz transmitter off + digitalWrite(PIN_RF_RX_VCC,HIGH); // Turn the 433Mhz receiver on + RFLinkHW(); +} + +void TriState_Send(unsigned long bitstream) { + int fpulse = 360; // Pulse width in microseconds + int fretrans = 8; // Number of code retransmissions + uint32_t fdatabit; + uint32_t fdatamask = 0x00000003; + uint32_t fsendbuff; + + digitalWrite(PIN_RF_RX_VCC,LOW); // Turn off power to the RF receiver + digitalWrite(PIN_RF_TX_VCC,HIGH); // Enable the 433Mhz transmitter + delayMicroseconds(TRANSMITTER_STABLE_DELAY); // short delay to let the transmitter become stable (Note: Aurel RTX MID needs 500µS/0,5ms) + + // reverse data bits (2 by 2) + for (unsigned short i=0; i<12; i++) { // reverse data bits (12 times 2 bits = 24 bits in total) + fsendbuff<<=2; + fsendbuff|=(bitstream & B11); + bitstream>>=2; + } + bitstream=fsendbuff; // store result + + for (int nRepeat = 0; nRepeat <= fretrans; nRepeat++) { + fsendbuff = bitstream; + // Send command + for (int i = 0; i < 12; i++) { // 12 times 2 bits = 24 bits in total + // read data bit + fdatabit = fsendbuff & fdatamask; // Get most right 2 bits + fsendbuff = (fsendbuff >> 2); // Shift right + // data can be 0, 1 or float. + if (fdatabit == 0) { // Write 0 + digitalWrite(PIN_RF_TX_DATA, HIGH); + delayMicroseconds(fpulse); + digitalWrite(PIN_RF_TX_DATA, LOW); + delayMicroseconds(fpulse * 3); + digitalWrite(PIN_RF_TX_DATA, HIGH); + delayMicroseconds(fpulse); + digitalWrite(PIN_RF_TX_DATA, LOW); + delayMicroseconds(fpulse * 3); + } else + if (fdatabit == 1) { // Write 1 + digitalWrite(PIN_RF_TX_DATA, HIGH); + delayMicroseconds(fpulse * 3); + digitalWrite(PIN_RF_TX_DATA, LOW); + delayMicroseconds(fpulse * 1); + digitalWrite(PIN_RF_TX_DATA, HIGH); + delayMicroseconds(fpulse * 3); + digitalWrite(PIN_RF_TX_DATA, LOW); + delayMicroseconds(fpulse * 1); + } else { // Write float + digitalWrite(PIN_RF_TX_DATA, HIGH); + delayMicroseconds(fpulse * 1); + digitalWrite(PIN_RF_TX_DATA, LOW); + delayMicroseconds(fpulse * 3); + digitalWrite(PIN_RF_TX_DATA, HIGH); + delayMicroseconds(fpulse * 3); + digitalWrite(PIN_RF_TX_DATA, LOW); + delayMicroseconds(fpulse * 1); + } + } + // Send sync bit + digitalWrite(PIN_RF_TX_DATA, HIGH); + delayMicroseconds(fpulse * 1); + digitalWrite(PIN_RF_TX_DATA, LOW); // and lower the signal + delayMicroseconds(fpulse * 31); + } + delayMicroseconds(TRANSMITTER_STABLE_DELAY); // short delay to let the transmitter become stable (Note: Aurel RTX MID needs 500µS/0,5ms) + digitalWrite(PIN_RF_TX_VCC,LOW); // Turn thew 433Mhz transmitter off + digitalWrite(PIN_RF_RX_VCC,HIGH); // Turn the 433Mhz receiver on + RFLinkHW(); +} +#endif //PLUGIN_TX_003 diff --git a/Plugins/Plugin_004.c b/Plugins/Plugin_004.c new file mode 100644 index 0000000..82497b1 --- /dev/null +++ b/Plugins/Plugin_004.c @@ -0,0 +1,341 @@ +//####################################################################################################### +//## This Plugin is only for use with the RFLink software package ## +//## Plugin-04: NewKAKU ## +//####################################################################################################### +/*********************************************************************************************\ + * This plugin takes care of receiving from and transmitting to "Klik-Aan-Klik-Uit" devices + * working according to the learning code system. This protocol is also used by the Home Easy devices. + * It includes direct DIM functionality. + * + * Author : StuntTeam + * Support : http://sourceforge.net/projects/rflink/ + * License : This code is free for use in any open source project when this header is included. + * Usage of any parts of this code in a commercial application is prohibited! + *********************************************************************************************** + * Pulse (T) is 275us PDM + * 0 = T,T,T,4T, 1 = T,4T,T,T, dim = T,T,T,T op bit 28 + * + * From: Wieltje @ http://www.circuitsonline.net/forum/view/message/1181410#1181410 + * _ _ + * '0': | |_| |____ (T,T,T,3T) + * _ _ + * '1': | |____| |_ (T,3T,T,T) + * _ _ + * dim: | |_| |_ (T,T,T,T) + * + * T = korte periode = 275 µs (of 375, werkt ook) + * lange periode = 3 of 4*T (werkt ook allebei) + * + * | 00100011110100100010011010 | 0 | 1 | 0000 | + * | ID# | All | State | unit# | + * + * NewKAKU supports: + * on/off ---- 000x Off/On + * all on/off ---- 001x AllOff/AllOn + * dim absolute xxxx 0110 Dim16 // dim on bit 27 + 4 extra bits for dim level + * + * NewKAKU bitstream= (First sent) AA AAAAAAAA AAAAAAAA AAAAAAAACCUUUU(LLLL) -> A=KAKU_address, C=command, U=KAKU-Unit, L=extra dimlevel bits (optional) + * + * Sample RF packet: + * 20;B8;NewKaku;ID=00c142;SWITCH=1;CMD=OFF; + * 20;B9;DEBUG;Pulses=132;Pulses(uSec)=200,2550,150,200,125,1200,150,200,150,1200,125,1200,150,225,125,1200,125,225,125,200,150,1200,150,200,150,1200,150,1200,125,200,125,200,125,1225,125,1200,125,225,150,200,150,1200,150,1200,150,200,150,1200,150,225,125,200,150,1200,125,200,150,1200,125,200,150,1200,150,200,125,1225,150,200,125,1200,150,1200,125,225,125,200,125,1200,150,1200,125,225,125,200,125,1225,125,200,125,1225,125,200,125,1200,125,200,150,1225,125,1200,150,200,125,200,125,1200,125,200,150,1200,125,200,125,1200,150,200,125,1200,125,200,125,1200,150,200,125,1200,150,200,150,1200,125; + * 20;06;DEBUG;Pulses=132;Pulses(uSec)=175,2575,150,200,150,1200,150,200,150,1200,150,1200,150,200,125,1200,150,200,125,200,150,1200,125,200,150,1200,150,1200,150,200,150,200,150,1225,150,1200,125,225,150,200,125,1200,150,1200,150,200,150,1200,150,200,150,200,125,1225,125,200,150,1200,125,200,150,1200,125,200,150,1200,150,200,150,1200,150,1200,125,200,150,200,125,1200,150,1200,125,225,150,200,125,1200,150,200,150,1200,150,200,150,1200,150,200,150,1225,125,1200,150,200,125,200,150,1200,150,200,125,1200,150,200,150,1200,150,200,150,1200,150,200,125,1225,125,200,125,1200,150,200,150,1200,150 + \*********************************************************************************************/ +#define NewKAKU_RawSignalLength 132 // regular KAKU packet length +#define NewKAKUdim_RawSignalLength 148 // KAKU packet length including DIM bits +#define NewKAKU_mT 650/RAWSIGNAL_SAMPLE_RATE // us, approx. in between 1T and 4T + +#ifdef PLUGIN_004 +boolean Plugin_004(byte function, char *string) { + // nieuwe KAKU bestaat altijd uit start bit + 32 bits + evt 4 dim bits. Ongelijk, dan geen NewKAKU + if ( (RawSignal.Number != NewKAKU_RawSignalLength) && (RawSignal.Number != NewKAKUdim_RawSignalLength) ) return false; + if (RawSignal.Pulses[0]==15) return true; // Home Easy, skip KAKU + boolean Bit; + int i; + //int P0,P1,P2,P3; + byte P0,P1,P2,P3; + byte dim=0; + byte dimbitpresent=0; + unsigned long bitstream=0L; + + // RawSignal.Pulses[1] startbit with duration of 1T => ignore + // RawSignal.Pulses[2] long space after startbit with duration of 8T => ignore + i=3; // RawSignal.Pulses[3] is first pulse of a T,xT,T,xT combination + do { + P0=RawSignal.Pulses[i] ; // * RawSignal.Multiply; + P1=RawSignal.Pulses[i+1]; // * RawSignal.Multiply; + P2=RawSignal.Pulses[i+2]; // * RawSignal.Multiply; + P3=RawSignal.Pulses[i+3]; // * RawSignal.Multiply; + + if (P0NewKAKU_mT) { + Bit=0; // T,T,T,4T + } else + if (P0NewKAKU_mT && P2 invalid signal + return false; + } + //if (i != 111) return false; // not the right location for the dim bit indicator + } else { + //Serial.println("Unknown pattern"); + return false; // Other pulse patterns are invalid within the AC KAKU signal. + } + if(i<130) { // all bits that belong to the 32-bit pulse sequence (32bits * 4 positions per bit + pulse/space for startbit) + bitstream=(bitstream<<1) | Bit; + } else { // remaining 4 bits that set the dim level + dim=(dim<<1) | Bit; + } + i+=4; // Next 4 pulses + } while(imillis()) ) { + // SignalHash=14; + // return true; // SignalHash 14 = HomeEasy, eg. cant switch KAKU after HE for 2 seconds + //} + if ((SignalHashPrevious==11) && ((RepeatingTimer+2000)>millis()) ) { + SignalHash=11; + return true; // SignalHash 11 = FA500, eg. cant switch KAKU after FA500 for 2 seconds + } + + SignalCRC=bitstream; + } else { + // already seen the RF packet recently + //Serial.println("Skip"); + return true; + } + //================================================================================== + // Output + // ---------------------------------- + sprintf(pbuffer, "20;%02X;", PKSequenceNumber++); // Node and packet number + Serial.print( pbuffer ); + // ---------------------------------- + Serial.print(F("NewKaku;")); // Label + sprintf(pbuffer, "ID=%08lx;",((bitstream) >> 6) ); // ID + Serial.print( pbuffer ); + sprintf(pbuffer, "SWITCH=%x;", ((bitstream)&0x0f)+1 ); + Serial.print( pbuffer ); + Serial.print(F("CMD=")); + int command = (bitstream >> 4) & 0x03; + if (command > 1) command ++; + if (i>140 && dimbitpresent==1) { // Command and Dim part + sprintf(pbuffer, "SET_LEVEL=%d;", dim ); + Serial.print( pbuffer ); + } else { + if ( command == 0 ) Serial.print(F("OFF;")); + if ( command == 1 ) Serial.print(F("ON;")); + if ( command == 3 ) Serial.print(F("ALLOFF;")); + if ( command == 4 ) Serial.print(F("ALLON;")); + } + Serial.println(); + // ---------------------------------- + RawSignal.Repeats=true; // suppress repeats of the same RF packet + RawSignal.Number=0; + return true; +} +#endif // Plugin_004 + +#ifdef PLUGIN_TX_004 +void AC_Send(unsigned long data, byte cmd); + +boolean PluginTX_004(byte function, char *string) { + boolean success=false; + //10;NewKaku;123456;3;ON; // ON, OFF, ALLON, ALLOFF, ALL 99, 99 + //10;NewKaku;0cac142;2;ON; + //10;NewKaku;050515;f;OFF; + //10;NewKaku;2100fed;1;ON; + //10;NewKaku;000001;10;ON; + //10;NewKaku;306070b;f;ON; + //10;NewKaku;306070b;10;ON; + //01234567890123456789012 + if (strncasecmp(InputBuffer_Serial+3,"NEWKAKU;",8) == 0) { + byte x=18; // pointer to the switch number + if (InputBuffer_Serial[17] != ';') { + if (InputBuffer_Serial[18] != ';') { + return false; + } else { + x=19; + } + } + + unsigned long bitstream=0L; + unsigned long tempaddress=0L; + byte cmd=0; + byte c=0; + byte Address=0; // Address 1..16 + + // ----- + InputBuffer_Serial[ 9]=0x30; // Get NEWKAKU/AC main address part from hexadecimal value + InputBuffer_Serial[10]=0x78; + InputBuffer_Serial[x-1]=0x00; + tempaddress=str2int(InputBuffer_Serial+9); + // ----- + //while((c=InputBuffer_Serial[x++])!=';'){ // Address: 1 to 16 + // if(c>='0' && c<='9'){Address=Address*10;Address=Address+c-'0';} + //} + InputBuffer_Serial[x-2]=0x30; // Get unit number from hexadecimal value + InputBuffer_Serial[x-1]=0x78; // x points to the first character of the unit number + if (InputBuffer_Serial[x+1] == ';') { + InputBuffer_Serial[x+1]=0x00; + cmd=2; + } else { + if (InputBuffer_Serial[x+2] == ';') { + InputBuffer_Serial[x+2]=0x00; + cmd=3; + } else { + return false; + } + } + Address=str2int(InputBuffer_Serial+(x-2)); // NewKAKU unit number + if (Address > 16) return false; // invalid address + Address--; // 1 to 16 -> 0 to 15 (transmitted value is 1 less than shown values) + x=x+cmd; // point to on/off/dim command part + // ----- + tempaddress=(tempaddress <<6) + Address; // Complete transmitted address + // ----- + cmd=str2cmd(InputBuffer_Serial+x); // Get ON/OFF etc. command + if (cmd == false) { // Not a valid command received? ON/OFF/ALLON/ALLOFF + cmd=str2int(InputBuffer_Serial+x); // get DIM value + } + // --------------- Prepare bitstream ------------ + bitstream=tempaddress & 0xFFFFFFCF; // adres geheel over nemen behalve de twee bits 5 en 6 die het schakel commando bevatten. + + // Dimming of groups is also possible but not supported yet! + // when level=0 is it better to transmit just the off command ? + + if (cmd == VALUE_ON || cmd == VALUE_OFF) { + bitstream|=(cmd == VALUE_ON)<<4; // bit-5 is the on/off command in the KAKU signal + cmd=0xff; + } else + if (cmd == VALUE_ALLON || cmd == VALUE_ALLOFF) { + bitstream|= B1 << 5; // bit 5 is the group indicator + bitstream|=(cmd == VALUE_ALLON)<<4; // bit-4 is the on/off indicator + cmd=0xff; + } + // bitstream now contains the AC/NewKAKU-bits that have to be transmitted + // --------------- NEWKAKU SEND ------------ + AC_Send(bitstream, cmd); + success=true; + } + // -------------------------------------- + return success; +} + +void AC_Send(unsigned long data, byte cmd) { + int fpulse = 260; // Pulse width in microseconds + int fretrans = 10; // Number of code retransmissions + + unsigned long bitstream = 0L; + byte command; + // prepare data to send + for (unsigned short i=0; i<32; i++) { // reverse data bits + bitstream<<=1; + bitstream|=(data & B1); + data>>=1; + } + if (cmd !=0xff) { // reverse dim bits + for (unsigned short i=0; i<4; i++) { + command<<=1; + command|=(cmd & B1); + cmd>>=1; + } + } + // Prepare transmit + digitalWrite(PIN_RF_RX_VCC,LOW); // Turn off power to the RF receiver + digitalWrite(PIN_RF_TX_VCC,HIGH); // Enable the 433Mhz transmitter + delayMicroseconds(TRANSMITTER_STABLE_DELAY); // short delay to let the transmitter become stable (Note: Aurel RTX MID needs 500µS/0,5ms) + // send bits + for (int nRepeat = 0; nRepeat <= fretrans; nRepeat++) { + data=bitstream; + if (cmd !=0xff) cmd=command; + digitalWrite(PIN_RF_TX_DATA, HIGH); + //delayMicroseconds(fpulse); //335 + delayMicroseconds(335); + digitalWrite(PIN_RF_TX_DATA, LOW); + delayMicroseconds(fpulse*10 + (fpulse >> 1)); //335*9=3015 //260*10=2600 + for (unsigned short i=0; i<32; i++) { + if (i==27 && cmd !=0xff) { // DIM command, send special DIM sequence TTTT replacing on/off bit + digitalWrite(PIN_RF_TX_DATA, HIGH); + delayMicroseconds(fpulse); + digitalWrite(PIN_RF_TX_DATA, LOW); + delayMicroseconds(fpulse); + digitalWrite(PIN_RF_TX_DATA, HIGH); + delayMicroseconds(fpulse); + digitalWrite(PIN_RF_TX_DATA, LOW); + delayMicroseconds(fpulse); + } else + switch (data & B1) { + case 0: + digitalWrite(PIN_RF_TX_DATA, HIGH); + delayMicroseconds(fpulse); + digitalWrite(PIN_RF_TX_DATA, LOW); + delayMicroseconds(fpulse); + digitalWrite(PIN_RF_TX_DATA, HIGH); + delayMicroseconds(fpulse); + digitalWrite(PIN_RF_TX_DATA, LOW); + delayMicroseconds(fpulse*5); // 335*3=1005 260*5=1300 260*4=1040 + break; + case 1: + digitalWrite(PIN_RF_TX_DATA, HIGH); + delayMicroseconds(fpulse); + digitalWrite(PIN_RF_TX_DATA, LOW); + delayMicroseconds(fpulse*5); + digitalWrite(PIN_RF_TX_DATA, HIGH); + delayMicroseconds(fpulse); + digitalWrite(PIN_RF_TX_DATA, LOW); + delayMicroseconds(fpulse); + break; + } + //Next bit + data>>=1; + } + // send dim bits when needed + if (cmd != 0xff) { // need to send DIM command bits + for (unsigned short i=0; i<4; i++) { // 4 bits + switch (cmd & B1) { + case 0: + digitalWrite(PIN_RF_TX_DATA, HIGH); + delayMicroseconds(fpulse); + digitalWrite(PIN_RF_TX_DATA, LOW); + delayMicroseconds(fpulse); + digitalWrite(PIN_RF_TX_DATA, HIGH); + delayMicroseconds(fpulse); + digitalWrite(PIN_RF_TX_DATA, LOW); + delayMicroseconds(fpulse*5); // 335*3=1005 260*5=1300 + break; + case 1: + digitalWrite(PIN_RF_TX_DATA, HIGH); + delayMicroseconds(fpulse); + digitalWrite(PIN_RF_TX_DATA, LOW); + delayMicroseconds(fpulse*5); + digitalWrite(PIN_RF_TX_DATA, HIGH); + delayMicroseconds(fpulse); + digitalWrite(PIN_RF_TX_DATA, LOW); + delayMicroseconds(fpulse); + break; + } + //Next bit + cmd>>=1; + } + } + //Send termination/synchronisation-signal. Total length: 32 periods + digitalWrite(PIN_RF_TX_DATA, HIGH); + delayMicroseconds(fpulse); + digitalWrite(PIN_RF_TX_DATA, LOW); + delayMicroseconds(fpulse*40); //31*335=10385 40*260=10400 + } + // End transmit + delayMicroseconds(TRANSMITTER_STABLE_DELAY); // short delay to let the transmitter become stable (Note: Aurel RTX MID needs 500µS/0,5ms) + digitalWrite(PIN_RF_TX_VCC,LOW); // Turn thew 433Mhz transmitter off + digitalWrite(PIN_RF_RX_VCC,HIGH); // Turn the 433Mhz receiver on + RFLinkHW(); +} +#endif // Plugin_TX_004 diff --git a/Plugins/Plugin_005.c b/Plugins/Plugin_005.c new file mode 100644 index 0000000..0d5d508 --- /dev/null +++ b/Plugins/Plugin_005.c @@ -0,0 +1,204 @@ +//####################################################################################################### +//## This Plugin is only for use with the RFLink software package ## +//## Plugin-03: Intertek Eurodomest 972080 ## +//####################################################################################################### +/*********************************************************************************************\ + * This plugin takes care of sending and receiving the Intertek Eurodomest 972080 protocol. + * + * Author : StuntTeam + * Support : http://sourceforge.net/projects/rflink/ + * License : This code is free for use in any open source project when this header is included. + * Usage of any parts of this code in a commercial application is prohibited! + *********************************************************************************************** + * Technical information: + * + * 0111 00011011 00011111 000 0 + * AAAA AAAAAAAA AAAAAAAA BBB C + * 0000 00000011 10101010 101 1 + * 0000 00000011 10101010 111 0 + * + * 0011 01101001 01101011 000 0 Eurodomest 1 on + * 0011 01101001 01101011 000 1 Eurodomest 1 off + * 0011 01101001 01101011 001 0 ED 2 on + * 0011 01101001 01101011 001 1 ED 2 off + * 0011 01101001 01101011 010 0 3 on + * 0011 01101001 01101011 010 1 3 off + * 0011 01101001 01101011 100 0 4 on + * 0011 01101001 01101011 100 1 4 off + * 0011 01101001 01101011 110 1 all on + * 0011 01101001 01101011 111 0 all off + * + * + * A = ID (20 bits) + * B = UnitCode (3 bits) + * C = switch code (ON/OFF) (1 bit) + * + * 20;2A;DEBUG;Pulses=50;Pulses(uSec)= 900,200,825,200,225,825,200,825,800,200,200,825,200,825,825,200,225,825,800,200,800,225,225,825,800,225,200,825,225,825,800,225,225,825,800,225,200,825,200,825,225,825,225,825,225,825,800,200,200; + * 20;9D;DEBUG;Pulses=50;Pulses(uSec)=1250,200,750,175,200,750,200,750,750,200,200,750,200,750,750,200,200,750,750,200,750,200,200,750,750,200,200,750,200,750,750,200,200,750,750,200,200,750,200,750,750,200,750,200,750,200,750,200,200; + \*********************************************************************************************/ +#define EURODOMEST_PulseLength 50 + +#define EURODOMEST_PULSEMID 400/RAWSIGNAL_SAMPLE_RATE +#define EURODOMEST_PULSEMIN 100/RAWSIGNAL_SAMPLE_RATE +#define EURODOMEST_PULSEMAX 900/RAWSIGNAL_SAMPLE_RATE + +#ifdef PLUGIN_005 +boolean Plugin_005(byte function, char *string) { + if (RawSignal.Number != EURODOMEST_PulseLength) return false; + if (RawSignal.Pulses[0]==63) return false; // No need to test, packet for plugin 63 + unsigned long bitstream=0; + byte unitcode=0; + byte command=0; + unsigned long address=0; + // ========================================================================== + if(RawSignal.Pulses[49] > EURODOMEST_PULSEMID) return false; // last pulse needs to be short, otherwise no Eurodomest protocol + // get all 24 bits + for(int x=2;x < EURODOMEST_PulseLength;x+=2) { + if(RawSignal.Pulses[x] > EURODOMEST_PULSEMID) { // long pulse + if (RawSignal.Pulses[x-1] > EURODOMEST_PULSEMID) return false; // not a 01 or 10 transmission + if(RawSignal.Pulses[x] > EURODOMEST_PULSEMAX) return false; // make sure the long pulse is within range + bitstream = (bitstream << 1) | 0x1; + } else { // short pulse + if (RawSignal.Pulses[x] < EURODOMEST_PULSEMIN) return false; // pulse too short to be Eurodomest + if (RawSignal.Pulses[x-1] < EURODOMEST_PULSEMID) return false; // not a 01 or 10 transmission + bitstream = (bitstream << 1); + } + } + //================================================================================== + // Prevent repeating signals from showing up + //================================================================================== + if(SignalHash!=SignalHashPrevious || RepeatingTimer> 4) &0xfffff; + if (address==0) return false; // Address would never be 0 + if (address==0xfffff) return false; // Address would never be FFFFF + // ---------------------------------- + unitcode=(( bitstream >> 1)& 0x7); + command=((bitstream) & 0x01); + if (unitcode == 3) return false; // invalid button code? + if (unitcode == 4) unitcode--; +// if (unitcode == 5) return false; // Note: unitcode 5 is present on the PCB and working but not used on any remotes. + if (unitcode > 7) return false; // invalid button code? + //================================================================================== + // Output + // ---------------------------------- + sprintf(pbuffer, "20;%02X;", PKSequenceNumber++); // Node and packet number + Serial.print( pbuffer ); + // ---------------------------------- + Serial.print(F("Eurodomest;")); // Label + sprintf(pbuffer, "ID=%06lx;",(address)&0xffffff) ; // ID + Serial.print( pbuffer ); + sprintf(pbuffer, "SWITCH=%02x;", unitcode); // ID + Serial.print( pbuffer ); + Serial.print(F("CMD=")); + if ( unitcode > 4) { + Serial.print(F("ALL")); + if ( command == 0) { + Serial.print(F("OFF;")); + } else { + Serial.print(F("ON;")); + } + } else { + if ( command == 1) { + Serial.print(F("OFF;")); + } else { + Serial.print(F("ON;")); + } + } + Serial.println(); + // ---------------------------------- + RawSignal.Repeats=true; // suppress repeats of the same RF packet + RawSignal.Number=0; + return true; +} +#endif //PLUGIN_005 + +#ifdef PLUGIN_TX_005 +void Eurodomest_Send(unsigned long address); + +boolean PluginTX_005(byte function, char *string) { + //10;EURODOMEST;03696b;0;ON; + //012345678901234567890123456 + boolean success=false; + if (strncasecmp(InputBuffer_Serial+3,"EURODOMEST;",11) == 0) { // KAKU Command eg. + unsigned long bitstream=0L; + if (InputBuffer_Serial[20] != ';') return success; + if (InputBuffer_Serial[22] != ';') return success; + + InputBuffer_Serial[12]=0x30; + InputBuffer_Serial[13]=0x78; + InputBuffer_Serial[20]=0x00; + bitstream=str2int(InputBuffer_Serial+12);// Address + InputBuffer_Serial[22]=0x00; + byte temp=str2int(InputBuffer_Serial+21);// Button number + bitstream=(bitstream) << 4; + if (temp == 1) bitstream=bitstream+0x02; // 0010 + if (temp == 2) bitstream=bitstream+0x04; // 0100 + if (temp == 3) bitstream=bitstream+0x08; // 1000 + if (temp == 6) bitstream=bitstream+0x0d; // 1101 + if (temp == 7) bitstream=bitstream+0x0f; // 1111 + if (temp > 7) { + return success; + } + byte command=0; + command = str2cmd(InputBuffer_Serial+23); + if (command == VALUE_OFF) { + bitstream=bitstream|1; + } + Eurodomest_Send(bitstream); // the full bitstream to send + success=true; + } + return success; +} + +void Eurodomest_Send(unsigned long address) { + int fpulse = 296; // Pulse witdh in microseconds + int fretrans = 7; // Number of code retransmissions + uint32_t fdatabit; + uint32_t fdatamask = 0x800000; + uint32_t fsendbuff; + + digitalWrite(PIN_RF_RX_VCC,LOW); // Turn off power to the RF receiver + digitalWrite(PIN_RF_TX_VCC,HIGH); // Enable the 433Mhz transmitter + delayMicroseconds(TRANSMITTER_STABLE_DELAY); // short delay to let the transmitter become stable (Note: Aurel RTX MID needs 500µS/0,5ms) + + for (int nRepeat = 0; nRepeat <= fretrans; nRepeat++) { + fsendbuff=address; + // Send command + for (int i = 0; i < 24; i++) { // Eurodomest packet is 24 bits + // read data bit + fdatabit = fsendbuff & fdatamask; // Get most left bit + fsendbuff = (fsendbuff << 1); // Shift left + + if (fdatabit != fdatamask) { // Write 0 + digitalWrite(PIN_RF_TX_DATA, HIGH); + delayMicroseconds(fpulse * 3); + digitalWrite(PIN_RF_TX_DATA, LOW); + delayMicroseconds(fpulse * 1); + } else { // Write 1 + digitalWrite(PIN_RF_TX_DATA, HIGH); + delayMicroseconds(fpulse * 1); + digitalWrite(PIN_RF_TX_DATA, LOW); + delayMicroseconds(fpulse * 3); + } + } + digitalWrite(PIN_RF_TX_DATA, HIGH); + delayMicroseconds(fpulse * 1); + digitalWrite(PIN_RF_TX_DATA, LOW); // and lower the signal + delayMicroseconds(fpulse * 32); + } + delayMicroseconds(TRANSMITTER_STABLE_DELAY); // short delay to let the transmitter become stable (Note: Aurel RTX MID needs 500µS/0,5ms) + digitalWrite(PIN_RF_TX_VCC,LOW); // Turn the 433Mhz transmitter off + digitalWrite(PIN_RF_RX_VCC,HIGH); // Turn the 433Mhz receiver on + RFLinkHW(); +} +#endif //PLUGIN_TX_005 diff --git a/Plugins/Plugin_006.c b/Plugins/Plugin_006.c new file mode 100644 index 0000000..3653a4d --- /dev/null +++ b/Plugins/Plugin_006.c @@ -0,0 +1,287 @@ +//####################################################################################################### +//## This Plugin is only for use with the RFLink software package ## +//## Plugin-006 Blyss & Avidsen ## +//####################################################################################################### +/*********************************************************************************************\ + * This Plugin takes care of receiving of the Blyss protocol + * + * Author : StuntTeam + * Support : http://sourceforge.net/projects/rflink/ + * License : This code is free for use in any open source project when this header is included. + * Usage of any parts of this code in a commercial application is prohibited! + ********************************************************************************************* + * Changelog: v1.0 initial release + ********************************************************************************************* + * Technical information: + * RF packets contain 106 pulses, 52 bits + * + * BLYSS Message Format: + * AAAAAAAA BBBBCCCC CCCCCCCC CCCCDDDD | EEEEFFFF FFFFGGGG GGGG + * + * A = Preamble, always 0xFE (0x32 in case of Avidsen) + * B = Global Channel (A=0,B=1,C=2,D=3) + * C = Address + * D = sub channel (channel 1=8, 2=4, 3=2, 4=1, 5=3) all channels = 0 + * E = Light Status + * F = Rolling Code (0x98 -> 0xDA -> 0x1E -> 0xE6 -> 0x67) + * G = Time Stamp (random value?) + * + * Details https://skyduino.wordpress.com/2012/07/19/hack-partie-2-reverse-engineering-des-interrupteurs-domotique-blyss/ + * https://barbudor.wiki.zoho.com/Système-domotique-Blyss-de-Castorama.html + * + * BlyssSend address,switch,cmd; => (16 bits,8 bits,on/off/allon/alloff) + * 20;0B;DEBUG;Pulses=106;Pulses(uSec)=2160,450,570,420,600,420,600,450,570,420,600,420,600,450,570,810,210,870,150,840,180,840,180,840,180,420,600,420,600,420,600,450,570,420,600,420,600,420,600,420,600,420,600,840,180,840,210,450,570,450,600,810,180,840,180,840,180,420,600,810,210,840,180,810,210,810,210,870,180,810,210,450,570,450,570,840,180,840,210,450,570,420,600,840,180,810,210,840,180,840,210,840,180,840,180,810,210,840,210,420,600,810,210,420,600,6990; + * 20;0C;Blyss;ID=ff98;SWITCH=A1;CMD=OFF; + \*********************************************************************************************/ +#define BLYSS_PULSECOUNT 106 +#define BLYSS_PULSEMID 500/RAWSIGNAL_SAMPLE_RATE + +#ifdef PLUGIN_006 +boolean Plugin_006(byte function, char *string) { + if (RawSignal.Number != BLYSS_PULSECOUNT) return false; + unsigned long bitstream=0L; + unsigned long bitstream1=0L; + byte bitcounter=0; + byte checksum=0; + int type=0; + //================================================================================== + // get bits + for(byte x=2;x < BLYSS_PULSECOUNT;x=x+2) { + if (RawSignal.Pulses[x] > BLYSS_PULSEMID) { + if (bitcounter < 32) { + bitstream = (bitstream << 1); + } else { + bitstream1 = (bitstream1 << 1); + } + bitcounter++; + } else { + if (bitcounter < 32) { + bitstream = (bitstream << 1) | 0x1; + } else { + bitstream1 = (bitstream1 << 1) | 0x1; + } + bitcounter++; + } + } + //================================================================================== + // all bits received, make sure checksum is okay + //================================================================================== + checksum=((bitstream) >> 24); // test preamble + if ((checksum != 0xFE) && (checksum != 0x32) ) return false; // must be 0xFE/32 + if (checksum == 0x32) type=1; + //================================================================================== + // Prevent repeating signals from showing up + //================================================================================== + if(SignalHash!=SignalHashPrevious || RepeatingTimer> 16) &0x0f; + if (status > 3) return false; + byte channel=((bitstream) >> 20) &0x0f; + unsigned int address=((bitstream) >> 4) &0xffff; + byte subchan=(bitstream) &0xf; + channel=channel+0x41; + if (subchan==8) { + subchan=1; + } else + if (subchan==4) { + subchan=2; + } else + if (subchan==2) { + subchan=3; + } else + if (subchan==1) { + subchan=4; + } else + if (subchan==3) { + subchan=5; + } + //================================================================================== + // Output + // ---------------------------------- + sprintf(pbuffer, "20;%02X;", PKSequenceNumber++);// Node and packet number + Serial.print( pbuffer ); + // ---------------------------------- + if (type == 0) { + Serial.print(F("Blyss;")); // Label + } else { + Serial.print(F("Avidsen;")); // Label + } + sprintf(pbuffer, "ID=%04x;", address); // ID + Serial.print( pbuffer ); + sprintf(pbuffer, "SWITCH=%c%d;", channel,subchan); + Serial.print( pbuffer ); + Serial.print(F("CMD=")); // command + if (status==0) Serial.print(F("ON;")); + if (status==1) Serial.print(F("OFF;")); + if (status==2) Serial.print(F("ALLON;")); + if (status==3) Serial.print(F("ALLOFF;")); + Serial.println(); + //================================================================================== + RawSignal.Repeats=true; // suppress repeats of the same RF packet + RawSignal.Number=0; + return true; +} +#endif // PLUGIN_006 + +#ifdef PLUGIN_TX_006 +void Blyss_Send(unsigned long address, byte devtype); + +boolean PluginTX_006(byte function, char *string) { + boolean success=false; + //10;Avidsen;00ff98;A1;OFF; + //012345678901234567890123456 + //10;Blyss;00ff98;A1;OFF; + //012345678901234567890123456 + int offset=0; + if (strncasecmp(InputBuffer_Serial+3,"AVIDSEN;",8) == 0) { // Blyss Command eg. + offset=2; + } + if ((strncasecmp(InputBuffer_Serial+3,"BLYSS;",6) == 0) || (offset==2)) { // Blyss Command eg. + unsigned long Bitstream = 0L; + if (InputBuffer_Serial[15+offset] != ';') return success; // check + if (InputBuffer_Serial[18+offset] != ';') return success; // check + + unsigned long Home=0; // Blyss channel A..P + byte Address=0; // Blyss subchannel 1..5 + byte c; + byte subchan=0; // subchannel + + InputBuffer_Serial[7+offset]=0x30; + InputBuffer_Serial[8+offset]=0x78; + InputBuffer_Serial[15+offset]=0; + Bitstream=str2int(InputBuffer_Serial+7+offset); // get address + + c=tolower(InputBuffer_Serial[16+offset]); // A..P + if(c>='a' && c<='p'){Home=c-'a';} + c=tolower(InputBuffer_Serial[17+offset]); // 1..5 + if(c>='1' && c<='5'){Address=Address+c-'0';} + + if (Address==1) subchan=0x80; + if (Address==2) subchan=0x40; + if (Address==3) subchan=0x20; + if (Address==4) subchan=0x10; + if (Address==5) subchan=0x30; + + Home = Home << 24; + Bitstream=(Bitstream) << 8; + Bitstream=Bitstream+subchan; + Bitstream=Bitstream+Home; + + c = str2cmd(InputBuffer_Serial+19+offset); // ALL ON/OFF command + if (c == VALUE_OFF) { + Bitstream=Bitstream|1; + } else { + if (c == VALUE_ALLOFF) { + Bitstream=Bitstream|3; + } else + if (c == VALUE_ALLON) { + Bitstream=Bitstream|2; + } + } + Blyss_Send(Bitstream, offset); // Bitstream contains the middle part of the bitstream to send + success=true; + } + return success; +} + +void Blyss_Send(unsigned long address, byte devtype) { + int fpulse = 400; // Pulse witdh in microseconds + int fretrans = 8; // Number of code retransmissions + uint32_t fdatabit; + uint32_t fdatamask = 0x800000; + uint32_t fsendbuff; + unsigned char RollingCode[] = { 0x98, 0xDA, 0x1E, 0xE6, 0x67, 0x98}; + + digitalWrite(PIN_RF_RX_VCC,LOW); // Power off the RF receiver + digitalWrite(PIN_RF_TX_VCC,HIGH); // Turn on the RF transmitter + delayMicroseconds(TRANSMITTER_STABLE_DELAY); // short delay to let the transmitter become stable (Note: Aurel RTX MID needs 500µS/0,5ms) + byte temp=(millis() &0xff); // used for the timestamp at the end of the RF packet + for (int nRepeat = 0; nRepeat <= fretrans; nRepeat++) { + // send SYNC 1P low, 6P high + digitalWrite(PIN_RF_TX_DATA, LOW); + delayMicroseconds(fpulse); + digitalWrite(PIN_RF_TX_DATA, HIGH); + delayMicroseconds(fpulse * 6); + // end send SYNC + // -------------- + // Send preamble (0xfe) - 8 bits + if (devtype == 0) { + fsendbuff=0x32; + } else { + fsendbuff=0xfe; + } + fdatamask=0x80; + for (int i = 0; i < 8; i++) { // Preamble + // read data bit + fdatabit = fsendbuff & fdatamask; // Get most left bit + fsendbuff = (fsendbuff << 1); // Shift left + if (fdatabit != fdatamask) { // Write 0 + digitalWrite(PIN_RF_TX_DATA, LOW); + delayMicroseconds(fpulse * 2); + digitalWrite(PIN_RF_TX_DATA, HIGH); + delayMicroseconds(fpulse * 1); + } else { // Write 1 + digitalWrite(PIN_RF_TX_DATA, LOW); + delayMicroseconds(fpulse * 1); + digitalWrite(PIN_RF_TX_DATA, HIGH); + delayMicroseconds(fpulse * 2); + } + } + // -------------- + fsendbuff=address; + fdatamask=0x8000000; + // Send command (channel/address/status) - 28 bits + for (int i = 0; i < 28; i++) { + // read data bit + fdatabit = fsendbuff & fdatamask; // Get most left bit + fsendbuff = (fsendbuff << 1); // Shift left + if (fdatabit != fdatamask) { // Write 0 + digitalWrite(PIN_RF_TX_DATA, LOW); + delayMicroseconds(fpulse * 2); + digitalWrite(PIN_RF_TX_DATA, HIGH); + delayMicroseconds(fpulse * 1); + } else { // Write 1 + digitalWrite(PIN_RF_TX_DATA, LOW); + delayMicroseconds(fpulse * 1); + digitalWrite(PIN_RF_TX_DATA, HIGH); + delayMicroseconds(fpulse * 2); + } + } + // -------------- + // Send rolling code & timestamp - 16 bits + fsendbuff=RollingCode[nRepeat]; + fsendbuff=(fsendbuff<<8)+temp; + //fsendbuff=0x9800 + temp; + fdatamask=0x8000; + for (int i = 0; i < 16; i++) { + // read data bit + fdatabit = fsendbuff & fdatamask; // Get most left bit + fsendbuff = (fsendbuff << 1); // Shift left + if (fdatabit != fdatamask) { // Write 0 + digitalWrite(PIN_RF_TX_DATA, LOW); + delayMicroseconds(fpulse * 2); + digitalWrite(PIN_RF_TX_DATA, HIGH); + delayMicroseconds(fpulse * 1); + } else { // Write 1 + digitalWrite(PIN_RF_TX_DATA, LOW); + delayMicroseconds(fpulse * 1); + digitalWrite(PIN_RF_TX_DATA, HIGH); + delayMicroseconds(fpulse * 2); + } + } + // -------------- + digitalWrite(PIN_RF_TX_DATA, LOW); + //delayMicroseconds(fpulse * 18); // delay between RF retransmits + delay(24); // delay 23.8 ms + } + delayMicroseconds(TRANSMITTER_STABLE_DELAY); // short delay to let the transmitter become stable (Note: Aurel RTX MID needs 500µS/0,5ms) + digitalWrite(PIN_RF_TX_VCC,LOW); // turn off the RF transmitter + digitalWrite(PIN_RF_RX_VCC,HIGH); // power on the RF receiver + RFLinkHW(); +} +#endif // PLUGIN_TX_006 diff --git a/Plugins/Plugin_007.c b/Plugins/Plugin_007.c new file mode 100644 index 0000000..e2bd586 --- /dev/null +++ b/Plugins/Plugin_007.c @@ -0,0 +1,320 @@ +//####################################################################################################### +//## This Plugin is only for use with the RFLink software package ## +//## Plugin-007 CONRAD RSL2 ## +//####################################################################################################### +/*********************************************************************************************\ + * This Plugin takes care of receiving of the Conrad RSL2 protocol + * + * Author : StuntTeam + * Support : http://sourceforge.net/projects/rflink/ + * License : This code is free for use in any open source project when this header is included. + * Usage of any parts of this code in a commercial application is prohibited! + ********************************************************************************************* + * Changelog: v1.0 initial release + ********************************************************************************************* + * Technical information: + * RF packets contain 66 pulses, 32 bits + * + * Conrad RSL2 Message Format: + * AABBCDDD EEEEEEEE EEEEEEEE EEEEEEEE + * + * A = always 10 + * B = Button code + * C = on/off command (inverted for some buttons/groups) + * D = group code + * E = 24 bit address + * + * Details: http://www.mikrocontroller.net/topic/252895 + * + * ConradSend address,switch,cmd; => (16 bits,8 bits,on/off/allon/alloff) + + 20;5B;DEBUG;Pulses=66;Pulses(uSec)=400,1200,350,1200,350,1200,350,1200,350,1225,350,1200,350,1200,350,1200,350,1200,350,1200,350,1200,350,1200,350,1200,350,1225,350,1200,350,1200,1200,350,350,1225,350,1200,1200,350,350,1200,350,1200,1200,350,350,1200,350,1200,350,1200,350,1200,350,1200,350,1200,350,1200,350,1200,350,1200,350; + + 400,1200,350,1200,350,1200,350,1200,350,1225,350,1200,350,1200,350,1200,350,1200,350,1200, + 350,1200,350,1200,350,1200,350,1225,350,1200,350,1200,1200,350,350,1225,350,1200,1200,350, + 350,1200,350,1200,1200,350,350,1200,350,1200,350,1200,350,1200,350,1200,350,1200,350,1200, + 350,1200,350,1200,350; + +20;3D;DEBUG;Pulses=68;Pulses(uSec)=600,6450,1290,330,390,1260,390,1260,360,1260,360,1260,390,1260,390,1260,1290,360,1260,360,1290,360,1290,330,1290,360,1260,360,1260,360,1290,330,1290,360,360,1260,360,1260,390,1260,390,1260,360,1260,1290,360,1260,360,360,1260,390,1260,360,1260,360,1260,360,1260,390,1260,1290,360,1290,360,1260,360,390,6990; +20;3E;DEBUG;Pulses=68;Pulses(uSec)=720,6450,1290,330,390,1260,360,1260,360,1260,390,1260,390,1260,360,1260,1260,360,1290,330,1290,330,1290,360,1260,360,1260,360,1290,330,1290,360,1290,360,360,1260,360,1260,390,1260,360,1260,360,1260,1260,360,1290,330,390,1260,360,1260,360,1260,360,1260,390,1260,390,1260,1260,360,1260,360,1290,330,390,6990; +20;3F;DEBUG;Pulses=68;Pulses(uSec)=630,6450,1290,360,360,1260,360,1260,390,1260,390,1260,360,1260,360,1260,1290,330,1290,330,1290,360,1260,360,1290,330,1290,330,1290,360,1260,360,1260,360,390,1260,390,1260,360,1260,360,1260,360,1260,1290,330,1290,360,360,1260,360,1260,390,1260,390,1260,360,1260,360,1260,1290,330,1290,330,1290,360,360,6990; + + \*********************************************************************************************/ +#define CONRADRSL2_PULSECOUNT 66 +#define CONRADRSL2_PULSEMID 600/RAWSIGNAL_SAMPLE_RATE + +#ifdef PLUGIN_007 +boolean Plugin_007(byte function, char *string){ + if ((RawSignal.Number != CONRADRSL2_PULSECOUNT) && (RawSignal.Number != CONRADRSL2_PULSECOUNT+2) ) return false; + unsigned long bitstream=0L; + byte checksum=0; + byte button=0; + byte group=0; + byte action=0; + byte start=0; + if (RawSignal.Number == CONRADRSL2_PULSECOUNT+2) { + start=2; + } + //================================================================================== + // get bits + for(byte x=1+start;x < RawSignal.Number-2;x=x+2) { + if (RawSignal.Pulses[x] > CONRADRSL2_PULSEMID) { + if (RawSignal.Pulses[x+1] > CONRADRSL2_PULSEMID) return false; // manchester check + bitstream = (bitstream << 1) | 0x1; // 1 + } else { + if (RawSignal.Pulses[x+1] < CONRADRSL2_PULSEMID) return false; // manchester check + bitstream = (bitstream << 1); // 0 + } + } + //================================================================================== + // Prevent repeating signals from showing up + //================================================================================== + if(SignalHash!=SignalHashPrevious || RepeatingTimer> 30)&0x0f); // first two bits should always be '10' + if (checksum != 2) return false; + //================================================================================== + button=(((bitstream) >> 24)&0xff); // 10100011 + // ----- check for possible valid values + if (button < 0x81) return false; + if (button > 0xbe) return false; + byte temp=(button)&0xf; + if (temp == 0x07 || temp == 0x0b || temp == 0x0f ) return false; + if (button == 0x83 || button == 0x86 || button == 0x89 || button == 0x8c || button == 0x91 ) return false; + if (button == 0x94 || button == 0x9a || button == 0x9d || button == 0xa1 || button == 0xa4 ) return false; + if (button == 0xaa || button == 0xad || button == 0xb1 || button == 0xb3 || button == 0xb4 ) return false; + if (button == 0xba || button == 0xbd ) return false; + // ----- + group=(button) & 0x7; // -- 111 + action=((button) >> 3) & 0x1; // -- a + button = ((button) >> 4) & 0x3; // --bb + // ----- + if (group == 3) { + action=button; + button=0; + } else { + if (button==3) { + if (group == 6) { // toggle command bit + button=0; + action=(~action)&1; + } + if (group == 1 || group == 5) { // no toggle + button=4; + } + if (group == 0) { + action=(~action)&1; // toggle command bit + button=8; + } + if (group == 2 || group == 4) { // no toggle + button=12; + } + } else + if (button==0) { + if (group == 6 || group == 1) { // no toggle + button=1; + } + if (group == 5) { // toggle command bit + button=5; + action=(~action)&1; + } + if (group == 0 || group == 4) { // no toggle + button=9; + } + if (group == 2) { // toggle command bit + button=13; + action=(~action)&1; + } + } else + if (button==2) { + if (group == 6) { // toggle command bit + button=2; + action=(~action)&1; + } + if (group == 1 || group == 5) button=6; // + + if (group == 0) { // toggle command bit + button=10; + action=(~action)&1; + } + + if (group == 2 || group == 4) { // no toggle + button=14; + //action=(~action)&1; + } + } else + if (button==1) { + if (group == 6) { // toggle command bit + button=3; + action=(~action)&1; + } + if (group == 1 || group == 5) { // no toggle + button=7; + //action=(~action)&1; + } + if (group == 0) { // toggle command bit + button=11; + action=(~action)&1; + } + if (group == 2 || group == 4) { // no toggle + button=15; + //action=(~action)&1; + } + } + } + //================================================================================== + // Output + // ---------------------------------- + sprintf(pbuffer, "20;%02X;", PKSequenceNumber++);// Node and packet number + Serial.print( pbuffer ); + // ---------------------------------- + Serial.print(F("Conrad;")); // Label + sprintf(pbuffer, "ID=%06lx;",((bitstream) &0xffffff) ); // ID + Serial.print( pbuffer ); + sprintf(pbuffer, "SWITCH=%02x;", button); // Button number + Serial.print( pbuffer ); + Serial.print(F("CMD=")); // command + if (group==3) { + Serial.print(F("ALL")); + } + if (action==1) { + Serial.print(F("ON;")); + } else { // 0 normal off, 2 group off + Serial.print(F("OFF;")); + } + Serial.println(); + //================================================================================== + RawSignal.Repeats=true; // suppress repeats of the same RF packet + RawSignal.Number=0; + return true; +} +#endif // PLUGIN_007 + +#ifdef PLUGIN_TX_007 +void RSL2_Send(unsigned long address); + +boolean PluginTX_007(byte function, char *string) { + boolean success=false; + //10;CONRAD;000fa0;0;OFF; + //10;CONRAD;009200;1;ON; + //10;CONRAD;ff0607;1;OFF; + //012345678901234567890123 + if (strncasecmp(InputBuffer_Serial+3,"CONRAD;",7) == 0) { // KAKU Command eg. + unsigned long bitstream=0L; + unsigned long command=0L; + if (InputBuffer_Serial[16] != ';') return success; + InputBuffer_Serial[8]=0x30; + InputBuffer_Serial[9]=0x78; + InputBuffer_Serial[16]=0; + bitstream=str2int(InputBuffer_Serial+8); // Address + + byte temp=str2int(InputBuffer_Serial+17); // het button/unit number (0x00..0x0f) + if (temp < 16) { // No button with a number higher than 15 + byte cmd=str2cmd(InputBuffer_Serial+19); // ON/OFF + if (cmd==VALUE_OFF) { + if (temp == 0) command=0xbe; + if (temp == 1) command=0x81; + if (temp == 2) command=0xae; + if (temp == 3) command=0x9e; + if (temp == 4) command=0xb5; + if (temp == 5) command=0x8d; + if (temp == 6) command=0xa5; + if (temp == 7) command=0x95; + if (temp == 8) command=0xb8; + if (temp == 9) command=0x84; + if (temp ==10) command=0xa8; + if (temp ==11) command=0x98; + if (temp ==12) command=0xb2; + if (temp ==13) command=0x8a; + if (temp ==14) command=0xa2; + if (temp ==15) command=0x92; + } else // ON + if (cmd==VALUE_ON) { + if (temp == 0) command=0xb6; + if (temp == 1) command=0x8e; + if (temp == 2) command=0xa6; + if (temp == 3) command=0x96; + if (temp == 4) command=0xb9; + if (temp == 5) command=0x85; + if (temp == 6) command=0xa9; + if (temp == 7) command=0x99; + if (temp == 8) command=0xb0; + if (temp == 9) command=0x88; + if (temp ==10) command=0xa0; + if (temp ==11) command=0x90; + if (temp ==12) command=0xbc; + if (temp ==13) command=0x82; + if (temp ==14) command=0xac; + if (temp ==15) command=0x9c; + } else // AllON + if (cmd==VALUE_ALLON) { + command=0x93; + } else // AllOff + if (cmd==VALUE_ALLOFF) { + command=0xa3; + } + command=command << 24; + bitstream=bitstream+command; + } + RSL2_Send(bitstream); // the full bitstream to send + success=true; + } + return success; +} + +void RSL2_Send(unsigned long address) { + int fpulse = 650; // Pulse witdh in microseconds 650? + int fpulse2 = 450; // Pulse witdh in microseconds 650? + int fretrans = 4; // Number of code retransmissions + uint32_t fdatabit; + uint32_t fdatamask = 0x80000000; + uint32_t fsendbuff; + + digitalWrite(PIN_RF_RX_VCC,LOW); // Spanning naar de RF ontvanger uit om interferentie met de zender te voorkomen. + digitalWrite(PIN_RF_TX_VCC,HIGH); // zet de 433Mhz zender aan + delayMicroseconds(TRANSMITTER_STABLE_DELAY); // short delay to let the transmitter become stable (Note: Aurel RTX MID needs 500µS/0,5ms) + + for (int nRepeat = 0; nRepeat <= fretrans; nRepeat++) { + fsendbuff=address; + + // send SYNC 1P High, 10P low + digitalWrite(PIN_RF_TX_DATA, HIGH); + delayMicroseconds(fpulse * 1); + digitalWrite(PIN_RF_TX_DATA, LOW); + delayMicroseconds(fpulse * 10); + // end send SYNC + // Send command + for (int i = 0; i < 32; i++) { // 32 bits + // read data bit + fdatabit = fsendbuff & fdatamask; // Get most left bit + fsendbuff = (fsendbuff << 1); // Shift left + if (fdatabit != fdatamask) { // Write 0 + digitalWrite(PIN_RF_TX_DATA, HIGH); + delayMicroseconds(fpulse2 * 1); + digitalWrite(PIN_RF_TX_DATA, LOW); + delayMicroseconds(fpulse2 * 3); + } else { // Write 1 + digitalWrite(PIN_RF_TX_DATA, HIGH); + delayMicroseconds(fpulse2 * 3); + digitalWrite(PIN_RF_TX_DATA, LOW); + delayMicroseconds(fpulse2 * 1); + } + } + digitalWrite(PIN_RF_TX_DATA, HIGH); + delayMicroseconds(fpulse2 * 1); + digitalWrite(PIN_RF_TX_DATA, LOW); + delayMicroseconds(fpulse * 14); + } + delayMicroseconds(TRANSMITTER_STABLE_DELAY); // short delay to let the transmitter become stable (Note: Aurel RTX MID needs 500µS/0,5ms) + digitalWrite(PIN_RF_TX_VCC,LOW); // zet de 433Mhz zender weer uit + digitalWrite(PIN_RF_RX_VCC,HIGH); // Spanning naar de RF ontvanger weer aan. + RFLinkHW(); +} +#endif // PLUGIN_TX_007 diff --git a/Plugins/Plugin_008.c b/Plugins/Plugin_008.c new file mode 100644 index 0000000..981443f --- /dev/null +++ b/Plugins/Plugin_008.c @@ -0,0 +1,250 @@ +//####################################################################################################### +//## This Plugin is only for use with the RFLink software package ## +//## Plugin-008 Kambrook ## +//####################################################################################################### +/*********************************************************************************************\ + * This Plugin takes care of receiving of the Kambrook protocol + * Device models: RF3399/RF3405/RF3672/RF3689/RF4471R + * Made by Ningbo Comen Electronics Technology Co. Ltd. + * + * Author : StuntTeam + * Support : http://sourceforge.net/projects/rflink/ + * License : This code is free for use in any open source project when this header is included. + * Usage of any parts of this code in a commercial application is prohibited! + ********************************************************************************************* + * Changelog: v1.0 initial release + ********************************************************************************************* + * Technical information: + * RF packets contain 96 pulses, 48 bits + * + * Kambrook Message Format: + * AAAAAAAA BBBBBBBB BBBBBBBB BBBBBBBB CCCCCCCC DDDDDDDD + * + * A = Preamble, always 0x55 + * B = Address + * C = Channel/Command + * D = Trailing, always 0xFF + * + * Details http://wiki.beyondlogic.org/index.php?title=Reverse_engineering_the_RF_protocol_on_a_Kambrook_Power_Point_Controller + * + * 20;33;DEBUG;Pulses=96;Pulses(uSec)=270,180,600,180,210,180,600,180,210,180,600,180,210,180,600,180,210,180,210,180,210,180,210,180,210,180,210,180,210,180,210,180,210,180,210,180,210,180,210,180,210,180,210,180,210,180,210,180,210,180,210,180,210,180,210,180,210,180,210,180,210,180,600,180,210,180,210,180,210,180,210,180,210,180,210,180,210,180,600,180,600,180,600,180,600,180,600,180,600,180,600,180,600,180,600,6990; + \*********************************************************************************************/ +#define KAMBROOK_PULSECOUNT 96 +#define KAMBROOK_PULSEMID 400/RAWSIGNAL_SAMPLE_RATE + +#ifdef PLUGIN_008 +boolean Plugin_008(byte function, char *string) { + if (RawSignal.Number != KAMBROOK_PULSECOUNT) return false; + unsigned long address=0L; + byte sync=0; + byte command=0; + byte trailing=0; + + byte bitcounter=0; + byte status=0; + byte channel=0; + byte subchan=0; + //================================================================================== + // get bits + for(byte x=1;x KAMBROOK_PULSEMID) { + if (bitcounter < 8) { + sync = (sync << 1) | 0x1; + } else + if (bitcounter < 32) { + address = (address << 1) | 0x1; + } else + if (bitcounter < 40) { + command = (command << 1) | 0x1; + } else { + trailing = (trailing << 1) | 0x1; + } + bitcounter++; + } else { + if (bitcounter < 8) { + sync = (sync << 1); + } else + if (bitcounter < 32) { + address = (address << 1); + } else + if (bitcounter < 40) { + command = (command << 1); + } else { + trailing = (trailing << 1); + } + bitcounter++; + } + } + //================================================================================== + // all bits received, make sure checksum/sanity check is okay + //================================================================================== + if (sync != 0x55) return false; + if (trailing != 0xff) return false; + //================================================================================== + // Prevent repeating signals from showing up + //================================================================================== + if(SignalHash!=SignalHashPrevious || RepeatingTimer> 1)&7) + 1; // button code + if (status == 0) subchan--; + + byte temp=(command) >> 4; // channel code + channel=0x41+temp; + //================================================================================== + // Output + // ---------------------------------- + sprintf(pbuffer, "20;%02X;", PKSequenceNumber++);// Node and packet number + Serial.print( pbuffer ); + // ---------------------------------- + Serial.print(F("Kambrook;")); // Label + sprintf(pbuffer, "ID=%06lx;",((address)&0xffffff) ); // ID + Serial.print( pbuffer ); + sprintf(pbuffer, "SWITCH=%c%d;", channel,subchan); + Serial.print( pbuffer ); + Serial.print(F("CMD=")); // command + if (status==0) Serial.print(F("OFF;")); + if (status==1) Serial.print(F("ON;")); + Serial.println(); + //================================================================================== + RawSignal.Repeats=true; // suppress repeats of the same RF packet + RawSignal.Number=0; + return true; +} +#endif // PLUGIN_008 + +#ifdef PLUGIN_TX_008 +void Kambrook_Send(unsigned long address); + +boolean PluginTX_008(byte function, char *string) { + boolean success=false; + //10;kambrook;050325;a1;ON; + //012345678901234567890123 + if (strncasecmp(InputBuffer_Serial+3,"KAMBROOK;",9) == 0) { // KAKU Command eg. + if (InputBuffer_Serial[18] != ';') return false; + + InputBuffer_Serial[10]=0x30; + InputBuffer_Serial[11]=0x78; // Get address from hexadecimal value + InputBuffer_Serial[18]=0x00; // Get address from hexadecimal value + + unsigned long bitstream=0L; // Main placeholder + byte Home=0; // channel A..D + byte Address=0; // subchannel 1..5 + byte c; + byte x=19; // teller die wijst naar het te behandelen teken + + bitstream=str2int(InputBuffer_Serial+10); // Address + + while((c=tolower(InputBuffer_Serial[x++]))!=';') { + if(c>='0' && c<='9'){Address=Address+c-'0';} // Home 0..9 + if(c>='a' && c<='d'){Home=c-'a';} // Address a..d + } + + Address=Address << 1; // shift left 1 bit + c=0; + c = str2cmd(InputBuffer_Serial+x); // ON/OFF command + if (c == VALUE_ON) { + Address--; + } + Address=Address & 0x0f; + // ------------------------------- + Home = Home << 4; // move home to bits 4-7 + bitstream=(bitstream) << 8; // shift main value 8 bits left + bitstream=bitstream + Home; // add to main value + bitstream=bitstream + Address; // add address bits to bits 0-3 + // ------------------------------- + Kambrook_Send(bitstream); // bitstream to send + success=true; + } + return success; +} + +void Kambrook_Send(unsigned long address) { + int fpulse = 300; // Pulse witdh in microseconds + int fpulse2 = 700; // Pulse witdh in microseconds + int fretrans = 5; // Number of code retransmissions + uint32_t fdatabit; + uint32_t fdatamask = 0x800000; + uint32_t fsendbuff; + + digitalWrite(PIN_RF_RX_VCC,LOW); // Spanning naar de RF ontvanger uit om interferentie met de zender te voorkomen. + digitalWrite(PIN_RF_TX_VCC,HIGH); // zet de 433Mhz zender aan + delayMicroseconds(TRANSMITTER_STABLE_DELAY); // short delay to let the transmitter become stable (Note: Aurel RTX MID needs 500µS/0,5ms) + for (int nRepeat = 0; nRepeat <= fretrans; nRepeat++) { + // -------------- + // Send preamble (0x55) - 8 bits + fsendbuff=0x55; + fdatamask=0x80; + for (int i = 0; i < 8; i++) { // Preamble + // read data bit + fdatabit = fsendbuff & fdatamask; // Get most left bit + fsendbuff = (fsendbuff << 1); // Shift left + if (fdatabit != fdatamask) { // Write 0 + digitalWrite(PIN_RF_TX_DATA, HIGH); + delayMicroseconds(fpulse); + digitalWrite(PIN_RF_TX_DATA, LOW); + delayMicroseconds(fpulse); + } else { // Write 1 + digitalWrite(PIN_RF_TX_DATA, HIGH); + delayMicroseconds(fpulse2); + digitalWrite(PIN_RF_TX_DATA, LOW); + delayMicroseconds(fpulse); + } + } + // -------------- + fsendbuff=address; + fdatamask=0x80000000; + // Send command (channel/address/status) - 32 bits + for (int i = 0; i < 32;i++){ //28;i++){ + // read data bit + fdatabit = fsendbuff & fdatamask; // Get most left bit + fsendbuff = (fsendbuff << 1); // Shift left + if (fdatabit != fdatamask) { // Write 0 + digitalWrite(PIN_RF_TX_DATA, HIGH); + delayMicroseconds(fpulse); + digitalWrite(PIN_RF_TX_DATA, LOW); + delayMicroseconds(fpulse); + } else { // Write 1 + digitalWrite(PIN_RF_TX_DATA, HIGH); + delayMicroseconds(fpulse2); + digitalWrite(PIN_RF_TX_DATA, LOW); + delayMicroseconds(fpulse); + } + } + // -------------- + // Send trailing bits - 8 bits + fsendbuff=0xFF; + fdatamask=0x80; + for (int i = 0; i < 8; i++) { + // read data bit + fdatabit = fsendbuff & fdatamask; // Get most left bit + fsendbuff = (fsendbuff << 1); // Shift left + if (fdatabit != fdatamask) { // Write 0 + digitalWrite(PIN_RF_TX_DATA, HIGH); + delayMicroseconds(fpulse); + digitalWrite(PIN_RF_TX_DATA, LOW); + delayMicroseconds(fpulse); + } else { // Write 1 + digitalWrite(PIN_RF_TX_DATA, HIGH); + delayMicroseconds(fpulse2); + digitalWrite(PIN_RF_TX_DATA, LOW); + delayMicroseconds(fpulse); + } + } + // -------------- + digitalWrite(PIN_RF_TX_DATA, LOW); + delayMicroseconds(fpulse2 * 14); + } + delayMicroseconds(TRANSMITTER_STABLE_DELAY); // short delay to let the transmitter become stable (Note: Aurel RTX MID needs 500µS/0,5ms) + digitalWrite(PIN_RF_TX_VCC,LOW); // zet de 433Mhz zender weer uit + digitalWrite(PIN_RF_RX_VCC,HIGH); // Spanning naar de RF ontvanger weer aan. + RFLinkHW(); +} +#endif // PLUGIN_008 diff --git a/Plugins/Plugin_009.c b/Plugins/Plugin_009.c new file mode 100644 index 0000000..e09be2c --- /dev/null +++ b/Plugins/Plugin_009.c @@ -0,0 +1,347 @@ +//####################################################################################################### +//## This Plugin is only for use with the RFLink software package ## +//## Plugin-06: X10 RF ## +//####################################################################################################### +/*********************************************************************************************\ + * This plugin takes care of sending and receiving the X10 RF protocol. + * + * Author : StuntTeam + * Support : http://sourceforge.net/projects/rflink/ + * License : This code is free for use in any open source project when this header is included. + * Usage of any parts of this code in a commercial application is prohibited! + *********************************************************************************************** + * Incoming event: "X10 , + * Send : "X10Send , + * + * Address = A1 - P16 + *********************************************************************************************** + * Technical information: + * RF packets are 68 bits long transferring 64 manchester encoded bits resulting in 32 bits / 4 bytes. + * + * address address^ data data^ + * 01100000 10011111 00000000 11111111 609F00FF + * 10011111 01100000 11111111 00000000 9F60FF00 + * + * 4 bytes are transmitted, the second and fourth are the inverse of the first and third byte. + * So the actual data is only 2 bytes + * + * 01100000 00000000 + * AAAABBBB CDEDDDDD + * + * A = Housecode 0110 a 6 0111 b 7 0100 c 4 0101 d 5 1000 e 8 1001 f 9 1010 g a 1011 h b + * 1110 i e 1111 j f 1100 k c 1101 l d 0000 m 0 0001 n 1 0010 o 2 0011 p 3 + * B = Unitcode 1-8 / 9-16 indicator + * C = Group/Dimmer indicator + * D = Unitcode + * E = on/off indicator + * + * on + * 20;06;DEBUG;Pulses=68;Pulses(uSec)=3300,4225,400,375,400,1325,400,1325,400,1325,400,375,400,375,400,375,400,375,400,1325,400,375,400,375,400,375,400,1350,400,1350,375,1350,400,1350,400,375,400,375,400,375,400,1325,400,1325,400,375,400,375,400,375,400,1350,400,1325,400,1325,400,375,400,375,400,1325,400,1325,400,1325,400 + * off + * 20;10;DEBUG;Pulses=68;Pulses(uSec)=3300,4225,400,375,400,1350,400,1350,400,1325,400,375,400,375,400,375,400,375,400,1325,400,375,400,375,400,375,400,1325,400,1325,400,1325,400,1325,400,375,400,375,400,1325,400,1350,400,1350,400,375,400,375,400,375,375,1350,400,1350,400,375,400,375,400,375,400,1325,400,1350,400,1325,400; + * 20;20;DEBUG;Pulses=66;Pulses(uSec)=425,350,375,1300,375,1300,375,1350,375,375,375,1350,375,375,375,375,375,1350,375,375,375,375,375,375,400,1350,375,375,400,1350,375,1350,400,1325,400,375,400,375,400,375,400,375,400,375,400,375,400,375,400,375,400,1325,400,1325,400,1325,400,1325,400,1325,400,1350,375,1350,375; + \*********************************************************************************************/ +#define X10_PulseLength 66 +#define X10_PULSEMID 600/RAWSIGNAL_SAMPLE_RATE + +#ifdef PLUGIN_009 +boolean Plugin_009(byte function, char *string) { + if ( (RawSignal.Number != (X10_PulseLength )) && (RawSignal.Number != (X10_PulseLength+2)) ) return false; + unsigned long bitstream=0L; + byte housecode=0; + byte unitcode=0; + byte command=0; + byte data[4]; + byte start=0; + if (RawSignal.Number == X10_PulseLength+2) { + if ( (RawSignal.Pulses[1]*RawSignal.Multiply > 3000) && (RawSignal.Pulses[2]*RawSignal.Multiply > 3000) ) { + start=2; + } else { + return false; // not an X10 packet + } + } + // get all 24 bits + for(byte x=2+start;x < ((X10_PulseLength)+start) ;x+=2) { + if(RawSignal.Pulses[x] > X10_PULSEMID) { + bitstream = (bitstream << 1) | 0x1; + } else { + bitstream = (bitstream << 1); + } + } + //================================================================================== + // Prevent repeating signals from showing up + //================================================================================== + if(SignalHash!=SignalHashPrevious || RepeatingTimer>24)&0xff; + data[1]=((bitstream)>>16)&0xff; + data[2]=((bitstream)>>8)&0xff; + data[3]=(bitstream)&0xff; + // ---------------------------------- + // perform sanity checks + data[1]=data[1]^0xff; + data[3]=data[3]^0xff; + if (data[0] != data[1]) return false; + if (data[2] != data[3]) return false; + // ---------------------------------- + data[1]=data[1]&0x0f; // lower nibble only + data[0]=data[0]&0xf0; // upper nibble only + + housecode=0; + if (data[0]==0x60) housecode=0; + if (data[0]==0x70) housecode=1; + if (data[0]==0x40) housecode=2; + if (data[0]==0x50) housecode=3; + if (data[0]==0x80) housecode=4; + if (data[0]==0x90) housecode=5; + if (data[0]==0xa0) housecode=6; + if (data[0]==0xb0) housecode=7; + if (data[0]==0xe0) housecode=8; + if (data[0]==0xf0) housecode=9; + if (data[0]==0xc0) housecode=10; + if (data[0]==0xd0) housecode=11; + if (data[0]==0x00) housecode=12; + if (data[0]==0x10) housecode=13; + if (data[0]==0x20) housecode=14; + if (data[0]==0x30) housecode=15; + + if (data[2]==0x00) { unitcode=1; command=1;} + if (data[2]==0x20) { unitcode=1; command=0;} + if (data[2]==0x10) { unitcode=2; command=1;} + if (data[2]==0x30) { unitcode=2; command=0;} + if (data[2]==0x08) { unitcode=3; command=1;} + if (data[2]==0x28) { unitcode=3; command=0;} + if (data[2]==0x18) { unitcode=4; command=1;} + if (data[2]==0x38) { unitcode=4; command=0;} + if (data[2]==0x40) { unitcode=5; command=1;} + if (data[2]==0x60) { unitcode=5; command=0;} + if (data[2]==0x50) { unitcode=6; command=1;} + if (data[2]==0x70) { unitcode=6; command=0;} + if (data[2]==0x48) { unitcode=7; command=1;} + if (data[2]==0x68) { unitcode=7; command=0;} + if (data[2]==0x58) { unitcode=8; command=1;} + if (data[2]==0x78) { unitcode=8; command=0;} + if (data[2]==0x88) { unitcode=0; command=2;} + if (data[2]==0x98) { unitcode=0; command=3;} + if (data[2]==0x80) { unitcode=0; command=4;} + if (data[2]==0x90) { unitcode=0; command=5;} + + if ( (data[1]==0x04) && (command < 2) ) { + unitcode=unitcode+8; + } + //================================================================================== + // ---------------------------------- + // All is OK, build event + // ---------------------------------- + // Output + // ---------------------------------- + sprintf(pbuffer, "20;%02X;", PKSequenceNumber++); // Node and packet number + Serial.print( pbuffer ); + // ---------------------------------- + Serial.print(F("X10;")); // Label + sprintf(pbuffer, "ID=%02x;", 0x41+housecode); // ID + Serial.print( pbuffer ); + sprintf(pbuffer, "SWITCH=%d;", unitcode); + Serial.print( pbuffer ); + Serial.print(F("CMD=")); + if ( command == 0) { + Serial.print(F("OFF;")); + } else + if ( command == 1) { + Serial.print(F("ON;")); + } else + if ( command == 2) { + Serial.print(F("BRIGHT;")); + } else + if ( command == 3) { + Serial.print(F("DIM;")); + } else + if ( command == 4) { + Serial.print(F("ALLOFF;")); + } else + if ( command == 5) { + Serial.print(F("ALLON;")); + } + Serial.println(); + // ---------------------------------- + RawSignal.Repeats=true; // suppress repeats of the same RF packet + RawSignal.Number=0; + return true; +} +#endif //PLUGIN_009 + +#ifdef PLUGIN_TX_009 +void X10_Send(uint32_t address); + +boolean PluginTX_009(byte function, char *string) { + boolean success=false; + //10;X10;000041;1;OFF; + //0123456789012345678 + // Hier aangekomen bevat string het volledige commando. Test als eerste of het opgegeven commando overeen komt + if (strncasecmp(InputBuffer_Serial+3,"X10;",4) == 0) { // X10 Command eg. + unsigned long bitstream=0L; + byte x=14; // teller die wijst naar het te behandelen teken + byte command=0; + byte Home=0; // Home A..P + byte Address=0; // Blyss subchannel 1..5 + byte c; + uint32_t newadd=0; + + InputBuffer_Serial[ 9]=0x30; + InputBuffer_Serial[10]=0x78; + InputBuffer_Serial[13]=0; + Home=str2int(InputBuffer_Serial+9); // Home: A..P + if (Home < 0x51) // take care of upper/lower case + Home=Home - 'A'; + else + if (Home < 0x71) // take care of upper/lower case + Home=Home - 'a'; + else { + return success; // invalid value + } + + while((c=tolower(InputBuffer_Serial[x++]))!=';'){ // Address: 1 to 16 + if(c>='0' && c<='9'){Address=Address*10;Address=Address+c-'0';} + } + + if (Home == 0) c = 0x60; + if (Home == 1) c = 0x70; + if (Home == 2) c = 0x40; + if (Home == 3) c = 0x50; + if (Home == 4) c = 0x80; + if (Home == 5) c = 0x90; + if (Home == 6) c = 0xa0; + if (Home == 7) c = 0xb0; + if (Home == 8) c = 0xe0; + if (Home == 9) c = 0xf0; + if (Home ==10) c = 0xc0; + if (Home ==11) c = 0xd0; + if (Home ==12) c = 0x00; + if (Home ==13) c = 0x10; + if (Home ==14) c = 0x20; + if (Home ==15) c = 0x30; + if (Address > 7) { + c = c + 4; + Address=Address-8; + } + // --------------- + Home=str2cmd(InputBuffer_Serial+x); + if (Home == 0) { // DIM/BRIGHT command + if (strcasecmp(InputBuffer_Serial+x,"DIM")==0) { + command=3; + } else + if (strcasecmp(InputBuffer_Serial+x,"BRIGHT")==0) { + command=2; + } + c = c + 4; + } else { + if (Home==VALUE_ON) { + command=1; + } else + if (Home==VALUE_OFF) { + command=0; + } else + if (Home==VALUE_ALLOFF) { + command=4; + c = c + 4; + } else + if (Home==VALUE_ALLON) { + command=5; + c = c + 4; + } + } + if (Address == 1 && command == 1) bitstream=0x00; + if (Address == 1 && command == 0) bitstream=0x20; + if (Address == 2 && command == 1) bitstream=0x10; + if (Address == 2 && command == 0) bitstream=0x30; + if (Address == 3 && command == 1) bitstream=0x08; + if (Address == 3 && command == 0) bitstream=0x28; + if (Address == 4 && command == 1) bitstream=0x18; + if (Address == 4 && command == 0) bitstream=0x38; + if (Address == 5 && command == 1) bitstream=0x40; + if (Address == 5 && command == 0) bitstream=0x60; + if (Address == 6 && command == 1) bitstream=0x50; + if (Address == 6 && command == 0) bitstream=0x70; + if (Address == 7 && command == 1) bitstream=0x48; + if (Address == 7 && command == 0) bitstream=0x68; + if (Address == 8 && command == 1) bitstream=0x58; + if (Address == 8 && command == 0) bitstream=0x78; + if (command == 2) bitstream=0x88; + if (command == 3) bitstream=0x98; + if (command == 4) bitstream=0x80; + if (command == 5) bitstream=0x90; + // ----------------------------- + newadd=bitstream <<8; + bitstream=bitstream^0xff; + newadd=newadd+bitstream; + bitstream=c^0xff; + bitstream=bitstream<<16; + newadd=newadd+bitstream; + bitstream=c; + bitstream=bitstream<<24; + newadd=newadd+bitstream; + bitstream=newadd; + // ----------------------------- + X10_Send(bitstream); // full bitstream to send + success=true; + } + return success; +} + +void X10_Send(uint32_t address) { + int fpulse = 375; // Pulse witdh in microseconds + int fretrans = 4; // Number of code retransmissions + uint32_t fdatabit; + uint32_t fdatamask = 0x80000000; + uint32_t fsendbuff; + + digitalWrite(PIN_RF_RX_VCC,LOW); // Disable RF receiver + digitalWrite(PIN_RF_TX_VCC,HIGH); // Enable RF transmitter + delayMicroseconds(TRANSMITTER_STABLE_DELAY); // short delay to let the transmitter become stable (Note: Aurel RTX MID needs 500µS/0,5ms) + + for (int nRepeat = 0; nRepeat <= fretrans; nRepeat++) { + fsendbuff=address; + + // send SYNC 12P High, 10P low + digitalWrite(PIN_RF_TX_DATA, HIGH); + delayMicroseconds(fpulse * 12); + digitalWrite(PIN_RF_TX_DATA, LOW); + delayMicroseconds(fpulse * 10); + // end send SYNC + // Send command + for (int i = 0; i < 32; i++) { // 32 bits + // read data bit + fdatabit = fsendbuff & fdatamask; // Get most left bit + fsendbuff = (fsendbuff << 1); // Shift left + if (fdatabit != fdatamask) { // Write 0 + digitalWrite(PIN_RF_TX_DATA, HIGH); + delayMicroseconds(fpulse * 1); + digitalWrite(PIN_RF_TX_DATA, LOW); + delayMicroseconds(fpulse * 1); + } else { // Write 1 + digitalWrite(PIN_RF_TX_DATA, HIGH); + delayMicroseconds(fpulse * 1); + digitalWrite(PIN_RF_TX_DATA, LOW); + delayMicroseconds(fpulse * 4); + } + } + // Send Stop/delay + digitalWrite(PIN_RF_TX_DATA, HIGH); + delayMicroseconds(fpulse * 1); + digitalWrite(PIN_RF_TX_DATA, LOW); + delayMicroseconds(fpulse * 20); + } + delayMicroseconds(TRANSMITTER_STABLE_DELAY); // short delay to let the transmitter become stable (Note: Aurel RTX MID needs 500µS/0,5ms) + digitalWrite(PIN_RF_TX_VCC,LOW); // Disable RF transmitter + digitalWrite(PIN_RF_RX_VCC,HIGH); // Enable RF receiver + RFLinkHW(); + return; +} +#endif //PLUGIN_TX_009 diff --git a/Plugins/Plugin_010.c b/Plugins/Plugin_010.c new file mode 100644 index 0000000..3c216d8 --- /dev/null +++ b/Plugins/Plugin_010.c @@ -0,0 +1,242 @@ + +//## This Plugin is only for use with the RFLink software package ## +//## Plugin-10 TRC02 RGB Controller ## +//####################################################################################################### +/*********************************************************************************************\ + * Decodes signals from a wireless RGB TRC02 controller remote control + * + * + * Author : StuntTeam, Marek Zlatos, + * Support : http://sourceforge.net/projects/rflink/ + * License : This code is free for use in any open source project when this header is included. + * Usage of any parts of this code in a commercial application is prohibited! + ********************************************************************************************* + * Changelog: v1.0 initial release + ********************************************************************************************* + * Technical information: + * Decodes signals from a wireless TRC02 RGB controller remote control + * -------------------------------------------------------------------------------------------- + * _Byte 0_ _Byte 1_ _Byte 2_ _Byte 3_ _Bit_ + * 76543210 76543210 76543210 76543210 0 + * AAAAAAAA BBBBBBBB CCCCCCCC DDDDDDDD E + * + * A = Rolling Code + * B = Rolling Code + * C = Rolling Code + * D = Command + * E = Checksum. bit is XOR of all bits in the RF message + * + * Commands: + * 00 ON + * 01 OFF + * 02 Dim Down + * 03 DIM UP + * 05 Color Mix UP + * 04 Color Mix Down + * 87 Color Wheel Red + * 34 Color Wheel Blue + * 78 Color Wheel Yellow + * 5D Color Wheel Green + * + * Sample: + * 20;30;DEBUG;Pulses=180;Pulses(uSec)=450,420,420,420,420,420,1410,960,420,420,420,420,420,420,420,420,420,420,930,420,420,960,420,420,420,420,420,420,420,420,420,420,930,960,420,420,420,420,930,420,420,420,420,420,420,960,420,420,420,420,420,420,420,420,420,420,420,420,420,420,420,420,420,420,930,390,1440,960,420,420,420,420,420,420,420,420,420,420,930,420,420,960,420,420,420,420,420,420,420,420,420,420,930,960,420,420,420,420,930,420,420,420,420,420,420,960,420,420,420,420,420,420,420,420,420,420,420,420,420,420,420,420,420,420,930,390,1440,960,420,420,420,420,420,420,420,420,420,420,930,420,420,960,420,420,420,420,420,420,420,420,420,420,930,960,420,420,420,420,930,420,420,420,420,420,420,960,420,420,420,420,420,420,420,420,420,420,420,420,420,420,420,420,420,420,930,6990 + * TRC02:00000011 00000010 00111100 00000000 1 + * 20;01;TRC02;ID=03023c;SWITCH=00;CMD=ON; + \*********************************************************************************************/ +#define RGB_MIN_PULSECOUNT 180 +#define RGB_MAX_PULSECOUNT 186 + +#define RGB_PULSE_STHI 1600/RAWSIGNAL_SAMPLE_RATE +#define RGB_PULSE_STLO 1300/RAWSIGNAL_SAMPLE_RATE +#define RGB_PULSE_HIHI 1100/RAWSIGNAL_SAMPLE_RATE +#define RGB_PULSE_HILO 900/RAWSIGNAL_SAMPLE_RATE +#define RGB_PULSE_LOHI 600/RAWSIGNAL_SAMPLE_RATE +#define RGB_PULSE_LOLO 400/RAWSIGNAL_SAMPLE_RATE + +#ifdef PLUGIN_010 +boolean Plugin_010(byte function, char *string) { + if (RawSignal.Number < RGB_MIN_PULSECOUNT || RawSignal.Number > RGB_MAX_PULSECOUNT) return false; + unsigned long bitstream=0L; // holds first 32 bits + + byte checksum=0; // holds the checksum calculation + byte crc=0; // holds the crc bit from the signal + byte bitcounter=0; // counts number of received bits (converted from pulses) + byte halfbit=0; // high pulse = 1, 2 low pulses = 0, halfbit keeps track of low pulses + byte bitmask = 0; + int command=0; + byte start_stop=0; + byte x=1; + //================================================================================== + for(x=1;x 1200 && RawSignal.Pulses[x]*RawSignal.Multiply < 1500) { + if (RawSignal.Pulses[x] > RGB_PULSE_STLO && RawSignal.Pulses[x] < RGB_PULSE_STHI) { + start_stop=0x01; + continue; + } else { + if (x > 100) return false; // bad packet + continue; + } + } + if (RawSignal.Pulses[x]*RawSignal.Multiply > 750 && RawSignal.Pulses[x]*RawSignal.Multiply < 1000) { + if (halfbit==1) { // cant receive a 1 bit after a single low value + return false; // pulse error, must not be a UPM packet or reception error + } + bitmask = !bitmask; + if (bitcounter < 32) { + if (bitmask == 1){ + bitstream = (bitstream << 1); + } else { + bitstream = (bitstream << 1) | 0x1; + } + bitcounter++; // only need to count the first 10 bits + } else { + crc =1; + break; + } + halfbit=0; // wait for next first low or high pulse + } else { + if (RawSignal.Pulses[x]*RawSignal.Multiply > 625 && RawSignal.Pulses[x]*RawSignal.Multiply < 250) return false; // Not a valid UPM pulse length + if (halfbit == 0) { // 2 times a low value = 0 bit + halfbit=1; // first half received + } else { + if (bitcounter < 32) { + if (bitmask == 1){ + bitstream = (bitstream << 1); + } else { + bitstream = (bitstream << 1) | 0x1; + } + checksum=checksum^1; + bitcounter++; // only need to count the first 10 bits + } else { + crc=0; + break; + } + halfbit=0; // wait for next first low or high pulse + } + } + } + //================================================================================== + // Validity checks + if (RawSignal.Pulses[x+2]*RawSignal.Multiply < 1200 || RawSignal.Pulses[x+2]*RawSignal.Multiply > 1500) return false; + //================================================================================== + // perform a checksum check to make sure the packet is a valid RGB control packet + // Checksum: xor all odd and all even bits should match the last bit + // ---------------------------------- + if (checksum != crc) { + //Serial.println("crc2 error"); + return false; + } + //================================================================================== + // now process the command + //================================================================================== + command = (bitstream) &0xff; // command + int id3 = (bitstream >> 8) &0xff; + bitstream = (bitstream >> 16) &0xffff; // rolling code + //================================================================================== + // Output + // ---------------------------------- + sprintf(pbuffer, "20;%02X;", PKSequenceNumber++); // Node and packet number + Serial.print( pbuffer ); + Serial.print(F("TRC02RGB;")); // Label + sprintf(pbuffer, "ID=%04x", bitstream); // ID + Serial.print( pbuffer ); + sprintf(pbuffer, "%02x;", id3); + Serial.print( pbuffer ); + sprintf(pbuffer, "SWITCH=%02x;", command); + Serial.print( pbuffer ); + Serial.print(F("CMD=")); + if (command==0x00) Serial.print("ON;"); + else if (command==0x01) Serial.print(F("OFF;")); + else if (command==0x02) Serial.print(F("DIM DOWN;")); + else if (command==0x03) Serial.print(F("DIM UP;")); + else if (command==0x05) Serial.print(F("COLORMIX UP;")); + else if (command==0x04) Serial.print(F("COLORMIX DOWN;")); + else if (command==0x87) Serial.print(F("COLOR RED;")); + else if (command==0x34) Serial.print(F("COLOR BLUE;")); + else if (command==0x78) Serial.print(F("COLOR YELLOW;")); + else if (command==0x5D) Serial.print(F("COLOR GREEN;")); + else { + sprintf(pbuffer, "SET_LEVEL=%d;", command ); + Serial.print( pbuffer ); + } + Serial.println(); + //================================================================================== + RawSignal.Repeats=true; // suppress repeats of the same RF packet + RawSignal.Number=0; + return true; +} +#endif // PLUGIN_010 + +#ifdef PLUGIN_TX_010 +void TRC02_Send(unsigned long address, int command); + +boolean PluginTX_010(byte function, char *string) { + //10;TRC02RGB;03023c;00; + //012345678901234567890123456 + boolean success=false; + if (strncasecmp(InputBuffer_Serial+3,"TRC02RGB;",9) == 0) { // KAKU Command eg. + TRC02_Send(strtoul(InputBuffer_Serial+12,NULL,16),strtoul(InputBuffer_Serial+19,NULL,16)); + success=true; + } + return success; +} + +void TRC02_Send(unsigned long address, int command) { + int fpulse = 500; + int fretrans = 2; // Number of code retransmissions + byte crc = 0; + uint32_t fdatabit; + uint32_t fdatamask = 0x80000000; + uint32_t fsendbuff; + + digitalWrite(PIN_RF_RX_VCC,LOW); // Turn off power to the RF receiver + digitalWrite(PIN_RF_TX_VCC,HIGH); // Enable the 433Mhz transmitter + delayMicroseconds(TRANSMITTER_STABLE_DELAY); // short delay to let the transmitter become stable (Note: Aurel RTX MID needs 500µS/0,5ms) + + for (int nRepeat = 0; nRepeat <= fretrans; nRepeat++) { + crc=0; + fsendbuff=address; + fsendbuff=(fsendbuff<<8)+command; + digitalWrite(PIN_RF_TX_DATA, HIGH); // start pulse + delayMicroseconds(fpulse * 3); + digitalWrite(PIN_RF_TX_DATA, LOW); + delayMicroseconds(fpulse); + for (int i = 0; i < 32; i++) { // TRC02 packet is 32 bits + 1 bit crc + // read data bit + fdatabit = fsendbuff & fdatamask; // Get most left bit + fsendbuff = (fsendbuff << 1); // Shift left + if (fdatabit != fdatamask) { // Write 0 + digitalWrite(PIN_RF_TX_DATA, LOW); + delayMicroseconds(fpulse); + digitalWrite(PIN_RF_TX_DATA, HIGH); + delayMicroseconds(fpulse); + crc += crc^0; + } else { // Write 1 + digitalWrite(PIN_RF_TX_DATA, HIGH); + delayMicroseconds(fpulse); + digitalWrite(PIN_RF_TX_DATA, LOW); + delayMicroseconds(fpulse); + crc += crc^1; + } + } + if (crc==1) { // crc pulse + delayMicroseconds(fpulse); + digitalWrite(PIN_RF_TX_DATA, LOW); + delayMicroseconds(fpulse); + digitalWrite(PIN_RF_TX_DATA, HIGH); + delayMicroseconds(fpulse); + } else { + delayMicroseconds(fpulse); + digitalWrite(PIN_RF_TX_DATA, HIGH); + delayMicroseconds(fpulse); + digitalWrite(PIN_RF_TX_DATA, LOW); + delayMicroseconds(fpulse); + } + } + delayMicroseconds(TRANSMITTER_STABLE_DELAY); // short delay to let the transmitter become stable (Note: Aurel RTX MID needs 500µS/0,5ms) + digitalWrite(PIN_RF_TX_VCC,LOW); // Turn thew 433Mhz transmitter off + digitalWrite(PIN_RF_RX_VCC,HIGH); // Turn the 433Mhz receiver on + RFLinkHW(); +} +#endif //PLUGIN_TX_010 diff --git a/Plugins/Plugin_011.c b/Plugins/Plugin_011.c new file mode 100644 index 0000000..a95c2d6 --- /dev/null +++ b/Plugins/Plugin_011.c @@ -0,0 +1,444 @@ +//####################################################################################################### +//## This Plugin is only for use with the RFLink software package ## +//## Plugin-11 Home Confort Smart Home - TEL-010 ## +//####################################################################################################### +/*********************************************************************************************\ + * Decodes signals from a Home Confort Smart Home - TEL-010 + * http://idk.home-confort.net/divers/t%C3%A9l%C3%A9commande-rf-pour-produits-smart-home + * + * Author : StuntTeam, François Pasteau + * Support : http://sourceforge.net/projects/rflink/ + * License : This code is free for use in any open source project when this header is included. + * Usage of any parts of this code in a commercial application is prohibited! + ********************************************************************************************* + * Changelog: v1.0 initial release + ********************************************************************************************* + * Technical information: + * Decodes signals from Home Confort Smart Home - TEL-010 devices + * -------------------------------------------------------------------------------------------- + * _Byte 0_ _Byte 1_ _Byte 2_ _Byte 3_ _Byte 4_ _Byte 5_ _Bits_ + * AAAAAAAA BBBBBBBB CCCDDDDD EEEEEEEE FFFFFFFF GGHHHHHH II + * 11111111 11111111 11100010 00000000 00000000 10000001 00 Type 1 (no address, using A1-D4) + * 00110110 10100100 01111010 00000000 00000000 10000001 00 Type 2 (using 19 bit? address and button number 1-? + * + * A = Address? (19 bits?) + * B = Address? (19 bits?) + * C = Address? (19 bits?) + * D = Command bits (see below) + * E = verification bytes?, Always 0x00? => possibly reserved for future use or other device types + * F = verification bytes?, Always 0x00? => possibly reserved for future use or other device types + * G = Command bits (see below) + * H = always '000001' ? + * I = Stop bits?, Always '00' + * + * D & G explanation: + * DDDDD GG + * 11233 45 + * 00000 00 + 11010 10 D3 + * 1=switch setting 00=A 10=B 01=C 11=D + * 2=group indicator 1=group command + * 3=button number 00=1 01=2 10=3 11=4 + * 4=command 0=off, 1=on + * 5=group indicator 1=group command + * ------------ + * 101010101010101010101010101010101010100101011001010101010101010101010101010101011001010101010110 00 + * 111111111111111111100010000000000000000010000001 00 A3 on + * 20;17;DEBUG;Pulses=100;Pulses(uSec)= + * 2675,200,600,200,600,700,100,700,100,200,600,700,100,700,100,200,600,700,100,225,600,725,75,225,600,225,575,725,75,225, + * 575,225,575,225,575,725,75,725,75,725,75,725,75,225,575,725,75,225,575,225,575,225,575,225,575,225,575,250,575,250, + * 575,250,575,250,575,250,550,250,550,250,550,250,550,250,550,250,550,250,550,250,575,725,75,250,550,250,550,250,550,250, + * 550,250,550,250,550,750,50,250,50; =99 + * + * 2675,200,600,200,600,700,100,700,100,200,600,700,100,700,100,200,600,700,100,225,600,725,75,225,600,225,575,725,75,225,575,225,575,225,575,725,75,725,75,725,75,725,75,225,575,725,75,225,575,225,575,225,575,225,575,225,575,250,575,250,575,250,575,250,575,250,550,250,550,250,550,250,550,250,550,250,550,250,550,250,575,725,75,250,550,250,550,250,550,250,550,250,550,250,550,750,50,250,50 + \*********************************************************************************************/ +#define HC_PULSECOUNT 100 + +#ifdef PLUGIN_011 +boolean Plugin_011(byte function, char *string) { + if (RawSignal.Number != HC_PULSECOUNT ) return false; + if (RawSignal.Pulses[1]*RawSignal.Multiply < 2000) return false; // First (start) pulse needs to be long + + unsigned long bitstream1=0; // holds first 24 bits + unsigned long bitstream2=0; // holds last 26 bits + byte bitcounter=0; // counts number of received bits (converted from pulses) + byte command=0; + byte channel=0; + byte subchan=0; + byte group=0; + //================================================================================== + for(int x=2;x < HC_PULSECOUNT-2;x+=2) { // get bytes + if (RawSignal.Pulses[x]*RawSignal.Multiply > 500) { // long pulse + if (RawSignal.Pulses[x]*RawSignal.Multiply > 800) return false; // Pulse range check + if (RawSignal.Pulses[x+1]*RawSignal.Multiply > 400) return false; // Manchester check + if (bitcounter < 24) { + bitstream1 = (bitstream1 << 1) | 0x1; + } else { + bitstream2 = (bitstream2 << 1) | 0x1; + } + bitcounter++; // only need to count the first 10 bits + } else { // short pulse + if (RawSignal.Pulses[x]*RawSignal.Multiply > 300) return false; // pulse range check + if (RawSignal.Pulses[x+1]*RawSignal.Multiply < 400) return false; // Manchester check + if (bitcounter < 24) { + bitstream1 = (bitstream1 << 1); + } else { + bitstream2 = (bitstream2 << 1); + } + bitcounter++; // only need to count the first 10 bits + } + if (bitcounter > 50) break; + } + if (RawSignal.Pulses[98]*RawSignal.Multiply > 300) return false; // pulse range check, last two pulses should be short + if (RawSignal.Pulses[99]*RawSignal.Multiply > 300) return false; // pulse range check + //================================================================================== + // first perform a check to make sure the packet is valid + byte tempbyte=(bitstream2 >>8) & 0xff; + if (tempbyte != 0x0) return false; // always 0x00? + tempbyte=(bitstream2 >>16) & 0xff; + if (tempbyte != 0x0) return false; // always 0x00? + tempbyte=(bitstream2) & 0x3f; + if (tempbyte != 0x01) return false; // low 6 bits are always '000001'? + //================================================================================== + // Prevent repeating signals from showing up + //================================================================================== + if(SignalHash!=SignalHashPrevious || ((RepeatingTimer+1500)> 3) & 0x03; // determine switch setting (a/b/c/d) + channel=0x41; + if (tempbyte==2) channel=0x42; + if (tempbyte==1) channel=0x43; + if (tempbyte==3) channel=0x44; + + subchan=((bitstream1)&0x03)+1; // determine button number + + command=(bitstream2 >> 7) & 0x01; // on/off command + group=(bitstream2 >> 6) & 0x01; // group setting + + bitstream1=bitstream1 >> 5; + //================================================================================== + // Output + // ---------------------------------- + sprintf(pbuffer, "20;%02X;", PKSequenceNumber++); // Node and packet number + Serial.print( pbuffer ); + Serial.print(F("HomeConfort;")); // Label + sprintf(pbuffer, "ID=%06lx;",((bitstream1) &0xffffff) ); // ID + Serial.print( pbuffer ); + sprintf(pbuffer, "SWITCH=%c%d;", channel,subchan); + Serial.print( pbuffer ); + Serial.print(F("CMD=")); + if (group==1) Serial.print(F("ALL")); + if (command==0) Serial.print(F("OFF;")); + if (command==1) Serial.print(F("ON;")); + Serial.println(); + //================================================================================== + RawSignal.Repeats=true; // suppress repeats of the same RF packet + RawSignal.Number=0; + return true; +} +#endif // PLUGIN_011 + +#ifdef PLUGIN_TX_011 +void HomeConfort_Send(unsigned long bitstream1, unsigned long bitstream2); + +boolean PluginTX_011(byte function, char *string) { + boolean success=false; + //10;HomeConfort;01b523;D3;ON; + //0123456789012345678901234567 + if (strncasecmp(InputBuffer_Serial+3,"HomeConfort;",12) == 0) { // KAKU Command eg. + if (InputBuffer_Serial[21] != ';') return success; + + InputBuffer_Serial[13]=0x30; + InputBuffer_Serial[14]=0x78; // Get address from hexadecimal value + InputBuffer_Serial[21]=0x00; // Get address from hexadecimal value + + unsigned long bitstream1=0L; // First Main placeholder + unsigned long bitstream2=0L; // Second Main placeholder + byte Home=0; // channel A..D + byte Address=0; // subchannel 1..5 + byte c; + byte x=22; // pointer + // ------------------------------- + bitstream1=str2int(InputBuffer_Serial+13); // Address (first 19 bits) + // ------------------------------- + while((c=tolower(InputBuffer_Serial[x++]))!=';') { + if(c>='0' && c<='9'){Address=Address+c-'0';} // Home 0..9 + if(c>='a' && c<='d'){Home=c-'a';} // Address a..d + } + // ------------------------------- + // prepare bitstream1 + // ------------------------------- + bitstream1 = bitstream1 << 5; // make space for first 5 command bits + Address--; // 1..4 to 0..3 + Address=Address & 0x03; // only accept 2 bits for the button number part + bitstream1=bitstream1+Address; + + if (Home == 0) c=0; // A + if (Home == 1) c=2; // B + if (Home == 2) c=1; // C + if (Home == 3) c=3; // D + Home=c << 3; // shift left for the right position + bitstream1=bitstream1+Home; + // ------------------------------- + // prepare bitsrteam2 + c=0; + c = str2cmd(InputBuffer_Serial+x); // ON/OFF command + bitstream2=1; // value off + if (c == VALUE_ON) { + bitstream2=0x81; // value on + } else { + if (c == VALUE_ALLOFF) { + bitstream2=0x41; + bitstream1=bitstream1+4; // set group + } else + if (c == VALUE_ALLON) { + bitstream2=0xc1; + bitstream1=bitstream1+4; // set group + } + } + // ------------------------------- + HomeConfort_Send(bitstream1,bitstream2); // bitstream to send + success=true; + } + return success; +} +/* +void HomeConfort_Send(unsigned long data1, unsigned long data2) { + int fpulse = 270; // Pulse width in microseconds + int fpulse2 = 710; // Pulse width in microseconds + int fretrans = 10; // Number of code retransmissions + + unsigned long bitstream1 = 0L; + unsigned long bitstream2 = 0L; + // prepare data to send + for (unsigned short i=0; i<24; i++) { // reverse data bits + bitstream1<<=1; + bitstream1|=(data1 & B1); + data1>>=1; + } + for (unsigned short i=0; i<24; i++) { // reverse data bits + bitstream2<<=1; + bitstream2|=(data2 & B1); + data2>>=1; + } + // ------------------------------- + // bitstream1 holds first 24 bits of the RF data, bitstream2 holds last 24 bits of the RF data + // ------------------------------- + // Prepare transmit + digitalWrite(PIN_RF_RX_VCC,LOW); // Turn off power to the RF receiver + digitalWrite(PIN_RF_TX_VCC,HIGH); // Enable the 433Mhz transmitter + delayMicroseconds(TRANSMITTER_STABLE_DELAY); // short delay to let the transmitter become stable (Note: Aurel RTX MID needs 500µS/0,5ms) + // send bits + for (int nRepeat = 0; nRepeat <= fretrans; nRepeat++) { + data1=bitstream1; + data2=bitstream2; + digitalWrite(PIN_RF_TX_DATA, HIGH); + delayMicroseconds(fpulse*10 + (fpulse >> 1)); //335*9=3015 //270*10=2700 + for (unsigned short i=0; i<24; i++) { + switch (data1 & B1) { + case 0: + digitalWrite(PIN_RF_TX_DATA, LOW); + delayMicroseconds(fpulse * 1); + digitalWrite(PIN_RF_TX_DATA, HIGH); + delayMicroseconds(fpulse2 * 1); + break; + case 1: + digitalWrite(PIN_RF_TX_DATA, LOW); + delayMicroseconds(fpulse2 * 1); + digitalWrite(PIN_RF_TX_DATA, HIGH); + delayMicroseconds(fpulse * 1); + break; + } + //Next bit + data1>>=1; + } + for (unsigned short i=0; i<24; i++) { + switch (data2 & B1) { + case 0: + digitalWrite(PIN_RF_TX_DATA, LOW); + delayMicroseconds(fpulse * 1); + digitalWrite(PIN_RF_TX_DATA, HIGH); + delayMicroseconds(fpulse2 * 1); // 335*3=1005 260*5=1300 260*4=1040 + break; + case 1: + digitalWrite(PIN_RF_TX_DATA, LOW); + delayMicroseconds(fpulse2 * 1); + digitalWrite(PIN_RF_TX_DATA, HIGH); + delayMicroseconds(fpulse * 1); + break; + } + //Next bit + data2>>=1; + } + //Send termination/synchronisation-signal. + //digitalWrite(PIN_RF_TX_DATA, HIGH); + //delayMicroseconds(fpulse); + digitalWrite(PIN_RF_TX_DATA, LOW); + delayMicroseconds(fpulse * 27); + + //RawSignal.Pulses[98]=300/RawSignal.Multiply; + //RawSignal.Pulses[99]=175/RawSignal.Multiply; + //RawSignal.Delay=125; // Delay between RF packets + + } + // End transmit + delayMicroseconds(TRANSMITTER_STABLE_DELAY); // short delay to let the transmitter become stable (Note: Aurel RTX MID needs 500µS/0,5ms) + digitalWrite(PIN_RF_TX_VCC,LOW); // Turn thew 433Mhz transmitter off + digitalWrite(PIN_RF_RX_VCC,HIGH); // Turn the 433Mhz receiver on + RFLinkHW(); +} + +void HomeConfort_Send(unsigned long data1, unsigned long data2) { + int fpulse = 270; // Pulse width in microseconds + int fpulse2 = 710; // Pulse width in microseconds + int fretrans = 10; // Number of code retransmissions + + unsigned long bitstream1 = 0L; + unsigned long bitstream2 = 0L; + // prepare data to send + for (unsigned short i=0; i<24; i++) { // reverse data bits + bitstream1<<=1; + bitstream1|=(data1 & B1); + data1>>=1; + } + for (unsigned short i=0; i<24; i++) { // reverse data bits + bitstream2<<=1; + bitstream2|=(data2 & B1); + data2>>=1; + } + // ------------------------------- + // bitstream1 holds first 24 bits of the RF data, bitstream2 holds last 24 bits of the RF data + // ------------------------------- + // Prepare transmit + digitalWrite(PIN_RF_RX_VCC,LOW); // Turn off power to the RF receiver + digitalWrite(PIN_RF_TX_VCC,HIGH); // Enable the 433Mhz transmitter + delayMicroseconds(TRANSMITTER_STABLE_DELAY); // short delay to let the transmitter become stable (Note: Aurel RTX MID needs 500µS/0,5ms) + // send bits + for (int nRepeat = 0; nRepeat <= fretrans; nRepeat++) { + data1=bitstream1; + data2=bitstream2; + digitalWrite(PIN_RF_TX_DATA, HIGH); + //delayMicroseconds(fpulse); //335 + delayMicroseconds(335); + digitalWrite(PIN_RF_TX_DATA, LOW); + delayMicroseconds(fpulse*10 + (fpulse >> 1)); //335*9=3015 //270*10=2700 + for (unsigned short i=0; i<24; i++) { + switch (data1 & B1) { + case 0: + digitalWrite(PIN_RF_TX_DATA, HIGH); + delayMicroseconds(fpulse * 1); + digitalWrite(PIN_RF_TX_DATA, LOW); + delayMicroseconds(fpulse2 * 1); + break; + case 1: + digitalWrite(PIN_RF_TX_DATA, HIGH); + delayMicroseconds(fpulse2 * 1); + digitalWrite(PIN_RF_TX_DATA, LOW); + delayMicroseconds(fpulse * 1); + break; + } + //Next bit + data1>>=1; + } + for (unsigned short i=0; i<24; i++) { + switch (data2 & B1) { + case 0: + digitalWrite(PIN_RF_TX_DATA, HIGH); + delayMicroseconds(fpulse * 1); + digitalWrite(PIN_RF_TX_DATA, LOW); + delayMicroseconds(fpulse2 * 1); // 335*3=1005 260*5=1300 260*4=1040 + break; + case 1: + digitalWrite(PIN_RF_TX_DATA, HIGH); + delayMicroseconds(fpulse2 * 1); + digitalWrite(PIN_RF_TX_DATA, LOW); + delayMicroseconds(fpulse * 1); + break; + } + //Next bit + data2>>=1; + } + //Send termination/synchronisation-signal. + digitalWrite(PIN_RF_TX_DATA, HIGH); + delayMicroseconds(fpulse); + digitalWrite(PIN_RF_TX_DATA, LOW); + delayMicroseconds(fpulse * 27); + + //RawSignal.Pulses[98]=300/RawSignal.Multiply; + //RawSignal.Pulses[99]=175/RawSignal.Multiply; + //RawSignal.Delay=125; // Delay between RF packets + + } + // End transmit + delayMicroseconds(TRANSMITTER_STABLE_DELAY); // short delay to let the transmitter become stable (Note: Aurel RTX MID needs 500µS/0,5ms) + digitalWrite(PIN_RF_TX_VCC,LOW); // Turn thew 433Mhz transmitter off + digitalWrite(PIN_RF_RX_VCC,HIGH); // Turn the 433Mhz receiver on + RFLinkHW(); +} +*/ +/* +20;E4;DEBUG;Pulses=511;Pulses(uSec)= +330,2760,180,600,180,600,630,180,630,180,180,600,630,180,630,150,210,600,630,150,210,600,630,180,180,600,180,600,630,180,180,600,180,600,180,600,630,180,630,180,630,180,630,180,180,600,630,180,180,600,180,600,180,600,180,600,180,600,180,600,180,600,180,600,180,600,180,600,180,600,180,600,180,600,180,600,180,600,180,600,180,600,630,180,180,600,180,600,180,600,180,600,210,600,210,600,630,150,210,180, +270,2760,180,600,180,600,630,180,630,180,180,600,630,180,630,180,180,600,630,180,180,600,630,180,180,600,180,600,630,180,180,600,180,600,180,600,630,180,630,180,630,180,630,180,180,600,630,150,210,600,210,600,210,600,210,600,180,600,180,600,180,600,180,600,180,600,180,600,180,630,180,630,180,630,180,630,180,630,180,600,180,600,630,180,180,600,180,600,180,600,180,600,180,600,180,600,630,180,180,150, +270,2760,210,600,210,600,630,150,630,150,210,600,630,180,630,180,180,600,630,180,180,600,630,180,180,630,180,630,630,180,180,630,180,630,180,630,630,180,630,180,630,180,630,180,180,600,630,180,180,600,180,600,180,600,180,600,180,600,180,600,180,600,180,600,180,600,180,600,180,600,180,600,180,600,180,600,180,600,180,600,210,600,630,150,210,600,210,600,210,600,210,600,180,600,180,600,630,180,180,180,270,2760,180,600,180,600,630,180,630,180,180,600,630,180,630,180,180,600,630,180,180,600,630,180,180,600,180,600,630,180,180,600,180,600,210,600,630,150,630,150,630,150,630,150,210,600,630,180,180,600,180,600,180,600,180,600,180,630,180,630,180,630,180,630,180,630,180,630,180,630,180,600,180,600,180,600,180,600,180,600,180,600,630,180,180,600,180,600,180,600,180,600,180,600,180,600,630,180,180,180,270,2760,210,600,180,600,630,180,630,180,180,630,630,180,630,180,180,630,630,180,180,630,630,180,180,630,180,600,630,180,180,600,180,600,180,600,630,180,630,180,630,180,630,180,180,600,630,180,180,600,180,600,180,600,180,600,180,600,180,600,180,600,180,600,180,600,210,600,210,600,210,600,210,600,210,600,210,600,210,600,210,600,630,180,180,600,180,630,180,630,180,630,180,630,180,630,630,180,180,180,270,2760,180,600,180,600,630,180,630,180,180; + + +20;CA;DEBUG;Pulses=100;Pulses(uSec)= + 2490,180,630,150,630,600,180,600,180,150,630,600,180,600,180,150,630,630,180,150,630,630,180,150,630,150,630,630,180,150,630,150,630,150,630,630,180,600,180,600,180,600,180,150,630,600,180,150,630,150,630,150,630,150,630,150,630,150,630,150,630,150,630,150,630,150,630,150,630,150,630,150,630,150,630,150,630,150,630,150,630,150,630,150,630,150,630,150,630,150,630,150,630,150,630,630,180,180,60,6990; +*/ + +#define PLUGIN_011_RFLOW 270 // 300 +#define PLUGIN_011_RFHIGH 720 // 800 + +void HomeConfort_Send(unsigned long bitstream1, unsigned long bitstream2) { + RawSignal.Repeats=8; // Number of RF packet retransmits + RawSignal.Delay=125; // Delay between RF packets + RawSignal.Number=100; // Length + + uint32_t fdatabit; + uint32_t fdatamask = 0x800000; + // ------------------------------- + // bitstream1 holds first 24 bits of the RF data, bitstream2 holds last 24 bits of the RF data + // ------------------------------- + RawSignal.Pulses[1]=2600/RawSignal.Multiply; + + for (byte i=2; i<103; i=i+2) { + if (i<50) { // first 24 bits + fdatabit = bitstream1 & fdatamask; // Get most left bit + bitstream1 = (bitstream1 << 1); // Shift left + + if (fdatabit != fdatamask) { // Write 0 + RawSignal.Pulses[i] = PLUGIN_011_RFLOW/RawSignal.Multiply; + RawSignal.Pulses[i+1]= PLUGIN_011_RFHIGH/RawSignal.Multiply; + } else { // Write 1 + RawSignal.Pulses[i] = PLUGIN_011_RFHIGH/RawSignal.Multiply; + RawSignal.Pulses[i+1]= PLUGIN_011_RFLOW/RawSignal.Multiply; + } + } else { + fdatabit = bitstream2 & fdatamask; // Get most left bit + bitstream2 = (bitstream2 << 1); // Shift left + + if (fdatabit != fdatamask) { // Write 0 + RawSignal.Pulses[i] = PLUGIN_011_RFLOW/RawSignal.Multiply; + RawSignal.Pulses[i+1]= PLUGIN_011_RFHIGH/RawSignal.Multiply; + } else { // Write 1 + RawSignal.Pulses[i] = PLUGIN_011_RFHIGH/RawSignal.Multiply; + RawSignal.Pulses[i+1]= PLUGIN_011_RFLOW/RawSignal.Multiply; + } + } + } + RawSignal.Pulses[98]=300/RawSignal.Multiply; + RawSignal.Pulses[99]=175/RawSignal.Multiply; + + RawSendRF(); +} +#endif // PLUGIN_TX_011 + diff --git a/Plugins/Plugin_012.c b/Plugins/Plugin_012.c new file mode 100644 index 0000000..5d06892 --- /dev/null +++ b/Plugins/Plugin_012.c @@ -0,0 +1,309 @@ +//####################################################################################################### +//## This Plugin is only for use with the RFLink software package ## +//## Plugin-03: FA500R ## +//####################################################################################################### +/*********************************************************************************************\ + * This plugin takes care of sending and receiving the Elro Flamingo FA500 protocol. + * Also works with compatible devices like the Mumbi M-FS300 and Silvercrest 91210/60494 RCS AAA3680, Unitec eim 821/art.48111 + * + * Author : StuntTeam + * Support : http://sourceforge.net/projects/rflink/ + * License : This code is free for use in any open source project when this header is included. + * Usage of any parts of this code in a commercial application is prohibited! + *********************************************************************************************** + * Incoming event: "FA500 , + * Send : "FA500Send , + * + * Address = A/B/C/D matching the remote control buttons. + *********************************************************************************************** + * Technical information: + * The FA500R remote control sends 3 different protocols. + * 4 x Method 1 - 28 bit code + * 6 x method 2 - AC compatible + * 5 x method 3 - 24/12 bit code + * It appears that the FA500S is only capable to react to the first method. So this has to be used to switch sockets + * Nodo can only distinguish the 3rd method properly. Method 1 and 2 have to be pulled apart first which + * is done via plugin 001. + * + * Device types: + * Elro FA500S Flamingo Switch + * Elro FA500DSS Flamingo Dimmer + * Elro FA500WD Flamingo Outdoor + * Elro FA500R Flamingo Remote Control + * + * PCB Markings: + * 50027.01B FLF-130105 + * KT50039.01A FLF-13-06-03 Chip marking: S007V0.1 + * KT50040.01A FLF-13-06-04 + * + * http://forum.arduino.cc/index.php?topic=202556.0 + * + * Sample: + * 20;60;DEBUG;Pulses=24;Pulses(uSec)=325,800,275,800,825,225,275,800,275,825,275,800,825,225,275,800,825,225,275,800,275,800,275; + * 20;61;DEBUG;Pulses=58;Pulses(uSec)=200,875,800,250,800,225,200,875,200,875,800,250,200,875,200,875,800,250,200,875,200,875,200,875,200,875,825,250,200,875,200,875,200,875,825,250,200,875,825,250,200,875,200,875,200,875,825,225,825,250,200,875,825,250,200,875,150; + + * 20;3E;DEBUG;Pulses=58;Pulses(uSec)=300,950,225,950,875,275,225,950,225,950,875,275,225,950,225,950,875,275,875,275,225,950,875,275,225,950,225,950,875,275,875,275,225,950,225,950,225,950,875,275,875,275,200,950,225,950,875,275,875,275,225,950,875,275,225,950,225; + * 20;3F;DEBUG;Pulses=64;Pulses(uSec)=525,250,200,900,200,4900,200,900,200,900,875,275,225,950,225,950,875,275,225,950,225,950,875,275,875,275,225,950,900,250,225,950,225,950,875,250,875,275,225,950,225,950,225,950,900,275,875,275,225,950,225,950,875,275,875,275,225,950,875,250,225,950,225; + * 20;40;DEBUG;Pulses=130;Pulses(uSec)=225,175,150,1250,150,200,150,1250,150,200,150,1250,150,200,150,1250,150,200,150,1275,150,200,150,1300,150,200,150,1300,150,200,150,1300,150,200,150,1300,150,1275,150,225,150,200,150,1300,150,200,150,1300,150,200,150,1300,150,1275,150,225,150,1275,150,225,150,200,150,1300,150,1275,150,225,150,1275,150,225,150,200,150,1300,150,200,150,1300,150,200,150,1300,150,200,150,1300,150,200,150,1300,150,200,150,1300,150,200,150,1300,150,200,150,1300,150,200,150,1300,150,200,150,1300,150,200,150,1300,150,200,150,1300,150,200,150,1300,150,1275,150,200,150; + * 20;41;DEBUG;Pulses=126;Pulses(uSec)=225,200,125,1250,150,200,150,1250,150,175,150,1250,150,175,150,1300,150,200,150,1300,150,200,150,1300,150,200,150,1300,150,200,150,1300,150,1300,150,225,150,200,150,1300,150,200,150,1300,150,200,150,1300,150,1275,150,200,150,1275,150,225,150,200,150,1300,150,1275,150,225,150,1275,150,225,150,200,150,1300,150,200,150,1300,150,200,150,1300,150,200,150,1300,150,200,150,1300,150,200,150,1300,150,200,150,1300,150,200,150,1300,150,200,150,1300,150,200,150,1300,150,200,150,1300,150,200,150,1300,150,200,150,1300,150,1275,150,200,150; + * 20;42;DEBUG;Pulses=116;Pulses(uSec)=175,1275,150,1225,150,200,150,200,150,200,150,1250,150,1250,150,1300,150,1275,150,200,150,200,150,200,150,1275,150,1300,150,1300,150,1300,150,225,150,1300,150,225,150,225,150,1300,150,1300,150,1300,150,200,150,1300,150,200,150,1300,150,1275,150,225,150,1275,150,225,150,200,150,225,150,1300,150,1300,150,225,150,1300,150,200,150,200,150,200,150,1275,150,1300,150,1300,150,1300,150,200,150,200,150,200,150,1300,150,1300,150,1300,150,1300,150,200,150,200,150,1275,150,225,150,1275,150,1300,150; + + 20;CC;DEBUG;Pulses=58;Pulses(uSec)=300,950,225,950,900,275,225,950,225,950,875,275,225,950,225,950,225,950,900,275,875,275,225,950,225,950,225,950,225,950,225,950,225,950,225,950,225,950,225,950,875,275,225,950,225,950,225,950,875,275,875,275,875,250,225,950,225; +20;CD;DEBUG;Pulses=64;Pulses(uSec)=525,250,200,900,200,4900,225,900,200,925,875,275,225,950,225,950,875,275,225,950,225,950,225,950,900,275,875,275,225,950,225,950,225,950,225,950,225,950,225,950,225,950,225,950,225,950,875,275,225,950,225,950,225,950,875,275,875,275,900,250,225,950,225; +20;CE;DEBUG;Pulses=130;Pulses(uSec)=225,200,150,1250,150,200,150,1250,150,200,150,1250,150,200,150,1250,150,200,150,1300,150,200,150,1300,150,200,150,1300,150,200,150,1300,150,200,150,1300,150,1275,150,200,150,200,150,1300,150,200,175,1300,150,200,150,1300,150,1275,150,200,150,1275,150,200,150,200,150,1300,150,1275,150,200,150,1275,150,200,150,200,150,1300,150,200,175,1300,150,200,150,1300,150,200,150,1300,150,200,150,1300,150,200,150,1300,150,200,150,1300,150,200,150,1300,150,200,150,1300,150,200,150,1300,150,200,150,1300,150,200,150,1300,150,200,150,1300,150,1275,150,200,150; +20;CF;DEBUG;Pulses=126;Pulses(uSec)=225,200,150,1250,150,200,150,1250,150,200,150,1250,150,200,150,1300,150,200,150,1300,150,200,150,1300,150,200,150,1300,150,200,150,1300,150,1275,150,200,150,200,150,1300,150,200,150,1300,150,200,150,1300,150,1275,150,200,150,1275,150,200,150,200,175,1300,150,1275,150,200,150,1300,150,200,150,200,150,1300,150,200,150,1300,150,200,150,1275,150,200,150,1300,150,200,150,1300,150,200,150,1300,150,200,150,1300,150,200,150,1300,150,200,150,1300,150,200,150,1300,150,200,150,1300,150,200,150,1300,150,200,150,1300,150,1275,150,200,150; +20;D0;DEBUG;Pulses=116;Pulses(uSec)=275,1250,150,1250,150,200,150,200,150,200,150,1250,150,1250,150,1300,150,1300,150,200,150,200,150,200,150,1300,150,1275,150,1300,150,1300,150,200,150,1300,150,200,150,200,150,1300,150,1300,150,1275,150,200,150,1275,150,225,150,1275,150,1300,150,225,150,1300,150,200,150,200,150,225,150,1300,150,1300,150,200,175,1275,175,200,150,200,150,200,150,1300,150,1275,150,1300,150,1275,175,200,150,200,150,200,150,1300,150,1300,150,1300,150,1275,150,200,150,200,175,1300,150,200,150,1300,150,1300,150; +20;D1;HomeEasy;ID=7a75a347;SWITCH=0b;CMD=ALLON; +20;D2;DEBUG;Pulses=50;Pulses(uSec)=3200,875,300,875,300,875,300,875,300,875,300,875,300,875,300,875,300,875,300,875,300,875,300,900,275,875,300,875,300,875,925,250,300,875,300,875,300,875,925,250,300,875,925,250,300,875,300,875,300; +20;D3;Kaku;ID=5f;SWITCH=20;CMD=OFF; +20;D4;DEBUG;Pulses=50;Pulses(uSec)=3150,875,300,875,300,875,300,875,300,875,300,875,300,875,300,875,300,875,300,875,300,875,300,875,300,875,300,875,300,875,925,250,300,875,300,875,300,875,900,250,300,875,925,250,300,875,300,875,300; +20;D5;Kaku;ID=5f;SWITCH=20;CMD=OFF; + + +20;CC;DEBUG;Pulses=58;Pulses(uSec)=300,950,225,950,900,275,225,950,225,950,875,275,225,950,225,950,225,950,900,275,875,275,225,950,225,950,225,950,225,950,225,950,225,950,225,950,225,950,225,950,875,275,225,950,225,950,225,950,875,275,875,275,875,250,225,950,225; +20;86;DEBUG;Pulses=58;Pulses(uSec)=250,4500,225,800,225,800,825,200,225,800,225,800,825,200,225,800,225,800,825,200,825,200,225,800,825,200,225,825,225,800,825,200,825,200,225,800,225,825,225,800,825,200,825,200,225,800,225,825,825,200,825,200,225,800,825,200,225; + 300,4500,225,800,225,825,825,200,225,800,225,800,825,200,825,200,225,800,825,200,225,800,825,200,825,200,825,200,225,800,825,200,825,200,825,200,225,825,225,800,825,200,825,200,225,800,225,825,225,800,225,800,825,200,825,200,225; +00010011010111011100110000110 + 0010010010100011111101011110 + + 20;D5;DEBUG;Pulses=364;Pulses(uSec)= + 250,4875,200,900,200,900,875,275,200,900,200,900,875,275,200,900,875,250,225,900,875,275,875,250,225,900,200,925,200,925,200,900,875,250,850,250,225,925,875,250,875,250,225,900,850,275,200,900,875,250,225,900,875,250,875,250,225,900, + 200,4875,200,900,225,900,850,250,200,900,225,900,850,275,200,900,875,250,225,900,875,275,850,250,200,900,200,900,200,925,200,900,875,250,875,250,200,925,850,250,875,275,200,900,875,275,200,900,875,250,225,900,875,250,850,250,200,900, + 200,4875,200,900,225,900,875,275,200,925,200,900,875,275,200,900,875,250,225,900,875,275,850,275,200,925,200,900,225,925,200,900,875,250,875,250,225,925,875,250,875,250,225,900,875,275,200,900,850,275,200,900,875,250,850,250,200,900, + 200,4875,200,900,225,925,850,250,225,900,200,900,850,275,225,900,850,275,200,900,875,275,875,250,200,900,225,900,200,925,200,900,875,250,875,250,225,925,850,250,875,250,225,900,875,250,225,900,875,250,200,900,875,250,875,250,200,925, + 150,2650,150,200,150,1250,150,200,150,1250,150,200,150,1250,150,200,150,1250,150,200,150,1250,150,200,150,1250,150,200,150,1225,150,200,150,1225,150,200,150,1250,150,1225,150,200,150,200,150,1250,150,200,150,1250,150,200,150,1250,150,1225,150,225,150,1225,150,200,150,200,150,1250,150,1225,150,200,150,1225,150,200,150,200,150,1250,150,200,150,1250,150,200,150,1250,150,200,150,1225,150,200,150,1250,150,200,150,1225,150,200,150,1225,150,200,150,1250,150,200,150,1250,150,1225,150,200,150,200,150,1250,150,200,150,1250,150,200,150,1250,150,1225,150,200,150; + + \*********************************************************************************************/ +#define FA500RM3_PulseLength 26 +#define FA500RM1_PulseLength 58 +#define FA500_PULSEMID 400/RAWSIGNAL_SAMPLE_RATE + +#ifdef PLUGIN_012 +boolean Plugin_012(byte function, char *string) { + if (RawSignal.Number!=(FA500RM3_PulseLength) && RawSignal.Number!=(FA500RM1_PulseLength)) return false; + + byte type=0; // 0=KAKU 1=ITK 2=PT2262 + byte housecode=0; + byte unitcode=0; + byte command=0; + unsigned long bitstream=0L; + unsigned long address=0L; + // ========================================================================== + if (RawSignal.Number==(FA500RM3_PulseLength) ) { + // get all 26pulses =>24 manchester bits => 12 actual bits + type=0; + for(byte x=2;x <=FA500RM3_PulseLength-2;x+=2) { // Method 3 + if(RawSignal.Pulses[x] > FA500_PULSEMID) { + bitstream = (bitstream << 1) | 0x1; + } else { + bitstream = (bitstream << 1); + } + } + } else { + // get all 58pulses =>28 bits + type=1; + for(byte x=1;x <=FA500RM1_PulseLength-2;x+=2) { // method 1 + if(RawSignal.Pulses[x] > FA500_PULSEMID) { + bitstream = (bitstream << 1) | 0x1; + } else { + bitstream = (bitstream << 1); + } + } + } + //================================================================================== + // perform sanity checks + if (bitstream==0) return false; // no bits detected? + if (type == 0) { + housecode=(((bitstream) >> 8) &0x0f); + unitcode=(( bitstream >> 1)& 0x7F); + if (unitcode != 0x0A) { // invalid housecode? + return false; + } + address=housecode; + command=(bitstream)&1; + } else { + // 001001 0010100011111101 0111 10 + // ^^^^^^ ^^ + address=bitstream; + address=address >> 22; + address=address << 2; + housecode=(bitstream)&3; + address=address+housecode; + // sort buttons based on first 6 bits and last 2 bits + if (address==0x26) { // A On/off + housecode=1; + } else + if (address==0x25) { // B On/off + housecode=4; + } else + if (address==0xe5) { // C On/off + housecode=5; + } else + if (address==0x66) { // D On/off + housecode=0; + } else { + return false; + } + command=2; // initialize to "unknown" + // Trick: here we use the on/off command from the other packet type as it is not detected in the current packet, it was passed via Pluses[0] in plugin 1 + if (RawSignal.Pulses[0]*RawSignal.Multiply > 1000 && RawSignal.Pulses[0]*RawSignal.Multiply < 1400) { + command=0; + } else + if (RawSignal.Pulses[0]*RawSignal.Multiply > 100 && RawSignal.Pulses[0]*RawSignal.Multiply < 400) { + command=1; + } + address=bitstream; + } + if (command > 1) { + //Serial.println("FA500R error3"); + return false; + } + if (housecode != 0x01 && housecode != 0x04 && housecode != 0x05 && housecode != 0x00) { // invalid button code? + //Serial.println("FA500R error4"); + return false; + } + //================================================================================== +// if (housecode == 1) housecode = 0x41; // A 0001 0001010 0/1 08 A 1 +// if (housecode == 4) housecode = 0x42; // B 0100 0001010 0/1 20 B 4 +// if (housecode == 5) housecode = 0x43; // C 0101 0001010 0/1 28 C 5 +// if (housecode == 0) housecode = 0x44; // D 0000 0001010 0/1 00 D 0 + // ---------------------------------- + // Output + // ---------------------------------- + sprintf(pbuffer, "20;%02X;", PKSequenceNumber++); // Node and packet number + Serial.print( pbuffer ); + // ---------------------------------- + Serial.print(F("FA500;")); // Label + if (type == 0) { + sprintf(pbuffer, "ID=%02x%02x;", unitcode, housecode); // ID + } else { + sprintf(pbuffer, "ID=%08lx;",(address) ); // ID + } + Serial.print( pbuffer ); + if (type == 0) { + sprintf(pbuffer, "SWITCH=%02x%02x;", unitcode, housecode); // ID + } else { + sprintf(pbuffer, "SWITCH=%02x;", housecode); // ID + } + Serial.print( pbuffer ); + Serial.print(F("CMD=")); + if ( command == 1) { + Serial.print(F("ON;")); + } else + if ( command == 0) { + Serial.print(F("OFF;")); + } else { + Serial.print(F("UNKOWN;")); + } + Serial.println(); + // ---------------------------------- + RawSignal.Repeats=true; // suppress repeats of the same RF packet + return true; +} +#endif //PLUGIN_012 + +#ifdef PLUGIN_TX_012 +void Flamingo_Send(int funitc, int fcmd); + +boolean PluginTX_012(byte function, char *string) { + boolean success=false; + //10;FA500;001b523;D3;ON; + //012345678901234567890123 + if (strncasecmp(InputBuffer_Serial+3,"FA500;",6) == 0) { // FA500 Command + if (InputBuffer_Serial[16] != ';') return false; + unsigned long bitstream=0L; + byte Home=0; + byte c; + + InputBuffer_Serial[7]=0x30; + InputBuffer_Serial[8]=0x78; + bitstream=str2int(InputBuffer_Serial+7); // get address + + c=tolower(InputBuffer_Serial[17]); // 1..5 + if(c>='1' && c<='5'){Home=Home+c-'0';} + + c=0; + c |= str2cmd(InputBuffer_Serial+20)==VALUE_OFF; // ON/OFF command + Flamingo_Send(bitstream, c); + success=true; + } + return success; +} + +void Flamingo_Send(int fbutton, int fcmd) { + int fpulse = 350; // Pulse witdh in microseconds + int fretrans = 9; // Number of code retransmissions + uint32_t fdatabit; + uint32_t fdatamask = 0x80000000; + uint32_t fsendbuff; + uint32_t fsendbuff1; + uint32_t fsendbuff2; + uint32_t fsendbuff3; + uint32_t fsendbuff4; + + if (fcmd == 0) { // OFF + fsendbuff1=0x24D319A0; // A Off + fsendbuff2=0x246008E0; + fsendbuff3=0x26BB9860; + fsendbuff4=0x26D4BFA0; + //fsendbuff1=0xD86E6650; + //fsendbuff2=0xDABDF710; + //fsendbuff3=0xDA42A790; + //fsendbuff4=0xDA614050; + } else { // ON + fsendbuff1=0x2561B560; // A On + fsendbuff2=0x24A3F5E0; + fsendbuff3=0x27B27B60; + fsendbuff4=0x24543A20; + //fsendbuff1=0xD97A4A10; + //fsendbuff2=0xDA9A8490; + //fsendbuff3=0xDB58C5D0; + //fsendbuff4=0xDBF40A90; + } + digitalWrite(PIN_RF_RX_VCC,LOW); // Spanning naar de RF ontvanger uit om interferentie met de zender te voorkomen. + digitalWrite(PIN_RF_TX_VCC,HIGH); // zet de 433Mhz zender aan + delayMicroseconds(TRANSMITTER_STABLE_DELAY); // short delay to let the transmitter become stable (Note: Aurel RTX MID needs 500µS/0,5ms) + + for (int nRepeat = 0; nRepeat < fretrans; nRepeat++) { + + if (nRepeat %4 ==0 ) fsendbuff=fsendbuff1; + if (nRepeat %4 ==1 ) fsendbuff=fsendbuff2; + if (nRepeat %4 ==2 ) fsendbuff=fsendbuff3; + if (nRepeat %4 ==3 ) fsendbuff=fsendbuff4; + + Serial.println(fsendbuff,HEX); + + // send SYNC 1P High, 15P low + digitalWrite(PIN_RF_TX_DATA, HIGH); + delayMicroseconds(fpulse * 1); + digitalWrite(PIN_RF_TX_DATA, LOW); + delayMicroseconds(fpulse * 15); + // end send SYNC + + // Send command + for (int i = 0; i < 28; i++) { // Flamingo command is only 28 bits + // read data bit + fdatabit = fsendbuff & fdatamask; // Get most left bit + fsendbuff = (fsendbuff << 1); // Shift left + + if (fdatabit != fdatamask) { // Write 0 + digitalWrite(PIN_RF_TX_DATA, HIGH); + delayMicroseconds(fpulse * 1); + digitalWrite(PIN_RF_TX_DATA, LOW); + delayMicroseconds(fpulse * 3); + } else { // Write 1 + digitalWrite(PIN_RF_TX_DATA, HIGH); + delayMicroseconds(fpulse * 3); + digitalWrite(PIN_RF_TX_DATA, LOW); + delayMicroseconds(fpulse * 1); + } + } + //digitalWrite(PIN_RF_TX_DATA, LOW); + //delayMicroseconds(fpulse * 15); + } + delayMicroseconds(TRANSMITTER_STABLE_DELAY); // short delay to let the transmitter become stable (Note: Aurel RTX MID needs 500µS/0,5ms) + digitalWrite(PIN_RF_TX_VCC,LOW); // zet de 433Mhz zender weer uit + digitalWrite(PIN_RF_RX_VCC,HIGH); // Spanning naar de RF ontvanger weer aan. + RFLinkHW(); +} +#endif //PLUGIN_TX_012 diff --git a/Plugins/Plugin_013.c b/Plugins/Plugin_013.c new file mode 100644 index 0000000..df15c08 --- /dev/null +++ b/Plugins/Plugin_013.c @@ -0,0 +1,276 @@ +//####################################################################################################### +//## This Plugin is only for use with the RFLink software package ## +//## Plugin-13: Powerfix RCB-i 3600 ## +//####################################################################################################### +/*********************************************************************************************\ + * This plugin takes care of sending and receiving the Powerfix RCB-i 3600 protocol + * Works with: Powerfix RCB-i 3600 - 4 power outlets and a remote, Quigg GT7000 + * + * Author : StuntTeam + * Support : http://sourceforge.net/projects/rflink/ + * License : This code is free for use in any open source project when this header is included. + * Usage of any parts of this code in a commercial application is prohibited! + *********************************************************************************************** + * Technical information: + * Partially based on http://wiki.pilight.org/doku.php/quigg_switch_gt_7000_v7_0 + * + * The Powerfix/Chacon RF packets are 42 pulses long resulting in 20 bits data packets + * + * 0000 1000 0000 000 1 0 0 0 1 + * AAAA AAAA AAAA BBC D E F G H + * + * A = 12 bits address (Chacon:0000 0000 0000 Powerfix:0000 1000 0000) + * B = 2 bits Unit code (bits reversed) + * C = Group Command (to all devices) + * D = State (ON/OFF/DIM UP/DIM DOWN) + * E = Dim command (1=dim/bright command) + * F = always 0 + * G = Parity bit calculated over command bits (BCDEFH) + * H = unknown, copy of D (state) + * + * ------------------------------------------- + * Powerfix: + * 0000 1000 0000 000 1 0 0 0 1 1 on + * 0000 1000 0000 000 0 0 0 0 0 1 off + * 0000 1000 0000 100 1 0 0 1 1 2 on + * 0000 1000 0000 100 0 0 0 1 0 2 off + * 0000 1000 0000 010 1 0 0 0 0 3 on + * 0000 1000 0000 010 0 0 0 0 1 3 off + * 0000 1000 0000 110 1 0 0 1 0 4 on + * 0000 1000 0000 110 0 0 0 1 1 4 off + * 0000 1000 0000 111 0 0 0 0 1 all off + * 0000 1000 0000 111 1 0 0 0 0 all on + * 0000 1000 0000 111 1 1 0 1 0 dim + * 0000 1000 0000 111 0 1 0 1 1 bright + * ------------------------------------------- + * Chacon: + * 0000 0000 0000 000 1 0 0 0 1 1 ON + * 0000 0000 0000 000 0 0 0 0 0 1 OFF + * 0000 0000 0000 100 1 0 0 1 1 2 ON + * 0000 0000 0000 100 0 0 0 1 0 2 OFF + * 0000 0000 0000 010 1 0 0 0 0 3 ON + * 0000 0000 0000 010 0 0 0 0 1 3 OFF + * 0000 0000 0000 110 1 0 0 1 0 4 ON + * 0000 0000 0000 110 0 0 0 1 1 4 OFF + * 0000 0000 0000 111 1 0 0 0 0 All ON + * 0000 0000 0000 111 0 0 0 0 1 All OFF + * 0000 0000 0000 111 1 1 0 1 0 All DIM + * 0000 0000 0000 111 0 1 0 1 1 All Bright + * ------------------------------------------- + * Sample Powerfix: + * 20;10;DEBUG;Pulses=42;Pulses(uSec)=630,570,1230,540,1230,540,1230,540,1230,570,1230,540,1230,540,1230,540,1230,570,1230,540,1230,540,1230,540,1230,570,1200,540,1200,570,1170,1230,540,570,1200,540,1200,540,1170,1230,540,6990; + * + * BUTTON: DIM + * 20;05;DEBUG;Pulses=42;Pulses(uSec)=600,600,1250,625,1250,625,1225,625,1250,1275,575,600,1250,625,1225,625,1250,625,1225,625,1225,625,1225,625,1225,1300,575,1300,575,1300,575,1300,600,1300,550,625,1225,1300,550,625,1175; + * 00101010110010101010101011010101010011001 + * BUTTON: BRIGHT + * 20;12;DEBUG;Pulses=42;Pulses(uSec)=600,600,1250,600,1250,600,1250,600,1250,1275,575,625,1225,625,1225,625,1250,625,1225,625,1225,625,1225,625,1250,1300,550,1300,575,1300,550,625,1250,1300,550,625,1225,1300,575,1300,525; + * 00101010110010101010101011010100110011010 + * BUTTON: ALL OFF + * 20;18;DEBUG;Pulses=42;Pulses(uSec)=600,600,1250,625,1225,625,1225,625,1250,1275,575,625,1225,625,1225,625,1250,625,1225,625,1225,625,1225,625,1225,1300,575,1300,550,1300,550,625,1250,650,1200,625,1225,650,1225,1300,500; + * 00101010110010101010101011010100101010110 + * BUTTON: ALL ON + * 20;15;DEBUG;Pulses=42;Pulses(uSec)=600,600,1250,600,1250,625,1225,625,1225,1275,575,625,1225,625,1225,625,1250,625,1225,650,1200,625,1225,625,1225,1300,550,1300,575,1300,550,1300,575,650,1225,625,1225,625,1225,625,1175; + * 00101010110010101010101011010101001010101 + * BUTTON: 1 ON + * 20;04;DEBUG;Pulses=42;Pulses(uSec)=600,600,1250,625,1225,625,1225,625,1225,1300,575,625,1225,625,1225,625,1250,625,1225,625,1225,625,1225,625,1225,625,1225,625,1225,625,1225,1300,575,625,1225,625,1225,625,1225,1300,500; + * 0101010110010101010101010101011001010110 + * BUTTON: 1 OFF + * 20;07;DEBUG;Pulses=42;Pulses(uSec)=600, 600,1250,600,1250,625,1250,625,1225,1275,575,625,1225,600,1250,625,1275,625,1225,625,1225,625,1225,625,1225,625,1225,625,1225,625,1225,625,1250,625,1225,625,1225,625,1225,625,1175; + * 0101010110010101010101010101010101010101 + * BUTTON: 2 ON + * 20;0A;DEBUG;Pulses=42;Pulses(uSec)=575,600,1250,625,1225,625,1225,625,1225,1300,550,625,1225,625,1225,625,1275,625,1225,625,1225,625,1225,625,1225,1300,550,625,1225,625,1225,1300,575,625,1225,625,1225,1300,575,1300,500; + * 0101010110010101010101011001011001011010 + * BUTTON: 2 OFF + * 20;0D;DEBUG;Pulses=42;Pulses(uSec)=600,600,1250,625,1225,625,1225,625,1250,1300,550,625,1225,625,1225,625,1250,625,1225,625,1225,625,1225,625,1250,1300,575,625,1225,625, + 1225,625,1250,625,1225,650,1225,1300,550,625,1175; + * 0101010110010101010101011001010101011001 + \*********************************************************************************************/ +#define POWERFIX_PulseLength 42 +#define POWEFIX_PULSEMID 900/RAWSIGNAL_SAMPLE_RATE +#define POWEFIX_PULSEMIN 450/RAWSIGNAL_SAMPLE_RATE +#define POWEFIX_PULSEMAX 1400/RAWSIGNAL_SAMPLE_RATE + +#ifdef PLUGIN_013 +boolean Plugin_013(byte function, char *string) { + if (RawSignal.Number!= POWERFIX_PulseLength) return false; + unsigned long bitstream=0L; + unsigned int address=0; + byte unitcode=0; + byte button=0; + byte command=0; + byte parity=0; + byte bitcount=0; + // ========================================================================== + if (RawSignal.Pulses[1] > POWEFIX_PULSEMID) return false; // start pulse must be short + for (byte x=2;x POWEFIX_PULSEMID) { + if (RawSignal.Pulses[x] > POWEFIX_PULSEMAX) return false; // Long pulse too long + if (RawSignal.Pulses[x+1] > POWEFIX_PULSEMID) return false; // pulse sequence check 01/10 + bitstream = (bitstream << 1) | 0x1; + if (bitcount > 11) parity=parity^1; + } else { + if (RawSignal.Pulses[x] < POWEFIX_PULSEMIN) return false; // Short pulse too short + if (RawSignal.Pulses[x+1] < POWEFIX_PULSEMID) return false; // pulse sequence check 01/10 + bitstream = (bitstream << 1); + } + bitcount++; + } + //================================================================================== + // Perform Sanity Checks + if (parity != 0) return false; // Parity check + if (((bitstream)&0x4) == 4) return false; // Tested bit should always be zero + //================================================================================== + // Prevent repeating signals from showing up + //================================================================================== + if(SignalHash!=SignalHashPrevious || (RepeatingTimer>8); // 12 bits address + unitcode=((bitstream >> 6)& 0x03); + if (unitcode==2) button=1; // bits are simply reversed + if (unitcode==1) button=2; + if (unitcode==3) button=3; + + parity=((bitstream)&0x3f); // re-use parity variable + command=((parity)>>4)&0x01; // On/Off command + if (((parity)&0x08) == 0x08) { // dim command + command=command+2; + } else { + if (((parity)&0x20) == 0x20) { // group command + command=command+4; + } + } + //================================================================================== + // ---------------------------------- + // Output + // ---------------------------------- + sprintf(pbuffer, "20;%02X;", PKSequenceNumber++); // Node and packet number + Serial.print( pbuffer ); + // ---------------------------------- + Serial.print(F("Powerfix;")); // Label + sprintf(pbuffer, "ID=%04x;", address); // ID + Serial.print( pbuffer ); + sprintf(pbuffer, "SWITCH=%02x;", button); // ID + Serial.print( pbuffer ); + Serial.print(F("CMD=")); + + if ( command == 0) { + Serial.print(F("OFF;")); + } else + if ( command == 1) { + Serial.print(F("ON;")); + } else + if ( command == 2) { + Serial.print(F("BRIGHT;")); + } else + if ( command == 3) { + Serial.print(F("DIM;")); + } else + if ( command == 4) { + Serial.print(F("ALLOFF;")); + } else + if ( command == 5) { + Serial.print(F("ALLON;")); + } + Serial.println(); + // ---------------------------------- + RawSignal.Repeats=true; // suppress repeats of the same RF packet + RawSignal.Number=0; + return true; +} +#endif //PLUGIN_013 + +#ifdef PLUGIN_TX_013 +void Powerfix_Send(unsigned long bitstream); + +boolean PluginTX_013(byte function, char *string) { + boolean success=false; + //10;POWERFIX;000080;0;ON; + //012345678901234567890123 + if (strncasecmp(InputBuffer_Serial+3,"POWERFIX;",9) == 0) { + if (InputBuffer_Serial[18] != ';') return success; + + InputBuffer_Serial[10]=0x30; + InputBuffer_Serial[11]=0x78; + InputBuffer_Serial[18]=0x00; // Get address from hexadecimal value + + unsigned long bitstream=0L; // Main placeholder + byte command=0; + byte c; + // ------------------------------- + bitstream=str2int(InputBuffer_Serial+10); // Address, first 12 bits of the 20 bits in total + bitstream=(bitstream)<<8; // shift left so that we can add the 8 command bits + // ------------------------------- + byte temp=str2int(InputBuffer_Serial+19);// button/unit number (0..3) + if (temp==1) command=0x82; + if (temp==2) command=0x40; + if (temp==3) command=0xc2; + // ------------------------------- + c=0; + c = str2cmd(InputBuffer_Serial+21); // ON/OFF command + if (c == VALUE_ON) { + command=command | 0x10; // turn "on" bit for on command + } else { + if (c == VALUE_ALLOFF) { // set "all off" bits + command=0xe0; + } else + if (c == VALUE_ALLON) { + command=0xf0; // set "all on" bits + } + } + // not supported yet.. + // dim: command=0xfa + // bright: command=0xea; + // ------------------------------- + bitstream=bitstream+command; + // ------------------------------- + Powerfix_Send(bitstream); // bitstream to send + success=true; + } + return success; +} + +#define PLUGIN_013_RFLOW 650 +#define PLUGIN_013_RFHIGH 1300 + +void Powerfix_Send(unsigned long bitstream) { + RawSignal.Repeats=7; // Number of RF packet retransmits + RawSignal.Delay=20; // Delay between RF packets + RawSignal.Number=42; // Length + + uint32_t fdatabit; + uint32_t fdatamask = 0x80000; + byte parity=1; // to calculate the parity bit + // ------------------------------- + RawSignal.Pulses[1]=PLUGIN_013_RFLOW/RawSignal.Multiply; // start pulse + for (byte i=2; i<40; i=i+2) { // address and command bits + fdatabit = bitstream & fdatamask; // Get most left bit + bitstream = (bitstream << 1); // Shift left + + if (fdatabit != fdatamask) { // Write 0 + RawSignal.Pulses[i] = PLUGIN_013_RFLOW/RawSignal.Multiply; + RawSignal.Pulses[i+1]= PLUGIN_013_RFHIGH/RawSignal.Multiply; + } else { // Write 1 + parity=parity^1; + RawSignal.Pulses[i] = PLUGIN_013_RFHIGH/RawSignal.Multiply; + RawSignal.Pulses[i+1]= PLUGIN_013_RFLOW/RawSignal.Multiply; + } + } + // parity + if (parity == 0) { // Write 0 + RawSignal.Pulses[40] = PLUGIN_013_RFLOW/RawSignal.Multiply; + RawSignal.Pulses[41] = PLUGIN_013_RFHIGH/RawSignal.Multiply; + } else { // Write 1 + RawSignal.Pulses[40] = PLUGIN_013_RFHIGH/RawSignal.Multiply; + RawSignal.Pulses[41] = PLUGIN_013_RFLOW/RawSignal.Multiply; + } + RawSendRF(); +} +#endif // PLUGIN_TX_013 diff --git a/Plugins/Plugin_014.c b/Plugins/Plugin_014.c new file mode 100644 index 0000000..ed02a22 --- /dev/null +++ b/Plugins/Plugin_014.c @@ -0,0 +1,147 @@ +//####################################################################################################### +//## This Plugin is only for use with the RFLink software package ## +//## Plugin-14: Ikea Koppla ## +//####################################################################################################### +/*********************************************************************************************\ + * This plugin takes care of sending and receiving the Ikea Koppla protocol + * + * Author : StuntTeam + * Support : http://sourceforge.net/projects/rflink/ + * License : This code is free for use in any open source project when this header is included. + * Usage of any parts of this code in a commercial application is prohibited! + *********************************************************************************************** + * Technical information: + * Packets are variable length as zero bits are 2 pulses and 1 bits are single pulses. + * Pulse lengths are ~1600 for a 0 bit and two times ~750 for a 1 bit + * Packets contain 28 bits + * + * AAAA BBBB CCCCCCCCCC DD EEEEEE FF + * 1110 0011 0000000001 01 000010 01 on + * 1110 0011 0000000001 01 010110 01 off + * 1110 1111 1111111111 00 001110 10 Pair + * + * A = Preamble, Always '1110' + * B = System code 0-0x0f + * C = Unit code bit selection, order: ch10,1,2,3,4,5,6,7,8,9 + * D = Checksum on B + * E = Level and Fade + * F = Checksum on D + * + * Sample: + * 20;07;DEBUG;Pulses=38;Pulses(uSec)=825,775,750,775,750,775,1600,1625,1600,775,750,775,750,1625,1600,1625,1600,1625,1625,1625,1600,1625,750,750,1600,775,750,1625,1600,1625,1600,775,750,1625,1600,775,750; + * 20;08;DEBUG;Pulses=40;Pulses(uSec)=925,775,750,775,750,775,1600,1625,1600,775,750,775,750,1625,1600,1625,1600,1625,1625,1625,1600,1625,750,775,1600,775,750,1625,750,750,1625,775,750,775,750,1625,1600,750,750; + \*********************************************************************************************/ +#define KOPPLA_PulseLength_MIN 36 +#define KOPPLA_PulseLength_MAX 52 +#define KOPPLA_PULSEMID 1300/RAWSIGNAL_SAMPLE_RATE +#define KOPPLA_PULSEMAX 1850/RAWSIGNAL_SAMPLE_RATE +#define KOPPLA_PULSEMIN 650/RAWSIGNAL_SAMPLE_RATE + +#ifdef PLUGIN_014 +boolean Plugin_014(byte function, char *string) { + if ((RawSignal.Number < KOPPLA_PulseLength_MIN) || (RawSignal.Number > KOPPLA_PulseLength_MAX) ) return false; + unsigned long bitstream=0L; + + byte preamble=0; + unsigned int sysunit=0; + byte levelfade=0; + byte command=0; + byte level=0; + int i=0; + + byte checksum1=0; + byte checksum2=0; + byte checksum=0x03; + byte tempcrc=0; + // ========================================================================== + for (byte x=1;x < RawSignal.Number;x++) { + if (RawSignal.Pulses[x] > KOPPLA_PULSEMID) { // long pulse, 0 bit + if (RawSignal.Pulses[x] > KOPPLA_PULSEMAX) return false; // long pulse is too long + bitstream = (bitstream << 1); // 0 bit + } else { // Short pulse + if (RawSignal.Pulses[x] < KOPPLA_PULSEMIN) return false; // short pulse is too short + if (RawSignal.Pulses[x+1] > KOPPLA_PULSEMID) return false; // need 2 short pulses for a 1 bit + if (RawSignal.Pulses[x+1] < KOPPLA_PULSEMIN) return false; // second short pulse is too short + x++; // skip second short pulse + bitstream = (bitstream << 1) | 0x1; // 1 bit + } + } + //================================================================================== + // Sort data and perform sanity checks + //================================================================================== + if (bitstream==0) return false; // No bits detected? + if ((((bitstream) >> 24)&0x0f) != 0x0e) return false; // Preamble should always be '1110' + sysunit=(((bitstream) >> 10)& 0x03fff); // 4 bits system code and 10 bits unit code + checksum1=(((bitstream) >> 8) &0x03); // first checksum + levelfade=(((bitstream) >> 2)& 0x3f); // 6 bits level and face code + checksum2=((bitstream)&0x03); // second checksum + // calculate checksum 1 + checksum=3; + for (i=0;i<7;i++) { + tempcrc=((sysunit) >> (i*2)) & 3; + checksum=checksum ^ tempcrc; + } + if (checksum1 != checksum) return false; + // calculate checksum 2 + checksum=3; + for (i=0;i<3;i++) { + tempcrc=((levelfade) >> (i*2)) & 3; + checksum=checksum ^ tempcrc; + } + if (checksum2 != checksum) return false; + // sort command / dim bits + if ((levelfade) == 0x2) { // on + command=1; + } else + if ((levelfade) == 0x16) { // off + command=0; + } else + if ((levelfade) == 0x0) { // up + command=2; + } else + if ((levelfade) == 0x4) { // down + command=3; + } else { + command=4; + for (i=2;i<6;i++) { + level=level<<1; + level=level | ((levelfade)>>i)&0x01; + } + } + //================================================================================== + // ---------------------------------- + // Output + // ---------------------------------- + sprintf(pbuffer, "20;%02X;", PKSequenceNumber++); // Node and packet number + Serial.print( pbuffer ); + // ---------------------------------- + Serial.print(F("Ikea Koppla;")); // Label + sprintf(pbuffer, "ID=%04x;", sysunit); // ID + Serial.print( pbuffer ); + sprintf(pbuffer, "SWITCH=%02x;", levelfade); // ID + Serial.print( pbuffer ); + Serial.print(F("CMD=")); + + if ( command == 0) { + Serial.print(F("OFF;")); + } else + if ( command == 1) { + Serial.print(F("ON;")); + } else + if ( command == 2) { + Serial.print(F("BRIGHT;")); + } else + if ( command == 3) { + Serial.print(F("DIM;")); + } else { + sprintf(pbuffer, "SET_LEVEL=%d;", level ); + Serial.print( pbuffer ); + } + Serial.println(); + // ---------------------------------- + RawSignal.Repeats=true; // suppress repeats of the same RF packet + RawSignal.Number=0; + return true; +} +#endif //PLUGIN_014 + diff --git a/Plugins/Plugin_015.c b/Plugins/Plugin_015.c new file mode 100644 index 0000000..4729c7e --- /dev/null +++ b/Plugins/Plugin_015.c @@ -0,0 +1,270 @@ +//####################################################################################################### +//## This Plugin is only for use with the RFLink software package ## +//## Plugin-15: HomeEasy EU ## +//####################################################################################################### +/*********************************************************************************************\ + * Dit protocol zorgt voor ontvangst en verzending HomeEasy EU zenders + * die werken volgens de automatische codering (Ontvangers met leer-knop) + * + * LET OP: GEEN SUPPORT VOOR DIRECTE DIMWAARDES!!! + * + * Author : StuntTeam + * Support : http://sourceforge.net/projects/rflink/ + * License : This code is free for use in any open source project when this header is included. + * Usage of any parts of this code in a commercial application is prohibited! + ********************************************************************************************* + * Technische informatie: + * Analyses Home Easy Messages and convert these into an eventcode + * Only new EU devices with automatic code system are supported + * Only On / Off status is decoded, no DIM values + * Only tested with Home Easy HE300WEU transmitter, doorsensor and PIR sensor + * Home Easy message structure, by analyzing bitpatterns so far ... + * AAAAAAAAAAA BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB CCCC DD EE FFFFFF G + * 11000111100 10111100011101110010001111100011 1100 10 11 000111 1 HE301EU ON + * 11000111100 10111100011101110010001111100011 1100 01 11 000111 1 HE301EU OFF + * 11000111100 1001 01 11 001011 HE842/844 ON + * 11000111100 01111010011100110010001011000111 1000 11 11 001011 0000000 HE842/844 OFF + * 1000 10 11 000111 HE844 ALLON; + * 1000 01 11 000111 HE844 ALLOFF; + * 11000111100 01111000111101100110010011000111 1000 11 11 000111 0000000 + * + * A = Startbits/Preamble, + * B = Address, 32 bits + * C = Unknown, Possibly: Device type + * D = Command, 1 bit only? + * E = Group indicator + * F = Channel 0-15 + * G = Stopbit + * + * SAMPLE: + * Pulses=116;Pulses(uSec)=200,1175,125,1175,125,200,150,200,125,200,150,1175,150,1175,150,1175,150,1175,125,200,150,200,150,200,125,1175,150,1175,150,1175,125,1175,150,200,125,200,150,1175,125,1175,150,200,125,1175,125,1175,150,200,150,200,150,1175,150,200,150,1175,150,200,150,1175,150,200,150,200,125,1175,150,200,125,1175,150,1175,125,1175,150,200,125,200,125,200,150,200,125,1175,150,1175,150,1175,150,200,150,200,125,200,150,1175,150,1175,150,1175,150,1175,125,200,150,200,125,1175,125,200,125,1175,150,1150,125; + * HE preamble: 11000111100 (63C) Address: 1111001101100101010010111000011 (79B2A5C3) Stopbits: 0 (0) Commands: 10001111001011 Command: 0 Channel: 1011 Group: 1 + * 20;04;HomeEasy;ID=7900b200;SWITCH=0b;CMD=ALLOFF; + * + * Preamble 200,1175,125,1175,125,200,150,200,125,200,150,1175,150,1175,150,1175,150,1175,125,200,150,200, + * Address 150,200,125,1175,150,1175,150,1175,125,1175,150,200,125,200,150,1175,125,1175,150,200,125,1175,125,1175,150,200,150,200,150,1175,150,200,150,1175,150,200,150,1175,150,200,150,200,125,1175,150,200,125,1175,150,1175,125,1175,150,200,125,200,125,200,150,200,125,1175,150,1175, + * Command 150,1175,150,200,150,200,125,200,150,1175,150,1175,150,1175,150,1175,125,200,150,200,125,1175,125,200,125,1175,150,1150, - 125; + \*********************************************************************************************/ +#define HomeEasy_PulseLength 116 +#define HomeEasy_PULSEMID 500/RAWSIGNAL_SAMPLE_RATE + +#ifdef PLUGIN_015 +boolean Plugin_015(byte function, char *string) { + if (RawSignal.Number != HomeEasy_PulseLength) return false; + unsigned long preamble = 0L; + unsigned long address = 0L; + unsigned long bitstream = 0L; + byte rfbit =0; + byte command = 0; + byte group = 0; + byte channel = 0; + byte type = 0; + byte temp = 0; + RawSignal.Pulses[0]=0; // undo any Home Easy to Kaku blocking that might be active + //================================================================================== + // convert pulses into bit sections (preamble, address, bitstream) + for(byte x=1;x<=HomeEasy_PulseLength;x=x+2) { + if ((RawSignal.Pulses[x] < HomeEasy_PULSEMID) && (RawSignal.Pulses[x+1] > HomeEasy_PULSEMID)) + rfbit = 1; + else + rfbit = 0; + + if ( x<=22) preamble = (preamble << 1) | rfbit; // 11 bits preamble + if ((x>=23) && (x<=86)) address = (address << 1) | rfbit; // 32 bits address + if ((x>=87) && (x<=114)) bitstream = (bitstream << 1) | rfbit; // 15 remaining bits + } + //================================================================================== + // To prevent false positives make sure the preamble is correct, it should always be 0x63c + // We compare only 10 bits to compensate for the first bit being seen incorrectly by some receiver modules + if ((preamble & 0x3ff) != 0x23c) return false; // comparing 10 bits is enough to make sure the packet is valid + //================================================================================== + // Prevent repeating signals from showing up + //================================================================================== + if(SignalHash!=SignalHashPrevious || (RepeatingTimer> 12) & 0x3); // 11b for HE301 + channel = (bitstream) & 0x3f; + if (type==3) { // HE301 + command = ((bitstream >> 8) & 0x1); // 0=on 1=off (both group and single device) + group = ((bitstream >> 7) & 0x1); // 1=group + } else { // HE800 21c7 = off 22c7=on + temp = ((bitstream >> 8) & 0x7); // 1=group + if (temp < 3) group=1; + command = ((bitstream >> 9) & 0x1); // 0=off 1=on + if (group==1) command=(~command)&1; // reverse bit for group: 1=group off 0=group on + } + // ---------------------------------- + // Output + // ---------------------------------- + sprintf(pbuffer, "20;%02X;", PKSequenceNumber++); // Node and packet number + Serial.print( pbuffer ); + // ---------------------------------- + Serial.print("HomeEasy;"); // Label + sprintf(pbuffer, "ID=%08lx;",(address) ); // ID + Serial.print( pbuffer ); + sprintf(pbuffer, "SWITCH=%02x;", channel); + Serial.print( pbuffer ); + strcpy(pbuffer,"CMD="); + if ( group == 1) { + strcat(pbuffer,"ALL"); + } + if ( command == 1) { + strcat(pbuffer,"OFF;"); + } else { + strcat(pbuffer,"ON;"); + } + Serial.print( pbuffer ); + Serial.println(); + // ---------------------------------- + RawSignal.Repeats = true; + RawSignal.Number=0; + return true; +} +#endif // PLUGIN_015 + +#ifdef PLUGIN_TX_015 +void HomeEasyEU_Send(unsigned long address, unsigned long command); + +boolean PluginTX_015(byte function, char *string) { + boolean success=false; + //10;HomeEasy;7900b200;b;ON; + //10;HomeEasy;d900ba00;23;OFF; + //10;HomeEasy;79b2a5c3;b;ON; + //10;HomeEasy;7a7322c7;b;ON; + //01234567890123456789012345 + if (strncasecmp(InputBuffer_Serial+3,"HOMEEASY;",9) == 0) { // KAKU Command eg. + if (InputBuffer_Serial[20] != ';') return false; + unsigned long bitstream = 0L; + unsigned long commandcode = 0L; + byte cmd=0; + byte group=0; + // ------------------------------ + InputBuffer_Serial[10]=0x30; + InputBuffer_Serial[11]=0x78; + InputBuffer_Serial[20]=0x00; + bitstream=str2int(InputBuffer_Serial+10); // Get Address from hexadecimal value + // ------------------------------ + InputBuffer_Serial[19]=0x30; + InputBuffer_Serial[20]=0x78; // Get home from hexadecimal value + commandcode=str2int(InputBuffer_Serial+19); // Get Button number + // ------------------------------ + if (InputBuffer_Serial[23] == ';') { // Get command + cmd=str2cmd(InputBuffer_Serial+24); + } else { + cmd=str2cmd(InputBuffer_Serial+23); + } + if (cmd == VALUE_OFF) cmd = 0; // off + if (cmd == VALUE_ON) cmd = 1; // on + if (cmd == VALUE_ALLON) {cmd = 1; group=1;} // allon + if (cmd == VALUE_ALLOFF){cmd = 0; group=1;} // alloff + // ------------------------------ + commandcode=commandcode & 0x3f; // get button number + if (group == 1) { // HE8xx: 21xx/22xx HE3xx: 31xx/32xx (off/on) (HE3xx code works for HE8xx) + commandcode=commandcode | 0x30C0; // group + if (cmd == 1) { + commandcode=(commandcode & 0xfdff) | 0x200; // group on > 32cx + } else { + commandcode=commandcode | 0x100; // group off > 31cx + } + } else { // HE8xx: 23cx/25cx HE3xx: 2D4x/2E4x (off/on) (HE3 code works for HE8xx) + commandcode=commandcode | 0x2040; // non-group + if (cmd == 1) { + commandcode=(commandcode & 0xfdff) | 0xe00; // On > 2ECx + } else { + commandcode=commandcode | 0xd00; // Off > 2DCx + } + } + //----------------------------------------------- + HomeEasyEU_Send(bitstream, commandcode); + success=true; + } + return success; +} + +void HomeEasyEU_Send(unsigned long address, unsigned long command) { + int fpulse = 275; // Pulse witdh in microseconds + int fretrans = 5; // Number of code retransmissions + uint32_t fdatabit; + uint32_t fdatamask = 0x80000000; + uint32_t fsendbuff; + + digitalWrite(PIN_RF_RX_VCC,LOW); // Spanning naar de RF ontvanger uit om interferentie met de zender te voorkomen. + digitalWrite(PIN_RF_TX_VCC,HIGH); // zet de 433Mhz zender aan + delayMicroseconds(TRANSMITTER_STABLE_DELAY); // short delay to let the transmitter become stable (Note: Aurel RTX MID needs 500µS/0,5ms) + for (int nRepeat = 0; nRepeat <= fretrans; nRepeat++) { + // -------------- Send Home Easy preamble (0x63c) - 11 bits + fsendbuff=0x63c; + fdatamask=0x400; + for (int i = 0; i < 11; i++) { // Preamble + // read data bit + fdatabit = fsendbuff & fdatamask; // Get most left bit + fsendbuff = (fsendbuff << 1); // Shift left + if (fdatabit != fdatamask) { // Write 0 + digitalWrite(PIN_RF_TX_DATA, HIGH); + delayMicroseconds(fpulse * 1); + digitalWrite(PIN_RF_TX_DATA, LOW); + delayMicroseconds(fpulse * 1); + } else { // Write 1 + digitalWrite(PIN_RF_TX_DATA, HIGH); + delayMicroseconds(fpulse * 1); + digitalWrite(PIN_RF_TX_DATA, LOW); + delayMicroseconds(fpulse * 5); + } + } + // -------------- Send Home Easy device Address + fsendbuff=address; + fdatamask=0x80000000; + // Send Address - 32 bits + for (int i = 0; i < 32;i++){ //28;i++){ + // read data bit + fdatabit = fsendbuff & fdatamask; // Get most left bit + fsendbuff = (fsendbuff << 1); // Shift left + if (fdatabit != fdatamask) { // Write 0 + digitalWrite(PIN_RF_TX_DATA, HIGH); + delayMicroseconds(fpulse * 1); + digitalWrite(PIN_RF_TX_DATA, LOW); + delayMicroseconds(fpulse * 1); + } else { // Write 1 + digitalWrite(PIN_RF_TX_DATA, HIGH); + delayMicroseconds(fpulse * 1); + digitalWrite(PIN_RF_TX_DATA, LOW); + delayMicroseconds(fpulse * 5); + } + } + // -------------- Send Home Easy command bits - 14 bits + fsendbuff=command; // 0xFF; + fdatamask=0x2000; + for (int i = 0; i < 14; i++) { + // read data bit + fdatabit = fsendbuff & fdatamask; // Get most left bit + fsendbuff = (fsendbuff << 1); // Shift left + if (fdatabit != fdatamask) { // Write 0 + digitalWrite(PIN_RF_TX_DATA, HIGH); + delayMicroseconds(fpulse * 1); + digitalWrite(PIN_RF_TX_DATA, LOW); + delayMicroseconds(fpulse * 1); + } else { // Write 1 + digitalWrite(PIN_RF_TX_DATA, HIGH); + delayMicroseconds(fpulse * 1); + digitalWrite(PIN_RF_TX_DATA, LOW); + delayMicroseconds(fpulse * 5); + } + } + // -------------- Send stop + digitalWrite(PIN_RF_TX_DATA, HIGH); + delayMicroseconds(fpulse * 1); + digitalWrite(PIN_RF_TX_DATA, LOW); // and lower the signal + delayMicroseconds(fpulse * 26); + } + delayMicroseconds(TRANSMITTER_STABLE_DELAY); // short delay to let the transmitter become stable (Note: Aurel RTX MID needs 500µS/0,5ms) + digitalWrite(PIN_RF_TX_VCC,LOW); // zet de 433Mhz zender weer uit + digitalWrite(PIN_RF_RX_VCC,HIGH); // Spanning naar de RF ontvanger weer aan. + RFLinkHW(); +} +#endif // PLUGIN_TX_015 diff --git a/Plugins/Plugin_030.c b/Plugins/Plugin_030.c new file mode 100644 index 0000000..01a60fd --- /dev/null +++ b/Plugins/Plugin_030.c @@ -0,0 +1,261 @@ +//####################################################################################################### +//## This Plugin is only for use with the RFLink software package ## +//## Plugin-30 AlectoV1 ## +//####################################################################################################### +/*********************************************************************************************\ + * This plugin takes care of decoding the protocol used for outdoor sensors of the Alecto weather stations + * following protocol version 1 + * This Plugin works at least with: Alecto WS3500, Silvercrest, Otio sht-10, Otio sht-20 + * Auriol H13726, Ventus WS155, Hama EWS 1500, Meteoscan W155/W160 + * Alecto WS4500, Ventus W044, Balance RF-WS105 + * + * Author : StuntTeam + * Support : http://sourceforge.net/projects/rflink/ + * License : This code is free for use in any open source project when this header is included. + * Usage of any parts of this code in a commercial application is prohibited! + ********************************************************************************************* + * Technische informatie: + * Message Format: (9 nibbles, 36 bits): + * + * Format for Temperature Humidity + * AAAAAAAA BBBB CCCC CCCC CCCC DDDDDDDD EEEE + * 00110110 1000 1011 0111 0000 00011100 0110 + * RC Type Temperature___ Humidity Checksum + * A = Rolling Code (includes channel number on low 2 bits of nibble1 (10=ch1 01=ch2 11=ch3) ) + * B = Message type (xyyx = temp/humidity if yy <> '11') => only accepting yy = 00 for negative temperatures for now + * 4 bits: bit 0 = battery state 0=OK, 1= below 2.6 volt + * bit 1&2 = 00/01/10 = temp/hum is transmitted, 11=non temp is transmitted + * bit 3 = 0=scheduled transmission, 1=requested transmission (button press) + * C = Temperature (two's complement) + * D = Humidity BCD format + * E = Checksum + * + * Sample: + * 20;F4;DEBUG;Pulses=74;Pulses(uSec)=450,1900,350,1900,350,3975,350,3975,350,1900,350,3975,350,3975,350,1900,350,3975,350,1900,350,1900,350,1900,350,3975,350,1900,350,3975,350,3975,350,1900,350,3975,350,3975,350,3975,350,1900,350,1900,350,1900,350,1900,350,1900,350,1900,350,1900,350,3975,350,3975,350,3975,350,1900,350,1900,350,1900,350,3975,350,3975,350,2025,350; + * 20;F5;Alecto V1;ID=006c;TEMP=00ed;HUM=38; + * + * Format for Rain + * AAAAAAAA BBBB CCCC DDDD DDDD DDDD DDDD EEEE + * 01100001 0110 1100 1010 1011 0010 0000 0010 + * RC Type Rain Checksum + * A = Rolling Code + * B = Message type (xyyx = NON temp/humidity data if yy = '11') + * C = fixed to 1100 + * D = Rain (bitvalue * 0.25 mm) + * E = Checksum + * + * Sample: + * 20;A8;DEBUG;Pulses=74;Pulses(uSec)=550,1925,425,4100,425,4100,425,1975,425,1975,425,1975,425,1975,425,4100,400,2000,425,4100,425,4100,425,1975,425,4100,425,4100,425,1975,425,1975,425,4100,425,1975,425,4100,400,1975,425,4100,425,1975,425,4100,425,4100,425,1975,450,1975,425,4100,450,1950,450,1950,450,1950,425,1975,450,1950,450,1950,475,1925,500,4025,475,1950,475; + * 20;A9;Alecto V1;ID=0086;RAIN=04d5; + * + * Format for Windspeed + * AAAAAAAA BBBB CCCC CCCC CCCC DDDDDDDD EEEE + * RC Type Windspd Checksum + * A = Rolling Code + * B = Message type (xyyx = NON temp/humidity data if yy = '11') + * C = Fixed to 1000 0000 0000 + * D = Windspeed (bitvalue * 0.2 m/s, correction for webapp = 3600/1000 * 0.2 * 100 = 72) + * E = Checksum + * + * Format for Winddirection & Windgust + * AAAAAAAA BBBB CCCD DDDD DDDD EEEEEEEE FFFF + 01110000 0000 1111 1011 0000 00000000 0101 + * RC Type Winddir Windgust Checksum + * A = Rolling Code + * B = Message type (xyyx = NON temp/humidity data if yy = '11') + * C = Fixed to 111x + * D = Wind direction 0-511 in 0.7 degree steps? + * E = Windgust (bitvalue * 0.2 m/s, correction for webapp = 3600/1000 * 0.2 * 100 = 72) + * F = Checksum + * + * Sample: + * 20;53;DEBUG;Pulses=74;Pulses(uSec)=425,3800,350,1825,350,1825,325,1825,350,1825,325,3800,350,3800,350,1825,325,3800,350,1825,325,1800,350,1825,350,1825,325,1825,325,3800,325,1825,350,1800,350,1825,325,3825,325,3800,325,1825,325,1825,325,1800,325,1825,350,3800,325,1825,325,3800,350,1800,350,1800,350,3800,350,1825,325,1825,325,1825,325,1825,350,1825,325,1925,325; + \*********************************************************************************************/ +#define WS3500_PULSECOUNT 74 + +#ifdef PLUGIN_030 +boolean Plugin_030(byte function, char *string) { + if (RawSignal.Number != WS3500_PULSECOUNT) return false; + unsigned long bitstream=0L; + byte nibble0=0; + byte nibble1=0; + byte nibble2=0; + byte nibble3=0; + byte nibble4=0; + byte nibble5=0; + byte nibble6=0; + byte nibble7=0; + byte checksum=0; + int temperature=0; + byte humidity=0; + unsigned int rain=0; + byte windspeed=0; + byte windgust=0; + int winddirection=0; + byte checksumcalc = 0; + byte rc=0; + byte battery=0; + //================================================================================== + for(byte x=2; x<=64; x=x+2) { + if (RawSignal.Pulses[x+1]*RawSignal.Multiply > 700) return false; // in between pulses should be short + if (RawSignal.Pulses[x]*RawSignal.Multiply > 2560) { + bitstream = ((bitstream >> 1) |(0x1L << 31)); + } else { + bitstream = (bitstream >> 1); + } + } + for(byte x=66; x<=72; x=x+2) { + if (RawSignal.Pulses[x]*RawSignal.Multiply > 2560) { + checksum = ((checksum >> 1) |(0x1L << 3)); + } else { + checksum = (checksum >> 1); + } + } + //================================================================================== + if (bitstream == 0) return false; // Perform a sanity check + //================================================================================== + // Prevent repeating signals from showing up + //================================================================================== + if( (SignalHash!=SignalHashPrevious) || ((RepeatingTimer+1000> 28) & 0xf; + nibble6 = (bitstream >> 24) & 0xf; + nibble5 = (bitstream >> 20) & 0xf; + nibble4 = (bitstream >> 16) & 0xf; + nibble3 = (bitstream >> 12) & 0xf; + nibble2 = (bitstream >> 8) & 0xf; + nibble1 = (bitstream >> 4) & 0xf; + nibble0 = bitstream & 0xf; + //================================================================================== + // Perform checksum calculations, Alecto checksums are Rollover Checksums by design! + if ((nibble2 & 0x6) != 6) { // temperature packet + checksumcalc = (0xf - nibble0 - nibble1 - nibble2 - nibble3 - nibble4 - nibble5 - nibble6 - nibble7) & 0xf; + } else { + if ((nibble3 & 0x7) == 3) { // Rain packet + checksumcalc = (0x7 + nibble0 + nibble1 + nibble2 + nibble3 + nibble4 + nibble5 + nibble6 + nibble7) & 0xf; + } else { // Wind packet + checksumcalc = (0xf - nibble0 - nibble1 - nibble2 - nibble3 - nibble4 - nibble5 - nibble6 - nibble7) & 0xf; + } + } + if (checksum != checksumcalc) return false; + //================================================================================== + battery=(nibble2)&0x1; // get battery indicator + nibble2=(nibble2)&0x06; // prepare nibble to contain only the needed bits + nibble3=(nibble3)&0x07; // prepare nibble to contain only the needed bits + //================================================================================== + rc = bitstream & 0xff; + + if ((nibble2) != 6) { // nibble 2 needs to be set to something other than 'x11x' to be a temperature packet + // Temperature packet + temperature = (bitstream >> 12) & 0xfff; + //fix 12 bit signed number conversion + if ((temperature & 0x800) == 0x800) { + if ((nibble2 & 0x6) != 0) return false; // reject alecto v4 on alecto v1... (causing high negative temperatures with valid checksums) + temperature=4096-temperature; // fix for minus temperatures + if (temperature > 0x258) return false; // temperature out of range ( > -60.0 degrees) + temperature=temperature | 0x8000; // turn highest bit on for minus values + } else { + if (temperature > 0x258) return false; // temperature out of range ( > 60.0 degrees) + } + humidity = (16 * nibble7) + nibble6; + if (humidity > 0x99) return false; // Humidity out of range, assume ALL data is bad? + //================================================================================== + // Output + // ---------------------------------- + sprintf(pbuffer, "20;%02X;", PKSequenceNumber++); // Node and packet number + Serial.print( pbuffer ); + // ---------------------------------- + Serial.print(F("Alecto V1;")); // Label + sprintf(pbuffer, "ID=%02x%02x;", (rc &0x03), (rc &0xfc) ); // ID is split into channel number and rolling code + Serial.print( pbuffer ); + sprintf(pbuffer, "TEMP=%04x;", temperature); + Serial.print( pbuffer ); + if (humidity < 0x99) { // Some AlectoV1 devices actually lack the humidity sensor and always report 99% + sprintf(pbuffer, "HUM=%02x;", humidity); // Only report humidity when it is below 99% + Serial.print( pbuffer ); + } + if (battery==0) { + Serial.print("BAT=OK;"); + } else { + Serial.print("BAT=LOW;"); + } + Serial.println(); + //================================================================================== + RawSignal.Repeats=true; // suppress repeats of the same RF packet + RawSignal.Number=0; + return true; + } else { + if ((nibble3) == 3) { // Rain packet + rain = ((bitstream >> 16) & 0xffff); + //================================================================================== + // Output + // ---------------------------------- + sprintf(pbuffer, "20;%02X;", PKSequenceNumber++); // Node and packet number + Serial.print( pbuffer ); + // ---------------------------------- + Serial.print(F("Alecto V1;")); // Label + sprintf(pbuffer, "ID=00%02x;", rc); // ID + Serial.print( pbuffer ); + sprintf(pbuffer, "RAIN=%04x;", rain); + Serial.print( pbuffer ); + Serial.println(); + //================================================================================== + RawSignal.Repeats=true; // suppress repeats of the same RF packet + RawSignal.Number=0; + return true; + } + if ((nibble3) == 1) { // windspeed packet + windspeed = ((bitstream >> 24) & 0xff); + windspeed = windspeed*72; + //================================================================================== + // Output + // ---------------------------------- + sprintf(pbuffer, "20;%02X;", PKSequenceNumber++); // Node and packet number + Serial.print( pbuffer ); + // ---------------------------------- + Serial.print(F("Alecto V1;")); // Label + sprintf(pbuffer, "ID=00%02x;", rc); // ID + Serial.print( pbuffer ); + sprintf(pbuffer, "WINSP=%04x;", windspeed); + Serial.print( pbuffer ); + Serial.println(); + //================================================================================== + RawSignal.Repeats=true; // suppress repeats of the same RF packet + RawSignal.Number=0; + return true; + } + if ((nibble3) == 7) { // winddir packet + winddirection = ((bitstream >> 15) & 0x1ff) / 45; // ??? + winddirection = winddirection & 0x0f; + windgust = ((bitstream >> 24) & 0xff); + windgust = windgust*72; + //================================================================================== + // Output + // ---------------------------------- + sprintf(pbuffer, "20;%02X;", PKSequenceNumber++); // Node and packet number + Serial.print( pbuffer ); + // ---------------------------------- + Serial.print(F("Alecto V1;")); // Label + sprintf(pbuffer, "ID=00%02x;", rc); // ID + Serial.print( pbuffer ); + sprintf(pbuffer, "WINDIR=%04d;", winddirection); + Serial.print( pbuffer ); + sprintf(pbuffer, "WINGS=%04x;", windgust); + Serial.print( pbuffer ); + Serial.println(); + //================================================================================== + RawSignal.Repeats=true; // suppress repeats of the same RF packet + RawSignal.Number=0; + return true; + } + } + return false; +} +#endif // PLUGIN_030 diff --git a/Plugins/Plugin_031.c b/Plugins/Plugin_031.c new file mode 100644 index 0000000..4409d02 --- /dev/null +++ b/Plugins/Plugin_031.c @@ -0,0 +1,181 @@ +//####################################################################################################### +//## This Plugin is only for use with the RFLink software package ## +//## Plugin-31 AlectoV3 ## +//####################################################################################################### +/*********************************************************************************************\ + * Dit protocol zorgt voor ontvangst van Alecto weerstation buitensensoren + * WS1100, WS1200, WSD-19 + * + * Author : StuntTeam + * Support : http://sourceforge.net/projects/rflink/ + * License : This code is free for use in any open source project when this header is included. + * Usage of any parts of this code in a commercial application is prohibited! + ********************************************************************************************* + * Changelog: v1.0 initial release + ********************************************************************************************* + * Technische informatie: + * Decodes signals from Alecto Weatherstation outdoor unit, type 3 (94/126 pulses, 47/63 bits, 433 MHz). + * WS1100 Message Format: (7 bits preamble, 5 Bytes, 40 bits): + * AAAAAAA AAAABBBB BBBB__CC CCCCCCCC DDDDDDDD EEEEEEEE + * Temperature Humidity Checksum + * A = start/unknown, first 8 bits are always 11111111 + * B = Rolling code + * C = Temperature (10 bit value with -400 base) + * D = Checksum + * E = Humidity + * + * WS1200 Message Format: (7 bits preamble, 7 Bytes, 56 bits): + * AAAAAAA AAAABBBB BBBB__CC CCCCCCCC DDDDDDDD DDDDDDDD EEEEEEEE FFFFFFFF + * Temperature Rain LSB Rain MSB ???????? Checksum + * A = start/unknown, first 8 bits are always 11111111 + * B = Rolling code + * C = Temperature (10 bit value with -400 base) + * D = Rain ( * 0.3 mm) + * E = ? + * F = Checksum + + AAAAAAA AAAABBBB BBBBCCCC CCCCCCCC DDDDDDDD DDDDDDDD EEEEEEEE FFFFFFFF + 1111111 00111010 01010010 10000010 00000000 00000000 11111111 10111010 1 24,2 gr 0 mm + 1111111 00111010 01010010 10000010 00000001 00000000 11111111 11111100 0 24,2 gr 0,3 mm + 1111111 00111010 01010010 10000010 00000010 00000000 11111111 00110110 0 24,2 gr 0,6 mm + 1111111 00111010 01010010 10000010 00001000 00000000 11111111 11101000 1 24,2 gr 2,4 mm + 1111111 00111010 01010010 10000010 00001101 00000000 11111111 10000111 0 24,2 gr 3.9 mm + 1111111 00111010 01010010 01110001 00001101 00000000 11111111 01000010 0 22,5 gr 3,9 mm + 1111111 00111010 01010010 01010111 00001101 00000000 11111111 00111011 1 19,9 gr 3,9 mm + 1111111 00111010 01010010 00111110 00010010 00000000 11111111 00111001 1 17,4 gr 5,4 mm + 1111111 00111010 01010010 00101000 00010010 00000000 11111111 00001000 0 15,2 gr 5,4 mm + + 1111111 00111010 01010001 00101011 10011010 00000001 11111111 10100011 -10,1 gr/123,0 mm + WS1200 temp:-101 + WS1200 rain LSB:154 + WS1200 rain MSB:1 + WS1200 rain:1230 + + 1111111 00111010 01010001 10101101 10011111 00000001 11111111 00110100 2,9 gr/124,5mm + WS1200 temp:29 + WS1200 rain LSB:159 + WS1200 rain MSB:1 + WS1200 rain:1245 + + + * 20;AE;DEBUG;Pulses=126;Pulses(uSec)=900,950,825,450,325,450,325,950,325,450,325,450,825,950,825,450,325,950,825,450,350,950,325,450,825,950,825,450,325,450,325,950,825,925,350,450,825,950,825,925,350,450,825,450,350,925,825,450,350,450,325,950,350,450,825,950,325,450,350,450,325,450,825,450,325,450,325,450,325,450,325,950,825,950,325,450,825,950,325,450,825,450,325,950,325,450,325,450,825,925,350,450,350,450,825,950,825,925,350,425,350,450,350,450,350,450,350,450,825,950,825,950,325,450,350,450,825,950,825,950,825,950,325,450,325; + * 20;AF;Alecto V3;ID=009a;TEMP=ffe7;RAIN=7a; + \*********************************************************************************************/ +#define WS1100_PULSECOUNT 94 +#define WS1200_PULSECOUNT 126 +#define ALECTOV3_PULSEMID 300/RAWSIGNAL_SAMPLE_RATE + +#ifdef PLUGIN_031 +uint8_t Plugin_031_ProtocolAlectoCRC8( uint8_t *addr, uint8_t len); +unsigned int Plugin_031_ProtocolAlectoRainBase=0; + +boolean Plugin_031(byte function, char *string) { + if ((RawSignal.Number != WS1100_PULSECOUNT) && (RawSignal.Number != WS1200_PULSECOUNT)) return false; + + unsigned long bitstream1=0L; + unsigned long bitstream2=0L; + byte rc=0; + int temperature=0; + byte humidity=0; + unsigned int rain=0; + byte checksum=0; + byte checksumcalc=0; + byte data[6]; + //================================================================================== + for (byte x=15; x<=77; x=x+2) { // get first 32 relevant bits + if (RawSignal.Pulses[x] < ALECTOV3_PULSEMID) { + bitstream1 = (bitstream1 << 1) | 0x1; + } else { + bitstream1 = (bitstream1 << 1); + } + } + for (byte x=79; x<=141; x=x+2) { // get second 32 relevant bits + if (RawSignal.Pulses[x] < ALECTOV3_PULSEMID) { + bitstream2 = (bitstream2 << 1) | 0x1; + } else { + bitstream2 = (bitstream2 << 1); + } + } + //================================================================================== + if (bitstream1 == 0) return false; // Sanity check + data[0] = (bitstream1 >> 24) & 0xff; // Sort data + data[1] = (bitstream1 >> 16) & 0xff; + data[2] = (bitstream1 >> 8) & 0xff; + data[3] = (bitstream1 >> 0) & 0xff; + data[4] = (bitstream2 >> 24) & 0xff; + data[5] = (bitstream2 >> 16) & 0xff; + // ---------------------------------- + if (RawSignal.Number == WS1200_PULSECOUNT) { // verify checksum + checksum = (bitstream2 >> 8) & 0xff; + checksumcalc = Plugin_031_ProtocolAlectoCRC8(data, 6); + } else { + checksum = (bitstream2 >> 24) & 0xff; + checksumcalc = Plugin_031_ProtocolAlectoCRC8(data, 4); + } + if (checksum != checksumcalc) return false; + // ---------------------------------- + rc = (bitstream1 >> 20) & 0xff; + temperature = ((bitstream1 >> 8) & 0x3ff); // 299=12b -400 (0x190) = FF9b + //temperature = ((bitstream1 >> 8) & 0x3ff) - 400; // 299=12b -400 (0x190) = FF9b + if (temperature < 400) { // negative temperature value + temperature = 400 - temperature; + temperature=temperature | 0x8000; // turn highest bit on for minus values + } else { + if (temperature > 0x258) return false; // temperature out of range ( > 60.0 degrees) + } + //================================================================================== + // Output + // ---------------------------------- + sprintf(pbuffer, "20;%02X;", PKSequenceNumber++); // Node and packet number + Serial.print( pbuffer ); + // ---------------------------------- + Serial.print(F("Alecto V3;")); // Label + sprintf(pbuffer, "ID=00%02x;", rc); // ID + Serial.print( pbuffer ); + sprintf(pbuffer, "TEMP=%04x;", temperature); + Serial.print( pbuffer ); + + if (RawSignal.Number == WS1100_PULSECOUNT) { + humidity = bitstream1 & 0xff; // alleen op WS1100? + sprintf(pbuffer, "HUM=%02x;", humidity); + Serial.print( pbuffer ); + } else { + rain = (((bitstream2 >> 24) & 0xff) * 256) + ((bitstream1 >> 0) & 0xff); + // check if rain unit has been reset! + if (rain < Plugin_031_ProtocolAlectoRainBase) Plugin_031_ProtocolAlectoRainBase=rain; + if (Plugin_031_ProtocolAlectoRainBase > 0) { + //UserVar[basevar+1 -1] += ((float)rain - Plugin_031_ProtocolAlectoRainBase) * 0.30; + sprintf(pbuffer, "RAIN=%02x;", (rain)&0xff); + Serial.print( pbuffer ); + } + Plugin_031_ProtocolAlectoRainBase = rain; + } + Serial.println(); + //================================================================================== + RawSignal.Repeats=true; // suppress repeats of the same RF packet + RawSignal.Number=0; // do not process the packet any further + return true; +} + +/*********************************************************************************************\ + * Calculates CRC-8 checksum + * reference http://lucsmall.com/2012/04/29/weather-station-hacking-part-2/ + * http://lucsmall.com/2012/04/30/weather-station-hacking-part-3/ + * https://github.com/lucsmall/WH2-Weather-Sensor-Library-for-Arduino/blob/master/WeatherSensorWH2.cpp + \*********************************************************************************************/ +uint8_t Plugin_031_ProtocolAlectoCRC8( uint8_t *addr, uint8_t len) +{ + uint8_t crc = 0; + // Indicated changes are from reference CRC-8 function in OneWire library + while (len--) { + uint8_t inbyte = *addr++; + for (uint8_t i = 8; i; i--) { + uint8_t mix = (crc ^ inbyte) & 0x80; // changed from & 0x01 + crc <<= 1; // changed from right shift + if (mix) crc ^= 0x31;// changed from 0x8C; + inbyte <<= 1; // changed from right shift + } + } + return crc; +} +#endif // PLUGIN_031 diff --git a/Plugins/Plugin_032.c b/Plugins/Plugin_032.c new file mode 100644 index 0000000..73da805 --- /dev/null +++ b/Plugins/Plugin_032.c @@ -0,0 +1,121 @@ +//####################################################################################################### +//## This Plugin is only for use with the RFLink software package ## +//## Plugin-32 AlectoV4 ## +//####################################################################################################### +/*********************************************************************************************\ + * This plugin takes care of decoding the protocol used for outdoor sensors of the Alecto weather stations + * following protocol version 4 + * This Plugin works at least with: Banggood SKU174397, Sako CH113, Homemart/Onemall FD030 and Blokker (Dake) 1730796 outdoor sensors + * But probably with many others as the OEM sensor is sold under many brand names + * + * Author : StuntTeam + * Support : http://sourceforge.net/projects/rflink/ + * License : This code is free for use in any open source project when this header is included. + * Usage of any parts of this code in a commercial application is prohibited! + ********************************************************************************************* + * Technische informatie: + * Message Format: (9 nibbles, 36 bits): + * + * Format for Temperature Humidity + * AAAAAAAA AAAA BCDD EEEE EEEE EEEE FFFFFFFF + * 01011100 0001 1000 1111 0111 1011 00001110 + 01110000 0000 1111 1011 0000 0000 00000101 + 10110101 0000 1x00 01001001 + + 01000101 1000 0110 1111 0000 1100 00100110 + 01011111 1101 1000 0000 1111 0001 00001110 + 01000101 1000 0010 + * + * A = Rolling Code + * B = 1 (fixed value) + * C = 0=scheduled transmission, 1=requested transmission (button press) + * D = Channel number (00=ch1 01=ch2 10=ch3) + * E = Temperature (two's complement) + * F = Humidity BCD format + * + * 20;3F;DEBUG;Pulses=74;Pulses(uSec)=525,1725,425,3600,425,1725,425,3600,425,3625,425,1725,425,3600,425,1725,425,1725,425,1700,425,3600,425,3600,425,3600,425,1725,425,1725,425,1725,425,1725,425,1725,400,1725,425,3600,425,1725,425,1725,425,1725,425,3600,400,1725,425,1725,425,3625,400,1725,425,1725,425,1750,400,3600,425,1725,400,1750,400,3625,425,1725,400,1725,425; + * 20;C2;DEBUG;Pulses=76;Pulses(uSec)=325,500,250,1800,375,3650,375,1775,375,3650,375,3650,375,1775,375,3650,375,1800,350,1800,375,3650,375,3650,375,3650,375,3650,375,1775,375,1775,375,1775,375,1775,375,1775,375,1775,375,1775,375,3650,375,3650,375,3650,375,1775,375,3650,375,3650,375,1775,375,1775,375,1775,375,1775,375,1775,375,1775,375,3650,375,3650,375,3650,375,3650,375; + * 20;3E;DEBUG;Pulses=78;Pulses(uSec)=525,250,500,375,600,1650,450,3550,475,1675,450,3550,475,3550,450,1675,450,3575,450,1675,450,1700,450,1700,450,3575,425,3600,450,3575,475,1700,425,1725,425,1725,425,1725,400,1725,425,1725,425,3625,425,1725,425,1725,425,1725,425,3600,425,1725,400,1725,425,3600,425,1725,425,1725,400,1725,425,3600,400,1725,425,1725,400,3600,425,1725,425,1725,400; + \*********************************************************************************************/ +#ifdef PLUGIN_032 +boolean Plugin_032(byte function, char *string) { + if (RawSignal.Number < 74 || RawSignal.Number > 78 ) return false; + unsigned long bitstream=0L; + int temperature=0; + int humidity=0; + byte rc=0; + byte rc2=0; + + //================================================================================== + byte start=0; + if (RawSignal.Number == 78) start=4; + if (RawSignal.Number == 76) start=2; + for(int x=2+start; x<=56+start; x=x+2) { // Get first 28 bits + if (RawSignal.Pulses[x+1]*RawSignal.Multiply > 550) return false; + if (RawSignal.Pulses[x]*RawSignal.Multiply > 3000) { + bitstream = (bitstream << 1) | 0x01; + } else { + if (RawSignal.Pulses[x]*RawSignal.Multiply > 1500) { + if (RawSignal.Pulses[x]*RawSignal.Multiply > 2100) return false; + bitstream = (bitstream << 1); + } else { + return false; + } + } + } + for(int x=58+start;x<=72+start; x=x+2) { // Get remaining 8 bits + if (RawSignal.Pulses[x+1]*RawSignal.Multiply > 550) return false; + if(RawSignal.Pulses[x]*RawSignal.Multiply > 3000) { + humidity = (humidity << 1) | 0x01; + } else { + humidity = (humidity << 1); + } + } + //================================================================================== + // Prevent repeating signals from showing up + //================================================================================== + if( (SignalHash!=SignalHashPrevious) || ((RepeatingTimer+3000) < millis()) ) { // 1000 + // not seen the RF packet recently + if (bitstream == 0) return false; // Sanity check + if (humidity==0) return false; // Sanity check + } else { + // already seen the RF packet recently + return true; + } + //================================================================================== + // Sort data + rc = (bitstream >> 20) & 0xff; + rc2= (bitstream >> 12) & 0xfb; + if ( ((rc2)&0x08) != 0x08) return false; // needs to be 1 + temperature = (bitstream) & 0xfff; + //fix 12 bit signed number conversion + if ((temperature & 0x800) == 0x800) { + temperature=4096-temperature; // fix for minus temperatures + if (temperature > 0x258) return false; // temperature out of range ( > 60.0 degrees) + temperature=temperature | 0x8000; // turn highest bit on for minus values + } else { + if (temperature > 0x258) return false; // temperature out of range ( > 60.0 degrees) + } + if (humidity > 99) return false; // Humidity out of range + //================================================================================== + // Output + // ---------------------------------- + sprintf(pbuffer, "20;%02X;", PKSequenceNumber++);// Node and packet number + Serial.print( pbuffer ); + // ---------------------------------- + Serial.print(F("Alecto V4;")); // Label + sprintf(pbuffer, "ID=%02x%02x;", rc, rc2); // ID + Serial.print( pbuffer ); + sprintf(pbuffer, "TEMP=%04x;", temperature); + Serial.print( pbuffer ); + if (humidity < 99) { // Only report valid humidty values + sprintf(pbuffer, "HUM=%02d;", humidity); // decimal value.. + Serial.print( pbuffer ); + } + Serial.println(); + //================================================================================== + RawSignal.Repeats=true; // suppress repeats of the same RF packet + RawSignal.Number=0; + return true; +} +#endif // PLUGIN_032 diff --git a/Plugins/Plugin_033.c b/Plugins/Plugin_033.c new file mode 100644 index 0000000..36ab814 --- /dev/null +++ b/Plugins/Plugin_033.c @@ -0,0 +1,95 @@ +//####################################################################################################### +//## This Plugin is only for use with the RFLink software package ## +//## Plugin-33 Conrad ## +//####################################################################################################### +/*********************************************************************************************\ + * This plugin takes care of decoding Conrad Pool Thermomether model 9771 + * + * Author : StuntTeam + * Support : http://sourceforge.net/projects/rflink/ + * License : This code is free for use in any open source project when this header is included. + * Usage of any parts of this code in a commercial application is prohibited! + ********************************************************************************************* + * Changelog: v1.0 initial release + ********************************************************************************************* + * Technical information: + * Decodes signals from a Conrad Model 9771 Pool Thermometer, (80 pulses, 40 bits, 433 MHz). + * Message Format: + * AAAAAAAA BBBBBBBB CCCCCCCCCC DD EEEE FFFF GGGG + * 00000000 00010010 1100101101 01 1001 0001 1001 + * + * A = Always 0 ? + * B = Device id ? + * C = Temperature digits + * D = Temperature ones + * E = Temperature tens + * F = Always 1? + * G = Unknown + * + * Sample: + * 20;8D;DEBUG;Pulses=80;Pulses(uSec)=1890,5760,1890,5730,1890,5760,1890,5730,1890,5760,1890,5760,1890,5760,1890,5760,1890,5760,1890,5760,1890,5760,5910,1830,1890,5640,1890,5760,5910,1830,1890,5640,5910,1830,1860,5640,5910,1830,1890,5610,5910,1830,5910,1830,5910,1830,1890,5400,5910,1830,1890,5610,1890,5760,5910,1830,5910,1830,1890,5520,1890,5760,5910,1860,1890,5610,1890,5760,1890,5760,5910,1830,1890,5610,5910,1830,5910,1830,1860,6990; + \*********************************************************************************************/ +#define CONRAD_PULSECOUNT 80 +#define CONRAD_PULSEMAX 5000/RAWSIGNAL_SAMPLE_RATE +#define CONRAD_PULSEMIN 2300/RAWSIGNAL_SAMPLE_RATE + +#ifdef PLUGIN_033 +boolean Plugin_033(byte function, char *string) { + if (RawSignal.Number != CONRAD_PULSECOUNT) return false; + unsigned long bitstream=0L; + unsigned int temperature=0; + unsigned int rc=0; + byte checksum=0; + byte bitcount=0; // bit counter (counting first 8 bits that need + //================================================================================== + // get all 28 bits + for(byte x=1;x <=CONRAD_PULSECOUNT-1;x+=2) { + if (RawSignal.Pulses[x] > CONRAD_PULSEMAX) { + if (RawSignal.Pulses[x+1] > CONRAD_PULSEMAX) if ( (x+1) < CONRAD_PULSECOUNT ) return false; // invalid pulse length + if (bitcount > 7) { + bitstream = (bitstream << 1) | 0x1; + } else { + return false; // first 8 bits should all be zeros + } + bitcount++; + } else { + if (RawSignal.Pulses[x] > CONRAD_PULSEMIN) return false; // invalid pulse length + if (RawSignal.Pulses[x+1] < CONRAD_PULSEMIN) return false; // invalid pulse length + if (bitcount > 7) bitstream = (bitstream << 1); + bitcount++; + } + } + //================================================================================== + // Prevent repeating signals from showing up + //================================================================================== + Serial.println(bitstream,HEX); + if( (SignalHash!=SignalHashPrevious) || (RepeatingTimer+1000> 4) & 0x0f; + if (checksum != 0x01) return false; + + rc=(bitstream >> 24); + temperature=(bitstream >> 14) & 0x3ff; + temperature=temperature-500; + //================================================================================== + // Output + // ---------------------------------- + sprintf(pbuffer, "20;%02X;", PKSequenceNumber++); // Node and packet number + Serial.print( pbuffer ); + Serial.print(F("Conrad;")); // Label + sprintf(pbuffer, "ID=%04x;", rc); // ID + Serial.print( pbuffer ); + sprintf(pbuffer, "TEMP=%04x;", temperature); + Serial.print( pbuffer ); + Serial.println(); + //================================================================================== + RawSignal.Repeats=true; // suppress repeats of the same RF packet + RawSignal.Number=0; + return true; +} +#endif // PLUGIN_033 diff --git a/Plugins/Plugin_034.c b/Plugins/Plugin_034.c new file mode 100644 index 0000000..2c10ded --- /dev/null +++ b/Plugins/Plugin_034.c @@ -0,0 +1,313 @@ +//####################################################################################################### +//## This Plugin is only for use with the RFLink software package ## +//## Plugin-34 Cresta ## +//####################################################################################################### +/*********************************************************************************************\ + * Dit protocol zorgt voor ontvangst van Cresta temperatuur weerstation buitensensoren + * Tevens alle sensoren die het Cresta (Hideki) protocol volgen waaronder: + * Hideki, TFA Nexus, Mebus, Irox, Irox-Pro X, Honeywell, Cresta TE923, TE923W, TE821W, + * WXR810, DV928, Ventus W906 + * + * Author : StuntTeam + * Support : http://sourceforge.net/projects/rflink/ + * License : This code is free for use in any open source project when this header is included. + * Usage of any parts of this code in a commercial application is prohibited! + ********************************************************************************************* + * Changelog: v1.0 initial release + ********************************************************************************************* + * Technical Information: + * Decodes signals from a Cresta Weatherstation outdoor unit, (29 pulses, 28 bits, 433 MHz). + * Message Format can be found at: http://members.upc.nl/m.beukelaar/Crestaprotocol.pdf + * Thermo/Hygro: 10 bytes, seen as 128, 132, 134, 136, 138 pulses + * Anemometer: 14 bytes + * UV Index: 11 bytes + * Rain: 9 bytes, seen as 132, 136 pulses + \*********************************************************************************************/ +#define CRESTA_MIN_PULSECOUNT 124 // unknown until we have a collection of all packet types but this seems to be the minimum +#define CRESTA_MAX_PULSECOUNT 284 // unknown until we have a collection of all packet types +#define CRESTA_PULSEMID 700/RAWSIGNAL_SAMPLE_RATE + +#ifdef PLUGIN_034 +byte Plugin_034_reverseBits(byte data); +byte Plugin_034_WindDirSeg(byte data); + +boolean Plugin_034(byte function, char *string){ + if ((RawSignal.Number < CRESTA_MIN_PULSECOUNT) || (RawSignal.Number > CRESTA_MAX_PULSECOUNT) ) return false; + + int sensor_data=0; + int windtemp=0; + int windchill=0; + int windgust=0; + int windspeed=0; + int winddirection=0; + int uv=0; + + byte checksum=0; + byte data[18]; + byte length=0; + byte channel=0; + int units=0; + byte battery=0; + + byte bytecounter=0; // used for counting the number of received bytes + byte bitcounter=0; // counts number of received bits (converted from pulses) + int pulseposition=1; // first pulse is always empty + byte halfbit=0; // high pulse = 1, 2 low pulses = 0, halfbit keeps track of low pulses + byte parity=0; // to calculate byte parity + // ================================================================================== + // get bytes and determine if byte parity is set correctly for the cresta protocol on the fly + do { + //if(RawSignal.Pulses[pulseposition]*RawSignal.Multiply > 700) { // high value = 1 bit + if(RawSignal.Pulses[pulseposition] > CRESTA_PULSEMID) { // high value = 1 bit + if (halfbit==1) { // cant receive a 1 bit after a single low value + return false; // pulse error, must not be a Cresta packet or reception error + } + if (bitcounter==8) { + if (parity != 1) { // now receiving parity bit + return false; // parity error, must not be a Cresta packet or reception error + } else { + bitcounter=0; // reset for next byte + parity=0; // reset for next byte + halfbit=0; // wait for next first low or high pulse + bytecounter++; // 1 byte received + } + } else { + data[bytecounter] = (data[bytecounter] << 1) | 0x1; // 1 bit + parity=parity ^1; // update parity + bitcounter++; // received a bit + halfbit=0; // waiting for first low or a new high pulse + } + } else { + if (halfbit == 0) { // 2 times a low value = 0 bit + halfbit=1; // first half received + } else { + if (bitcounter==8) { + if (parity != 0) { // now receiving parity bit + return false; // parity error, must not be a Cresta packet or reception error + } else { + bitcounter=0; // reset for next byte + parity=0; // reset for next byte + halfbit=0; // wait for next first low or high pulse + bytecounter++; // 1 byte received + } + } else { + data[bytecounter] = (data[bytecounter] << 1); // 0 bit + parity=parity ^0; // update parity + bitcounter++; // received a bit + halfbit=0; // wait for next first low or high pulse + } + } + } + pulseposition++; // point to next pulse + if (pulseposition > RawSignal.Number) break; // reached the end? done processing + } while(bytecounter < 16); // receive maximum number of bytes from pulses + // ================================================================================== + // all bytes received, make sure checksum is okay + // ================================================================================== + for (byte i=0;i>= 1; // drop bit 0 + if (length > 20) return false; // Additional check for illegal packet lengths to protect against false positives. + if (length == 0) return false; // Additional check for illegal packet lengths to protect against false positives. + // Checksum: XOR of all bytes from byte 1 till byte length+2, should result in 0 + checksum=0; + for (byte i=1;i< length+2 ;i++){ + checksum=checksum^data[i]; + } + if (checksum != 0) return false; + // ================================================================================== + // now process the various sensor types + // ================================================================================== + if ( data[1] > 0x1f && data[1] < 0x40) channel=1; + if ( data[1] > 0x3f && data[1] < 0x60) channel=2; + if ( data[1] > 0x5f && data[1] < 0x80) channel=3; + if ( data[1] > 0x7f && data[1] < 0xa0) channel=1; // no channel settings on Anemometer/rainmeter and uvsensor + if ( data[1] > 0x9f && data[1] < 0xc0) channel=4; + if ( data[1] > 0xbf && data[1] < 0xE0) channel=5; + data[3]=(data[3])&0x1f; + //================================================================================== + // Prevent repeating signals from showing up + //================================================================================== + unsigned long tempval=data[3]; + tempval=((tempval)<<16)+((data[1])<<8)+channel; + if((SignalHash!=SignalHashPrevious) || (RepeatingTimer>6; + // ---------------------------------- + if (data[3] == 0x0c ) { // Anemometer + units=((data[4]>>4)*10) + (data[4]&0x0f) ; + sensor_data=((data[5]&0x3f) * 100) + units; + if ((data[5] & 0x80) != 0x80) { + sensor_data = sensor_data | 0x8000; // set highest bit (minus bit) + } + windtemp=sensor_data; + + units=((data[6]>>4)*10) + (data[6]&0x0f) ; + sensor_data=((data[7]&0x3f) * 100) + units; + if ((data[7] & 0x80) != 0x80) { + sensor_data = sensor_data | 0x8000; // set highest bit (minus bit) + } + windchill=sensor_data; + + windspeed=((data[9]&0x0F)*100) + ((data[8]>>4)*10) + (data[8]&0x0F); + windgust=((data[10]>>4)*100) + ((data[10]&0x0F)*10) + (data[9]>>4); + + //windspeed = (data[9] << 8) + data[8]; + //windgust = (data[10] << 4) + ( (data[9] &0xf0) >> 4 ); + + winddirection = Plugin_034_WindDirSeg( ((data[11] &0xf0) >> 4) ); + winddirection = winddirection & 0x0f; // make sure we dont get overflows + //================================================================================== + // Output + // ---------------------------------- + Serial.print("20;"); + PrintHexByte(PKSequenceNumber++); + Serial.print(F(";Cresta;ID=")); // Label + PrintHexByte(data[1]); + PrintHexByte(channel); + // ---------------------------------- + sprintf(pbuffer, ";WINDIR=%04d;", winddirection); + Serial.print( pbuffer ); + sprintf(pbuffer, "WINSP=%04x;", windspeed); + Serial.print( pbuffer ); + sprintf(pbuffer, "WINGS=%04x;", windgust); + Serial.print( pbuffer ); + sprintf(pbuffer, "WINTMP=%04x;", windtemp); + Serial.print( pbuffer ); + sprintf(pbuffer, "WINCHL=%04x;", windchill); + Serial.print( pbuffer ); + if ( battery != 0) { + Serial.print(F("BAT=OK;")); // Label + } else { + Serial.print(F("BAT=LOW;")); // Label + } + Serial.println(); + //================================================================================== + } else + // ---------------------------------- + if (data[3] == 0x0d ) { // UV Sensor + units=((data[4]>>4)*10) + (data[4]&0x0f) ; + sensor_data=((data[5]&0x3f) * 100) + units; + if ((data[5] & 0x80) != 0x80) { + sensor_data = sensor_data | 0x8000; // set highest bit (minus bit) + } + // UV sensor reports the temperature but does not report negative values!, skip temperature info? + uv = ((data[8] & 0x0f)<<8)+data[7]; + //================================================================================== + // Output + // ---------------------------------- + Serial.print("20;"); + PrintHexByte(PKSequenceNumber++); + Serial.print(F(";Cresta;ID=")); // Label + PrintHexByte(data[1]); + PrintHexByte(channel); + // ---------------------------------- + sprintf(pbuffer, ";TEMP=%04x;", sensor_data); + Serial.print( pbuffer ); + sprintf(pbuffer, "UV=%04x;", uv); + Serial.print( pbuffer ); + if ( battery != 0) { + Serial.print(F("BAT=OK;")); // Label + } else { + Serial.print(F("BAT=LOW;")); // Label + } + Serial.println(); + //================================================================================== + } else // 9F 80 CC 4E 00 00 66 64 + // ---------------------------------- // 9f 80 cc 4e 01 00 66 65 + if (data[3] == 0x0e ) { // Rain meter // 9F 80 CC 4E 76 00 66 12 + sensor_data = (data[5]<<8)+data[4]; // 80=rain 4e=>0E = rain + sensor_data=sensor_data*7; // 66 = always 66 rain units * 0.7 = mm. + //================================================================================== + // Output + // ---------------------------------- + Serial.print("20;"); + PrintHexByte(PKSequenceNumber++); + Serial.print(F(";Cresta;ID=")); // Label + PrintHexByte(data[1]); + PrintHexByte(channel); + // ---------------------------------- + sprintf(pbuffer, ";RAIN=%04x;", sensor_data); + Serial.print( pbuffer ); + if ( battery != 0) { + Serial.print(F("BAT=OK;")); + } else { + Serial.print(F("BAT=LOW;")); + } + Serial.println(); + //================================================================================== + } else + // ---------------------------------- + if (data[3] == 0x1e ) { // Thermo/Hygro + units=((data[4]>>4)*10) + (data[4]&0x0f) ; + sensor_data=((data[5]&0x3f) * 100) + units; + if ((data[5] & 0x80) != 0x80) { + sensor_data = sensor_data | 0x8000; // set highest bit (minus bit) + } + //================================================================================== + // Output + // ---------------------------------- + Serial.print("20;"); + PrintHexByte(PKSequenceNumber++); + Serial.print(F(";Cresta;ID=")); // Label + PrintHexByte(data[1]); + PrintHexByte(channel); + // ---------------------------------- + sprintf(pbuffer, ";TEMP=%04x;", sensor_data); + Serial.print( pbuffer ); + sprintf(pbuffer, "HUM=%02x;", data[6]); + Serial.print( pbuffer ); + if ( battery != 0) { + Serial.print(F("BAT=OK;")); + } else { + Serial.print(F("BAT=LOW;")); + } + Serial.println(); + //================================================================================== + } else { + //================================================================================== + // Output + // ---------------------------------- + Serial.print("20;"); + PrintHexByte(PKSequenceNumber++); + Serial.print(F(";Cresta;DEBUG;ID=")); // Label + PrintHexByte(data[1]); + PrintHexByte(channel); + // ---------------------------------- + PrintHex8( data, length+2); + Serial.print(F(";")); + Serial.println(); + //================================================================================== + } + RawSignal.Repeats=true; // suppress repeats of the same RF packet + RawSignal.Number=0; + return true; +} + +// ********************************************************************************************* +// * Reverse all bits in a byte +// ********************************************************************************************* +byte Plugin_034_reverseBits(byte data) { + byte b = data; + for (byte i = 0; i < 8; ++i) { + data = (data << 1) | (b & 1); + b >>= 1; + } + return data; +} + +byte Plugin_034_WindDirSeg(byte data) { + // Encrypted using: a=-a&0xf; b=a^(a>>1); + data ^= (data & 8) >> 1; /* Solve bit 2 */ + data ^= (data & 4) >> 1; /* Solve bit 1 */ + data ^= (data & 2) >> 1; /* Solve bit 0 */ + return -data & 0xf; +} +#endif // PLUGIN_034 diff --git a/Plugins/Plugin_035.c b/Plugins/Plugin_035.c new file mode 100644 index 0000000..e2e7891 --- /dev/null +++ b/Plugins/Plugin_035.c @@ -0,0 +1,110 @@ +//####################################################################################################### +//## This Plugin is only for use with the RFLink software package ## +//## Plugin-35 Imagintronix ## +//####################################################################################################### +/*********************************************************************************************\ + * This plugin takes care of decoding Imagintronix sensors + * + * Author : StuntTeam + * Support : http://sourceforge.net/projects/rflink/ + * License : This code is free for use in any open source project when this header is included. + * Usage of any parts of this code in a commercial application is prohibited! + ********************************************************************************************* + * Changelog: v1.0 initial release + ********************************************************************************************* + * Technical information: + * + * ~0.5ms high puls followed by ~1ms pause = high + * ~1.5ms high puls followed by ~1ms pause = low + * + * Message Format: + * 11111111 01010101 00000101 01000101 11111111 10011110 + * FF550545FF9E + * AABCDDEEFFGG + * + * A = Preamble, always FF + * B = TX type, always 5 + * C = Address (5/6/7) > low 2 bits = 1/2/3 + * D = Soil moisture 05% + * E = temperature + * F = security code, always F + * G = Checksum 55+05+45+FF=19E CRC value = 9E + * + * Sample: + * 20;02;DEBUG;Pulses=96;Pulses(uSec)=390,870,420,870,420,870,420,870,420,870,420,870,420,870,420,870,1260,870,420,870,1260,870,420,870, 1230,870,420,870,1260,870,420,870,1260,870,1260,870,1260,870,1230,870,1260,870,420,870,1260,870,420,870,1260,870,420,870,1260,870,1260,870,1260,870,420,870,1260,870,420,870,420,870,420,870,420,870,420,870,420,870,420,870,420,870,420,870,420,870,1230,870,1260,870,420,870,420,870,420,840,420,840,1260,6990; + * 111111110101010100000101010001011111111110011110 + \*********************************************************************************************/ +#define IMAGINTRONIX_PULSECOUNT 96 +#define IMAGINTRONIX_PULSEMID 1000/RAWSIGNAL_SAMPLE_RATE +#define IMAGINTRONIX_PULSESHORT 550/RAWSIGNAL_SAMPLE_RATE + +#ifdef PLUGIN_035 +boolean Plugin_035(byte function, char *string) { + if (RawSignal.Number != IMAGINTRONIX_PULSECOUNT) return false; + unsigned int temperature=0; + unsigned int rc=0; + + byte checksum=0; + byte data[8]; + unsigned long bitstream=0L; + //================================================================================== + byte bytecounter=0; // used for counting the number of received bytes + byte bitcounter=0; // counts number of received bits (converted from pulses) + // get bits + for(byte x=1;x < IMAGINTRONIX_PULSECOUNT;x=x+2) { + if (RawSignal.Pulses[x] > IMAGINTRONIX_PULSEMID) { // long pulse = 0 bit + if (x < 95) if ((RawSignal.Pulses[x+1] > IMAGINTRONIX_PULSEMID) || (RawSignal.Pulses[x+1] < IMAGINTRONIX_PULSESHORT)) return false; + data[bytecounter] = (data[bytecounter] << 1); // 0 bit + bitcounter++; // received a bit + } else { // Short pulse = 1 bit + if (RawSignal.Pulses[x] > IMAGINTRONIX_PULSESHORT) return false; // Short pulse too long? + if (x < 95) if ((RawSignal.Pulses[x+1] > IMAGINTRONIX_PULSEMID) || (RawSignal.Pulses[x+1] < IMAGINTRONIX_PULSESHORT)) return false; + data[bytecounter] = (data[bytecounter] << 1) | 0x1; // 1 bit + bitcounter++; // received a bit + } + // prepare for next bit/byte + if (bitcounter==8) { // received 8 bits? + bitcounter=0; // reset for next byte + bytecounter++; // byte received, increase counter + if (bytecounter > 7) return false; // overflow, should not happen + } + } + //================================================================================== + // Verify packet and calculate checksum + //================================================================================== + if (data[0] != 0xff) return false; + checksum=data[1]+data[2]+data[3]+data[4]; + if (((checksum)&0xff) != data[5]) return false; + if ( (data[1] >>4) != 0x5) return false; + if (data[4] != 0xff) return false; + //================================================================================== + // Prevent repeating signals from showing up + //================================================================================== + if( (SignalHash!=SignalHashPrevious) || (RepeatingTimer+1000 temperature 0 - 51.1 + // |-|---------------------> set when minus temperatures -51.2 - 0 + // ================================================================================== +#define MEBUS_PULSECOUNT 58 + +#ifdef PLUGIN_040 +boolean Plugin_040(byte function, char *string) { + if (RawSignal.Number != MEBUS_PULSECOUNT) return false; + unsigned long bitstream=0L; + unsigned int temperature=0; + byte rc=0; + byte checksum=0; + byte data[7]; + byte channel=0; + //================================================================================== + // get all 28 bits + for(byte x=2;x <=MEBUS_PULSECOUNT-2;x+=2) { + if (RawSignal.Pulses[x+1]*RawSignal.Multiply > 550) return false; // make sure inbetween pulses are not too long + if (RawSignal.Pulses[x]*RawSignal.Multiply > 3400) { + bitstream = (bitstream << 1) | 0x1; + } else { + if (RawSignal.Pulses[x]*RawSignal.Multiply > 2000) return false; // invalid pulse length + if (RawSignal.Pulses[x]*RawSignal.Multiply < 1500) return false; // invalid pulse length + bitstream = (bitstream << 1); + } + } + //================================================================================== + // Prevent repeating signals from showing up + //================================================================================== + if( (SignalHash!=SignalHashPrevious) || (RepeatingTimer+1000> 24) & 0x0f; // prepare nibbles from bit stream + data[1] = (bitstream >> 20) & 0x0f; + data[2] = (bitstream >> 16) & 0x0f; + data[3] = (bitstream >> 12) & 0x0f; + data[4] = (bitstream >> 8) & 0x0f; + data[5] = (bitstream >> 4) & 0x0f; + data[6] = (bitstream >> 0) & 0x0f; + //================================================================================== + // first perform a checksum check to make sure the packet is a valid mebus packet + checksum=data[1]+data[2]+data[3]+data[4]+data[5]+data[6]; + checksum=(checksum-1)&0xf; + if (checksum != data[0]) return false; + //================================================================================== + rc=(data[1]<<4) + data[2]; + channel=(data[6])>>2; + temperature=(data[3]<<8)+(data[4]<<4)+data[5]; + if (temperature > 3000) { + temperature=4096-temperature; // fix for minus temperatures + if (temperature > 0x258) return false; // temperature out of range ( > -60.0 degrees) + temperature=temperature | 0x8000; // turn highest bit on for minus values + } else { + if (temperature > 0x258) return false; // temperature out of range ( > 60.0 degrees) + } + //================================================================================== + // Output + // ---------------------------------- + sprintf(pbuffer, "20;%02X;", PKSequenceNumber++); // Node and packet number + Serial.print( pbuffer ); + Serial.print(F("Mebus;")); // Label + sprintf(pbuffer, "ID=%02x%02x;", rc, channel);// ID + Serial.print( pbuffer ); + sprintf(pbuffer, "TEMP=%04x;", temperature); + Serial.print( pbuffer ); + Serial.println(); + //================================================================================== + RawSignal.Repeats=true; // suppress repeats of the same RF packet + RawSignal.Number=0; + return true; +} +#endif // PLUGIN_040 diff --git a/Plugins/Plugin_041.c b/Plugins/Plugin_041.c new file mode 100644 index 0000000..5bcf8c0 --- /dev/null +++ b/Plugins/Plugin_041.c @@ -0,0 +1,250 @@ +//####################################################################################################### +//## This Plugin is only for use with the RFLink software package ## +//## Plugin-43 LaCrosse ## +//####################################################################################################### +/*********************************************************************************************\ + * This plugin takes care of decoding LaCrosse weatherstation outdoor sensors + * It also works for all non LaCrosse sensors that follow this protocol. + * WS7000-15: Anemometer, WS7000-16: Rain precipitation, WS2500-19: Brightness Luxmeter, WS7000-20: Thermo/Humidity/Barometer + * + * Author : StuntTeam + * Support : http://sourceforge.net/projects/rflink/ + * License : This code is free for use in any open source project when this header is included. + * Usage of any parts of this code in a commercial application is prohibited! + ********************************************************************************************* + * Changelog: v1.0 initial release + ********************************************************************************************* + * Meteo Sensor: (162 pulses) + * Each frame is 80 bits long. It is composed of: + * 10 bits of 0 (preamble) + * 14 blocks of four bits separated by a 1 bit to be checked and skipped + * + * 0100 0111 1000 0101 0010 0100 0000 0011 0001 0001 1000 1101 1110 1011 + * aaaa bbbb cccc cccc cccc dddd dddd dddd eeee eeee eeee ffff gggg hhhh + * + * a = sensor type (04=Meteo sensor) + * b = sensor address + * c = temperature BCD, reversed + * d = humidity BCD, reversed + * e = air pressure, BCD reversed + 200 offset in hPa + * f = unknown? + * g = xor value + * h = checksum value + * + * Sample: + * 20;07;DEBUG;Pulses=162;Pulses(uSec)=825,275,750,275,750,300,750,300,750,300,750,275,750,275,750,300,750,300,750,300,250,800,725,300,750,300,250,800,725,300,225,800,225,800,250,800,250,800,725,300,250,800,725,300,750,300,725,300,250,800,250,800,225,800,750,300,250,800,725,300,250,800,725,300,250,800,725,300,725,300,250,800,725,300,725,300,250,800,725,300,250,800,725,300,725,300,725,300,725,300,250,800,225,800,225,800,725,300,725,300,225,800,225,800,725,300,725,300,725,300,250,800,250,800,725,300,725,300,725,300,250,800,725,300,725,300,725,300,225,800,225,800,225,800,725,300,225,800,225,800,250,800,725,300,225,800,225,800,225,800,250,800,250,800,225,800,725,300,225,800,225,600; + * 1010101010101010101001101001100101010110011010100101011001100110011010011010011001101010100101011010010110101001011010100110101001010110010101100101010101011001 00 + * 0000000000 1 0010 1 1110 1 0001 1 1010 1 0100 1 0010 1 0000 1 1100 1 1000 1 1000 1 0001 1 1011 1 0111 1 1101 0 + * 0010 1110 0001 1010 0100 0010 0000 1100 1000 1000 0001 1011 0111 1101 + * 0100 0111 1000 0101 0010 0100 0000 0011 0001 0001 1000 1101 1110 1011 + * 4 7 852 403 118 D E B + * 25.8 30.4 811+200 + * -------------------------------------------------------------------------------------------- + * Rain Packet: (92 pulses) + * Each frame is 46 bits long. It is composed of: + * 10 bits of 0 (preamble) + * 7 blocks of four bits separated by a 1 bit to be checked and skipped + * + * The 1st bit of each word is LSB, so we have to reverse the 4 bits of each word. + * Example + * 0000000000 0010 1111 1011 0010 1011 1111 1101 + * aaaa bbbb ccc1 ccc2 ccc3 dddd eeee + * 2 F B 2 B F D + * + * a = sensor type (2=Rain meter) + * b = sensor address + * c = rain data (LSB thus the right order is c3 c2 c1) + * d = Check Xor : (2 ^ F ^ B ^ 2 ^ B ^ F) = 0 + * e = Check Sum : (const5 + 2 + F + B + 2 + B + F) and F = D + * -------------------------------------------------------------------------------------------- + * Wind packet: (122 pulses) + * Each frame is composed of: + * 10bits of 0 (preamble) + * 10 blocks of four bits separated by a bit 1 to be checked and skipped + * + * The 1st bit of each word is LSB, so we have to reverse the 4 bits of each word. + * Example + * 0000000000 0011 0111 0101 0000 0001 0101 0100 0111 0110 1011 + * aaaa bbbb cccc cccc cccc dddd dddd ddee ffff gggg + * 3 7 5 0 1 5 4 1 3 6 B + * + * a = sensor type (2=Rain meter) + * b = sensor address + * c = speed + * d = direction + * f = Check Xor + * g = Check Sum + * -------------------------------------------------------------------------------------------- + * UV packet (132 pulses) + \*********************************************************************************************/ +#define LACROSSE41_PULSECOUNT1 92 // Rain sensor +#define LACROSSE41_PULSECOUNT2 162 // Meteo sensor +#define LACROSSE41_PULSECOUNT3 122 // Wind sensor +#define LACROSSE41_PULSECOUNT4 132 // Brightness sensor + +#define LACROSSE41_PULSEMID 500/RAWSIGNAL_SAMPLE_RATE + +#ifdef PLUGIN_041 +boolean Plugin_041(byte function, char *string) { + boolean success=false; + if ( (RawSignal.Number != LACROSSE41_PULSECOUNT1) && (RawSignal.Number != LACROSSE41_PULSECOUNT2) && + (RawSignal.Number != LACROSSE41_PULSECOUNT3) && (RawSignal.Number != LACROSSE41_PULSECOUNT4) ) return false; + + unsigned long bitstream1=0L; // holds first 16 bits + unsigned long bitstream2=0L; // holds last 28 bits + + int sensor_data=0; + + byte checksum=0; + + byte bitcounter=0; // counts number of received bits (converted from pulses) + byte bytecounter=0; // used for counting the number of received bytes + byte data[18]; + //================================================================================== + // Check preamble + for(int x=1;x<20;x+=2) { + if ((RawSignal.Pulses[x] < LACROSSE41_PULSEMID) || (RawSignal.Pulses[x+1] > LACROSSE41_PULSEMID) ) { + return false; // bad preamble bit detected, abort + } + } + if ((RawSignal.Pulses[21] > LACROSSE41_PULSEMID) || (RawSignal.Pulses[22] < LACROSSE41_PULSEMID) ) { + return false; // There should be a 1 bit after the preamble + } + // get bits/nibbles + for(int x=23;x> 1) | 0x08); // 1 bit, store in reversed bit order + } else { + data[bytecounter] = ((data[bytecounter] >> 1)&0x07); // 0 bit, store in reversed bit order + } + bitcounter++; + if (bitcounter == 4) { + x=x+2; + if (x > RawSignal.Number-2) break; // dont check the last marker + if ((RawSignal.Pulses[x] > LACROSSE41_PULSEMID) || (RawSignal.Pulses[x+1] < LACROSSE41_PULSEMID) ) { + return false; // There should be a 1 bit after each nibble + } + bitcounter=0; + bytecounter++; + if (bytecounter > 17) return false; // received too many nibbles/bytes, abort + } + } + //================================================================================== + // all bytes received, make sure checksum is okay + //================================================================================== + // check xor value + checksum=0; + for (byte i=0;i>1; + tempval=((tempval)<<16)+((data[3])<<8)+data[2]; + if( (SignalHash!=SignalHashPrevious) || (RepeatingTimer>2)*100; + sensor_data = sensor_data + data[6]*10; + sensor_data = sensor_data + data[5]; + sensor_data = sensor_data / 22.5; + sprintf(pbuffer, "WINDIR=%04x;", sensor_data); + Serial.println( pbuffer ); + + RawSignal.Repeats=false; + RawSignal.Number=0; + success=true; + } else + if (data[0] == 0x05) { // UV sensor + sprintf(pbuffer, "20;%02X;", PKSequenceNumber++); // Node and packet number + Serial.print( pbuffer ); + Serial.print(F("LaCrosseV3;ID=")); // Label + PrintHex8( data,2); + + sensor_data = data[4]*100; + sensor_data = sensor_data + data[3]*10; + sensor_data = sensor_data + data[2]; + sprintf(pbuffer, ";UV=%04x;", sensor_data); + Serial.println( pbuffer ); + + RawSignal.Repeats=false; + RawSignal.Number=0; + success=true; + } + //================================================================================== + return success; +} +#endif // PLUGIN_041 diff --git a/Plugins/Plugin_042.c b/Plugins/Plugin_042.c new file mode 100644 index 0000000..f5c397a --- /dev/null +++ b/Plugins/Plugin_042.c @@ -0,0 +1,293 @@ +//####################################################################################################### +//## This Plugin is only for use with the RFLink software package ## +//## Plugin-42 UPM/Esic ## +//####################################################################################################### +/*********************************************************************************************\ + * Dit protocol zorgt voor ontvangst van UPM/Esic weerstation buitensensoren + * Tevens alle sensoren die het UPM/Esic (W.H. Mandolyn International Ltd) protocol volgen waaronder: + * UPM, Esic, Emos, DVM, Clas Ohlson, Dickson + * WT260,WT260H,WT440H,WT450,WT450H,WDS500,RG700 + * + * Author : StuntTeam + * Support : http://sourceforge.net/projects/rflink/ + * License : This code is free for use in any open source project when this header is included. + * Usage of any parts of this code in a commercial application is prohibited! + ********************************************************************************************* + * Changelog: v1.0 initial release + ********************************************************************************************* + * Technical information: + * Decodes signals from a UPM/Esic Weatherstation outdoor unit, (52/54 pulses, 36 bits, 433 MHz). + * Supports two packet formats + * -------------------------------------------------------------------------------------------- + * FORMAT 1: + * + * ____Byte 0_____ ____Byte 1_____ ____Byte 2_____ ____Byte 3_____ _Nib4__ + * 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 3 2 1 0 + * x x x x c c c c d d y y b S S S s s s s s P P P p p p p p p p p z z C C + * + * x = Constant, 1100, probably preamble + * c = House Code (0 - 15) + * d = Device Code (1 - 4) ? + * y = ? + * b = Low battery indication + * S = Secondary value - Humidity/Wind direction (high bits) + * s = Secondary value - Humidity/Wind direction (low bits) + * P = Primary value - Temperature/Rain/Wind speed value (high bits) + * p = Primary value - Temperature/Rain/Wind speed value (low bits) + * z = Sequence number 0 - 2. Messages are sent in bursts of 3. For some senders this is always 0 + * C = Checksum. bit 1 is XOR of odd bits, bit 0 XOR of even bits in message + * + * If HouseCode = 10 and deviceCode = 2, then p and P is Wind speed + * and h and H is Wind direction + * + * If HouseCode = 10 and deviceCode = 3, then p and P is rain + * + * Temp (C) = RawValue / 16 - 50 + * Rain (total mm) = RawValue * 0,7 + * Wind Speed (mph)= RawValue (* 1/3,6 for km/h) + * Humidity (%) = RawValue / 2 + * Wind direction (deg) = RawValue * 22,5 + * -------------------------------------------------------------------------------------------- + * FORMAT 2: + * + * ____Byte 0_____ ____Byte 1_____ ____Byte 2_____ ____Byte 3_____ _Nib4__ + * 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 3 2 1 0 + * x x x x c c c c d d y y b h h h h h h h T T T T T T T T t t t t t t t p + * + * x = Constant, 1100, probably preamble + * c = House Code (0 - 15) + * d = Device (Channel) Code (1 - 4) ? + * y = ? + * b = ? + * h = Humidity (7 bits) (0111011 = 59 %) + * T = Temperature (8 bits) (see below) + * t = Temperature (7 bits) (see below) + * p = Parity (xor of all bits should give 0) + * + * The temperature is transmitted as (temp + 50.0) * 128, which equals (temp * 128) + 6400. Adding 50.0 °C makes + * all values positive, an unsigned 15 bit integer where the first 8 bits correspond to the whole part of the temperature + * (here 01001001, decimal 73, substract 50 = 23). Remaining 7 bits correspond to the fractional part. + * Sample: + * 20;64;DEBUG;Pulses=52;Pulses(uSec)=875,875,825,875,1725,1800,1725,1800,1725,850,825,1800,1725,875,800,850,825,1800,1725,1800,800,875,800,850,1725,1800,825,850,1725,850,825,1800,1725,1800,825,850,1725,875,800,1800,800,875,800,850,825,850,1725,1800,1750,1800,475; + * 11000001 00110000 11100100 01100000 0010 + * + *20;C3;DEBUG;Pulses=52;Pulses(uSec)=950,975,850,975,1850,1975,1875,1975,1850,975,850,1975,1850,975,850,975,850,2000,1850,975,875,975,850,975,850,2000,850,975,1850,2000,850,975,1850,2000,850,975,1875,975,850,975,850,975,850,2000,1850,1975,1850,2000,1850,1975,225; + *20;C4;DEBUG;Pulses=52;Pulses(uSec)=950,975,850,975,1850,2000,1875,2000,1850,975,850,2000,1850,975,850,975,850,2000,1850,975,875,950,875,975,850,2000,850,975,1850,2000,850,975,1850,2000,850,975,1875,975,850,975,850,975,850,2000,1850,2000,1850,2000,1850,2000,225; + *20;C5;DEBUG;Pulses=52;Pulses(uSec)=950,975,850,975,1850,2000,1875,2000,1850,975,850,2000,1850,975,850,975,850,2000,1850,975,875,975,850,975,850,1975,850,975,1850,1975,850,975,1850,2000,850,975,1875,975,850,975,850,975,850,2000,1850,2000,1850,2000,1850,2000,225; + *20;32;DEBUG;Pulses=48;Pulses(uSec)=850,900,875,900,1850,1875,1850,1875,1850,900,875,1875,1850,900,875,900,875,1875,1850,900,875,1875,1850,1875,1825,900,875,1875,875,900,1850,1875,875,900,875,900,1850,1875,1825,1875,1850,1875,1850,1875,1825,1875,500; + *20;33;UPM/Esic;ID=0001;TEMP=0104;HUM=33;BAT=OK; + + 925,900,875,900,1825,1875,1850,1875,1850,900, + 875,1875,1850,900,850,900,850,1875,1850,900, + 850,1875,1850,900,850,1875,1850,1875,875,900, + 1850,1875,875,900,875,900,1825,1875,1850,900, + 875,1875,1850,900,875,1875,850,900,875,900, + 500 51 +0011111011 0011010111 0110011101 10100 + \*********************************************************************************************/ +#define UPM_MIN_PULSECOUNT 46 +#define UPM_MAX_PULSECOUNT 56 + +#define UPM_PULSELOHI 1100/RAWSIGNAL_SAMPLE_RATE +#define UPM_PULSEHIHI 2075/RAWSIGNAL_SAMPLE_RATE +#define UPM_PULSEHILO 1600/RAWSIGNAL_SAMPLE_RATE + +#ifdef PLUGIN_042 +boolean Plugin_042(byte function, char *string) { + if (RawSignal.Number < UPM_MIN_PULSECOUNT || RawSignal.Number > UPM_MAX_PULSECOUNT) return false; + + byte rc=0; + unsigned long bitstream1=0L; // holds first 10 bits + unsigned long bitstream2=0L; // holds last 26 bits + + int temperature=0; + int humidity=0; + int rain=0; + int winds=0; + int windd=0; + byte battery=0; + byte units=0; + byte devicecode=0; + byte checksum=0; + byte bitcounter=0; // counts number of received bits (converted from pulses) + byte halfbit=0; // high pulse = 1, 2 low pulses = 0, halfbit keeps track of low pulses + byte msgformat=0; + //================================================================================== + for(int x=1;x UPM_PULSEHILO) && (RawSignal.Pulses[x] < UPM_PULSEHIHI)) { + if (halfbit==1) { // UPM cant receive a 1 bit after a single low value + return false; // pulse error, must not be a UPM packet or reception error + } + if (bitcounter < 10) { + bitstream1 = (bitstream1 << 1); + bitcounter++; // only need to count the first 10 bits + } else { + bitstream2 = (bitstream2 << 1); + bitcounter++; // only need to count the first 10 bits + } + halfbit=0; // wait for next first low or high pulse + } else { + if ((RawSignal.Pulses[x] > UPM_PULSELOHI) ) return false; // Not a valid UPM pulse length + if (halfbit == 0) { // 2 times a low value = 0 bit + halfbit=1; // first half received + } else { + if (bitcounter < 10) { + bitstream1 = (bitstream1 << 1) | 0x1; + bitcounter++; // only need to count the first 10 bits + } else { + bitstream2 = (bitstream2 << 1) | 0x1; + bitcounter++; // only need to count the first 10 bits + } + halfbit=0; // wait for next first low or high pulse + } + } + if (bitcounter > 36) return false; // too many bits, it cant be the right protocol + } + //================================================================================== + if ( (bitstream1 >> 6 ) != 0x0c ) return false; // sanity check, first 4 bits should always be '1100' to be a valid UPM/Esic packet + if ( bitstream1 == 0x00 ) return false; // sanity check + if ( bitstream2 == 0x00 ) return false; // sanity check + //================================================================================== + for (byte i=0;i<9;i=i+2){ // perform a checksum check to make sure the packet is a valid UPM/Esic packet + checksum=checksum ^ ((bitstream1 >> i) &3); // Checksum - xor all odd and all even bits should match the last two bits + } + for (byte i=2;i<25;i=i+2){ + checksum=checksum ^ ((bitstream2 >> i) &3); + } + if (checksum == (bitstream2 &3 )) { // did the format 1 checksum calculation match? + msgformat=1; // Yes, set it + } else { // else perform a bit parity check to see if we have format 2 + checksum=checksum ^ ((bitstream2) &3); // xor the last 2 bits + units = (checksum >> 1) & 0x01; // get the odd bit of the checksum result + checksum=(checksum & 0x01) ^ units; // xor the odd with the even bit of the checksum result + if (checksum == 0) { // did the format 2 parity checksum calculation match? + msgformat=2; + } else { + return false; + } + } + //================================================================================== + // Prevent repeating signals from showing up + //================================================================================== + if( (SignalHash!=SignalHashPrevious) || ((RepeatingTimer+1000> 2) & 0x0f; // devicecode format 1&2 + //================================================================================== + if (msgformat==1) { + battery = (bitstream2 >> 23) & 1; // battery state 1=low 0=ok + if ((rc==10) && (devicecode==2)) { // wind + units = (bitstream2 >> 4) & 0x0f; + winds = (bitstream2 >> 8) & 0x7f; + windd = (bitstream2 >> 15) & 0x0f; //0xff; // wind direction + //================================================================================== + // Output + // ---------------------------------- + Serial.print("20;"); + PrintHexByte(PKSequenceNumber++); + Serial.print(F(";UPM/Esic;ID=")); // Label + PrintHexByte(rc); + PrintHexByte(devicecode); + // ---------------------------------- + sprintf(pbuffer, ";WINSP=%02x;", winds); + Serial.print( pbuffer ); + sprintf(pbuffer, "WINDIR=%04d;", windd); + Serial.print( pbuffer ); + if (battery==1) { // battery status + Serial.print(F("BAT=LOW;")); + } else { + Serial.print(F("BAT=OK;")); + } + Serial.println(); + //================================================================================== + } else + if ((rc==10) && (devicecode==3)) { // rain + units = (bitstream2 >> 4) & 0x0f; + rain = (bitstream2 >> 8) & 0x7f; + rain = rain * 7; // Serial.print( (float)rain * 0.7 ); + //================================================================================== + // Output + // ---------------------------------- + Serial.print("20;"); + PrintHexByte(PKSequenceNumber++); + Serial.print(F(";UPM/Esic;ID=")); // Label + PrintHexByte(rc); + PrintHexByte(devicecode); + // ---------------------------------- + sprintf(pbuffer, ";RAIN=%04x;", rain); + Serial.print( pbuffer ); + if (battery==1) { // battery status + Serial.print(F("BAT=LOW;")); + } else { + Serial.print(F("BAT=OK;")); + } + Serial.println(); + //================================================================================== + } else { // temperature & Humidity + units = (bitstream2 >> 4) & 0x0f; // temperature + temperature = (bitstream2 >> 8) & 0x7f; + temperature = temperature-50; + temperature = (temperature*10) + units; + if (temperature > 0x3e8) return false; + humidity = (bitstream2 >> 15) & 0xff; // humidity + humidity = humidity / 2; + //if (humidity==0) return false; // dont accept Bad humidity status + //if (temperature > 1000) return false; // dont accept bad temperature + //================================================================================== + // Output + // ---------------------------------- + Serial.print("20;"); + PrintHexByte(PKSequenceNumber++); + Serial.print(F(";UPM/Esic;ID=")); // Label + PrintHexByte(rc); + PrintHexByte(devicecode); + // ---------------------------------- + sprintf(pbuffer,";TEMP=%04x;", temperature); + Serial.print( pbuffer ); + sprintf(pbuffer,"HUM=%02d;", humidity); // Humidity 0x15 = 21% decimal + Serial.print( pbuffer ); + if (battery==1) { // battery status + Serial.print(F("BAT=LOW;")); + } else { + Serial.print(F("BAT=OK;")); + } + Serial.println(); + //================================================================================== + } + } else { + units = (bitstream2 >> 1) & 0x7f; // temperature + temperature = (bitstream2 >> 8) & 0xff; + temperature = temperature-50; + temperature = (temperature*100) + units; + temperature = temperature/10; + if (temperature > 0x3e8) return false; + humidity = (bitstream2 >> 16) & 0x7f; // humidity + //================================================================================== + // Output + // ---------------------------------- + Serial.print("20;"); + PrintHexByte(PKSequenceNumber++); + Serial.print(F(";UPM/Esic F2;ID=")); // Label + PrintHexByte(rc); + PrintHexByte(devicecode); + // ---------------------------------- + sprintf(pbuffer,";TEMP=%04x;", temperature); + Serial.print( pbuffer ); + sprintf(pbuffer,"HUM=%02x;", humidity); + Serial.print( pbuffer ); + Serial.println(); + //================================================================================== + } + //================================================================================== + RawSignal.Repeats=true; // suppress repeats of the same RF packet + RawSignal.Number=0; + return true; +} +#endif // PLUGIN_042 diff --git a/Plugins/Plugin_043.c b/Plugins/Plugin_043.c new file mode 100644 index 0000000..eb054d2 --- /dev/null +++ b/Plugins/Plugin_043.c @@ -0,0 +1,211 @@ +//####################################################################################################### +//## This Plugin is only for use with the RFLink software package ## +//## Plugin-43 LaCrosse ## +//####################################################################################################### +/*********************************************************************************************\ + * This plugin takes care of decoding LaCrosse weatherstation outdoor sensors + * It also works for all non LaCrosse sensors that follow this protocol. + * Lacrosse TX3-TH Thermo/Humidity, Lacrosse TX4, Lacrosse TX4U + * WS7000-15: Anemometer, WS7000-16: Rain precipitation, WS2500-19: Brightness Luxmeter, WS7000-20: Thermo/Humidity/Barometer + * TFA 30.3125 (temperature + humidity), TFA 30.3120.90 (temperature) + * + * Author : StuntTeam + * Support : http://sourceforge.net/projects/rflink/ + * License : This code is free for use in any open source project when this header is included. + * Usage of any parts of this code in a commercial application is prohibited! + ********************************************************************************************* + * Changelog: v1.0 initial release + ********************************************************************************************* + * Technical information: + * Decodes signals from a LaCrosse Weatherstation outdoor unit, (88 pulses, 44 bits, 433 MHz). + * Partially based on http://www.f6fbb.org/domo/sensors/tx3_th.php + * + * Temperature sensor (TX3) + * Each frame is 44 bits long. It is composed of: + * • 2 blocks of four bits = 0A (start sequence) + * • 8 blocks of four bits (data) + * • 1 block of four bits (checksum) + * + * The active values of the frames are explained below: + * + * Example + * 0000 1010 0000 0000 1110 0111 0011 0001 0111 0011 1101 + * aaaa aaaa bbbb cccc cccd eeee ffff gggg hhhh iiii jjjj + * 0 A 0 0 7 0 7 3 1 7 3 D + * + * 000a0004070705030705 + * 20;d8;LaCrosse;ID=0401;TEMP=00fd; + * 000a0e04070608000608 + * 20;d9;LaCrosse;ID=0403;HUM=68; + * + * • a = Start sequence (always 0000 1010) + * • b = Packet type (0=Thermo E=hygro) + * • c = Address of sensor (changes when inserting batteries) + * • d = Parity bit (c+d+e bits sum is even) + * • e-i = Measured values: + * e = tens (x 10) + * f = ones (x 1) + * g = digits (x 0.1) (is zero in case of humidity) + * h = copy of e value + * i = copy of f value + * • j = Checksum (Lower four bits of the sum of all words) + * + * Checksum: (0 + A + 0 + 0 + E + 7 + 3 + 1 + 7 + 3) and F = D D + * Sample: + * 20;11;DEBUG;Pulses=88;Pulses(uSec)=1200,875,1125,875,1125,875,1125,900,400,900,1150,875,400,900,1150,875,1125,875,1125,875,1150,875,1150,875,400,900,400,875,375,900,1150,875,1125,875,400,900,1150,875,1125,875,1125,875,400,900,400,875,1125,900,400,875,1150,875,1150,900,1125,875,1150,875,400,900,400,875,400,900,1150,875,400,900,400,875,1125,875,400,900,1150,900,1125,875,1150,875,375,900,400,900,400,900,400; + * 20;9E;DEBUG;Pulses=88;Pulses(uSec)=1300,925,1225,925,1225,925,1200,925,425,925,1225,925,425,925,1225,925,1225,925,1225,925,1225,925,1225,925,1225,925,425,925,1225,925,1225,925,1225,925,425,925,425,925,1225,925,1225,925,425,925,425,925,425,925,1225,925,425,925,425,925,1225,925,425,925,1225,925,1225,925,1225,925,1225,925,425,925,425,925,425,925,1200,925,425,925,425,925,1225,925,1225,925,425,925,425,925,1225; + * 20;9F;LaCrosse;ID=0403;TEMP=010c; + * 20;A1;DEBUG;Pulses=88;Pulses(uSec)=1325,925,1225,925,1225,925,1225,925,425,925,1225,925,425,925,1225,925,425,925,425,925,425,925,1225,925,1225,925,425,925,1225,925,1225,925,1225,925,425,925,425,925,1225,925,1225,925,425,925,1225,925,425,925,1225,925,425,950,425,925,1225,925,1225,925,1225,925,1225,925,1225,925,1225,925,425,925,1225,925,425,925,1200,925,425,925,425,925,1225,925,425,925,1225,925,1225,925,1225; + * 20;A2;LaCrosse;ID=0403;HUM=56; + + * 1275,925,1225,925,1225,925,1200,925,425,925,1225,925,425,925,1225,900,1225,925,1200,925,1225,925,1225,925,1225,925,425,925,1225,925,1200,925,1225,925,425,925,425,900,1225,900,1225,925,425,925,425,925,425,925,1225,900,425,925,1225,925,1225,925,1225,925,425,950,1225,900,425,950,1225,925,425,925,425,900,425,950,1200,900,425,925,1225,925,1225,925,425,925,425,925,425,925,425 + +20;76;DEBUG;Pulses=88;Pulses(uSec)=810,1440,1200,930,1200,960,1200,930,390,930,1200,930,420,960,1200,930,1200,930,1200,930,1200,930,1200,930,1200,930,420,930,1200,960,1200,930,1200,930,420,930,420,930,1200,930,1200,960,390,930,420,930,420,960,1200,930,390,930,1200,930,420,930,1200,960,1170,930,1200,930,420,930,1200,930,420,930,420,930,420,930,1200,930,420,930,1200,930,420,930,420,930,420,930,1200,930,420,6990; +0000 1010 0000 0100 0110 0111 0101 0001 0111 0101 1101 +20;78;DEBUG;Pulses=88;Pulses(uSec)=240,1980,1200,960,1200,960,1200,960,390,930,1200,930,390,960,1200,960,390,930,420,930,420,930,1200,930,1200,960,390,930,1200,930,1200,930,1200,930,420,930,420,930,1200,930,1200,930,420,930,420,930,1200,930,1200,930,420,960,1200,960,390,960,1200,930,1200,930,1200,930,1200,930,1200,930,420,960,390,930,1200,930,1200,960,390,930,1200,930,420,930,420,930,1200,930,1200,930,1200,6990; + +0;29;DEBUG;Pulses=86;Pulses(uSec)=1260,930,1200,930,1200,930,420,960,1200,930,420,930,1200,930,1200,930,1200,930,1200,930,1200,930,1200,930,390,930,1200,960,1200,960,1200,930,420,930,390,960,1200,960,1200,930,420,930,420,930,390,960,1200,960,390,930,1200,930,1200,930,1200,930,420,930,420,960,1200,960,1200,930,390,960,390,960,390,930,1200,960,420,930,1200,930,1200,930,1200,930,1200,930,1200,930,1200,6990; +20;2A;DEBUG;Pulses=50;Pulses(uSec)=1200,900,1140,870,390,900,360,900,390,900,1140,930,420,930,1200,930,1200,930,1200,930,420,930,390,960,1200,930,1200,930,390,930,420,960,390,930,1200,930,390,960,1200,960,1200,930,1200,960,1170,930,1200,930,1200,6990; +20;2B;DEBUG;Pulses=86;Pulses(uSec)=1230,960,1200,960,1200,930,390,960,1200,930,420,930,1200,930,420,930,390,960,420,930,1200,930,1200,930,420,930,1200,960,1170,960,1200,930,390,930,420,930,390,930,1200,930,420,930,390,930,1200,960,1200,930,420,930,420,960,390,930,1200,930,1200,930,1200,930,1200,930,1200,930,390,960,390,930,1200,960,1200,930,420,930,420,960,390,930,420,960,390,930,1200,930,420,6990; + + * -------------------------------------------------------------------------------------------- + * Rain Packet: + * Each frame is 46 bits long. It is composed of: + * 10bits of 0 (start sequence) + * 7 blocks of four bits separated by a bit 1 to be checked and skipped + * + * The 1st bit of each word is LSB, so we have to reverse the 4 bits of each word. + * Example + * 0000 0000 0010 1111 1011 0010 1011 1111 1101 + * aaaa bbbb ccc1 ccc2 ccc3 dddd eeee + * 2 F B 2 B F D + * + * a = sensor type (2=Rain meter) + * b = sensor address + * c = rain data (LSB thus the right order is c3 c2 c1) + * d = Check Xor : (2 ^ F ^ B ^ 2 ^ B ^ F) = 0 + * e = Check Sum : (const5 + 2 + F + B + 2 + B + F) and F = D + \*********************************************************************************************/ +#define LACROSSE43_PULSECOUNT 88 +#define LACROSSE43_PULSEMID 750/RAWSIGNAL_SAMPLE_RATE +#define LACROSSE43_PULSEMAX 1350/RAWSIGNAL_SAMPLE_RATE +#define LACROSSE43_PULSEMINMAX 550/RAWSIGNAL_SAMPLE_RATE + +#define LACROSSE43_MIDLO 800/RAWSIGNAL_SAMPLE_RATE +#define LACROSSE43_MIDHI 1000/RAWSIGNAL_SAMPLE_RATE + +#ifdef PLUGIN_043 +boolean Plugin_043(byte function, char *string) { + boolean success=false; + if ( (RawSignal.Number < LACROSSE43_PULSECOUNT - 4) || (RawSignal.Number > LACROSSE43_PULSECOUNT + 4) ) return false; + unsigned long bitstream1=0L; // holds first 16 bits + unsigned long bitstream2=0L; // holds last 28 bits + + int temperature=0; + int humidity=0; + byte checksum=0; + byte bitcounter=0; // counts number of received bits (converted from pulses) + byte data[10]; + //================================================================================== + // get bytes + for(int x=1;x LACROSSE43_MIDHI)) { + if (x < 2) { // Make sure the first bit is correct.. + RawSignal.Pulses[1]=1200/RAWSIGNAL_SAMPLE_RATE; + } else { + if (x+1 < RawSignal.Number) return false; // in between pulse check + } + } + if (RawSignal.Pulses[x] > LACROSSE43_PULSEMID) { + if (RawSignal.Pulses[x] > LACROSSE43_PULSEMAX) return false; + if (bitcounter < 16) { + bitstream1 = (bitstream1 << 1); + bitcounter++; // only need to count the first 16 bits + } else { + bitstream2 = (bitstream2 << 1); + } + } else { + if (RawSignal.Pulses[x] > LACROSSE43_PULSEMINMAX) return false; + if (bitcounter < 16) { + bitstream1 = (bitstream1 << 1) | 0x1; + bitcounter++; // only need to count the first 16 bits + } else { + bitstream2 = (bitstream2 << 1) | 0x1; + } + } + } + //================================================================================== + // all bytes received, make sure checksum is okay + //================================================================================== + if ((bitstream1 == 0) && (bitstream2 == 0)) return false; + data[0] = (bitstream1 >> 12) & 0x0f; // prepare nibbles from bit stream + if (data[0] != 0x00) return false; + data[1] = (bitstream1 >> 8) & 0x0f; + if (data[1] != 0x0a) return false; + data[2] = (bitstream1 >> 4) & 0x0f; + data[3] = (bitstream1 >> 0) & 0x0f; + data[4] = (bitstream2 >> 24) & 0x0f; + data[5] = (bitstream2 >> 20) & 0x0f; + data[6] = (bitstream2 >> 16) & 0x0f; + data[7] = (bitstream2 >> 12) & 0x0f; + data[8] = (bitstream2 >> 8) & 0x0f; + data[9] = (bitstream2 >> 4) & 0x0f; + //================================================================================== + // first perform a checksum check to make sure the packet is a valid LaCrosse packet + for (byte i=0;i<10;i++){ + checksum=checksum + data[i]; + } + checksum=checksum & 0x0f; + if (checksum != (bitstream2 &0x0f )) return false; + //================================================================================== + // Prevent repeating signals from showing up, skips every second packet! + //================================================================================== + unsigned long tempval=(data[4])>>1; + tempval=((tempval)<<16)+((data[3])<<8)+data[2]; + if( (SignalHash!=SignalHashPrevious) || (RepeatingTimer>1; + + sprintf(pbuffer, "20;%02X;", PKSequenceNumber++); // Node and packet number + Serial.print( pbuffer ); + Serial.print(F("LaCrosse;ID=")); // Label + PrintHex8( data+3,2); + sprintf(pbuffer, ";TEMP=%04x;", temperature); + Serial.println( pbuffer ); + RawSignal.Repeats=false; + RawSignal.Number=0; + success=true; + } else + if (data[2] == 0x0e) { + humidity=(data[5]*16)+data[6]; + if (humidity==0) return false; // humidity should not be 0 + data[4]=(data[4])>>1; + + sprintf(pbuffer, "20;%02X;", PKSequenceNumber++); // Node and packet number + Serial.print( pbuffer ); + Serial.print(F("LaCrosse;ID=")); // Label + PrintHex8( data+3,2); + sprintf(pbuffer, ";HUM=%02x;", (humidity)&0xff); + Serial.println( pbuffer ); + RawSignal.Repeats=true; + RawSignal.Number=0; + success=true; + } + //================================================================================== + return success; +} +#endif // PLUGIN_043 diff --git a/Plugins/Plugin_044.c b/Plugins/Plugin_044.c new file mode 100644 index 0000000..7e1fe48 --- /dev/null +++ b/Plugins/Plugin_044.c @@ -0,0 +1,111 @@ +//####################################################################################################### +//## This Plugin is only for use with the RFLink software package ## +//## Plugin-44 Auriol V3 ## +//####################################################################################################### +/*********************************************************************************************\ + * This plugin takes care of decoding the Auriol protocol for sensor type Z32171A + * + * Author : StuntTeam + * Support : http://sourceforge.net/projects/rflink/ + * License : This code is free for use in any open source project when this header is included. + * Usage of any parts of this code in a commercial application is prohibited! + ********************************************************************************************* + * Changelog: v1.0 initial release + ********************************************************************************************* + * Technical Information: + * Decodes signals from a Auriol Weatherstation outdoor unit, (40 bits, 433 MHz). + * + * Auriol Message Format: + * 1011 1111 1001 1010 0110 0001 1011 0100 1001 0001 + * B F 9 A 6 1 B 4 9 1 + * AAAA AAAA BBBB CCDD EEEE EEEE EEEE FFFF FFFF GGHH + * + * A = ID? + * B = Rolling code? + * C = possibly battery indicator ? + * D = trend (2 bits) indicating temp equal/up/down ? + * E = Temperature => 0x61b (0x61b-0x4c4)=0x157 *5)=0x6b3 /9)=0xBE => 0xBE = 190 decimal! + * F = humidity: 49% + * G = ? + * H = channel: 1 (2 bits) + * + * Sample: + * 20;C2;DEBUG;Pulses=82;Pulses(uSec)=475,3850,450,1700,450,3825,450,3900,450,3725,450,3825,450,3825,450,3900,450,3725,450,1700,450,1700,450,3900,450,3725,450,1700,450,1700,450,1800,450,1625,450,3800,450,3825,450,1800,450,1625,450,1700,450,1700,450,1800,450,3725,450,3800,450,1700,450,1800,450,1625,450,3825,450,1700,450,3900,450,1625,450,1700,450,1700,450,3900,450,1625,450,1700,450,1700,450,3825,500; + \*********************************************************************************************/ +#define AURIOLV3_PULSECOUNT 82 + +#ifdef PLUGIN_044 +boolean Plugin_044(byte function, char *string) { + if (RawSignal.Number != AURIOLV3_PULSECOUNT) return false; + unsigned long bitstream1=0L; + unsigned long bitstream2=0L; + byte rc=0; + byte channel=0; + byte bitcounter=0; + unsigned long temperature=0; + byte humidity=0; + //================================================================================== + // get all the bits we need (40 bits) + for(int x=2;x < AURIOLV3_PULSECOUNT;x+=2) { + if (RawSignal.Pulses[x+1]*RawSignal.Multiply > 650) return false; + if (RawSignal.Pulses[x]*RawSignal.Multiply > 3500) { + if (bitcounter < 16) { + bitstream1 = (bitstream1 << 1) | 0x1; + bitcounter++; // only need to count the first 10 bits + } else { + bitstream2 = (bitstream2 << 1) | 0x1; + } + } else { + if (RawSignal.Pulses[x]*RawSignal.Multiply > 2000) return false; + if (RawSignal.Pulses[x]*RawSignal.Multiply < 1500) return false; + if (bitcounter < 16) { + bitstream1 = (bitstream1 << 1); + bitcounter++; // only need to count the first 10 bits + } else { + bitstream2 = (bitstream2 << 1); + } + } + } + //================================================================================== + // Perform sanity checks and prevent repeating signals from showing up + //================================================================================== + if( (SignalHash!=SignalHashPrevious) || (RepeatingTimer> 8) & 0xff ; // get rolling code + temperature = ((bitstream2)>>12) & 0xfff; // get 12 temperature bits + temperature = (temperature - 0x4c4) & 0xffff; + temperature = (((temperature) * 5) / 9) & 0xffff; + if (temperature > 3000) { + temperature=4096-temperature; // fix for minus temperatures + if (temperature > 0x258) return false; // temperature out of range ( > -60.0 degrees) + temperature=temperature | 0x8000; // turn highest bit on for minus values + } else { + if (temperature > 0x258) return false; // temperature out of range ( > 60.0 degrees) + } + humidity = (bitstream2 >> 4) & 0xff ; // humidity + channel=(bitstream2) & 0x03; // channel number + //================================================================================== + // Output + // ---------------------------------- + Serial.print("20;"); + PrintHexByte(PKSequenceNumber++); + Serial.print(F(";Auriol V3;ID=")); // Label + PrintHexByte(rc); + PrintHexByte(channel); + // ---------------------------------- + sprintf(pbuffer, ";TEMP=%04x;", temperature); // temp + Serial.print( pbuffer ); + sprintf(pbuffer, "HUM=%02x;", humidity); // hum + Serial.print( pbuffer ); + Serial.println(); + //================================================================================== + RawSignal.Repeats=true; // suppress repeats of the same RF packet + RawSignal.Number=0; + return true; +} +#endif // PLUGIN_044 diff --git a/Plugins/Plugin_045.c b/Plugins/Plugin_045.c new file mode 100644 index 0000000..e9ba947 --- /dev/null +++ b/Plugins/Plugin_045.c @@ -0,0 +1,105 @@ +//####################################################################################################### +//## This Plugin is only for use with the RFLink software package ## +//## Plugin-45 Auriol ## +//####################################################################################################### +/*********************************************************************************************\ + * This plugin takes care of decoding the Auriol protocol (Z31743) and other devices following the same protocol (Rubicson?) + * + * Author : StuntTeam + * Support : http://sourceforge.net/projects/rflink/ + * License : This code is free for use in any open source project when this header is included. + * Usage of any parts of this code in a commercial application is prohibited! + ********************************************************************************************* + * Changelog: v1.0 initial release + ********************************************************************************************* + * Technical Information: + * Decodes signals from a Auriol Weatherstation outdoor unit, (32/36 bits, 433 MHz). + * Auriol Message Format: + * 1101 0110 1000 0000 1101 1111 1111 0000 + * AAAA AAAA BCCC DDDD DDDD DDDD EEEE FFFG + * + * A = Rolling Code, no change during normal operation. (Device 'Session' ID) (Might also be 4 bits RC and 4 bits for channel number) + * B = Battery status, 1=OK, 0=LOW + * C = Always 000 + * D = Temperature (21.5 degrees is shown as decimal value 215, minus values have the high bit set and need to be subtracted from a base value of 4096) + * E = Unknown + * F = Unknown + * G = sum of all bits xored together + * + * Sample: + * 20;34;DEBUG;Pulses=66;Pulses(uSec)=325,3725,325,1825,325,1825,325,1825,325,3700,325,3700,325,3700,325,3700,325,3700,325,1850,300,1825,325,1850,325,1825,325,1850,325,1825,300,1825,325,3725,300,3725,325,1825,325,1825,300,3725,300,1850,325,3725,300,1850,325,3725,300,3700,300,3725,300,1825,325,3700,325,3700,300,3700,325,1825,325; + * 20;0A;DEBUG;Pulses=66;Pulses(uSec)=325,1850,300,1850,300,3700,300,1850,300,1850,300,1850,325,1850,300,1850,325,3700,325,1850,300,1850,300,1825,325,1850,300,1850,325,1825,300,1850,325,3725,300,3700,325,1825,300,1850,325,3700,300,3725,300,3725,300,1850,300,1850,300,3725,325,3700,300,1850,300,1825,325,1850,300,3700,300,1850,325; + \*********************************************************************************************/ +#define AURIOL_PULSECOUNT 66 + +#ifdef PLUGIN_045 +boolean Plugin_045(byte function, char *string) { + if (RawSignal.Number != AURIOL_PULSECOUNT) return false; + unsigned long bitstream=0L; + unsigned int temperature=0; + byte rc=0; + byte bat=0; + byte checksumcalc=0; + //================================================================================== + if (RawSignal.Number == AURIOL_PULSECOUNT) { + for(int x=2;x < AURIOL_PULSECOUNT;x+=2) { + if (RawSignal.Pulses[x+1]*RawSignal.Multiply > 550) return false; // inbetween pulses should not exceed a length of 550 + if(RawSignal.Pulses[x]*RawSignal.Multiply > 3000) { // long bit = 1 + bitstream = (bitstream << 1) | 0x1; + } else { + if(RawSignal.Pulses[x]*RawSignal.Multiply < 1600) return false; // pulse length too short to be valid? + if(RawSignal.Pulses[x]*RawSignal.Multiply > 2200) return false; // pulse length between 2000 - 3000 is invalid + bitstream = (bitstream << 1); // short bit = 0 + } + } + } + //================================================================================== + // Prevent repeating signals from showing up + //================================================================================== + if( (SignalHash!=SignalHashPrevious) || (RepeatingTimer+1000>i)&0x01); + } + if (checksumcalc != (bitstream&0x01) ) return false; + rc = (bitstream >> 20) & 0x07; // get 3 bits to perform another sanity check + if (rc != 0) return false; // selected bits should always be 000 + //================================================================================== + bat= (bitstream >> 23) & 0x01; // get battery strength indicator + temperature = (bitstream >> 8) & 0xfff; // get 12 temperature bits + rc = (bitstream >> 24) & 0xff; // get rolling code + if (temperature > 3000) { + temperature=4096-temperature; // fix for minus temperatures + if (temperature > 0x258) return false; // temperature out of range ( > -60.0 degrees) + temperature=temperature | 0x8000; // turn highest bit on for minus values + } else { + if (temperature > 0x258) return false; // temperature out of range ( > 60.0 degrees) + } + //================================================================================== + // Output + // ---------------------------------- + Serial.print("20;"); + PrintHexByte(PKSequenceNumber++); + Serial.print(F(";Auriol;ID=00")); // Label + PrintHexByte(rc); + // ---------------------------------- + sprintf(pbuffer, ";TEMP=%04x;", temperature); + Serial.print( pbuffer ); + if (bat==0) { // battery status + Serial.print(F("BAT=LOW;")); + } else { + Serial.print(F("BAT=OK;")); + } + Serial.println(); + //================================================================================== + RawSignal.Repeats=true; // suppress repeats of the same RF packet + RawSignal.Number=0; + return true; +} +#endif // PLUGIN_045 diff --git a/Plugins/Plugin_046.c b/Plugins/Plugin_046.c new file mode 100644 index 0000000..9b164c6 --- /dev/null +++ b/Plugins/Plugin_046.c @@ -0,0 +1,141 @@ +//####################################################################################################### +//## This Plugin is only for use with the RFLink software package ## +//## Plugin-46 Auriol / Xiron ## +//####################################################################################################### +/*********************************************************************************************\ + * This plugin takes care of decoding the Auriol protocol for sensor type Z31055A-TX and Xiron + * Watshome YT6018-2 (From Fujian Youtong) + * + * Author : StuntTeam + * Support : http://sourceforge.net/projects/rflink/ + * License : This code is free for use in any open source project when this header is included. + * Usage of any parts of this code in a commercial application is prohibited! + ********************************************************************************************* + * Changelog: v1.0 initial release + ********************************************************************************************* + * Technical Information: + * Decodes signals from a Auriol and Xiron Weatherstation outdoor unit, (36 bits, 433 MHz). + * + * Auriol Message Format: + * 10100100 10 00 000011100110 1111 00000000 Temp = 23.60 + * AAAAAAAA BB CC DDDDDDDDDDDD EEEE FFFFFFFF + * A = Rolling Code, no change during normal operation. (Device 'Session' ID) (Might also be 4 bits RC and 4 bits for channel number) + * B = Battery status indicator on highest bit, 1=OK 0=LOW + * C = Always 00 (Most likely channel number) + * D = Temperature (12 bit, 21.5 degrees is shown as decimal value 215, minus values have the high bit set and need to be subtracted from a base value of 4096) + * E = Always 1111 ? + * F = Always 0000 0000 ? + * + * Xiron Message Format: + * 01101110 10 00 000011101101 1111 00110011 + * AAAAAAAA BB CC DDDDDDDDDDDD EEEE FFFFFFFF + * ID ?? Ch Temperature ? Humidity + * + * A = ID (Rolling Code, changes after battery replacement) + * B = Battery status indicator on highest bit, 1=OK 0=LOW + * C = Channel (1,2,3) + * D = Temperature (12 bit, 21.5 degrees is shown as decimal value 215, minus values have the high bit set and need to be subtracted from a base value of 4096) + * E = Always 1111 ? + * F = Humidity + * + * + * Sample: + * 20;1F;DEBUG;Pulses=74;Pulses(uSec)=550,1575,525,675,525,1625,500,700,475,725,500,1675,500,700,500,725,475,1675,475,750,450,750,475,725,450,750,450,750,475,750,450,750,475,1675,450,1700,425,1700,450,750,450,750,450,1700,450,1700,450,775,450,1700,450,1700,450,1700,425,1700,425,775,450,775,450,775,425,775,425,775,425,775,450,775,425,775,425, + \*********************************************************************************************/ +#define PLUGIN_ID 46 +#define AURIOLV2_PULSECOUNT 74 + +#ifdef PLUGIN_046 +boolean Plugin_046(byte function, char *string) { + if (RawSignal.Number != AURIOLV2_PULSECOUNT) return false; + unsigned long bitstream1=0L; + unsigned long bitstream2=0L; + byte rc=0; + byte bat=0; + int temperature=0; + int humidity=0; + byte channel=0; + byte bitcounter=0; + byte type=0; + //================================================================================== + // get all the bits we need (36 bits) + for(int x=2;x < AURIOLV2_PULSECOUNT;x+=2) { + if (RawSignal.Pulses[x+1]*RawSignal.Multiply > 700) return false; + if (RawSignal.Pulses[x]*RawSignal.Multiply > 1400) { + if (RawSignal.Pulses[x]*RawSignal.Multiply > 2100) return false; + if (bitcounter < 24) { + bitstream1 = (bitstream1 << 1) | 0x1; + bitcounter++; // only need to count the first 10 bits + } else { + bitstream2 = (bitstream2 << 1) | 0x1; + } + } else { + if (RawSignal.Pulses[x]*RawSignal.Multiply > 1100) return false; + if (RawSignal.Pulses[x]*RawSignal.Multiply < 500) return false; + if (bitcounter < 24) { + bitstream1 = (bitstream1 << 1); + bitcounter++; // only need to count the first 10 bits + } else { + bitstream2 = (bitstream2 << 1); + } + } + } + //================================================================================== + if (bitstream1==0) return false; // Perform sanity check + if ((bitstream2 & 0xF00) != 0xF00) return false; // check if 'E' has all 4 bits set + if ((bitstream2 & 0xfff) != 0xF00) { + type=1; // Xiron + if (RawSignal.Pulses[0] != PLUGIN_ID) { + return false; // only accept plugin_001 translated Xiron packets + } + } else { + type=0; // Auriol + rc = (bitstream1 >> 12) & 0x07; // get 3 bits + if (rc != 0) return false; // should always be '000' + } + //================================================================================== + bat= (bitstream1 >> 15) & 0x01; // get battery strength indicator + rc = (bitstream1 >> 16) & 0xff; // get rolling code + channel = ((bitstream1 >> 12) & 0x3)+1; // channel indicator + temperature = (bitstream1) & 0xfff; // get 12 temperature bits + if (temperature > 3000) { + temperature=4096-temperature; // fix for minus temperatures + if (temperature > 0x258) return false; // temperature out of range ( > -60.0 degrees) + temperature=temperature | 0x8000; // turn highest bit on for minus values + } else { + if (temperature > 0x258) return false; // temperature out of range ( > 60.0 degrees) + } + if (type == 1) { + humidity = (bitstream2) & 0xff ; // humidity + } + //================================================================================== + // Output + // ---------------------------------- + Serial.print("20;"); + PrintHexByte(PKSequenceNumber++); + if (type == 0) { + Serial.print(F(";Auriol V2;ID=")); // Label + } else { + Serial.print(F(";Xiron;ID=")); // Label + } + PrintHexByte(rc); + PrintHexByte(channel); + // ---------------------------------- + sprintf(pbuffer, ";TEMP=%04x;", temperature); + Serial.print( pbuffer ); + if (type == 1) { + sprintf(pbuffer, "HUM=%02d;", humidity); + Serial.print( pbuffer ); + } + if (bat==0) { // battery status + Serial.print(F("BAT=LOW;")); + } else { + Serial.print(F("BAT=OK;")); + } + Serial.println(); + //================================================================================== + RawSignal.Repeats=true; // suppress repeats of the same RF packet + RawSignal.Number=0; + return true; +} +#endif // PLUGIN_046 diff --git a/Plugins/Plugin_048.c b/Plugins/Plugin_048.c new file mode 100644 index 0000000..ad7aa3e --- /dev/null +++ b/Plugins/Plugin_048.c @@ -0,0 +1,974 @@ +//####################################################################################################### +//## This Plugin is only for use with the RFLink software package ## +//## Plugin-48 Oregon V1/2/3 ## +//####################################################################################################### +/*********************************************************************************************\ + * This protocol takes care of receiving Oregon Scientific outdoor sensors that use the V1, V2 and V3 protocol + * + * models: THC238, THC268, THN132N, THWR288A, THRN122N, THN122N, AW129, AW131, THGR268, THGR122X, + * THGN122N, THGN123N, THGR122NX, THGR228N, THGR238, WTGR800, THGR918, THGRN228NX, THGN500, + * THGR810, RTGR328N, THGR328N, Huger BTHR918, BTHR918N, BTHR968, RGR126, RGR682, RGR918, PCR122 + * THWR800, THR128, THR138, THC138, OWL CM119, cent-a-meter, OWL CM113, Electrisave, RGR928 + * UVN128, UV138, UVN800, Huger-STR918, WGR918, WGR800, PCR800, WGTR800, RGR126, BTHG968 + * + * Author : StuntTeam, Thibaut Girka + * Support : http://sourceforge.net/projects/rflink/ + * License : This code is free for use in any open source project when this header is included. + * Usage of any parts of this code in a commercial application is prohibited! + ********************************************************************************************* + * Changelog: v0.1 beta + ********************************************************************************************* + * Technical information: + * Supports Oregon V1, V2 and V3 protocol messages + * Core code from https://github.com/Cactusbone/ookDecoder/blob/master/ookDecoder.ino + * Copyright (c) 2014 Charly Koza cactusbone@free.fr Copyright (c) 2012 Olivier Lebrun olivier.lebrun@connectingstuff.net + * Copyright (c) 2012 Dominique Pierre (zzdomi) Copyright (c) 2010 Jean-Claude Wippler jcw@equi4.com + * 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. + \*********************************************************************************************/ +#define OSV3_PULSECOUNT_MIN 50 // 126 +#define OSV3_PULSECOUNT_MAX 290 // make sure to check the max length in plugin 1 as well..! + +#ifdef PLUGIN_048 +/* + * Many devices use 160 bits, known exceptions: + * 0xEA4c 136 bits // TH132N + * 0xEA7c 240 bits // UV138 + * 0x5A5D / 1A99 176 bits // THGR918 / WTGR800 + * 0x5A6D 192 bits // BTHR918N + * 0x8AEC 192 bits // RTGR328N + * 0x9AEC 208 bits // RTGR328N + * 0xDA78 144 bits // UVN800 + * 0x2A19 184 bits // RCR800 + * 0x2A1d 168 bits // WGR918 + */ +// ===================================================================================================== +class DecodeOOK { +protected: + byte total_bits, bits, flip, state, pos, data[25]; + virtual char decode(word width) = 0; +public: + enum { UNKNOWN, T0, T1, T2, T3, OK, DONE }; + // ------------------------------------- + DecodeOOK() { resetDecoder(); } + // ------------------------------------- + bool nextPulse(word width) { + if (state != DONE) + + switch (decode(width)) { + case -1: resetDecoder(); break; + case 1: done(); break; + } + return isDone(); + } + // ------------------------------------- + bool isDone() const { return state == DONE; } + // ------------------------------------- + const byte* getData(byte& count) const { + count = pos; + return data; + } + // ------------------------------------- + void resetDecoder() { + total_bits = bits = pos = flip = 0; + state = UNKNOWN; + } + // ------------------------------------- + // add one bit to the packet data buffer + // ------------------------------------- + virtual void gotBit(char value) { + total_bits++; + byte *ptr = data + pos; + *ptr = (*ptr >> 1) | (value << 7); + + if (++bits >= 8) { + bits = 0; + if (++pos >= sizeof data) { + resetDecoder(); + return; + } + } + state = OK; + } + // ------------------------------------- + // store a bit using Manchester encoding + // ------------------------------------- + void manchester(char value) { + flip ^= value; // manchester code, long pulse flips the bit + gotBit(flip); + } + // ------------------------------------- + // move bits to the front so that all the bits are aligned to the end + // ------------------------------------- + void alignTail(byte max = 0) { + // align bits + if (bits != 0) { + data[pos] >>= 8 - bits; + for (byte i = 0; i < pos; ++i) + data[i] = (data[i] >> bits) | (data[i + 1] << (8 - bits)); + bits = 0; + } + // optionally shift bytes down if there are too many of 'em + if (max > 0 && pos > max) { + byte n = pos - max; + pos = max; + for (byte i = 0; i < pos; ++i) + data[i] = data[i + n]; + } + } + // ------------------------------------- + void reverseBits() { + for (byte i = 0; i < pos; ++i) { + byte b = data[i]; + for (byte j = 0; j < 8; ++j) { + data[i] = (data[i] << 1) | (b & 1); + b >>= 1; + } + } + } + // ------------------------------------- + void reverseNibbles() { + for (byte i = 0; i < pos; ++i) + data[i] = (data[i] << 4) | (data[i] >> 4); + } + // ------------------------------------- + void done() { + while (bits) + gotBit(0); // padding + state = DONE; + } +}; + +/* +class OregonDecoderV1_org : public DecodeOOK { + public: + OregonDecoderV1_org() {} + virtual char decode(word width) { + if (200 <= width && width < 1200) { + byte w = width >= 700; + switch (state) { + case UNKNOWN: + if (w == 0) + ++flip; + else if (10 <= flip && flip <= 50) { + flip = 1; + manchester(1); + } + else + return -1; + break; + case OK: + if (w == 0) + state = T0; + else + manchester(1); + break; + case T0: + if (w == 0) + manchester(0); + else + return -1; + break; + } + return 0; + } + if (width >= 2500 && pos >= 9) + return 1; + return -1; + } +}; +*/ +class OregonDecoderV1 : public DecodeOOK { + public: + OregonDecoderV1() {} + virtual char decode(word width) { + if (900 <= width && width < 3400) { + byte w = width >= 2000; + switch (state) { + case UNKNOWN: // Detect preamble + if (w == 0) + ++flip; + else + return -1; + break; + case OK: + if (w == 0) + state = T0; + else + manchester(1); + break; + case T0: + if (w == 0) + manchester(0); + else + return -1; + break; + default: // Unexpected state + return -1; + } + return (pos == 4) ? 1 : 0; // Messages are fixed-size + } + if (width >= 3400) { + if (flip < 10 || flip > 50) + return -1; // No preamble + switch (state) { + case UNKNOWN: + // First sync pulse, lowering edge + state = T1; + break; + case T1: + // Second sync pulse, lowering edge + state = T2; + break; + case T2: + // Last sync pulse, determines the first bit! + if (width <= 5900) { + state = T0; + flip = 1; + } else { + state = OK; + flip = 0; + manchester(0); + } + break; + } + return 0; + } + return -1; + } +}; + +class OregonDecoderV2 : public DecodeOOK { +public: + OregonDecoderV2() {} + + // ------------------------------------- + // add one bit to the packet data buffer + // ------------------------------------- + virtual void gotBit(char value) { + if (!(total_bits & 0x01)) + { + data[pos] = (data[pos] >> 1) | (value ? 0x80 : 00); + } + total_bits++; + pos = total_bits >> 4; + if (pos >= sizeof data) { + resetDecoder(); + return; + } + state = OK; + } + // ------------------------------------- + virtual char decode(word width) { + //if (200 <= width && width < 1200) { + // byte w = width >= 675; + if (150 <= width && width < 1200) { + byte w = width >= 600; + switch (state) { + case UNKNOWN: + if (w != 0) { + // Long pulse + ++flip; + } + else if (w == 0 && 24 <= flip) { + // Short pulse, start bit + flip = 0; + state = T0; + } + else { + // Reset decoder + return -1; + } + break; + case OK: + if (w == 0) { + // Short pulse + state = T0; + } + else { + // Long pulse + manchester(1); + } + break; + case T0: + if (w == 0) { + // Second short pulse + manchester(0); + } + else { + // Reset decoder + return -1; + } + break; + } + } + else if (width >= 2500 && pos >= 8) { + return 1; + } + else { + return -1; + } + return total_bits == 160 ? 1 : 0; + } +}; + +class OregonDecoderV3 : public DecodeOOK { +public: + OregonDecoderV3() {} + // ------------------------------------- + // add one bit to the packet data buffer + // ------------------------------------- + virtual void gotBit(char value) { + data[pos] = (data[pos] >> 1) | (value ? 0x80 : 00); + total_bits++; + pos = total_bits >> 3; + if (pos >= sizeof data) { + resetDecoder(); + return; + } + state = OK; + } + // ------------------------------------- + virtual char decode(word width) { + if (200 <= width && width < 1200) { + byte w = width >= 675; + switch (state) { + case UNKNOWN: + if (w == 0) + ++flip; + else if (32 <= flip) { + flip = 1; + manchester(1); + } + else + return -1; + break; + case OK: + if (w == 0) + state = T0; + else + manchester(1); + break; + case T0: + if (w == 0) + manchester(0); + else + return -1; + break; + } + } + else { + return -1; + } + return total_bits == 80 ? 1 : 0; + } +}; + +OregonDecoderV1 orscV1; +OregonDecoderV2 orscV2; +OregonDecoderV3 orscV3; + +volatile word pulse; +// ===================================================================================================== +// ===================================================================================================== +byte osdata[13]; + +void reportSerial(class DecodeOOK& decoder) { + byte pos; + const byte* data = decoder.getData(pos); + for (byte i = 0; i < pos; ++i) { + if (i < 13) osdata[i]=data[i]; + } + decoder.resetDecoder(); +} +// ===================================================================================================== +// calculate a packet checksum by performing a +byte checksum(byte type, int count, byte check) { + byte calc=0; + // type 1, add all nibbles, deduct 10 + if (type == 1) { + for(byte i = 0; i> 4; + calc += (osdata[i]&0xF); + } + calc=calc-10; + } else + // type 2, add all nibbles up to count, add the 13th nibble , deduct 10 + if (type == 2) { + for(byte i = 0; i> 4; + calc += (osdata[i]&0xF); + } + calc += (osdata[6]&0xF); + calc=calc-10; + } else + // type 3, add all nibbles up to count, subtract 10 only use the low 4 bits for the compare + if (type == 3) { + for(byte i = 0; i> 4; + calc += (osdata[i]&0xF); + } + calc=calc-10; + calc=(calc&0x0f); + } else + if (type == 4) { + for(byte i = 0; i> 4; + calc += (osdata[i]&0xF); + } + calc=calc-10; + + } + if (check == calc ) return 0; + return 1; +} +// ===================================================================================================== +boolean Plugin_048(byte function, char *string) { + if ((RawSignal.Number < OSV3_PULSECOUNT_MIN) || (RawSignal.Number > OSV3_PULSECOUNT_MAX) ) return false; + + byte rc=0; + byte found = 0; + + int temp = 0; + byte hum = 0; + int comfort = 0; + int baro = 0; + int forecast = 0; + int uv = 0; + int wdir = 0; + int wspeed = 0; + int awspeed = 0; + int rain = 0; + int raintot = 0; + + word p = pulse; + // ================================================================================== + for (int x = 1; x < RawSignal.Number; x++) { + //for (int x = 0; x < RawSignal.Number; x++) { + p = RawSignal.Pulses[x]*RawSignal.Multiply; + if (p != 0) { + if (orscV1.nextPulse(p)) { + reportSerial(orscV1); + found=1; + } + if (orscV2.nextPulse(p)) { + reportSerial(orscV2); + found=2; + } + if (orscV3.nextPulse(p)) { + reportSerial(orscV3); + found=3; + } + } + } + if (found == 0) return false; + + // ================================================================================== + // Protocol and device info: + // ================================================================================== + //Serial.print("Oregon V"); + //Serial.print(found); + //Serial.print(": "); + //for(byte x=0; x<13;x++) { + // Serial.print( osdata[x],HEX ); + // Serial.print((" ")); + //} + //Serial.println(); + //Serial.print("Oregon ID="); + unsigned int id=(osdata[0]<<8)+ (osdata[1]); + rc=osdata[0]; + //Serial.println(id,HEX); + // ================================================================================== + // Process the various device types: + // ================================================================================== + // Oregon V1 packet structure + // SL-109H, AcuRite 09955 + // TEMP + CRC + // ================================================================================== + // 8487101C + // 84+87+10=11B > 1B+1 = 1C + if (found==1) { // OSV1 + int sum = osdata[0]+osdata[1]+osdata[2]; // max. value is 0x2FD + sum= (sum &0xff) + (sum>>8); // add overflow to low byte + if (osdata[3] != (sum & 0xff) ) { + //Serial.println("CRC Error"); + return false; + } + // ------------- + //temp = ((osdata[2]>>4) * 100) + ((osdata[1] & 0x0F) * 10) + ((osdata[1] >> 4)); + //if ((osdata[2] & 0x02) == 2) temp=temp | 0x8000; // bit 1 set when temp is negative, set highest bit on temp valua + temp = ((osdata[2] & 0x0F) * 100) + ((osdata[1] >> 4) * 10) + ((osdata[1] & 0x0F)); + if ((osdata[2] & 0x20) == 0x20) temp=temp | 0x8000; // bit 1 set when temp is negative, set highest bit on temp valua + // ---------------------------------- + // Output + // ---------------------------------- + Serial.print("20;"); + PrintHexByte(PKSequenceNumber++); + Serial.print(F(";OregonV1;ID=00")); // Label + //sprintf(pbuffer, "ID=00%02x;", rc & 0xCF); + PrintHexByte((rc)&0xcf); // rolling code + channel + // ---------------------------------- + sprintf(pbuffer, ";TEMP=%04x;", temp); + Serial.print( pbuffer ); + if (osdata[2] & 0x80) { + Serial.print(F("BAT=LOW;")); + } else { + Serial.print(F("BAT=OK;")); + } + Serial.println(); + } + // ================================================================================== + // ea4c Outside (Water) Temperature: THC238, THC268, THN132N, THWR288A, THRN122N, THN122N, AW129, AW131 + // TEMP + BAT + CRC + // ca48 Pool (Water) Temperature: THWR800 + // 0a4d Indoor Temperature: THR128, THR138, THC138 + // ================================================================================== + // OSV2 EA4C20725C21D083 // THN132N + // OSV2 EA4C101360193023 // THN132N + // OSV2 EA4C40F85C21D0D4 // THN132N + // OSV2 EA4C20809822D013 + // 0123456789012345 + // 0 1 2 3 4 5 6 7 + if(id == 0xea4c || id == 0xca48 || id == 0x0a4d) { + byte sum=(osdata[7]&0x0f) <<4; + sum=sum+(osdata[6]>>4); + if ( checksum(2,6, sum) !=0) { // checksum = all nibbles 0-11+13 results is nibbles 15 <<4 + 12 + //Serial.println("CRC Error"); + return false; + } + // ------------- + temp = ((osdata[5]>>4) * 100) + ((osdata[5] & 0x0F) * 10) + ((osdata[4] >> 4)); + if ((osdata[6] & 0x0F) >= 8) temp=temp | 0x8000; + // ---------------------------------- + // Output + // ---------------------------------- + Serial.print("20;"); + PrintHexByte(PKSequenceNumber++); + Serial.print(F(";Oregon Temp;ID=")); // Label + //PrintHexByte(rc); + PrintHexByte(osdata[3]); + PrintHexByte(osdata[2]); + // ---------------------------------- + sprintf(pbuffer, ";TEMP=%04x;", temp); + Serial.print( pbuffer ); + //if ((osdata[3] & 0x0F) >= 4) { + if ((osdata[4] & 0x0c) >= 4) { + Serial.print(F("BAT=LOW;")); + } else { + Serial.print(F("BAT=OK;")); + } + Serial.println(); + } else + // ================================================================================== + // 1a2d Indoor Temp/Hygro: THGN122N, THGN123N, THGR122NX, THGR228N, THGR238, THGR268, THGR122X + // 1a3d Outside Temp/Hygro: THGR918, THGRN228NX, THGN500 + // fa28 Indoor Temp/Hygro: THGR810 + // *aac Outside Temp/Hygro: RTGR328N + // ca2c Outside Temp/Hygro: THGR328N + // fab8 Outside Temp/Hygro: WTGR800 + // TEMP + HUM sensor + BAT + CRC + // ================================================================================== + // OSV2 AACC13783419008250AD[RTGR328N,...] Id:78 ,Channel:0 ,temp:19.30 ,hum:20 ,bat:10 + // OSV2 1A2D40C4512170463EE6[THGR228N,...] Id:C4 ,Channel:3 ,temp:21.50 ,hum:67 ,bat:90 + // OSV2 1A2D1072512080E73F2C[THGR228N,...] Id:72 ,Channel:1 ,temp:20.50 ,hum:78 ,bat:90 + // OSV2 1A2D103742197005378E // THGR228N + // OSV3 FA28A428202290834B46 // + // OSV3 FA2814A93022304443BE // THGR810 + // OSV2 1A2D1002 02060552A4C + // 1A3D10D91C273083.. + // 1A3D10D90C284083.. + // 01234567890123456789 + // 0 1 2 3 4 5 + // F+A+2+8+1+4+A+9+3+0+2+2+3+0+4+4=4d-a=43 + if(id == 0xfa28 || id == 0x1a2d || id == 0x1a3d || (id&0xfff)==0xACC || id == 0xca2c || id == 0xfab8 ) { + if ( checksum(1,8,osdata[8]) !=0) return false; // checksum = all nibbles 0-15 results is nibbles 16.17 + // ------------- + temp = ((osdata[5]>>4) * 100) + ((osdata[5] & 0x0F) * 10) + ((osdata[4] >> 4)); + if ((osdata[6] & 0x0F) >= 8) temp=temp | 0x8000; + // ------------- + hum = ((osdata[7] & 0x0F)*16)+ (osdata[6] >> 4); + // ---------------------------------- + // Output + // ---------------------------------- + Serial.print("20;"); + PrintHexByte(PKSequenceNumber++); + Serial.print(F(";Oregon TempHygro;ID=")); // Label + PrintHexByte(osdata[1]); + PrintHexByte(osdata[3]); + // ---------------------------------- + sprintf(pbuffer, ";TEMP=%04x;", temp); + Serial.print( pbuffer ); + sprintf(pbuffer, "HUM=%02x;", hum); + Serial.print( pbuffer ); + if ((osdata[4] & 0x0F) >= 4) { + Serial.print(F("BAT=LOW;")); + } else { + Serial.print(F("BAT=OK;")); + } + Serial.println(); + } else + // ================================================================================== + // 5a5d Indoor Temp/Hygro/Baro: Huger - BTHR918 + // 5a6d Indoor Temp/Hygro/Baro: BTHR918N, BTHR968. BTHG968 + // TEMP + HUM + BARO + FORECAST + BAT + // NO CRC YET + // ================================================================================== + // 5A 6D 00 7A 10 23 30 83 86 31 + // 5+a+6+d+7+a+1+2+3+3+8+3=47 -a=3d +8=4f +8+6=55 + // 5+a+6+d+7+a+1+2+3+3=3c-a=32 + // 5+a+6+d+7+a+1+2+3+3+0+8+3+8+6=55 -a =4b +3=4e !=1 + // 0 1 2 3 4 5 6 7 8 9 + if(id == 0x5a6d || id == 0x5a5d || id == 0x5d60) { + // ------------- + temp = ((osdata[5]>>4) * 100) + ((osdata[5] & 0x0F) * 10) + ((osdata[4] >> 4)); + if ((osdata[6] & 0x0F) >= 8) temp=temp | 0x8000; + // ------------- + hum = ((osdata[7] & 0x0F)*10)+ (osdata[6] >> 4); + + //0: normal, 4: comfortable, 8: dry, C: wet + int tmp_comfort = osdata[7] >> 4; + if (tmp_comfort == 0x00) + comfort=0; + else if (tmp_comfort == 0x04) + comfort=1; + else if (tmp_comfort == 0x08) + comfort=2; + else if (tmp_comfort == 0x0C) + comfort=3; + + // ------------- + baro = (osdata[8] + 856); // max value = 1111 / 0x457 + + //2: cloudy, 3: rainy, 6: partly cloudy, C: sunny + int tmp_forecast = osdata[9]>>4; + if (tmp_forecast == 0x02) + forecast = 3; + else if (tmp_forecast == 0x03) + forecast = 4; + else if (tmp_forecast == 0x06) + forecast = 2; + else if (tmp_forecast == 0x0C) + forecast = 1; + + // ---------------------------------- + // Output + // ---------------------------------- + Serial.print("20;"); + PrintHexByte(PKSequenceNumber++); + Serial.print(F(";Oregon BTHR;ID=")); // Label + PrintHexByte(rc); + PrintHexByte(osdata[2]); + // ---------------------------------- + sprintf(pbuffer, ";TEMP=%04x;", temp); + Serial.print( pbuffer ); + sprintf(pbuffer, "HUM=%02x;", hum); + Serial.print( pbuffer ); + sprintf(pbuffer, "HSTATUS=%d;", comfort); + Serial.print( pbuffer ); + sprintf(pbuffer, "BARO=%04x;", baro); + Serial.print( pbuffer ); + sprintf(pbuffer, "BFORECAST=%d;", forecast); + Serial.print( pbuffer ); + + //below is not correct, and for now discarded + //if (((osdata[3] & 0x0F) & 0x04) != 0) { + // Serial.print("BAT=LOW;"); + //} else { + // Serial.print("BAT=OK;"); + //} + Serial.println(); + } else + // ================================================================================== + // 2914 Rain Gauge: + // 2d10 Rain Gauge: + // 2a1d Rain Gauge: RGR126, RGR682, RGR918, RGR928, PCR122 + // 2A1D0065502735102063 + // 2+A+1+D+0+0+6+5+5+0+2+7+3+5+1+0+2+0=3e-a=34 != 63 + // 2+A+1+D+0+0+6+5+5+0+2+7+3+5+1+0+2+0+6=44-a=3A + // ================================================================================== + if(id == 0x2a1d || id == 0x2d10 || id == 0x2914) { // Rain sensor + //Checksum - add all nibbles from 0 to 8, subtract 9 and compare + /* + int cs = 0; + for (byte i = 0; i < pos-2; ++i) { //all but last byte + cs += data[i] >> 4; + cs += data[i] & 0x0F; + } + int csc = (data[8] >> 4) + ((data[9] & 0x0F)*16); + cs -= 9; //my version as A fails? + Serial.print(csc); + Serial.print(" vs "); + Serial.println(cs); + */ + rain = ((osdata[5]>>4) * 100) + ((osdata[5] & 0x0F) * 10) + (osdata[4] >> 4); + raintot = ((osdata[7] >> 4) * 10) + (osdata[6]>>4); + // ---------------------------------- + // Output + // ---------------------------------- + Serial.print("20;"); + PrintHexByte(PKSequenceNumber++); + Serial.print(F(";Oregon Rain;ID=")); // Label + PrintHexByte(rc); + PrintHexByte(osdata[3]); + // ---------------------------------- + sprintf(pbuffer, ";RAIN=%04x;", rain); + Serial.print( pbuffer ); + sprintf(pbuffer, "RAINTOT=%04x;", raintot); + Serial.print( pbuffer ); + if ((osdata[3] & 0x0F) >= 4) { + Serial.print(F("BAT=LOW;")); + } else { + Serial.print(F("BAT=OK;")); + } + Serial.println(); + } else + // ================================================================================== + // 2a19 Rain Gauge: PCR800 + // RAIN + BAT + CRC + // ================================================================================== + // OSV3 2A19048E399393250010 + // 01234567890123456789 + // 0 1 2 3 4 5 6 7 8 9 + // 2+A+1+9+0+4+8+E+3+9+9+3+9+3+2+5=5b-A=51 => 10 + if(id == 0x2a19) { // Rain sensor + int sum = (osdata[9] >> 4); + if ( checksum(3,9,sum) !=0) { // checksum = all nibbles 0-17 result is nibble 18 + //Serial.print("CRC Error, "); + return false; + } + rain = ((osdata[5]>>4) * 100) + ((osdata[5] & 0x0F) * 10) + (osdata[4] >> 4); + //Serial.print(" RainTotal="); + //raintot = ((osdata[7] >> 4) * 10) + (osdata[6]>>4); + //Serial.print(raintot); + // ---------------------------------- + // Output + // ---------------------------------- + Serial.print("20;"); + PrintHexByte(PKSequenceNumber++); + Serial.print(F(";Oregon Rain2;ID=")); // Label + PrintHexByte(rc); + PrintHexByte(osdata[4]); + // ---------------------------------- + sprintf(pbuffer,";RAIN=%04x;", rain); + Serial.print( pbuffer ); + //sprintf(pbuffer, "RAINTOT=%04x;", raintot); + //Serial.print( pbuffer ); + if ((osdata[3] & 0x0F) >= 4) { + Serial.print(F("BAT=LOW;")); + } else { + Serial.print(F("BAT=OK;")); + } + Serial.println(); + } else + // ================================================================================== + // 1a89 Anemometer: WGR800 + // WIND DIR + SPEED + AV SPEED + CRC + // ================================================================================== + // OSV3 1A89048800C026400543 + // OSV3 1A89048800C00431103B + // OSV3 1a89048848c00000003e W + // OSV3 1a890488c0c00000003e E + // 1A89042CB0C047000147 + // 0 1 2 3 4 5 6 7 8 9 + // 1+A+8+9+0+4+8+8+0+0+C+0+0+4+3+1+1+0=45-a=3b + if(id == 0x1a89) { // Wind sensor + if ( checksum(1,9,osdata[9]) !=0) return false; + wdir=((osdata[4] >> 4) & 0x0f); + // ------------- + wspeed = (osdata[6] >> 4) * 10; + wspeed = wspeed + (osdata[6] &0x0f) * 100; + wspeed = wspeed + (osdata[5] &0x0f); + // ------------- + awspeed = (osdata[8] >> 4) * 100; + awspeed = awspeed + (osdata[7] &0x0f) * 10; + awspeed = awspeed + (osdata[7] >> 4); + // ---------------------------------- + // Output + // ---------------------------------- + Serial.print("20;"); + PrintHexByte(PKSequenceNumber++); + Serial.print(F(";Oregon Wind;ID=")); // Label + PrintHexByte(rc); + PrintHexByte(osdata[2]); + // ---------------------------------- + sprintf(pbuffer, ";WINDIR=%04d;", wdir); + Serial.print( pbuffer ); + sprintf(pbuffer, "WINSP=%04x;", wspeed); + Serial.print( pbuffer ); + sprintf(pbuffer, "AWINSP=%04x;", awspeed); + Serial.print( pbuffer ); + if ((osdata[3] & 0x0F) >= 4) { + Serial.print(F("BAT=LOW;")); + } else { + Serial.print(F("BAT=OK;")); + } + Serial.println(); + } else + // ================================================================================== + // 3a0d Anemometer: Huger-STR918, WGR918 + // 1984 Anemometer: + // 1994 Anemometer: + // WIND DIR + SPEED + AV SPEED + BAT + CRC + // 3A0D006F400800000031 + // ================================================================================== + if(id == 0x3A0D || id == 0x1984 || id == 0x1994 ) { + if ( checksum(1,9,osdata[9]) !=0) { + //Serial.print("CRC Error, "); + return false; + } + wdir = ((osdata[5]>>4) * 100) + ((osdata[5] & 0x0F * 10) ) + (osdata[4] >> 4); + wdir=wdir / 22.5; + wspeed = ((osdata[7] & 0x0F) * 100) + ((osdata[6]>>4) * 10) + ((osdata[6] & 0x0F)) ; + awspeed = ((osdata[8]>>4) * 100) + ((osdata[8] & 0x0F) * 10)+((osdata[7] >>4)) ; + // ---------------------------------- + // Output + // ---------------------------------- + Serial.print("20;"); + PrintHexByte(PKSequenceNumber++); + Serial.print(F(";Oregon Wind2;ID=")); // Label + PrintHexByte(rc); + PrintHexByte(osdata[2]); + // ---------------------------------- + sprintf(pbuffer, ";WINDIR=%04d;", wdir); + Serial.print( pbuffer ); + sprintf(pbuffer, "WINSP=%04x;", wspeed); + Serial.print( pbuffer ); + sprintf(pbuffer, "AWINSP=%04x;", awspeed); + Serial.print( pbuffer ); + if ((osdata[3] & 0x0F) >= 4) { + Serial.print(F("BAT=LOW;")); + } else { + Serial.print(F("BAT=OK;")); + } + Serial.println(); + } else + // ================================================================================== + // ea7c UV Sensor: UVN128, UV138 + // UV + BAT + // NO CRC YET + // ================================================================================== + if(id == 0xea7c) { + uv=((osdata[5] & 0x0F) * 10) + (osdata[4] >> 4); + Serial.print(uv); + // ---------------------------------- + // Output + // ---------------------------------- + Serial.print("20;"); + PrintHexByte(PKSequenceNumber++); + Serial.print(F(";Oregon UVN128/138;ID=")); // Label + PrintHexByte(rc); + PrintHexByte(osdata[2]); + // ---------------------------------- + sprintf(pbuffer, ";UV=%04x;", uv); + Serial.print( pbuffer ); + if ((osdata[3] & 0x0F) >= 4) { + Serial.print(F("BAT=LOW;")); + } else { + Serial.print(F("BAT=OK;")); + } + Serial.println(); + } else + // ================================================================================== + // da78 UV Sensor: UVN800 + // UV + // NO CRC YET + // ================================================================================== + if( id == 0xda78) { + uv=(osdata[6] & 0xf0) + (osdata[5] &0x0f) ; + // ---------------------------------- + // Output + // ---------------------------------- + Serial.print("20;"); + PrintHexByte(PKSequenceNumber++); + Serial.print(F(";Oregon UVN800;ID=")); // Label + PrintHexByte(rc); + PrintHexByte(osdata[2]); + // ---------------------------------- + sprintf(pbuffer, ";UV=%04x;", uv); + Serial.print( pbuffer ); + if ((osdata[3] & 0x0F) >= 4) { + Serial.print(F("BAT=LOW;")); + } else { + Serial.print(F("BAT=OK;")); + } + Serial.println(); + } else + // ================================================================================== + // *aec Date&Time: RTGR328N + // 8A EC 13 FC 60 81 43 91 11 30 0 0 0 ; + // 8+A+E+C+1+3+F+C+6+0+8+1+4+3+9+1=6B -A=61 != 30 + // 8+A+E+C+1+3+F+C+6+0+8+1+4+3+9=6a-A=60 0=0 + // NO CRC YET + //20;06;Oregon Unknown;DEBUG=8A EC 13 FC 60 81 43 91 11 30 0 0 0 ; + //20;20;Oregon Unknown;DEBUG=8A EC 13 FC 40 33 44 91 11 30 0 0 0 ; + + + // OSV3 2A19048E399393250010 + // 01234567890123456789 + // 0 1 2 3 4 5 6 7 8 9 + // 2+A+1+9+0+4+8+E+3+9+9+3+9+3+2+5=5b-A=51 => 10 + + // ================================================================================== + // 8AEA1378077214924242C16CBD 21:49 29/04/2014 + // 0 1 2 3 4 5 6 7 8 9 0 1 2 + // 8+A+E+A+1+3+7+8+0+7+7+2+1+4+9+2+4+2+4+2+C+1+6+C=88 != BD + // Date & Time + // ================================================================================== + // eac0 Ampere meter: cent-a-meter, OWL CM113, Electrisave + // ================================================================================== + if(id == 0xeac0) { + //Serial.println("UNKNOWN LAYOUT"); + // ---------------------------------- + // Output + // ---------------------------------- + Serial.print("20;"); + PrintHexByte(PKSequenceNumber++); + // ---------------------------------- + Serial.print(F(";Oregon Unknown;DEBUG=")); // Label + PrintHex8( osdata, 13); + Serial.println(";"); + } else + // ================================================================================== + // 0x1a* / 0x2a* 0x3a** Power meter: OWL CM119 + // ================================================================================== + // 0x628* Power meter: OWL CM180 + // ================================================================================== + // OSV3 6284 3C 7801 D0 + // OSV3 6280 3C 2801 A0A8BA05 00 00 ?? ?? ?? + // ================================================================================== + // 1a99 Anemometer: WGTR800 + // WIND + TEMP + HUM + CRC + // ================================================================================== + if(id == 0x1a99) { // Wind sensor + //Serial.println("UNKNOWN LAYOUT"); + // ---------------------------------- + // Output + // ---------------------------------- + Serial.print("20;"); + PrintHexByte(PKSequenceNumber++); + // ---------------------------------- + Serial.print(F(";Oregon Unknown;DEBUG=")); // Label + PrintHex8( osdata, 13); + Serial.println(";"); + } else + // ================================================================================== + if( (id&0xf00)==0xA00 ) { // Any Oregon sensor with id xAxx + // ---------------------------------- + // Output + // ---------------------------------- + Serial.print("20;"); + PrintHexByte(PKSequenceNumber++); + // ---------------------------------- + Serial.print(F(";Oregon Unknown;DEBUG=")); // Label + PrintHex8( osdata, 13); + Serial.println(";"); + } + // ================================================================================== + RawSignal.Repeats=true; // suppress repeats of the same RF packet + RawSignal.Number=0; + return true; +} +#endif // PLUGIN_048 diff --git a/Plugins/Plugin_060.c b/Plugins/Plugin_060.c new file mode 100644 index 0000000..8f0f68d --- /dev/null +++ b/Plugins/Plugin_060.c @@ -0,0 +1,78 @@ +//####################################################################################################### +//## This Plugin is only for use with the RFLink software package ## +//## Plugin-060 AlarmSensor ## +//####################################################################################################### +/*********************************************************************************************\ + * This protocol provides support for some Alarm sensors that are part of a Varel/Chubb/Ajax alarm + * + * Author : StuntTeam + * Support : http://sourceforge.net/projects/rflink/ + * License : This code is free for use in any open source project when this header is included. + * Usage of any parts of this code in a commercial application is prohibited! + ********************************************************************************************* + * Changelog: v1.0 initial release + ********************************************************************************************* + * Technical data: + * Devices send 25 pulses, first pulse is part of the start bit. Remaining bits are Manchester encoded, 24 bits + * + * The PCB contains a Holtec HT12E Encoder chip + * The PCB has two switch blocks: SW1 with switches 1-8 (Device code?) + * SW2 with switches 1-4 (House code?) + * + * Sample: + * 20;74;DEBUG;Pulses=26;Pulses(uSec)=425,425,800,875,350,875,350,875,350,875,350,875,350,875,350,875,350,400,800,875,350,400,825,875,350; + * 1001101010101010 01100110 + * 10000000 1010 + \*********************************************************************************************/ +#define ALARMPIRV2_PULSECOUNT 26 + +#define ALARMPIRV2_PULSEMID 700/RAWSIGNAL_SAMPLE_RATE +#define ALARMPIRV2_PULSEMAX 1000/RAWSIGNAL_SAMPLE_RATE +#define ALARMPIRV2_PULSESHORT 550/RAWSIGNAL_SAMPLE_RATE +#define ALARMPIRV2_PULSEMIN 250/RAWSIGNAL_SAMPLE_RATE + +#ifdef PLUGIN_060 +boolean Plugin_060(byte function, char *string) { + if (RawSignal.Number != ALARMPIRV2_PULSECOUNT) return false; + unsigned long bitstream=0L; + byte data[3]; + if (RawSignal.Pulses[1] > ALARMPIRV2_PULSESHORT) return false; // First pulse is start bit and should be short! + for(byte x=2;x < ALARMPIRV2_PULSECOUNT;x=x+2) { + if (RawSignal.Pulses[x] > ALARMPIRV2_PULSEMID) { // long pulse 800-875 (700-1000 accepted) + if (RawSignal.Pulses[x] > ALARMPIRV2_PULSEMAX) return false; // pulse too long + if (RawSignal.Pulses[x+1] > ALARMPIRV2_PULSEMID) return false; // invalid manchester code + bitstream = bitstream << 1; + } else { // short pulse 350-425 (250-550 accepted) + if (RawSignal.Pulses[x] < ALARMPIRV2_PULSEMIN) return false; // pulse too short + if (RawSignal.Pulses[x+1] < ALARMPIRV2_PULSEMID) return false; // invalid manchester code + bitstream = (bitstream << 1) | 0x1; + } + } + //================================================================================== + // Prevent repeating signals from showing up + //================================================================================== + if( (SignalHash!=SignalHashPrevious) || (RepeatingTimer+2000 ALARMPIRV1_PULSEMID) { + if (RawSignal.Pulses[x] > ALARMPIRV1_PULSEMAX) return false; // pulse too long + if (RawSignal.Pulses[x-1] > ALARMPIRV1_PULSEMID) return false; // invalid pulse sequence 10/01 + bitstream = bitstream << 1; + } else { + if (RawSignal.Pulses[x] < ALARMPIRV1_PULSEMIN) return false; // pulse too short + if (RawSignal.Pulses[x-1] < ALARMPIRV1_PULSEMID) return false; // invalid pulse sequence 10/01 + bitstream = (bitstream << 1) | 0x1; + } + } + //================================================================================== + // Prevent repeating signals from showing up + //================================================================================== + if( (SignalHash!=SignalHashPrevious) || (RepeatingTimer+2000>16; + if ( (bitstream2)==0xff) { + if ( (bitstream)&0xffff==0xff) return false; + } + //================================================================================== + // Output + // ---------------------------------- + sprintf(pbuffer, "20;%02X;", PKSequenceNumber++); // Node and packet number + Serial.print( pbuffer ); + // ---------------------------------- + Serial.print(F("X10;")); // Label + sprintf(pbuffer, "ID=%06lx;",(bitstream)&0xffffff) ; // ID + Serial.print( pbuffer ); + Serial.print(F("SWITCH=01;")); + Serial.print(F("CMD=ON;")); // this device reports movement only + Serial.println(); + //================================================================================== + RawSignal.Repeats=true; // suppress repeats of the same RF packet + RawSignal.Number=0; + return true; +} +#endif // Plugin_061 diff --git a/Plugins/Plugin_062.c b/Plugins/Plugin_062.c new file mode 100644 index 0000000..44b52bc --- /dev/null +++ b/Plugins/Plugin_062.c @@ -0,0 +1,76 @@ +//####################################################################################################### +//## This Plugin is only for use with the RFLink software package ## +//## Plugin-062 Chuango AlarmSensors ## +//####################################################################################################### +/*********************************************************************************************\ + * This protocol provides support for Chuango Alarm sensors (Motion detectors and door/window contacts) + * Note that these modules are reported as X10 switches to the Home Automation software so that they work correctly + * + * Author : StuntTeam + * Support : http://sourceforge.net/projects/rflink/ + * License : This code is free for use in any open source project when this header is included. + * Usage of any parts of this code in a commercial application is prohibited! + ********************************************************************************************* + * Technical data: + * Devices send 50 pulses, 24 bits + * + * Sample: + * 20;03;DEBUG;Pulses=50;Pulses(uSec)=1725,325,1650,325,1625,350,575,1400,1600,400,1525,475,450,1550,1475,500,1500,500,450,1475,550,1450, + * 550,1425,1600,400,525,1475,1500,450,1500,525,1475,500,425,1525,475,1475,500,1450,525,1450,1575,400,1550,425,1500,500,400; + * 20;03;DEBUG;Pulses=50;Pulses(uSec)=1620,420,1530,450,1560,390,510,1440,1560,420,1530,420,450,1530,1440,510,1440,540,360,1500,450,1470,480,1470,1560,390,510,1440,1530,420,1500,480,1470,570,330,1590,390,1530,450,1500,480,1470,1530,420,1530,420,1530,420,450,3360; + * 101010011010011010010101100110101001010101101010 0 + * 00010010 01110100 01111000 = 0x 12 74 78 + \*********************************************************************************************/ +#define ALARMPIRV1_PULSECOUNT 50 + +#define ALARMPIRV1_PULSEMID 700/RAWSIGNAL_SAMPLE_RATE +#define ALARMPIRV1_PULSEMAX 2000/RAWSIGNAL_SAMPLE_RATE +#define ALARMPIRV1_PULSEMIN 150/RAWSIGNAL_SAMPLE_RATE + +#ifdef PLUGIN_062 +boolean Plugin_062(byte function, char *string) { + if (RawSignal.Number != ALARMPIRV1_PULSECOUNT) return false; + if (RawSignal.Pulses[0]==63) return false; // No need to test, packet for plugin 63 + unsigned long bitstream=0L; + //================================================================================== + for(byte x=2;x<=48;x=x+2) { + if (RawSignal.Pulses[x] > ALARMPIRV1_PULSEMID) { + if (RawSignal.Pulses[x] > ALARMPIRV1_PULSEMAX) return false; // pulse too long + if (RawSignal.Pulses[x-1] > ALARMPIRV1_PULSEMID) return false; // invalid pulse sequence 10/01 + bitstream = (bitstream << 1) | 0x1; + } else { + if (RawSignal.Pulses[x] < ALARMPIRV1_PULSEMIN) return false; // pulse too short + if (RawSignal.Pulses[x-1] < ALARMPIRV1_PULSEMID) return false; // invalid pulse sequence 10/01 + bitstream = bitstream << 1; + } + } + //================================================================================== + // Prevent repeating signals from showing up + //================================================================================== + if( (SignalHash!=SignalHashPrevious) || (RepeatingTimer+2000 OREGON_PLA_PULSECOUNT) ) return false; + unsigned long bitstream=0L; + //================================================================================== + byte bits=0; + byte rfbit = 1; + + if ( (RawSignal.Number < 50) || (RawSignal.Number > 52)) return false; + // Check preamble + for(byte x=1;x<28;x=x+2){ + if (RawSignal.Pulses[x]*RawSignal.Multiply > 600) return false; + if (RawSignal.Pulses[x+1]*RawSignal.Multiply > 600) return false; + } + // Check bits + for(byte x=29;x<=RawSignal.Number;x++){ + if (RawSignal.Pulses[x]*RawSignal.Multiply > 600) { // toggle bit value + if (RawSignal.Pulses[x]*RawSignal.Multiply > 1600) break; // done.. + rfbit = (~rfbit)&1; + bitstream = (bitstream << 1) | rfbit; + if (RawSignal.Pulses[x+1]*RawSignal.Multiply < 600) x++; + } else { + bitstream = (bitstream << 1) | rfbit; // short pulse keep bit value + if (RawSignal.Pulses[x+1]*RawSignal.Multiply < 600) x++; + } + } + //================================================================================== + // Prevent repeating signals from showing up + //================================================================================== + if( (SignalHash!=SignalHashPrevious) || (RepeatingTimer+2000>4; + bits=(bitstream)&0x3; + bits=bits+0x30; // signal 3 for plugin 63 + //================================================================================== + // Output + // ---------------------------------- + sprintf(pbuffer, "20;%02X;", PKSequenceNumber++); // Node and packet number + Serial.print( pbuffer ); + // ---------------------------------- + Serial.print(F("X10;")); // Label + sprintf(pbuffer, "ID=%06lx;",(bitstream)&0xffffff) ; // ID + channel number + Serial.print( pbuffer ); + sprintf(pbuffer, "SWITCH=%02x;", bits); // channel number + Serial.print( pbuffer ); + Serial.print(F("CMD=ON;")); // this device reports movement only + Serial.println(); + //================================================================================== + RawSignal.Repeats=true; // suppress repeats of the same RF packet + RawSignal.Number=0; + return true; +} +#endif // Plugin_063 diff --git a/Plugins/Plugin_070.c b/Plugins/Plugin_070.c new file mode 100644 index 0000000..52ac343 --- /dev/null +++ b/Plugins/Plugin_070.c @@ -0,0 +1,163 @@ +//####################################################################################################### +//## This Plugin is only for use with the RFLink software package ## +//## Plugin-70 Select Plus Wireless Doorbell ## +//####################################################################################################### +/*********************************************************************************************\ + * This Plugin takes care of reception of the Select Plus wireless doorbell (Sold at Action for 6 euro's) + * PCB markings: Quhwa QH-C-CE-3V aka QH-832AC + * Also sold as "1 by One" and "Delta" wireless doorbell. + * + * Author : StuntTeam + * Support : http://sourceforge.net/projects/rflink/ + * License : This code is free for use in any open source project when this header is included. + * Usage of any parts of this code in a commercial application is prohibited! + ********************************************************************************************* + * Changelog: v1.0 initial release + ********************************************************************************************* + * Technical information: + * + * There are two known models: + * SelectPlus (200689103 - Black - Datecode:0614) also sold as "1 by One" (O00 QH-0031) + * PCB details: Quhwa, QH-C-CE-3V, 09.10.16, CPU: MC908T + * SelectPlus (200689101 - White - Datecode:0914) + * PCB details: Quhwa, QH-C-CE-3V, REV1.0, CPU: MC908T + * + * Each frame is 35 pulses long. It is composed of: + * 101011001010110010110011010 10101010 + * The first block appears to be an unique ID + * The second block appears to be some kind of identifier which always is 0xAA (10101010) + * Converting the pulses into bits results in a 13 bit unique address and a 4 bit identifier: + * + * B) 1110000110011 0000 => 1C33 0 B)lack push button + * W) 1101110110100 0000 => 1BB4 0 W)hite push button + * + * Note: The transmitter sends 43 times the same packet when the bell button is pressed + * the retransmit is killed to prevent reporting the same press multiple times + * + * Sample: + * B) 20;62;DEBUG;Pulses=36;Pulses(uSec)=1000,1000,225,1000,225,1000,225,300,900,300,900,300,900,300,900,1000,225,1000,225,300,925,300,900,1000,225,1000,225,275,900,300,900,300,900,300,900; + * W) 20;A2;DEBUG;Pulses=36;Pulses(uSec)=325,950,250,950,250,250,925,950,250,950,250,950,250,275,925,950,250,950,250,250,925,950,250,275,925,250,925,275,925,250,925,275,925,275,925; + * w) 20;66;DEBUG;Pulses=36;Pulses(uSec)=650,2000,550,2000,550,550,2000,2000,550,2000,550,2000,550,550,2000,2000,550,2000,550,550,2000,2000,550,550,2000,550,2000,550,1950,550,2000,550,2000,550,2000; + * b) 20;05;DEBUG;Pulses=36;Pulses(uSec)=2100,2100,500,2050,500,2100,500,600,1950,600,1950,600,1950,600,1950,2050,500,2050,500,600,1950,600,1950,2100,500,2050,500,600,1950,600,1950,600,1950,600,1950; + \*********************************************************************************************/ +#define SELECTPLUS_PULSECOUNT 36 +#define SELECTPLUS_PULSEMID 650/RAWSIGNAL_SAMPLE_RATE +#define SELECTPLUS_PULSEMAX 2125/RAWSIGNAL_SAMPLE_RATE + +#ifdef PLUGIN_070 +boolean Plugin_070(byte function, char *string) { + if (RawSignal.Number !=SELECTPLUS_PULSECOUNT) return false; + unsigned long bitstream=0L; + byte checksum=0; + //================================================================================== + // get bytes + for(byte x=2;x < SELECTPLUS_PULSECOUNT;x=x+2) { + if (RawSignal.Pulses[x] < SELECTPLUS_PULSEMID) { + if (RawSignal.Pulses[x+1] < SELECTPLUS_PULSEMID) return false; // invalid pulse sequence 10/01 + bitstream = (bitstream << 1); + } else { + if (RawSignal.Pulses[x] > SELECTPLUS_PULSEMAX) return false; // invalid pulse duration, pulse too long + if (RawSignal.Pulses[x+1] > SELECTPLUS_PULSEMID) return false; // invalid pulse sequence 10/01 + bitstream = (bitstream << 1) | 0x1; + } + } + if (bitstream == 0) return false; // sanity check + //================================================================================== + // Prevent repeating signals from showing up + //================================================================================== + if((SignalHash!=SignalHashPrevious) || ((RepeatingTimer+2000)>4)&0xffff ); // ID + Serial.print( pbuffer ); + Serial.print(F("SWITCH=1;CMD=ON;")); + Serial.print(F("CHIME=01;")); + Serial.println(); + //================================================================================== + RawSignal.Repeats=true; // suppress repeats of the same RF packet + RawSignal.Number=0; // do not process the packet any further + return true; +} +#endif // PLUGIN_070 + +#ifdef PLUGIN_TX_070 +void SelectPlus_Send(unsigned long address); + +boolean PluginTX_070(byte function, char *string) { + boolean success=false; + unsigned long bitstream=0L; + //10;SELECTPLUS;001c33;1;OFF; + //012345678901234567890123456 + if (strncasecmp(InputBuffer_Serial+3,"SELECTPLUS;",11) == 0) { + InputBuffer_Serial[12]=0x30; + InputBuffer_Serial[13]=0x78; + InputBuffer_Serial[20]=0; + bitstream=str2int(InputBuffer_Serial+12); + bitstream=bitstream << 4; + SelectPlus_Send(bitstream); // Send RF packet + success=true; + } + return success; +} + +void SelectPlus_Send(unsigned long address) { + int fpulse = 364; // Pulse witdh in microseconds + int fretrans = 16; // number of RF packet retransmissions + uint32_t fdatabit; + uint32_t fdatamask = 0x10000; + uint32_t fsendbuff; + + digitalWrite(PIN_RF_RX_VCC,LOW); // Power off the RF receiver (if wired that way) to protect against interference + digitalWrite(PIN_RF_TX_VCC,HIGH); // Enable 433Mhz transmitter + delayMicroseconds(TRANSMITTER_STABLE_DELAY); // short delay to let the transmitter become stable (Note: Aurel RTX MID needs 500µS/0,5ms) + + for (int nRepeat = 0; nRepeat <= fretrans; nRepeat++) { + fsendbuff=address; + // send SYNC 3P High + digitalWrite(PIN_RF_TX_DATA, HIGH); + delayMicroseconds(fpulse * 3); + // end send SYNC + // Send command + for (int i = 0; i < 17;i++) { // SelectPlus address is only 13 bits, last 4 bits are always zero + // read data bit7 + fdatabit = fsendbuff & fdatamask; // Get most left bit + fsendbuff = (fsendbuff << 1); // Shift left + + if (fdatabit != fdatamask) { // Write 0 + digitalWrite(PIN_RF_TX_DATA, LOW); // short low + delayMicroseconds(fpulse * 1); + digitalWrite(PIN_RF_TX_DATA, HIGH); // long high + delayMicroseconds(fpulse * 3); + } else { // Write 1 + digitalWrite(PIN_RF_TX_DATA, LOW); + delayMicroseconds(fpulse * 3); // long low + digitalWrite(PIN_RF_TX_DATA, HIGH); + delayMicroseconds(fpulse * 1); // short high + } + } + digitalWrite(PIN_RF_TX_DATA, LOW); // and lower the signal + if (nRepeat < fretrans) { + delayMicroseconds(fpulse * 16); // delay between RF transmits + } + + } + delayMicroseconds(TRANSMITTER_STABLE_DELAY); // short delay to let the transmitter become stable (Note: Aurel RTX MID needs 500µS/0,5ms) + digitalWrite(PIN_RF_TX_VCC,LOW); // Disable the 433Mhz transmitter + digitalWrite(PIN_RF_RX_VCC,HIGH); // Enable the 433Mhz receiver + RFLinkHW(); +} +#endif // PLUGIN_070 diff --git a/Plugins/Plugin_071.c b/Plugins/Plugin_071.c new file mode 100644 index 0000000..ace6339 --- /dev/null +++ b/Plugins/Plugin_071.c @@ -0,0 +1,95 @@ +//####################################################################################################### +//## This Plugin is only for use with the RFLink software package ## +//## Plugin-71 Plieger York doorbell ## +//####################################################################################################### +/*********************************************************************************************\ + * This plugin takes care of decoding the Plieger York Doorbell protocol + * + * Author : StuntTeam + * Support : http://sourceforge.net/projects/rflink/ + * License : This code is free for use in any open source project when this header is included. + * Usage of any parts of this code in a commercial application is prohibited! + ********************************************************************************************* + * Changelog: v1.0 initial release + ********************************************************************************************* + * Technical Information: + * Decodes signals from a Plieger York Doorbell, (66 pulses, 32 bits, 433 MHz). + * Plieger Message Format: + * 0000000001010101 00000000 00011100 c2 0x1c + * 00000011 c3 0x03 + * 11100000 c1 0xE0 + * -------- 8 bits chime number (3 chimes, can be changed with a jumped on the transmitter) + * -------- 8 bits always 0 + * ---------------- 16 bits code which can be changed with a button on the inside of the transmitter + * + * Note: The transmitter sends two times the same packet when the bell button is pressed + * the retransmit is killed to prevent reporting the same press twice + * + * Sample packet: (Nodo Pulse timing) + * Pulses=66, Pulses(uSec)=700,250,275,725,750,250,275,725,750,250,275,725,750,250,275,725,750,250, + * 275,725,750,250,275,725,750,250,275,725,750,250,275,725,275,725,275,725,275,725,275,725,275,725, + * 275,725,275,725,275,725,275,725,275,725,275,725,750,250,750,250,750,250,275,725,275,725,225, + * 20;8C;DEBUG;Pulses=66;Pulses(uSec)=1800,550,600,1500,1600,550,600,1500,1600,550,600,1500,1600,550,600,1500,1600,550,600,1500,1600,500,600,1500,1600,550,600,1550,1600,550,600,1500,600,1500,600,1500,600,1500,600,1500,600,1500,600,1500,600,1500,600,1500,600,1500,600,1500,600,1500,1600,550,1600,500,1600,550,600,1500,600,1500,450; + * 20;2D;DEBUG;Pulses=66;Pulses(uSec)=875,275,300,750,800,275,300,750,800,275,300,750,800,275,300,750,800,275,300,750,800,250,300,750,800,275,275,750,800,275,300,750,300,750,300,750,300,750,300,750,300,750,300,750,300,750,300,750,300,750,300,750,300,750,800,275,800,275,800,250,300,750,300,750,225; + * 20;2E;Plieger York;ID=aaaa;SWITCH=1;CMD=ON;CHIME=02; + \*********************************************************************************************/ +#define PLIEGER_PULSECOUNT 66 +#define PLIEGER_PULSEMID 700/RAWSIGNAL_SAMPLE_RATE +#define PLIEGER_PULSEMAX 1900/RAWSIGNAL_SAMPLE_RATE + +#ifdef PLUGIN_071 +boolean Plugin_071(byte function, char *string) { + if (RawSignal.Number != PLIEGER_PULSECOUNT) return false; + unsigned long bitstream=0L; + unsigned int id=0; + byte chime=0; + //================================================================================== + // get all 32 bits + for(byte x=1;x <=PLIEGER_PULSECOUNT-2;x+=2) { + if(RawSignal.Pulses[x] > PLIEGER_PULSEMID) { + if (RawSignal.Pulses[x] > PLIEGER_PULSEMAX) return false; + if (RawSignal.Pulses[x+1] > PLIEGER_PULSEMID) return false; // Valid Manchester check + bitstream = (bitstream << 1) | 0x1; + } else { + if (RawSignal.Pulses[x+1] < PLIEGER_PULSEMID) return false; // Valid Manchester check + bitstream = (bitstream << 1); + } + } + //================================================================================== + // Prevent repeating signals from showing up + //================================================================================== + if( (SignalHash!=SignalHashPrevious) || (RepeatingTimer+1000> 8) &0xff) != 0x00) return false; // these 8 bits are always 0 + chime=bitstream &0xff; + if (chime != 0x1c && chime !=0x03 && chime != 0xE0) return false; // the chime number can only have 3 values + //================================================================================== + id=(bitstream >> 16) & 0xffff; // get 16 bits unique address + if (chime == 0xE0) chime =1; + if (chime == 0x1C) chime =2; + //================================================================================== + // Output + // ---------------------------------- + Serial.print("20;"); + PrintHexByte(PKSequenceNumber++); + Serial.print(F(";Plieger;")); // Label + // ---------------------------------- + sprintf(pbuffer, "ID=%04x;", id); // ID + Serial.print( pbuffer ); + Serial.print("SWITCH=1;CMD=ON;"); + sprintf(pbuffer, "CHIME=%02x;", chime); // chime number + Serial.print( pbuffer ); + Serial.println(); + //================================================================================== + RawSignal.Repeats=true; // suppress repeats of the same RF packet + RawSignal.Number=0; // do not process the packet any further + return true; +} +#endif // PLUGIN_071 diff --git a/Plugins/Plugin_072.c b/Plugins/Plugin_072.c new file mode 100644 index 0000000..729eada --- /dev/null +++ b/Plugins/Plugin_072.c @@ -0,0 +1,139 @@ +//####################################################################################################### +//## This Plugin is only for use with the RFLink software package ## +//## Plugin-072 Byron Wireless Doorbell ## +//####################################################################################################### +/*********************************************************************************************\ + * This Plugin takes care of reception And sending of the Byron SX doorbell + * + * Author : Maurice Ruiter (Dodge) + * Support : http://sourceforge.net/projects/rflink/ + * License : This code is free for use in any open source project when this header is included. + * Usage of any parts of this code in a commercial application is prohibited! + ********************************************************************************************* + * Changelog: v1.0 initial release + ********************************************************************************************* + * Technical information: + * 26 pulses, manchester code, 12 bits + * + * 111111110001 + * AAAAAAAABBBB + * + * A = 8 bit Address + * B = chime number + * Valid chime numbers: 1,2,6,9,a,c,d,e ? + * --------------------------------------------------------- + * 20;25;DEBUG;Pulses=26;Pulses(uSec)=275,250,250,525,250,225,250,525,250,225,250,525,525,225,250,525,525,225,250,225,250,225,250,525,525; + * 20;F0;DEBUG;Pulses=511;Pulses(uSec)=450,225,575,200,575,225,575,200,575,200,575,200,575,225,575,550,250,200,575,200,575,200,575,550,250,2825,250,200,575,200,575,200,575,225,575,200,575,200,575,200,575,550,250,200,575,200,575,200,575,525,250,2825,250,200,575,200,575,200,575,225,575,200,575,200,575,200,575,550,250,225,575,200,575,225,575,550,250,2825,250,200,575,200,575,200,575,200,575,200,575,200,575,200,575,550,250,200,575,225,575,200,575,550,250,2850,250,200,575,200,575,200,575,200,575,200,575,200,575,200,575,550,250,225,575,200,575,200,575,550,250,2850,225,225,575,200,575,225,575,200,575,200,575,200,575,200,575,550,250,225,575,200,575,200,575,525,250,2825,250,225,575,200,575,200,575,200,575,200,575,200,575,200,575,550,250,200,575,200,575,225,575,525,250,2825,250,225,575,225,575,200,575,200,575,200,575,225,575,200,575,550,250,200,575,200,575,200,575,550,250,2825,250,200,575,200,575,200,575,225,575,225,575,200,575,200,575,550,250,200,575,200,575,200,575,525,250,2825,250,200,575,200,575,200,575,200,575,200,575,200,575,200,575,550,250,225,575,200,575,225,575,550,250,2825,250,200,575,200,575,200,575,200,575,200,575,225,575,200,575,550,250,200,575,225,575,200,575,550,250,2850,250,200,575,200,575,200,575,200,575,200,575,200,575,200,575,550,250,225,575,225,575,200,575,550,250,2850,250,200,575,200,575,225,575,200,575,200,575,200,575,200,575,550,250,225,575,200,575,200,575,550,250,2825,250,200,575,200,575,200,575,200,575,225,575,200,575,200,575,550,250,200,575,200,575,225,575,550,250,2825,250,200,575,225,575,200,575,200,575,200,575,225,575,200,575,525,250,200,575,200,575,200,575,550,250,2825,250,200,575,225,575,200,575,200,575,200,575,225,575,200,575,550,250,200,575,200,575,200,575,525,250,2825,250,200,575,200,575,225,575,200,575,200,575,200,575,225,575,550,250,200,575,225,575,225,575,525,250,2825,250,200,575,200,575,200,575,200,575,225,575,200,575,225,575,550,250,225,575,200,575,225,575,550,250,2825,250,200,575,200,575,200,575,200,575,200,575,200,575,225,575,525,250,225,575,200,575,225,575,550,250,2850,225,225,575,200,575,225,575,200,575,200,575,200,575,200,575,525; + * 20;31;DEBUG;Pulses=511;Pulses(uSec)=450,550,250,550,250,550,250,550,250,525,250,525,250,550,250,550,250,225,575,200,575,200,575,550,250,2825,250,550,250,550,250,550,250,550,250,550,250,550,250,525,250,525,250,200,575,225,575,200,575,550,250,2825,250,525,250,550,250,550,250,550,250,550,250,550,250,550,250,550,250,200,575,200,575,200,575,525,250,2825,250,550,250,550,250,525,250,550,250,550,250,550,250,550,250,550,250,200,575,200,575,200,575,525,250,2825,250,550,250,525,250,525,250,525,250,550,250,550,250,550,250,550,250,225,575,200,575,200,575,525,250,2825,250,550,250,525,250,525,250,525,250,525,250,550,250,550,250,550,250,200,575,200,575,225,575,550,250,2825,225,550,250,525,250,525,250,550,250,550,250,525,250,525,250,525,250,225,575,200,575,225,575,550,250,2850,250,550,250,550,250,550,250,525,250,525,250,525,250,550,250,550,250,225,575,200,575,225,575,550,250,2850,225,550,250,550,250,550,250,550,250,525,250,525,250,525,250,550,250,200,575,200,575,200,575,525,250,2825,250,550,250,550,250,550,250,550,250,550,250,550,250,525,250,525,250,200,575,200,575,200,575,550,250,2825,250,525,250,550,250,550,250,550,250,550,250,550,250,550,250,550,250,200,575,200,575,200,575,550,250,2825,250,550,250,525,250,525,250,525,250,550,250,550,250,550,250,550,250,200,575,200,575,200,575,525,250,2825,250,550,250,550,250,550,250,525,250,550,250,550,250,550,250,550,250,200,575,200,575,225,575,525,250,2825,250,550,250,550,250,525,250,525,250,550,250,550,250,525,250,525,250,200,575,225,575,225,575,550,250,2850,250,550,250,550,250,525,250,525,250,525,250,550,250,550,250,550,250,200,575,225,575,200,575,550,250,2850,250,550,250,550,250,550,250,550,250,525,250,525,250,550,250,550,250,225,575,225,575,200,575,550,250,2825,250,550,250,550,250,550,250,550,250,550,250,550,250,550,250,525,250,200,575,200,575,200,575,550,250,2825,250,525,250,550,250,550,250,550,250,550,250,550,250,550,250,525,250,200,575,200,575,200,575,550,250,2825,250,525,250,550,250,550,250,550,250,550,250,550,250,550,250,550,250,200,575,200,575,200,575,525,250,2825,250,550,250,550,250,525,250,525,250,525,250,550,250,550,250,550; + * 20;25;DEBUG;Pulses=26;Pulses(uSec)=250,550,250,550,250,550,250,550,250,550,250,550,250,525,250,525,250,200,575,225,575,200,575,550,250; + \*********************************************************************************************/ +#define PLUGIN_ID 72 +#define BYRON_PULSECOUNT 26 + +#define BYRONSTART 3000 +#define BYRONSPACE 250 +#define BYRONLOW 350 +#define BYRONHIGH 675 + +#ifdef PLUGIN_072 +boolean Plugin_072(byte function, char *string){ + if (RawSignal.Number !=BYRON_PULSECOUNT) return false; + if (RawSignal.Pulses[0] != PLUGIN_ID) return false; // only accept plugin1 translated packets + if (RawSignal.Pulses[1]*RAWSIGNAL_SAMPLE_RATE > 425) return false; // first pulse is start bit and must be short + unsigned long bitstream=0L; + //================================================================================== + for(byte x=2;x < BYRON_PULSECOUNT-1;x=x+2) { + if (RawSignal.Pulses[x]*RAWSIGNAL_SAMPLE_RATE < 350) { // 200-275 (150-350 is accepted) + if (RawSignal.Pulses[x]*RAWSIGNAL_SAMPLE_RATE < 150) return false; // pulse too short + if (RawSignal.Pulses[x+1]*RAWSIGNAL_SAMPLE_RATE < 350) return false; // bad manchester code + bitstream = (bitstream << 1); + } else { // 500-575 (450-650 is accepted) + if (RawSignal.Pulses[x+1]*RAWSIGNAL_SAMPLE_RATE > 450) return false; // bad manchester code + if (RawSignal.Pulses[x]*RAWSIGNAL_SAMPLE_RATE < 450) return false; // pulse too short + if (RawSignal.Pulses[x]*RAWSIGNAL_SAMPLE_RATE > 650) return false; // pulse too long + bitstream = (bitstream << 1) | 0x1; + } + } + //================================================================================== + // Prevent repeating signals from showing up + //================================================================================== + if( (SignalHash!=SignalHashPrevious) || (RepeatingTimer+1000>4)&0xff);// ID + Serial.print( pbuffer ); + Serial.print(F("SWITCH=1;CMD=ON;")); + sprintf(pbuffer, "CHIME=%02x;", (bitstream)&0xf); // chime number + Serial.print( pbuffer ); + Serial.println(); + //================================================================================== + RawSignal.Repeats=true; // suppress repeats of the same RF packet + RawSignal.Number=0; + return true; +} +#endif // PLUGIN_072 + +#ifdef PLUGIN_TX_072 +boolean PluginTX_072(byte function, char *string) { + boolean success=false; + //10;BYRON;112233;01;OFF; + //01234567890123456789012 + if (strncasecmp(InputBuffer_Serial+3,"BYRON;",5) == 0) { // KAKU Command eg. + if (InputBuffer_Serial[15] != ';') return false; + + InputBuffer_Serial[9]='0'; + InputBuffer_Serial[10]='x'; + InputBuffer_Serial[15]=0; + unsigned int tempbyte1=0; + tempbyte1=str2int(InputBuffer_Serial+9); // get parameter 1 + + int tempbyte2=0; + InputBuffer_Serial[14]='0'; + InputBuffer_Serial[15]='x'; + InputBuffer_Serial[18]=0; + tempbyte2=str2int(InputBuffer_Serial+14); // get parameter 2 + //----------------------------------------------- + unsigned long bitstream1=tempbyte1; // address + unsigned long bitstream=tempbyte2; // ringtone + + RawSignal.Multiply=50; + RawSignal.Repeats=20; + RawSignal.Delay=3; // 1 = 900 3=2825 5= 6= x + RawSignal.Pulses[1]=BYRONLOW/RawSignal.Multiply; + //RawSignal.Pulses[1]=BYRONSTART/RawSignal.Multiply; + for (byte x=17;x>=2;x=x-1) { + if ((bitstream1 & 1) == 1) + RawSignal.Pulses[x] = BYRONHIGH/RawSignal.Multiply; + else + RawSignal.Pulses[x] = BYRONLOW/RawSignal.Multiply; + bitstream1 = bitstream1 >> 1; + } + for (byte x=25;x>=18;x=x-1) { + if ((bitstream & 1) == 1) + RawSignal.Pulses[x] = BYRONHIGH/RawSignal.Multiply; + else + RawSignal.Pulses[x] = BYRONLOW/RawSignal.Multiply; + bitstream = bitstream >> 1; + } + //RawSignal.Pulses[26]=BYRONSTART/RawSignal.Multiply; + RawSignal.Pulses[26]=BYRONSPACE/RawSignal.Multiply; + RawSignal.Number=26; + RawSendRF(); + RawSignal.Multiply=RAWSIGNAL_SAMPLE_RATE; + success=true; + //----------------------------------------------- + } + return success; +} +#endif // PLUGIN_TX_072 diff --git a/Plugins/Plugin_073.c b/Plugins/Plugin_073.c new file mode 100644 index 0000000..ee277f6 --- /dev/null +++ b/Plugins/Plugin_073.c @@ -0,0 +1,166 @@ +//####################################################################################################### +//## This Plugin is only for use with the RFLink software package ## +//## Plugin-073 Deltronic Doorbell ## +//####################################################################################################### +/*********************************************************************************************\ + * This Plugin takes care of reception And sending of the Deltronic doorbell + * + * Author : Stuntteam / Jonas Jespersen + * Support : http://sourceforge.net/projects/rflink/ + * License : This code is free for use in any open source project when this header is included. + * Usage of any parts of this code in a commercial application is prohibited! + ********************************************************************************************* + * Technical information: + * + * The doorbell uses the UM3750 circuit which sends out a 12 bit signal: + * + * AAAAAAAA BBBB + * + * A = Always 1 + * B = Address (Can be either 1, 5 or 9) + * + * Address 1 + * 20;0D;DEBUG;Pulses=26;Pulses(uSec)=600,1150,525,1175,500,1175,475,1200,500,1175,500,1200,475,1175,475,1200,475,575,1075,575,1075,575,1075,1225,450; + * 000000001110 + * Address 5 + * 20;17;DEBUG;Pulses=26;Pulses(uSec)=550,1075,425,1100,400,1125,425,1100,400,1125,400,1150,375,1125,400,1125,375,550,900,1125,375,550,900,1150,375; + * 000000001010 + * Address 9 + * 20;1B;DEBUG;Pulses=26;Pulses(uSec)=600,1150,500,1175,525,1175,500,1175,500,1175,500,1175,500,1175,475,1200,500,1200,475,575,1075,600,1075,1200,475; + * 000000000110 + \*********************************************************************************************/ +#define DELTRONIC_PULSECOUNT 26 +#define LENGTH_DEVIATION 300 + +#ifdef PLUGIN_073 +boolean Plugin_073(byte function, char *string) { + if (RawSignal.Number != DELTRONIC_PULSECOUNT) return false; + //================================================================================== + unsigned long bitstream = 0L; + unsigned long checksum = 0L; + //================================================================================== + if (RawSignal.Pulses[1]*RAWSIGNAL_SAMPLE_RATE > 675) return false; // First pulse is start bit and should be short! + for(byte x=2;x < DELTRONIC_PULSECOUNT;x=x+2) { + if (RawSignal.Pulses[x]*RAWSIGNAL_SAMPLE_RATE > 800) { // long pulse (800-1275) + if (RawSignal.Pulses[x]*RAWSIGNAL_SAMPLE_RATE > 1275) return false; // pulse too long to be valid + if (RawSignal.Pulses[x+1]*RAWSIGNAL_SAMPLE_RATE > 675) return false; // invalid manchestercode (10 01) + bitstream = (bitstream << 1) | 0x1; // 10 => 1 bit + } else { // short pulse + if (RawSignal.Pulses[x]*RAWSIGNAL_SAMPLE_RATE < 250) return false; // too short + if (RawSignal.Pulses[x+1]*RAWSIGNAL_SAMPLE_RATE < 700) return false; // invalid manchestercode (10 01) + bitstream = bitstream << 1; // 01 => 0 bit + } + } + //================================================================================== + // Prevent repeating signals from showing up + //================================================================================== + if( (SignalHash!=SignalHashPrevious) || (RepeatingTimer+2000>= 1; + } + // Send sync + digitalWrite(PIN_RF_TX_DATA, LOW); + delayMicroseconds(periodSync); + digitalWrite(PIN_RF_TX_DATA, HIGH); + delayMicroseconds(period); + } + + digitalWrite(PIN_RF_TX_DATA, LOW); + delayMicroseconds(TRANSMITTER_STABLE_DELAY); // short delay to let the transmitter become stable (Note: Aurel RTX MID needs 500µS/0,5ms) + digitalWrite(PIN_RF_TX_VCC,LOW); // Disable the 433Mhz transmitter + digitalWrite(PIN_RF_RX_VCC,HIGH); // Enable the 433Mhz receiver + RFLinkHW(); +} +#endif // PLUGIN_TX_073 diff --git a/Plugins/Plugin_074.c b/Plugins/Plugin_074.c new file mode 100644 index 0000000..00f1edf --- /dev/null +++ b/Plugins/Plugin_074.c @@ -0,0 +1,192 @@ +//####################################################################################################### +//## This Plugin is only for use with the RFLink software package ## +//## Plugin-74: RL-02 Digital Doorbell ## +//####################################################################################################### +/*********************************************************************************************\ + * This plugin takes care of sending and receiving the RL-02 Digital Doorbell protocol. + * + * Author : Jonas Jespersen + * Support : http://sourceforge.net/projects/rflink/ + * License : This code is free for use in any open source project when this header is included. + * Usage of any parts of this code in a commercial application is prohibited! + *********************************************************************************************** + * Technical information: + * + * The RL-02 Digital Doorbell uses a protocol similar to PT2262 which send 12 bits that can be either + * 1, 0 or f (float). + * + * The doorbell transmitter has 2 buttons; One for ringing the doorbell and a button inside the transmitter + * for changing the chime. Everytime the change chime button is pressed the doorbell toggles through a number + * of available chimes, and when ring button is pressed the chime last selected with the change chime button + * will sound. + * + * AAAAAAAAAAA B + * + * A = Always f0ff0f0ffff + * B = f or 1 (f = Change chime button, 1 = Ring button) + * + * Ring: + * 20;6D;DEBUG;Pulses=50;Pulses(uSec)=175,400,450,50,100,400,100,400,100,400,450,50,100,400,450,50,100,425,100,400,100,400,450,50,100,400,100,400,100,400,450,50,100,400,425,75,100,400,425,75,100,400,450,75,425,75,425,75,75; + * + * Change chime: + * 20;0B;DEBUG;Pulses=50;Pulses(uSec)=175,400,450,50,100,400,100,400,100,400,450,50,100,400,450,50,100,400,100,400,100,400,425,50,100,400,100,400,100,400,450,50,100,400,425,50,100,400,425,50,100,400,425,75,100,400,425,75,100; + \*********************************************************************************************/ +#define RL02_CodeLength 12 +#define RL02_T 125 // 175 uS + +#ifdef PLUGIN_074 +boolean Plugin_074(byte function, char *string) { + if (RawSignal.Number != (RL02_CodeLength * 4) + 2) return false; + + unsigned long bitstream = 0L; + unsigned long checksum = 0L; + int i, j; + boolean error = false; + // ========================================================================== + for (i = 0; i < RL02_CodeLength; i++) { + j = (RL02_T * 2) / RAWSIGNAL_SAMPLE_RATE; + + if (RawSignal.Pulses[4 * i + 1] < j && RawSignal.Pulses[4 * i + 2] > j && RawSignal.Pulses[4 * i + 3] < j && RawSignal.Pulses[4 * i + 4] > j) { // 0101 + bitstream = (bitstream >> 1); // 0 + } else + if (RawSignal.Pulses[4 * i + 1] < j && RawSignal.Pulses[4 * i + 2] > j && RawSignal.Pulses[4 * i + 3] > j && RawSignal.Pulses[4 * i + 4] < j) { // 0110 + bitstream = (bitstream >> 1 | (1 << (RL02_CodeLength - 1))); // float + } else + if (RawSignal.Pulses[4 * i + 1] > j && RawSignal.Pulses[4 * i + 2] < j && RawSignal.Pulses[4 * i + 3] > j && RawSignal.Pulses[4 * i + 4] < j) { // 1010 + bitstream = (bitstream >> 1); // 1 + } else { + if (i == 0) { + if (RawSignal.Pulses[4 * i + 1] > j && RawSignal.Pulses[4 * i + 2] > j && RawSignal.Pulses[4 * i + 3] < j && RawSignal.Pulses[4 * i + 4] > j) { // 1101 + bitstream = (bitstream >> 1 | (1 << (RL02_CodeLength - 1))); // 1 + } else { + error = true; + } + } else { + error = true; + } + } + } + // ========================================================================== + // all bytes received, make sure packet is valid and checksum is okay + // ========================================================================== + if (error == true) return false; + if (bitstream == 0) return false; // sanity check + checksum = (bitstream) & 0x000007FFL; + if (checksum != 0x000007ADL) return false; + if (bitstream == 0) return false; // sanity check + // ========================================================================== + // ---------------------------------- + // Output + // ---------------------------------- + Serial.print("20;"); + PrintHexByte(PKSequenceNumber++); + Serial.print(F(";Byron MP;")); // Label + // ---------------------------------- + sprintf(pbuffer, "ID=%04x;", (bitstream & 0x00000800L) ? 1 : 0); // ID: 0 = Ring button, 1 = Change chime button + Serial.print( pbuffer ); + Serial.print(F("SWITCH=1;CMD=ON;")); + Serial.print(F("CHIME=01;")); + Serial.println(); + // ---------------------------------- + RawSignal.Repeats=true; + RawSignal.Number=0; + return true; +} +#endif //PLUGIN_074 + +#ifdef PLUGIN_TX_074 +void RL02_Send(unsigned long address); + +boolean PluginTX_074(byte function, char *string) { + boolean success=false; + unsigned long bitstream=0; + //10;Byron MP;001c33;1;OFF; + //012345678901234567890123456 + if (strncasecmp(InputBuffer_Serial+3,"BYRON MP;",9) == 0) { + InputBuffer_Serial[10]=0x30; + InputBuffer_Serial[11]=0x78; + InputBuffer_Serial[18]=0; + bitstream=str2int(InputBuffer_Serial+10); + bitstream = ((bitstream) << 11) | 0x000007ADL; + RL02_Send(bitstream); // Send RF packet + success=true; + } + return success; +} + +void RL02_Send(unsigned long address) { + int fpulse = 175; // Pulse witdh in microseconds + int fretrans = 7; // Number of code retransmissions + uint32_t fdatabit; + uint32_t fdatamask = 0x00000001; + uint32_t fsendbuff; + + digitalWrite(PIN_RF_RX_VCC,LOW); // Turn off power to the RF receiver + digitalWrite(PIN_RF_TX_VCC,HIGH); // Enable the 433Mhz transmitter + delayMicroseconds(TRANSMITTER_STABLE_DELAY); // short delay to let the transmitter become stable (Note: Aurel RTX MID needs 500µS/0,5ms) + + for (int nRepeat = 0; nRepeat <= fretrans; nRepeat++) { + fsendbuff = address; + // Send command + + for (int i = 0; i < 11; i++) { // RL-02 packet is 12 bits + // read data bit + fdatabit = fsendbuff & fdatamask; // Get most right bit + fsendbuff = (fsendbuff >> 1); // Shift right + + // PT2262 data can be 0, 1 or float. + if (fdatabit != fdatamask) { // Write 0 + digitalWrite(PIN_RF_TX_DATA, HIGH); + delayMicroseconds(fpulse); + digitalWrite(PIN_RF_TX_DATA, LOW); + delayMicroseconds(fpulse * 3); + digitalWrite(PIN_RF_TX_DATA, HIGH); + delayMicroseconds(fpulse); + digitalWrite(PIN_RF_TX_DATA, LOW); + delayMicroseconds(fpulse * 3); + } else { // Write float + digitalWrite(PIN_RF_TX_DATA, HIGH); + delayMicroseconds(fpulse * 1); + digitalWrite(PIN_RF_TX_DATA, LOW); + delayMicroseconds(fpulse * 3); + digitalWrite(PIN_RF_TX_DATA, HIGH); + delayMicroseconds(fpulse * 3); + digitalWrite(PIN_RF_TX_DATA, LOW); + delayMicroseconds(fpulse * 1); + } + } + + fdatabit = fsendbuff & fdatamask; // Get most right bit + // Send last bit. Can be either 1 or float + if (fdatabit != fdatamask) { // Write 1 + digitalWrite(PIN_RF_TX_DATA, HIGH); + delayMicroseconds(fpulse * 3); + digitalWrite(PIN_RF_TX_DATA, LOW); + delayMicroseconds(fpulse); + digitalWrite(PIN_RF_TX_DATA, HIGH); + delayMicroseconds(fpulse * 3); + digitalWrite(PIN_RF_TX_DATA, LOW); + delayMicroseconds(fpulse); + } else { // Write float + digitalWrite(PIN_RF_TX_DATA, HIGH); + delayMicroseconds(fpulse * 1); + digitalWrite(PIN_RF_TX_DATA, LOW); + delayMicroseconds(fpulse * 3); + digitalWrite(PIN_RF_TX_DATA, HIGH); + delayMicroseconds(fpulse * 3); + digitalWrite(PIN_RF_TX_DATA, LOW); + delayMicroseconds(fpulse * 1); + } + + // Send sync bit + digitalWrite(PIN_RF_TX_DATA, HIGH); + delayMicroseconds(fpulse * 1); + digitalWrite(PIN_RF_TX_DATA, LOW); // and lower the signal + delayMicroseconds(fpulse * 31); + } + delayMicroseconds(TRANSMITTER_STABLE_DELAY); // short delay to let the transmitter become stable (Note: Aurel RTX MID needs 500µS/0,5ms) + digitalWrite(PIN_RF_TX_VCC,LOW); // Turn thew 433Mhz transmitter off + digitalWrite(PIN_RF_RX_VCC,HIGH); // Turn the 433Mhz receiver on + RFLinkHW(); +} +#endif // PLUGIN_TX_074 diff --git a/Plugins/Plugin_075.c b/Plugins/Plugin_075.c new file mode 100644 index 0000000..6f8a2a3 --- /dev/null +++ b/Plugins/Plugin_075.c @@ -0,0 +1,143 @@ +//####################################################################################################### +//## This Plugin is only for use with the RFLink software package ## +//## Plugin-75 Lidl doorbell ## +//####################################################################################################### +/*********************************************************************************************\ + * This plugin takes care of decoding the Lidl/SilverCrest Doorbell protocol for model Z31370-TX + * + * Author : StuntTeam + * Support : www.nodo-domotica.nl + * Date : 9-02-2015 + * Version : 1.0 + * Compatibility : RFLink 1.0 + * License : This code is free for use in any open source project when this header is included. + * Usage of any parts of this code in a commercial application is prohibited! + ********************************************************************************************* + * Changelog: v1.0 initial release + ********************************************************************************************* + * Technical Information: + * Decodes signals from a Lidl Doorbell, (90 pulses, 40 bits, 433 MHz). + * Lidl Doorbell Message Format: + * 1010 1111 1000 0010 0001 1011 0011 0000 0000 0000 AF821B3000 + * -------------- always zero + * + * Sample packet: (Nodo Pulse timing) + * 1850,1200,1400,1200,1400,1200,1400,1200,1400, (Preamble) + * 700,400,200,900,700,400,200,900,700,400,700,400,700,400,700,400,700,400,200,900,200,900,200,900,200,900,200,900,700,400,250,900,200,900,200,900,700, + * 400,700,400,700,400,200,900,700,400,700,400,200,900,200,900,700,400,700,400,200,900,200,900,200,900,200,900,200,900,200,900,200,900,200,900,200,900, + * 200,900,200,900,250,900,200 + ********************************************************************************************* + * Technical Information: + * Decodes signals from a Lidl SilverCrest Z31370-TX Doorbell, (114 pulses, 48 bits, 433 MHz). + * SilverCrest Doorbell Message Format: + * 01011100 10100101 10001000 | 00000100 11010110 10111011 10010111 5CA588 04D6BB97 + * always the same | device unique number + * + * Sample packet: + * 20;14;DEBUG;Pulses=114;Pulses(uSec)=360,60,60,390,360,60,60,390,60,390,60,390,390,60,360,60,60,390,360,60,60,390,360,60,360,60,60,390,360,60,60,390,60,390,360,60,360,60,360,60,30,390,360,60,360,60,360,60,390,60,360,60,60,390,390,60,360,60,360,60,390,60,360,60,390,60,360,60,360,60,390,60,60,390,60,390,30,390,60,390,60,390,360,60,60,390,60,390,60,390,60,390,360,60,360,60,60,390,60,390,60,390,360,60,60,390,60,390,360,60,60,390,360,1260; + \*********************************************************************************************/ + // ================================================================================== +#define LIDL_PULSECOUNT 90 // type 0 +#define LIDL_PULSECOUNT2 114 // type 1 +#define PLUGIN_ID 75 + +#ifdef PLUGIN_075 +boolean Plugin_075(byte function, char *string) { + if ((RawSignal.Number != LIDL_PULSECOUNT) && (RawSignal.Number != LIDL_PULSECOUNT2)) return false; + unsigned long bitstream=0; + unsigned long bitstream2=0; + + int bitcount=0; + byte type=0; + //================================================================================== + // get all bits + if (RawSignal.Number == LIDL_PULSECOUNT) { + if (RawSignal.Pulses[1]*RawSignal.Multiply > 1000 && RawSignal.Pulses[2]*RawSignal.Multiply > 1000 && + RawSignal.Pulses[3]*RawSignal.Multiply > 1000 && RawSignal.Pulses[4]*RawSignal.Multiply > 1000 && + RawSignal.Pulses[5]*RawSignal.Multiply > 1000 && RawSignal.Pulses[6]*RawSignal.Multiply > 1000 && + RawSignal.Pulses[7]*RawSignal.Multiply > 1000 && RawSignal.Pulses[8]*RawSignal.Multiply > 1000 && RawSignal.Pulses[9]*RawSignal.Multiply > 1000 ) { + // + } else { + return false; + } + for(int x=10;x <=90;x+=2) { + if (bitcount < 28){ + if (RawSignal.Pulses[x]*RawSignal.Multiply > 550) { + bitstream = (bitstream << 1); + bitcount++; + } else { + bitstream = (bitstream << 1) | 0x1; + bitcount++; + } + } else { + if (RawSignal.Pulses[x]*RawSignal.Multiply > 550) { + bitstream2 = (bitstream2 << 1); + bitcount++; + } else { + bitstream2 = (bitstream2 << 1) | 0x1; + bitcount++; + } + } + } + } else { + if (RawSignal.Pulses[0] != PLUGIN_ID) return false; // only accept plugin1 translated packets + type=1; + for(byte x=1;x < LIDL_PULSECOUNT2-1;x+=2) { + if (RawSignal.Pulses[x]*RawSignal.Multiply > 200) { + if (RawSignal.Pulses[x+1]*RawSignal.Multiply > 200) return false; // invalid pulse length + if (bitcount > 23) { + bitstream2 = (bitstream2 << 1); + } else { + bitstream = (bitstream << 1); + } + bitcount++; + } else { + if (RawSignal.Pulses[x+1]*RawSignal.Multiply < 200) return false; // invalid pulse length + if (bitcount > 23) { + bitstream2 = (bitstream2 << 1) | 0x1; + } else { + bitstream = (bitstream << 1) | 0x1; + } + bitcount++; + } + } + } + //================================================================================== + // all bytes received, make sure checksum is okay + //================================================================================== + if (type==0) { + if ((bitstream2 &0xfff) != 0x00) return false; // these 8 bits are always 0 + } else { + if (bitstream != 0x5ca588) return false; + } + //================================================================================== + // Prevent repeating signals from showing up + //================================================================================== + if((SignalHash!=SignalHashPrevious) || ((RepeatingTimer+2000) 1000) return false; // every preceding pulse must be below 1000! + if (RawSignal.Pulses[x]*RAWSIGNAL_SAMPLE_RATE > 2000) { // long pulse + if (RawSignal.Pulses[x]*RAWSIGNAL_SAMPLE_RATE > 2800) return false; // long pulse too long + bitstream = (bitstream << 1) | 0x1; + } else { + if (RawSignal.Pulses[x]*RAWSIGNAL_SAMPLE_RATE > 1500) return false; // short pulse too long + if (RawSignal.Pulses[x]*RAWSIGNAL_SAMPLE_RATE < 1000) return false; // short pulse too short + bitstream = bitstream << 1; + } + } + //================================================================================== + if (bitstream == 0) return false; + if (bitstream == 0xFFFFFF) return false; + if (((bitstream)&0xffff) == 0xffff) return false; + //================================================================================== + // Output + // ---------------------------------- + sprintf(pbuffer, "20;%02X;", PKSequenceNumber++);// Node and packet number + Serial.print( pbuffer ); + // ---------------------------------- + Serial.print(F("FA20RF;")); // Label + sprintf(pbuffer, "ID=%06lx;", bitstream ); // ID + Serial.print( pbuffer ); + Serial.print(F("SMOKEALERT=ON;")); + Serial.println(); + //================================================================================== + RawSignal.Repeats=true; // suppress repeats of the same RF packet + RawSignal.Number=0; // do not process the packet any further + return true; +} +#endif // PLUGIN_080 + +#ifdef PLUGIN_TX_080 +boolean PluginTX_080(byte function, char *string) { + boolean success=false; + //10;FA20RF;67f570;1;ON; + //012345678901234567890 + unsigned long bitstream=0; + if (strncasecmp(InputBuffer_Serial+3,"FA20RF;",7) == 0) { // KAKU Command eg. + if (InputBuffer_Serial[18] != ';') return false; + InputBuffer_Serial[8]=0x30; + InputBuffer_Serial[9]=0x78; + InputBuffer_Serial[16]=0; + bitstream=str2int(InputBuffer_Serial+8); + byte cmd=str2cmd(InputBuffer_Serial+19); // ON/OFF + if (cmd!=VALUE_ON) return true; // pretend command was ok but we dont have to send anything.. + // ---------- SMOKEALERT SEND ----------- + RawSignal.Multiply=50; + RawSignal.Repeats=10; + RawSignal.Delay=20; + RawSignal.Pulses[1]=FA20RFSTART/RawSignal.Multiply; + //RawSignal.Pulses[2]=FA20RFSPACE/RawSignal.Multiply; + //RawSignal.Pulses[3]=FA20RFSPACE/RawSignal.Multiply; + RawSignal.Pulses[2]=(FA20RFSPACE+125)/RawSignal.Multiply; + RawSignal.Pulses[3]=(FA20RFSPACE+25)/RawSignal.Multiply; + for(byte x=49;x>=3;x=x-2) { + RawSignal.Pulses[x]=FA20RFSPACE/RawSignal.Multiply; + if ((bitstream & 1) == 1) + RawSignal.Pulses[x+1] = FA20RFHIGH/RawSignal.Multiply; + else + RawSignal.Pulses[x+1] = FA20RFLOW/RawSignal.Multiply; + bitstream = bitstream >> 1; + } + RawSignal.Pulses[51]=FA20RFSPACE/RawSignal.Multiply; + RawSignal.Pulses[52]=0; + RawSignal.Number=52; + RawSendRF(); + RawSignal.Multiply=RAWSIGNAL_SAMPLE_RATE; // restore setting + success=true; + } + return success; +} +#endif // PLUGIN_080 diff --git a/Plugins/Plugin_081.c b/Plugins/Plugin_081.c new file mode 100644 index 0000000..f685670 --- /dev/null +++ b/Plugins/Plugin_081.c @@ -0,0 +1,177 @@ +//####################################################################################################### +//## This Plugin is only for use with the RFLink software package ## +//## Plugin-082 Mertik Maxitrol ## +//####################################################################################################### +/*********************************************************************************************\ + * This Plugin takes care of reception of Mertik Maxitrol / DRU for fireplaces + * PCB markings: G6R H4TB / G6R 4HT. + * + * Author : StuntTeam + * Support : http://sourceforge.net/projects/rflink/ + * License : This code is free for use in any open source project when this header is included. + * Usage of any parts of this code in a commercial application is prohibited! + ********************************************************************************************* + * Changelog: v1.0 initial release + ********************************************************************************************* + * Technical information: + * + * 0001100101101001011001101 + * ----------------------- data bits (10=1 01=0) + * -- preamble, always 00? + * Shortened: (10=1 01=0) + * 01100101101001011001101 + * 0 1 0 0 1 1 0 0 1 0 1 1 + * + * 010011001011 + * ---- command => 4 bits + * -------- address => 8 bits + * + * command bits: + * 0111 7 off + * 0011 3 on + * 1011 b up + * 1101 d down + * 1000 8 stop + * 1010 a go up + * 1100 c go down + * + * Sample RF packet: + * Pulses=26;Pulses(uSec)=475,300,325,700,325,700,325,700,325,700,725,300,725,300,725,300,725,300,725,300,325,700,725,300,725; + \*********************************************************************************************/ +#define MAXITROL_PULSECOUNT 46 + +#define PLUGIN_081_RFSTART 100 +#define PLUGIN_081_RFSPACE 250 +#define PLUGIN_081_RFLOW 400 +#define PLUGIN_081_RFHIGH 750 + +#ifdef PLUGIN_081 +boolean Plugin_081(byte function, char *string) { + if (RawSignal.Number !=MAXITROL_PULSECOUNT) return false; + unsigned int bitstream=0L; + byte address=0; + byte command=0; + byte status=0; + //================================================================================== + // get bits + for(int x=3;x <= MAXITROL_PULSECOUNT-1;x=x+2) { + if (RawSignal.Pulses[x]*RAWSIGNAL_SAMPLE_RATE < 550) { + if (RawSignal.Pulses[x+1]*RAWSIGNAL_SAMPLE_RATE < 550) return false; + bitstream = (bitstream << 1); // 0 + } else { + if (RawSignal.Pulses[x]*RAWSIGNAL_SAMPLE_RATE > 900) return false; + if (RawSignal.Pulses[x+1]*RAWSIGNAL_SAMPLE_RATE > 550) return false; + bitstream = (bitstream << 1) | 0x1; // 1 + } + } + //================================================================================== + // all bytes received, make sure packet is valid + if (RawSignal.Pulses[1]*RAWSIGNAL_SAMPLE_RATE > 550) return false; + if (RawSignal.Pulses[2]*RAWSIGNAL_SAMPLE_RATE > 550) return false; + //================================================================================== + // Prevent repeating signals from showing up + //================================================================================== + if( (SignalHash!=SignalHashPrevious) || (RepeatingTimer>4)&0xff; + if (command == 0xB) status=1; // up + else if (command == 0xD) status=2; // down + else if (command == 0x7) status=3; // off + else if (command == 0x3) status=4; // on + else if (command == 0x8) status=5; // stop + else if (command == 0xa) status=6; // go up + else if (command == 0xc) status=7; // go down + else { + return false; + } + //================================================================================== + // Output + // ---------------------------------- + sprintf(pbuffer, "20;%02X;", PKSequenceNumber++); // Node and packet number + Serial.print( pbuffer ); + Serial.print(F("Mertik;")); // Label + sprintf(pbuffer, "ID=%02x;", address); // ID + Serial.print( pbuffer ); + sprintf(pbuffer, "SWITCH=%02x;", status); + Serial.print( pbuffer ); + Serial.print(F("CMD=")); + if (status==1) Serial.print(F("UP;")); + if (status==2) Serial.print(F("DOWN;")); + if (status==3) Serial.print(F("OFF;")); + if (status==4) Serial.print(F("ON;")); + if (status==5) Serial.print(F("STOP;")); + if (status==6) Serial.print(F("GOUP;")); + if (status==7) Serial.print(F("GODOWN;")); + Serial.println(); + //================================================================================== + RawSignal.Repeats=true; // suppress repeats of the same RF packet + RawSignal.Number=0; + return true; +} +#endif // PLUGIN_081 + +#ifdef PLUGIN_TX_081 +boolean PluginTX_081(byte function, char *string) { + boolean success=false; + unsigned long bitstream=0L; + //10;MERTIK;64;UP; + //0123456789012345 + if (strncasecmp(InputBuffer_Serial+3,"MERTIK;",7) == 0) { // KAKU Command eg. + if (InputBuffer_Serial[12] != ';') return false; + unsigned int bitstream2=0; // holds last 8 bits + + InputBuffer_Serial[8]=0x30; + InputBuffer_Serial[9]=0x78; // Get address from hexadecimal value + InputBuffer_Serial[12]=0x00; // Get address from hexadecimal value + bitstream=str2int(InputBuffer_Serial+8); // Address (first 16 bits) + + if(strcasecmp(InputBuffer_Serial+13,"stop")==0) bitstream2=0x8; + else if(strcasecmp(InputBuffer_Serial+13,"on")==0) bitstream2=0x3; + else if(strcasecmp(InputBuffer_Serial+13,"off")==0) bitstream2=0x7; + else if(strcasecmp(InputBuffer_Serial+13,"up")==0) bitstream2=0xB; + else if(strcasecmp(InputBuffer_Serial+13,"down")==0) bitstream2=0xD; + else if(strcasecmp(InputBuffer_Serial+13,"go_up")==0) bitstream2=0xA; + else if(strcasecmp(InputBuffer_Serial+13,"go_down")==0) bitstream2=0xC; + if (bitstream2==0) return false; + //----------------------------------------------- + RawSignal.Multiply=50; + RawSignal.Repeats=10; + RawSignal.Delay=20; + RawSignal.Pulses[1]=PLUGIN_081_RFLOW/RawSignal.Multiply; + RawSignal.Pulses[2]=PLUGIN_081_RFLOW/RawSignal.Multiply; + for(byte x=18;x>=3;x=x-2) { + if ((bitstream & 1) == 1) { + RawSignal.Pulses[x] = PLUGIN_081_RFLOW/RawSignal.Multiply; + RawSignal.Pulses[x-1] = PLUGIN_081_RFHIGH /RawSignal.Multiply; + } else { + RawSignal.Pulses[x] = PLUGIN_081_RFHIGH /RawSignal.Multiply; + RawSignal.Pulses[x-1] = PLUGIN_081_RFLOW/RawSignal.Multiply; + } + bitstream = bitstream >> 1; + } + for(byte x=26;x>=19;x=x-2) { + if ((bitstream2 & 1) == 1) { + RawSignal.Pulses[x] = PLUGIN_081_RFLOW/RawSignal.Multiply; + RawSignal.Pulses[x-1] = PLUGIN_081_RFHIGH /RawSignal.Multiply; + } else { + RawSignal.Pulses[x] = PLUGIN_081_RFHIGH /RawSignal.Multiply; + RawSignal.Pulses[x-1] = PLUGIN_081_RFLOW/RawSignal.Multiply; + } + bitstream2 = bitstream2 >> 1; + } + RawSignal.Pulses[27]=PLUGIN_081_RFSTART/RawSignal.Multiply; + RawSignal.Number=27; + RawSendRF(); + success=true; + //----------------------------------------------- + } + return success; +} +#endif // PLUGIN_081 diff --git a/Plugins/Plugin_082.c b/Plugins/Plugin_082.c new file mode 100644 index 0000000..e4d1132 --- /dev/null +++ b/Plugins/Plugin_082.c @@ -0,0 +1,177 @@ +//####################################################################################################### +//## This Plugin is only for use with the RFLink software package ## +//## Plugin-082 Mertik Maxitrol ## +//####################################################################################################### +/*********************************************************************************************\ + * This Plugin takes care of reception of Mertik Maxitrol / DRU for fireplaces + * PCB markings: G6R H4T1. + * + * Author : StuntTeam + * Support : http://sourceforge.net/projects/rflink/ + * License : This code is free for use in any open source project when this header is included. + * Usage of any parts of this code in a commercial application is prohibited! + ********************************************************************************************* + * Changelog: v1.0 initial release + ********************************************************************************************* + * Technical information: + * + * 0001100101101001011001101 + * ----------------------- data bits (10=1 01=0) + * -- preamble, always 00? + * Shortened: (10=1 01=0) + * 01100101101001011001101 + * 0 1 0 0 1 1 0 0 1 0 1 1 + * + * 010011001011 + * ---- command => 4 bits + * -------- address => 8 bits + * + * command bits: + * 0111 7 off + * 0011 3 on + * 1011 b up + * 1101 d down + * 1000 8 stop + * 1010 a go up + * 1100 c go down + * + * Sample RF packet: + * Pulses=26;Pulses(uSec)=475,300,325,700,325,700,325,700,325,700,725,300,725,300,725,300,725,300,725,300,325,700,725,300,725; + \*********************************************************************************************/ +#define MAXITROL_PULSECOUNT 26 + +#define PLUGIN_082_RFSTART 100 +#define PLUGIN_082_RFSPACE 250 +#define PLUGIN_082_RFLOW 400 +#define PLUGIN_082_RFHIGH 750 + +#ifdef PLUGIN_082 +boolean Plugin_082(byte function, char *string) { + if (RawSignal.Number !=MAXITROL_PULSECOUNT) return false; + unsigned int bitstream=0L; + byte address=0; + byte command=0; + byte status=0; + //================================================================================== + // get bits + for(int x=3;x <= MAXITROL_PULSECOUNT-1;x=x+2) { + if (RawSignal.Pulses[x]*RAWSIGNAL_SAMPLE_RATE < 550) { + if (RawSignal.Pulses[x+1]*RAWSIGNAL_SAMPLE_RATE < 550) return false; + bitstream = (bitstream << 1); // 0 + } else { + if (RawSignal.Pulses[x]*RAWSIGNAL_SAMPLE_RATE > 900) return false; + if (RawSignal.Pulses[x+1]*RAWSIGNAL_SAMPLE_RATE > 550) return false; + bitstream = (bitstream << 1) | 0x1; // 1 + } + } + //================================================================================== + // all bytes received, make sure packet is valid + if (RawSignal.Pulses[1]*RAWSIGNAL_SAMPLE_RATE > 550) return false; + if (RawSignal.Pulses[2]*RAWSIGNAL_SAMPLE_RATE > 550) return false; + //================================================================================== + // Prevent repeating signals from showing up + //================================================================================== + if( (SignalHash!=SignalHashPrevious) || (RepeatingTimer>4)&0xff; + if (command == 0xB) status=1; // up + else if (command == 0xD) status=2; // down + else if (command == 0x7) status=3; // off + else if (command == 0x3) status=4; // on + else if (command == 0x8) status=5; // stop + else if (command == 0xa) status=6; // go up + else if (command == 0xc) status=7; // go down + else { + return false; + } + //================================================================================== + // Output + // ---------------------------------- + sprintf(pbuffer, "20;%02X;", PKSequenceNumber++); // Node and packet number + Serial.print( pbuffer ); + Serial.print(F("Mertik;")); // Label + sprintf(pbuffer, "ID=%02x;", address); // ID + Serial.print( pbuffer ); + sprintf(pbuffer, "SWITCH=%02x;", status); + Serial.print( pbuffer ); + Serial.print(F("CMD=")); + if (status==1) Serial.print(F("UP;")); + if (status==2) Serial.print(F("DOWN;")); + if (status==3) Serial.print(F("OFF;")); + if (status==4) Serial.print(F("ON;")); + if (status==5) Serial.print(F("STOP;")); + if (status==6) Serial.print(F("GOUP;")); + if (status==7) Serial.print(F("GODOWN;")); + Serial.println(); + //================================================================================== + RawSignal.Repeats=true; // suppress repeats of the same RF packet + RawSignal.Number=0; + return true; +} +#endif // PLUGIN_082 + +#ifdef PLUGIN_TX_082 +boolean PluginTX_082(byte function, char *string) { + boolean success=false; + unsigned long bitstream=0L; + //10;MERTIK;64;UP; + //0123456789012345 + if (strncasecmp(InputBuffer_Serial+3,"MERTIK;",7) == 0) { // KAKU Command eg. + if (InputBuffer_Serial[12] != ';') return false; + unsigned int bitstream2=0; // holds last 8 bits + + InputBuffer_Serial[8]=0x30; + InputBuffer_Serial[9]=0x78; // Get address from hexadecimal value + InputBuffer_Serial[12]=0x00; // Get address from hexadecimal value + bitstream=str2int(InputBuffer_Serial+8); // Address (first 16 bits) + + if(strcasecmp(InputBuffer_Serial+13,"stop")==0) bitstream2=0x8; + else if(strcasecmp(InputBuffer_Serial+13,"on")==0) bitstream2=0x3; + else if(strcasecmp(InputBuffer_Serial+13,"off")==0) bitstream2=0x7; + else if(strcasecmp(InputBuffer_Serial+13,"up")==0) bitstream2=0xB; + else if(strcasecmp(InputBuffer_Serial+13,"down")==0) bitstream2=0xD; + else if(strcasecmp(InputBuffer_Serial+13,"go_up")==0) bitstream2=0xA; + else if(strcasecmp(InputBuffer_Serial+13,"go_down")==0) bitstream2=0xC; + if (bitstream2==0) return false; + //----------------------------------------------- + RawSignal.Multiply=50; + RawSignal.Repeats=10; + RawSignal.Delay=20; + RawSignal.Pulses[1]=PLUGIN_082_RFLOW/RawSignal.Multiply; + RawSignal.Pulses[2]=PLUGIN_082_RFLOW/RawSignal.Multiply; + for(byte x=18;x>=3;x=x-2) { + if ((bitstream & 1) == 1) { + RawSignal.Pulses[x] = PLUGIN_082_RFLOW/RawSignal.Multiply; + RawSignal.Pulses[x-1] = PLUGIN_082_RFHIGH /RawSignal.Multiply; + } else { + RawSignal.Pulses[x] = PLUGIN_082_RFHIGH /RawSignal.Multiply; + RawSignal.Pulses[x-1] = PLUGIN_082_RFLOW/RawSignal.Multiply; + } + bitstream = bitstream >> 1; + } + for(byte x=26;x>=19;x=x-2) { + if ((bitstream2 & 1) == 1) { + RawSignal.Pulses[x] = PLUGIN_082_RFLOW/RawSignal.Multiply; + RawSignal.Pulses[x-1] = PLUGIN_082_RFHIGH /RawSignal.Multiply; + } else { + RawSignal.Pulses[x] = PLUGIN_082_RFHIGH /RawSignal.Multiply; + RawSignal.Pulses[x-1] = PLUGIN_082_RFLOW/RawSignal.Multiply; + } + bitstream2 = bitstream2 >> 1; + } + RawSignal.Pulses[27]=PLUGIN_082_RFSTART/RawSignal.Multiply; + RawSignal.Number=27; + RawSendRF(); + success=true; + //----------------------------------------------- + } + return success; +} +#endif // PLUGIN_082 diff --git a/Plugins/Plugin_090.c b/Plugins/Plugin_090.c new file mode 100644 index 0000000..ed1f756 --- /dev/null +++ b/Plugins/Plugin_090.c @@ -0,0 +1,217 @@ +//####################################################################################################### +//## This Plugin is only for use with Nodo 3.7 Slave Devices (variablesend support) ## +//## Plugin-090 Nodo Slave ## +//####################################################################################################### +// ********************************************************************************************* +// * This Plugin takes care of reception of Nodo 3.7 Slave Devices +// * +// * Author : StuntTeam +// * Support : http://sourceforge.net/projects/rflink/ +// * License : This code is free for use in any open source project when this header is included. +// * Usage of any parts of this code in a commercial application is prohibited! +// ********************************************************************************************* +// * Changelog: v1.0 initial release +// ********************************************************************************************* +// ---------------------------------- +// Nodo Slave Unit 1, 2 and 4 till 9: // Individual sensor data +// +// Variabele 5 : Temperature +// Variabele 6 : Humidity 0 - 100 +// Variabele 7 : Rain fall +// Variabele 8 : Wind speed +// Variabele 9 : Wind direction 0 - 15 +// Variabele 10 : Wind gust +// Variabele 11, 12 en 13 : Temperature +// Variabele 14 : Humidity 0 - 100 +// Variabele 15 : UV meter 0 - 1024 +// Variabele 16 : Barometric pressure +// +// ---------------------------------- +// Nodo Slave Unit 10 till 16: // Combined sensor data +// +// Variabele 5 : Temperature +// Variabele 6 : Humidity 0 - 100 +// Variabele 7 : Rain fall +// Variabele 8 : Wind speed +// Variabele 9 : Wind direction 0 - 15 +// Variabele 10 : Wind gust +// Variabele 11, 12 en 13 : Temperature +// Variabele 14 : Humidity 0 - 100 +// Variabele 15 : UV meter 0 - 1024 +// Variabele 16 : Barometric pressure +// +// ---------------------------------- +// Nodo Slave Unit 3: // Pulse Meters +// +// Variabele 1 till 16 : Pulse value 1 till 16 +// +// You can use a Nodo slave device with ID 03 to send variabel numbers 1 til 16 +// which will be passed to Domoticz as pulse meter values (electricity/water/gas etc.) +// ---------------------------------- +// +// * Sample: +// * 20;9E;DEBUG;Pulses=194;Pulses(uSec)=3100,900,500,350,1475,375,525,300,1475,375,500,350,450,375,575,250,525,300,1475,375,1450,400,1450,400,425,425,425,425,1425,425,425,425,425,400,425,400,425,400,425,400,425,400,450,400,425,400,450,400,450,400,450,400,425,400,425,425,425,425,425,425,400,425,425,425,400,425,1425,425,400,425,400,425,400,425,400,425,400,425,425,425,400,425,425,425,425,425,1450,425,425,425,425,425,425,425,425,425,400,425,1425,425,1425,425,400,425,400,425,400,425,400,425,400,425,400,425,400,425,425,425,425,425,400,425,400,425,400,425,400,425,400,425,400,425,400,450,400,450,400,450,400,450,400,450,400,450,1400,450,400,450,400,450,400,425,400,450,400,425,1425,425,400,425,1425,425,1425,450,400,450,400,450,375,450,375,450,400,450,1425,450,400,450,1425,425,1425,450,400,450,400,425,400,450,400,450,400,450,400; +// * 20;9F;Slave;ID=0307;DEBUG=0014; +// * slave nodo 7, var 3 value 14 + +// 3100,900, +/*( 194-2=192/2 = 96 +500,350,1475,375,525,300,1475,375,500,350,450,375,575,250,525,300,1475,375,1450,400,1450,400,425,425,425,425,1425,425,425,425,425,400,425,400,425,400,425,400,425,400,450,400, +425,400,450,400,450,400,450,400,425,400,425,425,425,425,425,425,400,425,425,425,400,425,1425,425,400,425,400,425,400,425,400,425,400,425,425,425,400,425,425,425,425,425,1450, +425,425,425,425,425,425,425,425,425,400,425,1425,425,1425,425,400,425,400,425,400,425,400,425,400,425,400,425,400,425,425,425,425,425,400,425,400,425,400,425,400,425,400,425, +400,425,400,450,400,450,400,450,400,450,400,450,400,450,1400,450,400,450,400,450,400,425,400,450,400,425,1425,425,400,425,1425,425,1425,450,400,450,400,450,375,450,375,450, +400,450,1425,450,400,450,1425,425,1425,450,400,450,400,425,400,450,400,450,400,450,400; +01010000 11100100 00000000 00000000 10000000 00100000 11000000 00000000 00000001 00000101 10000010 11000000 +00001010 00100111 00000000 00000000 00000001 00000100 00000011 00000000 10000000 10100000 01000001 00000011 +50E400008020C0010582C0 +A270000104030080A04103 +*/ +// ********************************************************************************************* +#define NODO_PULSE_MID 1000/RAWSIGNAL_SAMPLE_RATE // PWM: Pulsen langer zijn '1' +#define NodoSlave_PULSECOUNT 194 + +#ifdef PLUGIN_090 + +struct DataBlockStruct { // 16*sizeof(struct DataBlockStruct)+2 = 194 eg. 16*12+2 + byte Version; + byte SourceUnit; // event->SourceUnit=DataBlock.SourceUnit&0x1F; // Maskeer de bits van het Home adres. + byte DestinationUnit; // event->DestinationUnit=DataBlock.DestinationUnit; // destination nodo + byte Flags; + byte Type; // event->Type=DataBlock.Type; + byte Command; // event->Command=DataBlock.Command; + byte Par1; // event->Par1=DataBlock.Par1; + unsigned long Par2; // event->Par2=DataBlock.Par2; + byte Checksum; +}; + +boolean Plugin_090(byte function, char *string) { + if (RawSignal.Number!=NodoSlave_PULSECOUNT ) return false; // Per byte twee posities + startbit. + byte b,x,y,z; + unsigned long varvalue=0L; + + struct DataBlockStruct DataBlock; + byte *B=(byte*)&DataBlock; // B wijst naar de eerste byte van de struct + z=3; // RawSignal pulse teller: 0=niet gebruiktaantal, 1=startpuls, 2=space na startpuls, 3=1e pulslengte. Dus start loop met drie. + + for(x=0;x NODO_PULSE_MID) + b|=1<command = DataBlock.Command = 4 = EVENT_VARIABLE + // ---------------------------------- + // Output + // ---------------------------------- + sprintf(pbuffer, "20;%02X;", PKSequenceNumber++); // Node and packet number + Serial.print( pbuffer ); + // ---------------------------------- + Serial.print(F("Slave;")); // Label + //================================================================================== + // slave id 03 aka source nodo id 03 => variable 1..6 will be pulse meter data + //================================================================================== + if (DataBlock.SourceUnit == 3 ) { // Pulse Meter variables + sprintf(pbuffer, "ID=%02x%02x%02x;", DataBlock.DestinationUnit, DataBlock.SourceUnit, DataBlock.Par1); // ID + Serial.print( pbuffer ); + varvalue = ul2float(DataBlock.Par2); // convert + if ((DataBlock.Par1 < 1) || (DataBlock.Par1 > 16)) { // Unsupported (for now) variable, just show the contents + sprintf(pbuffer, "DEBUG=%08lx;", varvalue); // value + } else { + sprintf(pbuffer, "METER=%08lx;", varvalue); // value + } + Serial.print( pbuffer ); + Serial.println(); + } else + //================================================================================== + // slave id > 09 aka source nodo id > 09 => Combined variables + // slave id < 10 aka source nodo id < 10 => Regular variables + //================================================================================== + if ((DataBlock.SourceUnit > 0) && (DataBlock.SourceUnit < 17)) { + if (DataBlock.SourceUnit > 9 ) { + sprintf(pbuffer, "ID=%02x%02x;",DataBlock.SourceUnit , DataBlock.DestinationUnit); // ID + } else { + sprintf(pbuffer, "ID=%02x%02x%02x;", DataBlock.DestinationUnit, DataBlock.SourceUnit, DataBlock.Par1); // ID + } + Serial.print( pbuffer ); + if ((DataBlock.Par1 < 5) || (DataBlock.Par1 > 16)) { // Unsupported (for now) variable, just show the contents + int varvalue = ul2float(DataBlock.Par2); // convert + sprintf(pbuffer, "DEBUG=%04x;", varvalue); // value + } else + if (DataBlock.Par1 == 5){ // Variable 5 : temperature + int temperature = 10 * ul2float(DataBlock.Par2); // convert + if (temperature <= 0) temperature=-temperature | 0x8000; // set high bit for negative temperatures + sprintf(pbuffer, "TEMP=%04x;", temperature); // value + } else + if (DataBlock.Par1 == 6){ // Variable 6 : humidity + int humidity = ul2float(DataBlock.Par2) + 0.5; // add 0.5 to make sure it's rounded the way it should and assign as integer to remove decimal value + sprintf(pbuffer, "HUM=%02d;", humidity); // value + } else + if (DataBlock.Par1 == 7){ // Variable 7 : Rain in mm. + int rain = ul2float(DataBlock.Par2); // convert + sprintf(pbuffer, "RAIN=%04x;", rain); // value + } else + if (DataBlock.Par1 == 8){ // Variable 8 : Wind speed + int winsp = ul2float(DataBlock.Par2); // convert + sprintf(pbuffer, "WINSP=%04x;", winsp); // value + } else + if (DataBlock.Par1 == 9){ // Variable 9 : Wind Direction + int windir = ul2float(DataBlock.Par2); // convert + sprintf(pbuffer, "WINDIR=%04d;", windir); // value + } else + if (DataBlock.Par1 == 10){ // Variable 10: Wind Gust + int wings = ul2float(DataBlock.Par2); // convert + sprintf(pbuffer, "WINGS=%04x;", wings); // value + } + if ((DataBlock.Par1 > 10) && (DataBlock.Par1 < 14)) { // Variable 11 12 or 13 : emulate temperature sensor + int temperature = 10 * ul2float(DataBlock.Par2); // convert + if (temperature <= 0) temperature=-temperature | 0x8000; // set high bit for negative temperatures + sprintf(pbuffer, "TEMP=%04x;", temperature); // value + } else + if (DataBlock.Par1 == 14){ // Variable 14 : emulate humidity sensor + int humidity = ul2float(DataBlock.Par2) + 0.5; // add 0.5 to make sure it's rounded the way it should and assign as integer to remove decimal value + sprintf(pbuffer, "HUM=%02d;", humidity); // value + } else + if (DataBlock.Par1 == 15){ // Variable 15 : UV sensor + int light = ul2float(DataBlock.Par2); // supplied is value between 0 and 1024 + light = map(light, 0,1024,1,100); // Map value to 1 - 100 + sprintf(pbuffer, "UV=%04x;", light); // value + } else + if (DataBlock.Par1 == 16){ // Variable 16 : Barometric pressure sensor + int baro = ul2float(DataBlock.Par2); // convert + sprintf(pbuffer, "BARO=%04x;", baro); // value + } + Serial.print( pbuffer ); + Serial.println(); + } + //================================================================================== + } else { // Not a variable event + if ((DataBlock.Command == 0) && (DataBlock.Type==0)) return false; + // ---------------------------------- + // Output + // ---------------------------------- + sprintf(pbuffer, "20;%02X;", PKSequenceNumber++); // Node and packet number + Serial.print( pbuffer ); + // ---------------------------------- + Serial.print(F("Slave;Debug=")); // Label + sprintf(pbuffer, "%02x %02x %02x %02x %02x ", DataBlock.DestinationUnit, DataBlock.SourceUnit, DataBlock.Command, DataBlock.Type, DataBlock.Par1); + Serial.print( pbuffer ); + sprintf(pbuffer, "%d;", DataBlock.Par2); // ID + Serial.print( pbuffer ); + Serial.println(); + } + //================================================================================== + RawSignal.Repeats=true; // suppress repeats of the same RF packet + RawSignal.Number=0; + return true; +} +#endif // PLUGIN_090 diff --git a/Plugins/Plugin_100.c b/Plugins/Plugin_100.c new file mode 100644 index 0000000..cb05087 --- /dev/null +++ b/Plugins/Plugin_100.c @@ -0,0 +1,166 @@ +//####################################################################################################### +//## This Plugin is only for use with the RFLink software package ## +//## Plugin-100 AlectoV2 ## +//## > 868 MHz Plugin < ## +//####################################################################################################### + +/*********************************************************************************************\ + * Dit protocol zorgt voor ontvangst van Alecto weerstation buitensensoren + * + * Author : Martinus van den Broek + * Support : http://sourceforge.net/projects/rflink/ + * License : This code is free for use in any open source project when this header is included. + * Usage of any parts of this code in a commercial application is prohibited! + ********************************************************************************************* + * Technische informatie: + * DKW2012 Message Format: (11 Bytes, 88 bits): + * AAAAAAAA AAAABBBB BBBB__CC CCCCCCCC DDDDDDDD EEEEEEEE FFFFFFFF GGGGGGGG GGGGGGGG HHHHHHHH IIIIIIII + * Temperature Humidity Windspd_ Windgust Rain____ ________ Winddir Checksum + * A = start/unknown, first 8 bits are always 11111111 + * B = Rolling code + * C = Temperature (10 bit value with -400 base) + * D = Humidity + * E = windspeed (* 0.3 m/s, correction for webapp = 3600/1000 * 0.3 * 100 = 108)) + * F = windgust (* 0.3 m/s, correction for webapp = 3600/1000 * 0.3 * 100 = 108)) + * G = Rain ( * 0.3 mm) + * H = winddirection (0 = north, 4 = east, 8 = south 12 = west) + * I = Checksum, calculation is still under investigation + * + * WS3000 and ACH2010 systems have no winddirection, message format is 8 bit shorter + * Message Format: (10 Bytes, 80 bits): + * AAAAAAAA AAAABBBB BBBB__CC CCCCCCCC DDDDDDDD EEEEEEEE FFFFFFFF GGGGGGGG GGGGGGGG HHHHHHHH + * Temperature Humidity Windspd_ Windgust Rain____ ________ Checksum + * + * -------------------------------------------------------------------------------------------- + * DCF Time Message Format: (NOT DECODED!, we already have time sync through webapp) + * AAAAAAAA BBBBCCCC DDDDDDDD EFFFFFFF GGGGGGGG HHHHHHHH IIIIIIII JJJJJJJJ KKKKKKKK LLLLLLLL MMMMMMMM + * 11 Hours Minutes Seconds Year Month Day ? Checksum + * B = 11 = DCF + * C = ? + * D = ? + * E = ? + * F = Hours BCD format (7 bits only for this byte, MSB could be '1') + * G = Minutes BCD format + * H = Seconds BCD format + * I = Year BCD format (only two digits!) + * J = Month BCD format + * K = Day BCD format + * L = ? + * M = Checksum + \*********************************************************************************************/ +#define DKW2012_MIN_PULSECOUNT 164 +#define DKW2012_MAX_PULSECOUNT 176 +#define ACH2010_MIN_PULSECOUNT 160 // reduce this value (144?) in case of bad reception +#define ACH2010_MAX_PULSECOUNT 160 + +#ifdef PLUGIN_100 + +uint8_t Plugin_100_ProtocolAlectoCRC8( uint8_t *addr, uint8_t len); + +boolean Plugin_100(byte function, char *string) { + if (!(((RawSignal.Number >= ACH2010_MIN_PULSECOUNT) && (RawSignal.Number <= ACH2010_MAX_PULSECOUNT)) || ((RawSignal.Number >= DKW2012_MIN_PULSECOUNT) && (RawSignal.Number <= DKW2012_MAX_PULSECOUNT)))) return false; + + byte c=0; + byte rfbit; + byte data[10]; + byte msgtype=0; + byte rc=0; + byte checksum=0; + byte checksumcalc=0; + byte maxidx = 8; + //================================================================================== + if(RawSignal.Number > ACH2010_MAX_PULSECOUNT) maxidx = 9; + // Get message back to front as the header is almost never received complete for ACH2010 + byte idx = maxidx; + for(byte x=RawSignal.Number; x>0; x=x-2) { + if(RawSignal.Pulses[x-1]*RAWSIGNAL_SAMPLE_RATE < 0x300) rfbit = 0x80; else rfbit = 0; + data[idx] = (data[idx] >> 1) | rfbit; + c++; + if (c == 8) { + if (idx == 0) break; + c = 0; + idx--; + } + } + //================================================================================== + checksum = data[maxidx]; + checksumcalc = Plugin_100_ProtocolAlectoCRC8(data, maxidx); + + msgtype = (data[0] >> 4) & 0xf; // msg type must be 5 or 10 + rc = (data[0] << 4) | (data[1] >> 4); // rolling code + + if (checksum != checksumcalc) return false; + if ((msgtype != 10) && (msgtype != 5)) return true; // why true? + //================================================================================== + unsigned int temp = 0; + unsigned int rain=0; + byte hum = 0; + int wdir = 0; + int wspeed = 0; + int wgust = 0; + + temp = (((data[1] & 0x3) * 256 + data[2]) - 400); + hum = data[3]; + wspeed = data[4] * 108; + wspeed = wspeed / 10; + wgust = data[5] * 108; + wgust = wgust / 10; + rain = (data[6] * 256) + data[7]; + if (RawSignal.Number >= DKW2012_MIN_PULSECOUNT) { + wdir = (data[8] & 0xf); + } + // ---------------------------------- + // Output + // ---------------------------------- + sprintf(pbuffer, "20;%02X;", PKSequenceNumber++); // Node and packet number + Serial.print( pbuffer ); + // ---------------------------------- + if (RawSignal.Number >= DKW2012_MIN_PULSECOUNT) { + Serial.print("DKW2012;"); // Label + } else { + Serial.print("Alecto V2;"); // Label + } + sprintf(pbuffer, "ID=00%02x;", rc); // ID + Serial.print( pbuffer ); + sprintf(pbuffer, "TEMP=%04x;", temp); + Serial.print( pbuffer ); + sprintf(pbuffer, "HUM=%02x;", hum); + Serial.print( pbuffer ); + sprintf(pbuffer, "WINSP=%04x;", wspeed); + Serial.print( pbuffer ); + sprintf(pbuffer, "WINGS=%04x;", wgust); + Serial.print( pbuffer ); + sprintf(pbuffer, "RAIN=%04x;", rain); + Serial.print( pbuffer ); + if (RawSignal.Number >= DKW2012_MIN_PULSECOUNT) { + sprintf(pbuffer, "WINDIR=%04d;", wdir); + Serial.print( pbuffer ); + } + Serial.println(); + // ---------------------------------- + RawSignal.Repeats=true; // suppress repeats of the same RF packet + RawSignal.Number=0; // do not process the packet any further + return true; +} + +/*********************************************************************************************\ + * Calculates CRC-8 checksum + * reference http://lucsmall.com/2012/04/29/weather-station-hacking-part-2/ + * http://lucsmall.com/2012/04/30/weather-station-hacking-part-3/ + * https://github.com/lucsmall/WH2-Weather-Sensor-Library-for-Arduino/blob/master/WeatherSensorWH2.cpp + \*********************************************************************************************/ +uint8_t Plugin_100_ProtocolAlectoCRC8( uint8_t *addr, uint8_t len) { + uint8_t crc = 0; + // Indicated changes are from reference CRC-8 function in OneWire library + while (len--) { + uint8_t inbyte = *addr++; + for (uint8_t i = 8; i; i--) { + uint8_t mix = (crc ^ inbyte) & 0x80; // changed from & 0x01 + crc <<= 1; // changed from right shift + if (mix) crc ^= 0x31;// changed from 0x8C; + inbyte <<= 1; // changed from right shift + } + } + return crc; +} +#endif // PLUGIN_100 diff --git a/Plugins/Plugin_101.c b/Plugins/Plugin_101.c new file mode 100644 index 0000000..8887d59 --- /dev/null +++ b/Plugins/Plugin_101.c @@ -0,0 +1,912 @@ +//####################################################################################################### +//## This Plugin is only for use with the RFLink software package ## +//## Plugin-48 Oregon V1/2/3 ## +//####################################################################################################### +/*********************************************************************************************\ + * This protocol takes care of receiving a few 868 Mhz protocols + * + * Protocols : Visonic, + * ELV EM-Serie: EM-1000S, EM-100-EM, EM-1000-GZ + * ELV KS serie: Thermo (AS3), Thermo/Hygro (AS2000, ASH2000, S2000, S2001A, S2001IA, ASH2200, S300IA) + * Rain (S2000R), Wind (S2000W), Thermo/Hygro/Baro (S2001I, S2001ID), UV (S2500H) + * Pyrano (Strahlungsleistung), Kombi (KS200, KS300) + * FS20 FS serie + * + * Author : StuntTeam + * Support : http://sourceforge.net/projects/rflink/ + * License : This code is free for use in any open source project when this header is included. + * Usage of any parts of this code in a commercial application is prohibited! + ********************************************************************************************* + * Changelog: v0.1 beta + ********************************************************************************************* + * Technical information: + * Supports Oregon V1, V2 and V3 protocol messages + * Core code from https://github.com/Cactusbone/ookDecoder/blob/master/ookDecoder.ino + * Copyright (c) 2014 Charly Koza cactusbone@free.fr Copyright (c) 2012 Olivier Lebrun olivier.lebrun@connectingstuff.net + * Copyright (c) 2012 Dominique Pierre (zzdomi) Copyright (c) 2010 Jean-Claude Wippler jcw@equi4.com + * 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. + \*********************************************************************************************/ +#define PLUGIN_ID 101 +#define PLUGIN_NAME "Few868" + +#define OSV3_PULSECOUNT_MIN 126 +#define OSV3_PULSECOUNT_MAX 278 + + +// ===================================================================================================== +class DecodeOOK { +protected: + byte total_bits, bits, flip, state, pos, data[25]; + virtual char decode(word width) = 0; +public: + enum { UNKNOWN, T0, T1, T2, T3, OK, DONE }; + // ------------------------------------- + DecodeOOK() { resetDecoder(); } + // ------------------------------------- + bool nextPulse(word width) { + if (state != DONE) + + switch (decode(width)) { + case -1: resetDecoder(); break; + case 1: done(); break; + } + return isDone(); + } + // ------------------------------------- + bool isDone() const { return state == DONE; } + // ------------------------------------- + const byte* getData(byte& count) const { + count = pos; + return data; + } + // ------------------------------------- + void resetDecoder() { + total_bits = bits = pos = flip = 0; + state = UNKNOWN; + } + // ------------------------------------- + // add one bit to the packet data pbuffer + // ------------------------------------- + virtual void gotBit(char value) { + total_bits++; + byte *ptr = data + pos; + *ptr = (*ptr >> 1) | (value << 7); + + if (++bits >= 8) { + bits = 0; + if (++pos >= sizeof data) { + resetDecoder(); + return; + } + } + state = OK; + } + // ------------------------------------- + // store a bit using Manchester encoding + // ------------------------------------- + void manchester(char value) { + flip ^= value; // manchester code, long pulse flips the bit + gotBit(flip); + } + // ------------------------------------- + // move bits to the front so that all the bits are aligned to the end + // ------------------------------------- + void alignTail(byte max = 0) { + // align bits + if (bits != 0) { + data[pos] >>= 8 - bits; + for (byte i = 0; i < pos; ++i) + data[i] = (data[i] >> bits) | (data[i + 1] << (8 - bits)); + bits = 0; + } + // optionally shift bytes down if there are too many of 'em + if (max > 0 && pos > max) { + byte n = pos - max; + pos = max; + for (byte i = 0; i < pos; ++i) + data[i] = data[i + n]; + } + } + // ------------------------------------- + void reverseBits() { + for (byte i = 0; i < pos; ++i) { + byte b = data[i]; + for (byte j = 0; j < 8; ++j) { + data[i] = (data[i] << 1) | (b & 1); + b >>= 1; + } + } + } + // ------------------------------------- + void reverseNibbles() { + for (byte i = 0; i < pos; ++i) + data[i] = (data[i] << 4) | (data[i] >> 4); + } + // ------------------------------------- + void done() { + while (bits) + gotBit(0); // padding + state = DONE; + } +}; + +// 868 MHz decoders + +/// OOK decoder for Visonic devices. +class VisonicDecoder : public DecodeOOK { +public: + VisonicDecoder () {} + + virtual char decode (word width) { + if (200 <= width && width < 1000) { + byte w = width >= 600; + switch (state) { + case UNKNOWN: + case OK: + state = w == 0 ? T0 : T1; + break; + case T0: + gotBit(!w); + if (w) + return 0; + break; + case T1: + gotBit(!w); + if (!w) + return 0; + break; + } + // sync error, flip all the preceding bits to resync + for (byte i = 0; i <= pos; ++i) + data[i] ^= 0xFF; + } else if (width >= 2500 && 8 * pos + bits >= 36 && state == OK) { + for (byte i = 0; i < 4; ++i) + gotBit(0); + alignTail(5); // keep last 40 bits + // only report valid packets + byte b = data[0] ^ data[1] ^ data[2] ^ data[3] ^ data[4]; + if ((b & 0xF) == (b >> 4)) + return 1; + } else + return -1; + return 0; + } +}; + +/// OOK decoder for FS20 type EM devices. +class EMxDecoder : public DecodeOOK { +public: + EMxDecoder () : DecodeOOK (30) {} // ignore packets repeated within 3 sec + + // see also http://fhz4linux.info/tiki-index.php?page=EM+Protocol + virtual char decode (word width) { + if (200 <= width && width < 1000) { + byte w = width >= 600; + switch (state) { + case UNKNOWN: + if (w == 0) + ++flip; + else if (flip > 20) + state = OK; + else + return -1; + break; + case OK: + if (w == 0) + state = T0; + else + return -1; + break; + case T0: + gotBit(w); + break; + } + } else if (width >= 1500 && pos >= 9) + return 1; + else + return -1; + return 0; + } +}; + +/// OOK decoder for FS20 type KS devices. +class KSxDecoder : public DecodeOOK { +public: + KSxDecoder () {} + + // see also http://www.dc3yc.homepage.t-online.de/protocol.htm + virtual char decode (word width) { + if (200 <= width && width < 1000) { + byte w = width >= 600; + switch (state) { + case UNKNOWN: + gotBit(w); + bits = pos = 0; + if (data[0] != 0x95) + state = UNKNOWN; + break; + case OK: + state = w == 0 ? T0 : T1; + break; + case T0: + gotBit(1); + if (!w) + return -1; + break; + case T1: + gotBit(0); + if (w) + return -1; + break; + } + } else if (width >= 1500 && pos >= 6) + return 1; + else + return -1; + return 0; + } +}; + +/// OOK decoder for FS20 type FS devices. +class FSxDecoder : public DecodeOOK { +public: + FSxDecoder () {} + + // see also http://fhz4linux.info/tiki-index.php?page=FS20%20Protocol + virtual char decode (word width) { + if (300 <= width && width < 775) { + byte w = width >= 500; + switch (state) { + case UNKNOWN: + if (w == 0) + ++flip; + else if (flip > 20) + state = T1; + else + return -1; + break; + case OK: + state = w == 0 ? T0 : T1; + break; + case T0: + gotBit(0); + if (w) + return -1; + break; + case T1: + gotBit(1); + if (!w) + return -1; + break; + } + } else if (width >= 1500 && pos >= 5) + return 1; + else + return -1; + return 0; + } +}; + +// ===================================================================================================== +// ===================================================================================================== + + +OregonDecoderV1 orscV1; +OregonDecoderV2 orscV2; +OregonDecoderV3 orscV3; + +volatile word pulse; +// ===================================================================================================== +// ===================================================================================================== +byte osdata[13]; + +void reportSerial(class DecodeOOK& decoder) { + byte pos; + const byte* data = decoder.getData(pos); + for (byte i = 0; i < pos; ++i) { + if (i < 13) osdata[i]=data[i]; + } + decoder.resetDecoder(); +} +// ===================================================================================================== +// calculate a packet checksum by performing a +byte checksum(byte type, int count, byte check) { + byte calc=0; + // type 1, add all nibbles, deduct 10 + if (type == 1) { + for(byte i = 0; i> 4; + calc += (osdata[i]&0xF); + } + calc=calc-10; + } else + // type 2, add all nibbles up to count, add the 13th nibble , deduct 10 + if (type == 2) { + for(byte i = 0; i> 4; + calc += (osdata[i]&0xF); + } + calc += (osdata[6]&0xF); + calc=calc-10; + } else + // type 3, add all nibbles up to count, subtract 10 only use the low 4 bits for the compare + if (type == 3) { + for(byte i = 0; i> 4; + calc += (osdata[i]&0xF); + } + calc=calc-10; + calc=(calc&0x0f); + } else + if (type == 4) { + for(byte i = 0; i> 4; + calc += (osdata[i]&0xF); + } + calc=calc-10; + + } + if (check == calc ) return 0; + return 1; +} +// ===================================================================================================== +boolean Plugin_048(byte function, char *string) { + boolean success=false; + +#ifdef PLUGIN_048_CORE + if ((RawSignal.Number < OSV3_PULSECOUNT_MIN) || (RawSignal.Number > OSV3_PULSECOUNT_MAX) ) return false; + + byte basevar=0; + byte rc=0; + byte found = 0; + byte channel = 0; + + int temp = 0; + byte hum = 0; + int comfort = 0; + int baro = 0; + int forecast = 0; + int uv = 0; + int wdir = 0; + int wspeed = 0; + int awspeed = 0; + int rain = 0; + int raintot = 0; + + word p = pulse; + // ================================================================================== + for (int x = 0; x < RawSignal.Number; x++) { + p = RawSignal.Pulses[x]*RAWSIGNAL_SAMPLE_RATE; + if (p != 0) { + if (orscV1.nextPulse(p)) { + reportSerial(orscV1); + found=1; + } + if (orscV2.nextPulse(p)) { + reportSerial(orscV2); + found=2; + } + if (orscV3.nextPulse(p)) { + reportSerial(orscV3); + found=3; + } + } + } + if (found == 0) break; + + // ================================================================================== + // Protocol and device info: + // ================================================================================== + //Serial.print("Oregon V"); + //Serial.print(found); + //Serial.print(": "); + //for(byte x=0; x<13;x++) { + // Serial.print( osdata[x],HEX ); + // Serial.print((" ")); + //} + //Serial.println(); + //Serial.print("Oregon ID="); + unsigned int id=(osdata[0]<<8)+ (osdata[1]); + rc=osdata[0]; + //Serial.println(id,HEX); + // ================================================================================== + // Process the various device types: + // ================================================================================== + // Oregon V1 packet structure + // SL-109H, AcuRite 09955 + // TEMP + CRC + // ================================================================================== + // 8487101C + // 84+87+10=11B > 1B+1 = 1C + if (found==1) { // OSV1 + int sum = osdata[0]+osdata[1]+osdata[2]; // max. value is 0x2FD + sum= (sum &0xff) + (sum>>8); // add overflow to low byte + if (osdata[3] != (sum & 0xff) ) { + //Serial.println("CRC Error"); + break; + } + // ------------- + temp = ((osdata[2]>>4) * 100) + ((osdata[1] & 0x0F) * 10) + ((osdata[1] >> 4)); + if ((osdata[2] & 0x02) == 2) temp=temp | 0x8000; // bit 1 set when temp is negative, set highest bit on temp valua + // ---------------------------------- + // Output + // ---------------------------------- + sprintf(pbuffer, "20;%02X;", PKSequenceNumber++); // Node and packet number + Serial.print( pbuffer ); + // ---------------------------------- + Serial.print("OregonV1;"); // Label + sprintf(pbuffer, "ID=00%02x;", rc); // ID + Serial.print( pbuffer ); + sprintf(pbuffer, "TEMP=%04x;", temp); + Serial.print( pbuffer ); + Serial.println(); + } + // ================================================================================== + // ea4c Outside (Water) Temperature: THC238, THC268, THN132N, THWR288A, THRN122N, THN122N, AW129, AW131 + // TEMP + BAT + CRC + // ca48 Pool (Water) Temperature: THWR800 + // 0a4d Indoor Temperature: THR128, THR138, THC138 + // ================================================================================== + // OSV2 EA4C20725C21D083 // THN132N + // OSV2 EA4C101360193023 // THN132N + // OSV2 EA4C40F85C21D0D4 // THN132N + // OSV2 EA4C20809822D013 + // 0123456789012345 + // 0 1 2 3 4 5 6 7 + if(id == 0xea4c || id == 0xca48 || id == 0x0a4d) { + byte sum=(osdata[7]&0x0f) <<4; + sum=sum+(osdata[6]>>4); + if ( checksum(2,6, sum) !=0) { // checksum = all nibbles 0-11+13 results is nibbles 15 <<4 + 12 + //Serial.println("CRC Error"); + break; + } + // ------------- + temp = ((osdata[5]>>4) * 100) + ((osdata[5] & 0x0F) * 10) + ((osdata[4] >> 4)); + if ((osdata[6] & 0x0F) >= 8) temp=temp | 0x8000; + // ---------------------------------- + // Output + // ---------------------------------- + sprintf(pbuffer, "20;%02X;", PKSequenceNumber++); // Node and packet number + Serial.print( pbuffer ); + // ---------------------------------- + Serial.print("Oregon Temp;"); // Label + sprintf(pbuffer, "ID=%02x%02x;", rc,osdata[2]); // ID + Serial.print( pbuffer ); + sprintf(pbuffer, "TEMP=%04x;", temp); + Serial.print( pbuffer ); + if ((osdata[3] & 0x0F) >= 4) { + Serial.print("BAT=LOW;"); + } else { + Serial.print("BAT=OK;"); + } + Serial.println(); + } else + // ================================================================================== + // 1a2d Indoor Temp/Hygro: THGN122N, THGN123N, THGR122NX, THGR228N, THGR238, THGR268, THGR122X + // 1a3d Outside Temp/Hygro: THGR918, THGRN228NX, THGN500 + // fa28 Indoor Temp/Hygro: THGR810 + // *aac Outside Temp/Hygro: RTGR328N + // ca2c Outside Temp/Hygro: THGR328N + // fab8 Outside Temp/Hygro: WTGR800 + // TEMP + HUM sensor + BAT + CRC + // ================================================================================== + // OSV2 AACC13783419008250AD[RTGR328N,...] Id:78 ,Channel:0 ,temp:19.30 ,hum:20 ,bat:10 + // OSV2 1A2D40C4512170463EE6[THGR228N,...] Id:C4 ,Channel:3 ,temp:21.50 ,hum:67 ,bat:90 + // OSV2 1A2D1072512080E73F2C[THGR228N,...] Id:72 ,Channel:1 ,temp:20.50 ,hum:78 ,bat:90 + // OSV2 1A2D103742197005378E // THGR228N + // OSV3 FA28A428202290834B46 // + // OSV2 1A2D1002 02060552A4C + // 1A3D10D91C273083.. + // 1A3D10D90C284083.. + // OSV3 FA2814A93022304443BE // THGR810 + // 01234567890123456789 + // 0 1 2 3 4 5 + // F+A+2+8+1+4+A+9+3+0+2+2+3+0+4+4=4d-a=43 + if(id == 0xfa28 || id == 0x1a2d || id == 0x1a3d || (id&0xfff)==0xACC || id == 0xca2c || id == 0xfab8 ) { + if ( checksum(1,8,osdata[8]) !=0) break; // checksum = all nibbles 0-15 results is nibbles 16.17 + // ------------- + temp = ((osdata[5]>>4) * 100) + ((osdata[5] & 0x0F) * 10) + ((osdata[4] >> 4)); + if ((osdata[6] & 0x0F) >= 8) temp=temp | 0x8000; + // ------------- + hum = ((osdata[7] & 0x0F)*10)+ (osdata[6] >> 4); + // ---------------------------------- + // Output + // ---------------------------------- + sprintf(pbuffer, "20;%02X;", PKSequenceNumber++); // Node and packet number + Serial.print( pbuffer ); + // ---------------------------------- + Serial.print("Oregon TempHygro;"); // Label + sprintf(pbuffer, "ID=%02x%02x;", rc,osdata[2]); // ID + Serial.print( pbuffer ); + sprintf(pbuffer, "TEMP=%04x;", temp); + Serial.print( pbuffer ); + sprintf(pbuffer, "HUM=%02x;", hum); + Serial.print( pbuffer ); + if ((osdata[3] & 0x0F) >= 4) { + Serial.print("BAT=LOW;"); + } else { + Serial.print("BAT=OK;"); + } + Serial.println(); + } else + // ================================================================================== + // 5a5d Indoor Temp/Hygro/Baro: Huger - BTHR918 + // 5a6d Indoor Temp/Hygro/Baro: BTHR918N, BTHR968. BTHG968 + // TEMP + HUM + BARO + FORECAST + BAT + // NO CRC YET + // ================================================================================== + // 5A 6D 00 7A 10 23 30 83 86 31 + // 5+a+6+d+7+a+1+2+3+3+8+3=47 -a=3d +8=4f +8+6=55 + // 5+a+6+d+7+a+1+2+3+3=3c-a=32 + // 0 1 2 3 4 5 6 7 8 9 + if(id == 0x5a6d || id == 0x5a5d || id == 0x5d60) { + // ------------- + temp = ((osdata[5]>>4) * 100) + ((osdata[5] & 0x0F) * 10) + ((osdata[4] >> 4)); + if ((osdata[6] & 0x0F) >= 8) temp=temp | 0x8000; + // ------------- + hum = ((osdata[7] & 0x0F)*10)+ (osdata[6] >> 4); + + //0: normal, 4: comfortable, 8: dry, C: wet + int tmp_comfort = osdata[7] >> 4; + if (tmp_comfort == 0x00) + comfort=0; + else if (tmp_comfort == 0x04) + comfort=1; + else if (tmp_comfort == 0x08) + comfort=2; + else if (tmp_comfort == 0x0C) + comfort=3; + + // ------------- + baro = (osdata[8] + 856); // max value = 1111 / 0x457 + + //2: cloudy, 3: rainy, 6: partly cloudy, C: sunny + int tmp_forecast = osdata[9]>>4; + if (tmp_forecast == 0x02) + forecast = 3; + else if (tmp_forecast == 0x03) + forecast = 4; + else if (tmp_forecast == 0x06) + forecast = 2; + else if (tmp_forecast == 0x0C) + forecast = 1; + + // ---------------------------------- + // Output + // ---------------------------------- + sprintf(pbuffer, "20;%02X;", PKSequenceNumber++); // Node and packet number + Serial.print( pbuffer ); + // ---------------------------------- + Serial.print("Oregon BTHR;"); // Label + sprintf(pbuffer, "ID=%02x%02x;", rc,osdata[2]); // ID + Serial.print( pbuffer ); + sprintf(pbuffer, "TEMP=%04x;", temp); + Serial.print( pbuffer ); + sprintf(pbuffer, "HUM=%02x;", hum); + Serial.print( pbuffer ); + sprintf(pbuffer, "HSTATUS=%d;", comfort); + Serial.print( pbuffer ); + sprintf(pbuffer, "BARO=%04x;", baro); + Serial.print( pbuffer ); + sprintf(pbuffer, "BFORECAST=%d;", forecast); + Serial.print( pbuffer ); + + //below is not correct, and for now discarded + //if (((osdata[3] & 0x0F) & 0x04) != 0) { + // Serial.print("BAT=LOW;"); + //} else { + // Serial.print("BAT=OK;"); + //} + Serial.println(); + } else + // ================================================================================== + // 2914 Rain Gauge: + // 2d10 Rain Gauge: + // 2a1d Rain Gauge: RGR126, RGR682, RGR918, PCR122 + // 2A1D0065502735102063 + // 2+A+1+D+0+0+6+5+5+0+2+7+3+5+1+0+2+0=3e-a=34 != 63 + // ================================================================================== + if(id == 0x2a1d || id == 0x2d10 || id == 0x2914) { // Rain sensor + //Checksum - add all nibbles from 0 to 8, subtract 9 and compare + /* + int cs = 0; + for (byte i = 0; i < pos-2; ++i) { //all but last byte + cs += data[i] >> 4; + cs += data[i] & 0x0F; + } + int csc = (data[8] >> 4) + ((data[9] & 0x0F)*16); + cs -= 9; //my version as A fails? + Serial.print(csc); + Serial.print(" vs "); + Serial.println(cs); + */ + rain = ((osdata[5]>>4) * 100) + ((osdata[5] & 0x0F) * 10) + (osdata[4] >> 4); + raintot = ((osdata[7] >> 4) * 10) + (osdata[6]>>4); + // ---------------------------------- + // Output + // ---------------------------------- + sprintf(pbuffer, "20;%02X;", PKSequenceNumber++); // Node and packet number + Serial.print( pbuffer ); + // ---------------------------------- + Serial.print("Oregon Rain;"); // Label + sprintf(pbuffer, "ID=%02x%02x;", rc,osdata[3]); // ID + Serial.print( pbuffer ); + sprintf(pbuffer, "RAIN=%04x;", rain); + Serial.print( pbuffer ); + sprintf(pbuffer, "RAINTOT=%04x;", raintot); + Serial.print( pbuffer ); + if ((osdata[3] & 0x0F) >= 4) { + Serial.print("BAT=LOW;"); + } else { + Serial.print("BAT=OK;"); + } + Serial.println(); + } else + // ================================================================================== + // 2a19 Rain Gauge: PCR800 + // RAIN + BAT + CRC + // ================================================================================== + // OSV3 2A19048E399393250010 + // 01234567890123456789 + // 0 1 2 3 4 5 6 7 8 9 + // 2+A+1+9+0+4+8+E+3+9+9+3+9+3+2+5=5b-A=51 => 10 + if(id == 0x2a19) { // Rain sensor + int sum = (osdata[9] >> 4); + if ( checksum(3,9,sum) !=0) { // checksum = all nibbles 0-17 result is nibble 18 + //Serial.print("CRC Error, "); + break; + } + rain = ((osdata[5]>>4) * 100) + ((osdata[5] & 0x0F) * 10) + (osdata[4] >> 4); + //Serial.print(" RainTotal="); + //raintot = ((osdata[7] >> 4) * 10) + (osdata[6]>>4); + //Serial.print(raintot); + // ---------------------------------- + // Output + // ---------------------------------- + sprintf(pbuffer, "20;%02X;", PKSequenceNumber++); // Node and packet number + Serial.print( pbuffer ); + // ---------------------------------- + Serial.print("Oregon Rain2;"); // Label + sprintf(pbuffer, "ID=%02x%02x;", rc,osdata[4]); // ID + Serial.print( pbuffer ); + sprintf(pbuffer, "RAIN=%04x;", rain); + Serial.print( pbuffer ); + //sprintf(pbuffer, "RAINTOT=%04x;", raintot); + //Serial.print( pbuffer ); + if ((osdata[3] & 0x0F) >= 4) { + Serial.print("BAT=LOW;"); + } else { + Serial.print("BAT=OK;"); + } + Serial.println(); + } else + // ================================================================================== + // 1a89 Anemometer: WGR800 + // WIND DIR + SPEED + AV SPEED + CRC + // ================================================================================== + // OSV3 1A89048800C026400543 + // OSV3 1A89048800C00431103B + // OSV3 1a89048848c00000003e W + // OSV3 1a890488c0c00000003e E + // 1A89042CB0C047000147 + // 0 1 2 3 4 5 6 7 8 9 + // 1+A+8+9+0+4+8+8+0+0+C+0+0+4+3+1+1+0=45-a=3b + if(id == 0x1a89) { // Wind sensor + if ( checksum(1,9,osdata[9]) !=0) break; + Serial.print(" WINDIR="); + wdir=(osdata[4] >> 4); + wdir=wdir*225; + wdir=wdir/10; + Serial.print(wdir); + // ------------- + wspeed = (osdata[6] >> 4) * 10; + wspeed = wspeed + (osdata[6] &0x0f) * 100; + wspeed = wspeed + (osdata[5] &0x0f); + // ------------- + awspeed = (osdata[8] >> 4) * 100; + awspeed = awspeed + (osdata[7] &0x0f) * 10; + awspeed = awspeed + (osdata[7] >> 4); + // ---------------------------------- + // Output + // ---------------------------------- + sprintf(pbuffer, "20;%02X;", PKSequenceNumber++); // Node and packet number + Serial.print( pbuffer ); + // ---------------------------------- + Serial.print("Oregon Wind;"); // Label + sprintf(pbuffer, "ID=%02x%02x;", rc,osdata[2]); // ID + Serial.print( pbuffer ); + sprintf(pbuffer, "WINDIR=%04x;", wdir); + Serial.print( pbuffer ); + sprintf(pbuffer, "WINSP=%04x;", wspeed); + Serial.print( pbuffer ); + sprintf(pbuffer, "AWINSP=%04x;", awspeed); + Serial.print( pbuffer ); + if ((osdata[3] & 0x0F) >= 4) { + Serial.print("BAT=LOW;"); + } else { + Serial.print("BAT=OK;"); + } + Serial.println(); + } else + // ================================================================================== + // 3a0d Anemometer: Huger-STR918, WGR918 + // 1984 Anemometer: + // 1994 Anemometer: + // WIND DIR + SPEED + AV SPEED + BAT + CRC + // 3A0D006F400800000031 + // ================================================================================== + if(id == 0x3A0D || id == 0x1984 || id == 0x1994 ) { + if ( checksum(1,9,osdata[9]) !=0) { + Serial.print("CRC Error, "); + //break; + } + wdir = ((osdata[5]>>4) * 100) + ((osdata[5] & 0x0F * 10) ) + (osdata[4] >> 4); + wspeed = ((osdata[7] & 0x0F) * 100) + ((osdata[6]>>4) * 10) + ((osdata[6] & 0x0F)) ; + awspeed = ((osdata[8]>>4) * 100) + ((osdata[8] & 0x0F) * 10)+((osdata[7] >>4)) ; + // ---------------------------------- + // Output + // ---------------------------------- + sprintf(pbuffer, "20;%02X;", PKSequenceNumber++); // Node and packet number + Serial.print( pbuffer ); + // ---------------------------------- + Serial.print("Oregon Wind2;"); // Label + sprintf(pbuffer, "ID=%02x%02x;", rc,osdata[2]); // ID + Serial.print( pbuffer ); + sprintf(pbuffer, "WINDIR=%04x;", wdir); + Serial.print( pbuffer ); + sprintf(pbuffer, "WINSP=%04x;", wspeed); + Serial.print( pbuffer ); + sprintf(pbuffer, "AWINSP=%04x;", awspeed); + Serial.print( pbuffer ); + if ((osdata[3] & 0x0F) >= 4) { + Serial.print("BAT=LOW;"); + } else { + Serial.print("BAT=OK;"); + } + Serial.println(); + } else + // ================================================================================== + // ea7c UV Sensor: UVN128, UV138 + // UV + BAT + // NO CRC YET + // ================================================================================== + if(id == 0xea7c) { + uv=((osdata[5] & 0x0F) * 10) + (osdata[4] >> 4); + Serial.print(uv); + // ---------------------------------- + // Output + // ---------------------------------- + sprintf(pbuffer, "20;%02X;", PKSequenceNumber++); // Node and packet number + Serial.print( pbuffer ); + // ---------------------------------- + Serial.print("Oregon UVN128/138;"); // Label + sprintf(pbuffer, "ID=%02x%02x;", rc,osdata[2]); // ID + Serial.print( pbuffer ); + sprintf(pbuffer, "UV=%04x;", uv); + Serial.print( pbuffer ); + if ((osdata[3] & 0x0F) >= 4) { + Serial.print("BAT=LOW;"); + } else { + Serial.print("BAT=OK;"); + } + Serial.println(); + } else + // ================================================================================== + // da78 UV Sensor: UVN800 + // UV + // NO CRC YET + // ================================================================================== + if( id == 0xda78) { + uv=(osdata[6] & 0xf0) + (osdata[5] &0x0f) ; + // ---------------------------------- + // Output + // ---------------------------------- + sprintf(pbuffer, "20;%02X;", PKSequenceNumber++); // Node and packet number + Serial.print( pbuffer ); + // ---------------------------------- + Serial.print("Oregon UVN800;"); // Label + sprintf(pbuffer, "ID=%02x%02x;", rc,osdata[2]); // ID + Serial.print( pbuffer ); + sprintf(pbuffer, "UV=%04x;", uv); + Serial.print( pbuffer ); + if ((osdata[3] & 0x0F) >= 4) { + Serial.print("BAT=LOW;"); + } else { + Serial.print("BAT=OK;"); + } + Serial.println(); + } else + // ================================================================================== + // *aec Date&Time: RTGR328N + // NO CRC YET + // ================================================================================== + // 8AEA1378077214924242C16CBD 21:49 29/04/2014 + // 0 1 2 3 4 5 6 7 8 9 0 1 2 + // 8+A+E+A+1+3+7+8+0+7+7+2+1+4+9+2+4+2+4+2+C+1+6+C=88 != BD + // Date & Time + //if( (id &0xfff) == 0xaea) { + // Serial.print("RTGR328N RC: "); + // Serial.print(osdata[3], HEX); + // Serial.print(" Date: "); + // Serial.print( (osdata[9] >> 4) + 10 * (osdata[10] & 0xf) ); + // Serial.print(" - "); + // Serial.print( osdata[8] >> 4 ); + // Serial.print(" - "); + // Serial.print( (osdata[7] >> 4) + 10 * (osdata[8] & 0xf) ); + // Serial.print(" Time: "); + // Serial.print( (osdata[6] >> 4) + 10 * (osdata[7] & 0xf) ); + // Serial.print(":"); + // Serial.print((osdata[5] >> 4) + 10 * (osdata[6] & 0xf)); + // Serial.print(":"); + // Serial.print((osdata[4] >> 4) + 10 * (osdata[5] & 0xf)); + // Serial.println(); + // break; + //} + // ================================================================================== + // eac0 Ampere meter: cent-a-meter, OWL CM113, Electrisave + // ================================================================================== + if(id == 0xeac0) { + //Serial.println("UNKNOWN LAYOUT"); + // ---------------------------------- + // Output + // ---------------------------------- + sprintf(pbuffer, "20;%02X;", PKSequenceNumber++); // Node and packet number + Serial.print( pbuffer ); + // ---------------------------------- + Serial.print("Oregon Unknown;"); // Label + for(byte x=0; x<13;x++) { + Serial.print( osdata[x],HEX ); + Serial.print((" ")); + } + Serial.println(";"); + return success; + } else + // ================================================================================== + // 0x1a* / 0x2a* 0x3a** Power meter: OWL CM119 + // ================================================================================== + // ================================================================================== + // 1a99 Anemometer: WGTR800 + // WIND + TEMP + HUM + CRC + // ================================================================================== + if(id == 0x1a99) { // Wind sensor + //Serial.println("UNKNOWN LAYOUT"); + // ---------------------------------- + // Output + // ---------------------------------- + sprintf(pbuffer, "20;%02X;", PKSequenceNumber++); // Node and packet number + Serial.print( pbuffer ); + // ---------------------------------- + Serial.print("Oregon Unknown;"); // Label + for(byte x=0; x<13;x++) { + Serial.print( osdata[x],HEX ); + Serial.print((" ")); + } + Serial.println(";"); + return success; + } else + // ================================================================================== + if( (id&0xf00)==0xA00 ) { // Wind sensor + // ---------------------------------- + // Output + // ---------------------------------- + sprintf(pbuffer, "20;%02X;", PKSequenceNumber++); // Node and packet number + Serial.print( pbuffer ); + // ---------------------------------- + Serial.print("Oregon Unknown;"); // Label + for(byte x=0; x<13;x++) { + Serial.print( osdata[x],HEX ); + Serial.print((" ")); + } + Serial.println(";"); + } + // ================================================================================== + RawSignal.Repeats=true; // suppress repeats of the same RF packet + RawSignal.Number=0; + success = true; +#endif // PLUGIN_048_CORE + return success; +} diff --git a/Plugins/Plugin_254.c b/Plugins/Plugin_254.c new file mode 100644 index 0000000..373f651 --- /dev/null +++ b/Plugins/Plugin_254.c @@ -0,0 +1,58 @@ +//####################################################################################################### +//## This Plugin is only for use with the RFLink software package ## +//## Plugin-254: Signal Analyzer ## +//####################################################################################################### +/*********************************************************************************************\ + * This plugin shows pulse lengths that have been received on RF and have not been decoded by + * one of the other plugins. The primary use of this plugin is to provide an easy way to debug and + * analyse currently unsupported RF signals + * + * Author : StuntTeam + * Support : http://sourceforge.net/projects/rflink/ + * License : This code is free for use in any open source project when this header is included. + * Usage of any parts of this code in a commercial application is prohibited! + *********************************************************************************************** + * Technical description: + * This plugin just outputs unsupported RF packets, use this plugin to find signals from new devices + * Even if you do not know what to do with the data yourself you might want to share your data so + * others can analyse it. + \*********************************************************************************************/ +#ifdef PLUGIN_254 +boolean Plugin_254(byte function, char *string) { + if (QRFDebug==true) { // debug is on? + if(RawSignal.Number<26)return false; // make sure the packet is long enough to have a meaning + // ---------------------------------- + // Output + // ---------------------------------- + sprintf(pbuffer, "20;%02X;", PKSequenceNumber++); // Node and packet number + Serial.print( pbuffer ); + // ---------------------------------- + Serial.print(F("DEBUG;Pulses=")); // debug data + Serial.print(RawSignal.Number); // print number of pulses + Serial.print(F(";Pulses(uSec)=")); // print pulse durations + //PrintHex8(RawSignal.Pulses+1,RawSignal.Number-1); + PrintHex8(RawSignal.Pulses+1,RawSignal.Number); + } else { + if (RFUDebug==false) return false; // debug is on? + if(RawSignal.Number<26)return false; // make sure the packet is long enough to have a meaning + // ---------------------------------- + // Output + // ---------------------------------- + sprintf(pbuffer, "20;%02X;", PKSequenceNumber++); // Node and packet number + Serial.print( pbuffer ); + // ---------------------------------- + Serial.print(F("DEBUG;Pulses=")); // debug data + Serial.print(RawSignal.Number); // print number of pulses + Serial.print(F(";Pulses(uSec)=")); // print pulse durations + //for(int x=1;xmillis() || RepeatingTimer>millis()) { + if (FetchSignal(PIN_RF_RX_DATA,HIGH)) { // RF: *** data start *** + if ( PluginRXCall(0,0) ) { // Check all plugins to see which plugin can handle the received signal. + RepeatingTimer=millis()+SIGNAL_REPEAT_TIME; + return true; + } + } + }// while + return false; +} +/**********************************************************************************************\ + * Haal de pulsen en plaats in buffer. + * bij de TSOP1738 is in rust is de uitgang hoog. StateSignal moet LOW zijn + * bij de 433RX is in rust is de uitgang laag. StateSignal moet HIGH zijn + * + \*********************************************************************************************/ +const unsigned long LoopsPerMilli=345; +const unsigned long Overhead=0; + +// Because this is a time critical routine, we use global variables so that the variables +// do not need to be initialized at each function call. +int RawCodeLength=0; +unsigned long PulseLength=0L; +unsigned long numloops=0L; +unsigned long maxloops=0L; + +boolean Ftoggle=false; +uint8_t Fbit=0; +uint8_t Fport=0; +uint8_t FstateMask=0; +/*********************************************************************************************/ +boolean FetchSignal(byte DataPin, boolean StateSignal) { + uint8_t Fbit = digitalPinToBitMask(DataPin); + uint8_t Fport = digitalPinToPort(DataPin); + uint8_t FstateMask = (StateSignal ? Fbit : 0); + + if ((*portInputRegister(Fport) & Fbit) == FstateMask) { // Als er signaal is + // Als het een herhalend signaal is, dan is de kans groot dat we binnen hele korte tijd weer in deze + // routine terugkomen en dan midden in de volgende herhaling terecht komen. Daarom wordt er in dit + // geval gewacht totdat de pulsen voorbij zijn en we met het capturen van data beginnen na een korte + // rust tussen de signalen.Op deze wijze wordt het aantal zinloze captures teruggebracht. + if (RawSignal.Time) { // Eerst een snelle check, want dit bevindt zich in een tijdkritisch deel... + if (RawSignal.Repeats && (RawSignal.Time+SIGNAL_REPEAT_TIME)>millis()) { // ...want deze check duurt enkele micro's langer! + PulseLength=micros()+SIGNAL_TIMEOUT*1000; // delay + while ((RawSignal.Time+SIGNAL_REPEAT_TIME)>millis() && PulseLength>micros()) + if ((*portInputRegister(Fport) & Fbit) == FstateMask) + PulseLength=micros()+SIGNAL_TIMEOUT*1000; + while((RawSignal.Time+SIGNAL_REPEAT_TIME)>millis() && (*portInputRegister(Fport) & Fbit) != FstateMask); + } + } + RawCodeLength=1; // Start at 1 for legacy reasons. Element 0 can be used to pass special information like plugin number etc. + Ftoggle=false; + maxloops = (SIGNAL_TIMEOUT * LoopsPerMilli); + do{ // read the pulses in microseconds and place them in temporary buffer RawSignal + numloops = 0; + while (((*portInputRegister(Fport) & Fbit) == FstateMask) ^ Ftoggle) // while() loop *A* + if (numloops++ == maxloops) break; // timeout + PulseLength=((numloops + Overhead)* 1000) / LoopsPerMilli; // Contains pulslength in microseconds + if (PulseLength=MIN_RAW_PULSES) { + RawSignal.Repeats=0; // no repeats + RawSignal.Multiply=RAWSIGNAL_SAMPLE_RATE; // sample size. + RawSignal.Number=RawCodeLength-1; // Number of received pulse times (pulsen *2) + RawSignal.Pulses[RawSignal.Number+1]=0; // Last element contains the timeout. + RawSignal.Time=millis(); // Time the RF packet was received (to keep track of retransmits + return true; + } else { + RawSignal.Number=0; + } + } + return false; +} +/*********************************************************************************************/ +// RFLink Board specific: Generate a short pulse to switch the Aurel Transceiver from TX to RX mode. +void RFLinkHW( void ) { + delayMicroseconds(36); + digitalWrite(PIN_BSF_0,LOW); + delayMicroseconds(16); + digitalWrite(PIN_BSF_0,HIGH); + return; +} +/*********************************************************************************************\ + * Send rawsignal buffer to RF * DEPRICATED * DO NOT USE * +\*********************************************************************************************/ +void RawSendRF(void) { // * DEPRICATED * DO NOT USE * + int x; + digitalWrite(PIN_RF_RX_VCC,LOW); // Spanning naar de RF ontvanger uit om interferentie met de zender te voorkomen. + digitalWrite(PIN_RF_TX_VCC,HIGH); // zet de 433Mhz zender aan + delayMicroseconds(TRANSMITTER_STABLE_DELAY); // short delay to let the transmitter become stable (Note: Aurel RTX MID needs 500µS/0,5ms) + + RawSignal.Pulses[RawSignal.Number]=1; // due to a bug in Arduino 1.0.1 + + for(byte y=0; y