#ifdef USES_P199 //####################################################################################################### //#################################### Plugin 199: RF 433 receiver/sender ############################## //####################################################################################################### // Purpose: Control DIO (Klik-Aan-Klik-Uit) RF 433MHz devices directly from ESP Easy (receive and send) // Status : "Proof of concept" // Connect the RF Receiver data pin to the first pin selected in the webgui // Connect the RF Transmitter data pin to the second pin selected in the webgui // Events: // newKaku_
#= (0=off, 1-15=dimvalue, 16=on) // Kaku_
#= (0=off, 1=on) // HE300EU_
#= (0=off, 1=on) // Commands: // DIOSend
, , // KakuSend
, , // This is a Work in Progress mini project! // It has limited use because in most cases, your fancy Home Automation controller can handle 433MHz devices quite well using RFLink. // It was implemented because in some cases i would like to have local "Klik-Aan-Klik-Uit" support using a standalone ESP Easy. // (Just because i own quite a lot of these Kaku devices) // Current state / limitations: // Implemented send and receive support for KaKu with automatic code (no code wheel) // Implemented send and receive support for old KaKu unit's with code wheels // Implemented receive support for HomeEasy HE300EU remotes // RF Sender and RF receiver each need their own antenna! (as opposed to using a transceiver) //#define MIN_PULSE_LENGTH 100 // Too short pulses are considered to be noise... //#define SIGNAL_TIMEOUT 6000 // temps entre 2 paquets de pulse en µs //#define MIN_RAW_PULSES 58 // Minimum number of pulses to be received, otherwise considered to be noise... Hideki 58 pulses #define RAW_BUFFER_SIZE 256 // Maximum number of pulses to be received, OSv2: min 172 si tout FF sur THN132N max:259 si tout est AA sur THGR122NX #define RAW_BUFFER_WIDTH 20 // Maximum number of bitstream #define PULSE_MULTIPLY 20 void RF_ISR() ICACHE_RAM_ATTR; // We need our own rawsignal buffer here. // During plugin rawsignal checks, the IRQ routine will alPlugin_199_ready be working on the next signal burst... volatile byte buf_stream = 0; volatile byte Plugin_199_RFBuffer[RAW_BUFFER_SIZE]; volatile boolean Plugin_199_ready = false; volatile byte Redondant_check= 0; volatile byte Plugin_199_pulses[RAW_BUFFER_SIZE + 2][RAW_BUFFER_WIDTH]; volatile int Plugin_199_number[RAW_BUFFER_WIDTH]; //unsigned long Plugin_199_codeHash; //unsigned long Plugin_199_lastTime; //unsigned long elapsed; int8_t Plugin_199_RXpin = -1; int8_t Plugin_199_TXpin = -1; volatile boolean debugframe = false; volatile unsigned int minpulse; volatile unsigned int interframe; volatile byte minrawpulse; #define PLUGIN_199 #define PLUGIN_ID_199 199 #define PLUGIN_NAME_199 "RF433 Receiver/sender" #define PLUGIN_VALUENAME1_199 "Type" #define PLUGIN_VALUENAME2_199 "Adress" #define PLUGIN_VALUENAME3_199 "Temperature|Channel" #define PLUGIN_VALUENAME4_199 "Hum.Bat|Order" #define P199_debugframe PCONFIG(1) #define P199_minpulse PCONFIG(2) // Too short pulses are considered to be noise... #define P199_interframe PCONFIG(3) // temps entre 2 paquets de pulse en µs #define P199_minrawpulse PCONFIG(4) // Minimum number of pulses to be received, otherwise considered to be noise... Hideki 58 pulses #define P199_preambleOS PCONFIG(5) // Nombre de pulse de preamble mini pour une trame Oregon #define P199_SeuilOS PCONFIG(6) boolean Plugin_199(byte function, struct EventStruct *event, String& string) { boolean success = false; switch (function) { case PLUGIN_DEVICE_ADD: { Device[++deviceCount].Number = PLUGIN_ID_199; Device[deviceCount].Type = DEVICE_TYPE_DUAL; Device[deviceCount].VType = Sensor_VType::SENSOR_TYPE_TEMP_HUM; Device[deviceCount].Ports = 0; Device[deviceCount].PullUpOption = false; // Allow to set internal pull-up resistors. Device[deviceCount].InverseLogicOption = false; // Allow to invert the boolean state (e.g. a switch) Device[deviceCount].FormulaOption = true; // Allow to enter a formula to convert values during read. (not possible with Custom enabled) Device[deviceCount].ValueCount = 4; // The number of output values of a plugin. The value should match the number of keys PLUGIN_VALUENAME1_xxx Device[deviceCount].DecimalsOnly = false; // Allow to set the number of decimals (otherwise treated a 0 decimals) Device[deviceCount].SendDataOption = true; // Allow to send data to a controller. Device[deviceCount].TimerOption = true; // Allow to set the "Interval" timer for the plugin. Device[deviceCount].TimerOptional = false; // When taskdevice timer is not set and not optional, use default "Interval" delay (Settings.Delay) Device[deviceCount].GlobalSyncOption = true; // No longer used. Was used for ESPeasy values sync between nodes break; } case PLUGIN_GET_DEVICENAME: { string = F(PLUGIN_NAME_199); break; } case PLUGIN_GET_DEVICEVALUENAMES: { strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[0], PSTR(PLUGIN_VALUENAME1_199)); strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[1], PSTR(PLUGIN_VALUENAME2_199)); strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[2], PSTR(PLUGIN_VALUENAME3_199)); strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[3], PSTR(PLUGIN_VALUENAME4_199)); break; } case PLUGIN_GET_DEVICEGPIONAMES: { event->String1 = formatGpioName_input(F("Receive_Pin")); event->String2 = formatGpioName_input(F("Transmit_Pin")); break; } case PLUGIN_SET_DEFAULTS: { PCONFIG(1) = 0; // Desactive debug PCONFIG(2) = 100; // Min pulse pour filtrer les pulses trop courts PCONFIG(3) = 6000; // Interframe PCONFIG(4) = 58; // Minimum number of pulses to be received, otherwise considered to be noise... Hideki 58 pulses PCONFIG(5) = 10; // 10 bit de preamble pour Oregon PCONFIG(6) = 750; // 750µs de seuil entre pulse court/long success = true; break; } case PLUGIN_WEBFORM_LOAD: { addFormSubHeader(F("General parameters")); addFormCheckBox(F("Debug mode: raw frame"), F("P199_debugframe"),P199_debugframe); addFormNumericBox(F("Durée min pulse, filtré si inférieur (typ 150us)"), F("P199_minpulse"), P199_minpulse, 5, 750); addFormNumericBox(F("temps entre 2 paquets de pulse en µs (typ 6000us"), F("P199_interframe"), P199_interframe, 1000, 10000); addFormNumericBox(F("Nombre de pulse min (Hideki 58 pulses)"), F("P199_minrawpulse"), P199_minrawpulse, 10, 250); addFormSubHeader(F("Oregon Sci V2 parameters")); addFormNumericBox(F("Nb min de bit de preamble (typ 24bits, spec 32bits)"), F("P199_preambleOS"), P199_preambleOS, 5, 32); addFormNumericBox(F("Seuil entre pulse court/long (typ 750)"), F("P199_SeuilOS"), P199_SeuilOS, 400, 900); addFormSubHeader(F("DIO transmission: IP//control?cmd=DIOsend,address_decimale,Channel,state/dim (0=off, 1-15=dimvalue, 16=on)")); success = true; break; } case PLUGIN_WEBFORM_SAVE: { P199_debugframe = isFormItemChecked(F("P199_debugframe")); P199_minpulse = getFormItemInt(F("P199_minpulse")); P199_interframe = getFormItemInt(F("P199_interframe")); P199_minrawpulse = getFormItemInt(F("P199_minrawpulse")); P199_preambleOS = getFormItemInt(F("P199_preambleOS")); P199_SeuilOS = getFormItemInt(F("P199_SeuilOS")); success = true; break; } case PLUGIN_INIT: { if (Settings.TaskDevicePin1[event->TaskIndex] != -1) { Plugin_199_RXpin = Settings.TaskDevicePin1[event->TaskIndex]; pinMode(Plugin_199_RXpin, INPUT_PULLUP); attachInterrupt(Plugin_199_RXpin, RF_ISR, CHANGE); success = true; } if (Settings.TaskDevicePin1[event->TaskIndex] != -1) { Plugin_199_TXpin = Settings.TaskDevicePin2[event->TaskIndex]; pinMode(Plugin_199_TXpin, OUTPUT); } debugframe = P199_debugframe; minpulse = P199_minpulse; interframe = P199_interframe; minrawpulse = P199_minrawpulse; break; } case PLUGIN_READ: //PLUGIN_ONCE_A_SECOND: //PLUGIN_TEN_PER_SECOND: // { //if (Plugin_199_ready) Serial.println("TOP"); Redondant_check = 0; if (buf_stream>=RAW_BUFFER_WIDTH) buf_stream = RAW_BUFFER_WIDTH; //if (Plugin_199_ready) detachInterrupt(Plugin_199_RXpin); while (Plugin_199_ready) { //Serial.printf("Buf %d :",buf_stream); if (Plugin_199_number[buf_stream]>=172) { if (OregScV2 (16,400,200,850,615,1400,1100,event->BaseVarIndex)); else OregScV2 (P199_preambleOS,100,100,P199_SeuilOS,P199_SeuilOS,1800,1800, event->BaseVarIndex); } else if (Plugin_199_number[buf_stream] == 132 || Plugin_199_number[buf_stream] == 148) decodeNewKaku(event->BaseVarIndex); else if (Plugin_199_number[buf_stream]==58) Hideki(event->BaseVarIndex); else Serial.printf("#count %d\n",Plugin_199_number[buf_stream]); if (debugframe && Plugin_199_ready && Redondant_check==0) { String log = F("**RAW frame** #Pulses="); log += Plugin_199_number[buf_stream]; log += F(" ; Timing us= "); for (int i=1;i0) Plugin_199_ready=true; break; } case PLUGIN_WRITE: { String command = parseString(string, 1); if (command == F("diosend")) { success = true; sendDIO(event->Par1, event->Par2, event->Par3); } break; } } return success; } //******************************************************************************** // Interrupt handler for RF messages //******************************************************************************** void RF_ISR() { static unsigned int counter = 0; static unsigned long TimeStamp = 0; unsigned long TimeElapsed = 0; TimeElapsed = micros() - TimeStamp; TimeStamp = micros(); if ((TimeElapsed > minpulse) && (counter < (RAW_BUFFER_SIZE-1))) // minpulse pour filtrer les pulses trop courts et <256 fronts { counter++; Plugin_199_RFBuffer[counter] = TimeElapsed / PULSE_MULTIPLY; } else counter = 0; if (TimeElapsed > interframe) //x ms mini de bit de stop { if (((counter >= 172) || (counter == 58) || (counter == 132) || (counter == 148)) && (counter >=minrawpulse)) // >=172pulses pour OSv2, 58pulses pour Hideki, 132pulses pour DIOonoff, 148pulses pour DIOdimmer { if (buf_stream<=0) buf_stream=0; if (buf_stream>=RAW_BUFFER_WIDTH) buf_stream = RAW_BUFFER_WIDTH; else buf_stream++; Plugin_199_number[buf_stream] = counter; // copy IRQ RF working buffer to RawSignal struct for (unsigned int x = 0; x <= counter; x++) Plugin_199_pulses[x][buf_stream] = Plugin_199_RFBuffer[x]; Plugin_199_ready = true; counter = 0; //Serial.println("Top"); } else counter = 0; } if (counter >= RAW_BUFFER_SIZE) counter = 0; // si plus de 256 pulses } //******************************************************************************** // Decode Hideki 58 pulses soient 29 bits: // 1 logique = haut [460;500] µs puis bas [4460;4540]µs // 0 logique = haut [460;500] µs puis bas [1920;1960]µs // xxxxYYYYYYYYaaaabbbbccccZZBTS codage MSB // x:Chksum Y:ID abc:T°C(Bin to Dec à diviser par 10) Z:Ch B:Batterie faible(0) T:Tx auto(0)/manuel(1) S:Bit de stop>5ms // 1001|1100 0110|0000 1110 1111|10|1|1|S // 0xF+sum nibble | C6 | 239 | ch2 | batt low | Tx man | Stop //******************************************************************************** boolean Hideki (byte BaseVarIndex) { boolean success = false; int i=0; byte chksum=0; int pulse; unsigned long bitstream = 0L; int sens_id=0; int temp_raw=0; float temperature=0; float Hum_Bat=0; for (i=1;i3500) bitstream = (bitstream | 0x1) << 1; // Detection 1 else if (pulse>1500) bitstream = (bitstream & 0xFFFFFFFE) << 1; // Detection 0 } bitstream>>=1; //Serial.printf("%X",bitstream); sens_id=((bitstream>>8)&0xFF00)|((bitstream>>2)&0x03); // Detection de l'ID temp_raw=(bitstream>>4)&0xFFF; // Detection de temperature if ((bitstream&0x8000)==0x8000) // Detection de signe negatif temp_raw|=0xFFFFF000; temperature=temp_raw*0.1; // Calcul des dixiemes Hum_Bat=((bitstream>>1)&0x01)*0.1; // Pas d'humidité, uniquement niveau batterie chksum=(bitstream>>20) + (bitstream>>16) + (bitstream>>12) + (bitstream>>8) + (bitstream>>4) + (bitstream) + 0x0F; chksum&=0x0F; if ((chksum==((bitstream>>24)&0x0F)) && (UserVar[BaseVarIndex + 1] != sens_id || UserVar[BaseVarIndex + 2] != temperature ) && Redondant_check==0) //Data differentes et pas en train de vérifier redondance { UserVar[BaseVarIndex] = 101; UserVar[BaseVarIndex + 1] = sens_id; UserVar[BaseVarIndex + 2] = temperature; UserVar[BaseVarIndex + 3] = Hum_Bat; String log = F("**Hideki protocol** Adress: 0x"); log += String(sens_id,HEX); log += F(" ; Temp: "); log += temperature; log += F(" ; 0.Batt: "); log += Hum_Bat; addLog(LOG_LEVEL_INFO, log); Plugin_199_ready = false; success = true;//Serial.printf("Hideki Code: %X - T° : %.1f\n",sens_id,temperature); } else if (UserVar[BaseVarIndex + 1] == sens_id && UserVar[BaseVarIndex + 2] == temperature && Redondant_check==0);// {Serial.printf("Hid cur red\n");} //Redondance du bitstream courant => pas d'action else if (UserVar[BaseVarIndex + 1] == sens_id && UserVar[BaseVarIndex + 2] == temperature && Redondant_check==1) Redondant_check=2;// Serial.printf("Hid red\n"); //Confirmation redondance => Redondant_check=2 else if (chksum==((bitstream>>24)&0x0F)) Redondant_check=3; //Serial.printf("Hid no red\n");//Pas de redondance : Nouvelle data else addLog(LOG_LEVEL_INFO, "Hideki: Erreur Chksum"); //Serial.printf("Hideki chk error\n"); //Erreur de chksum return success; } //******************************************************************************** // Decode Oregon Scientific V2 //******************************************************************************** boolean OregScV2(byte Nb_Preamble, int Pul0_min, int Pul1_min, int Pul0_mid, int Pul1_mid, int Pul0_max, int Pul1_max, byte BaseVarIndex) { boolean success = false; boolean bit_level=!(Plugin_199_number[buf_stream]%2); //0=niveau bas, 1=niveau haut. Le dernier pulse de >5ms est forcément à low. On peut donc déduire le niveau du 1er pulse int i=0; byte cpt_bit=0; int pulse; byte payload[Plugin_199_number[buf_stream]]; byte decod_step=0; //0=Waiting preamble, 1=Waiting sync, 2=get data bool half_bit=true; int sens_type=0; int sens_id=0; float temperature=0; float Hum_Bat=0; byte chksum=0; byte chk_14_cal=0; //Chk pour THN132N byte chk_17_cal=0; //Chk pour THGR122NX for (i=0;i<=Plugin_199_number[buf_stream];i++) { pulse = Plugin_199_pulses[i][buf_stream]*PULSE_MULTIPLY; bit_level=!bit_level; // if(i<40) Serial.printf("%d:%d:%d\n",i,bit_level,pulse); switch (decod_step) { case 0: // Waiting_preamble: Normalement 32 bits, mais si au moins 24 c'est acceptable if ((!bit_level && pulse>=Pul0_mid && pulse<=Pul0_max && cpt_bit=Pul1_mid && pulse<=Pul1_max && cpt_bit=Pul0_mid && pulse<=Pul0_max && cpt_bit>=Nb_Preamble) || (bit_level && pulse>=Pul1_mid && pulse<=Pul1_max && cpt_bit>=Nb_Preamble)) decod_step=1; //27 pulses => attente debut de sync else if(cpt_bit>0 ) { cpt_bit=0; //Erreur: pulse court avant 16 pulse long if(i>=32) { addLog(LOG_LEVEL_INFO,"Oregon Sci: Erreur preamble => pulse court avant 16 pulse long"); //Serial.printf("OS err preamb\n"); i=Plugin_199_number[buf_stream] + 1; // Permet de sortir prématurément de la boucle for en cas d'erreur } } break; case 1: // Waiting_sync: if((!bit_level && pulse>=Pul0_mid && pulse<=Pul0_max) || (bit_level && pulse>=Pul1_mid && pulse<=Pul1_max)); //pulse long à l'état bas ou pulse long à l'état haut => pas d'action else if (bit_level==0) //1er pulse court à l'état bas => Décodage des data { decod_step=2; i+=10; // Permet de sauter la sync pour directement stocker les données utiles dans le payload cpt_bit=1; // On init à 1 sinon le 1er cpt_bit%2==0 est faux et donc on rate le 1er bit. } else //Erreur: 1er pulse court à l'état haut (ald de bas) { decod_step=0; cpt_bit=0; i=Plugin_199_number[buf_stream]+1; // Permet de sortir prématurément de la boucle for en cas d'erreur //Serial.printf("OS err sync\n"); addLog(LOG_LEVEL_INFO,"Oregon Sci: Erreur sync => Preamble OK mais 1er pulse court n'est pas low"); //if Serial.println("Err: 1er pulse court n'est pas low"); } break; case 2: // Decodage des data if ((!bit_level && pulse>=Pul0_min && pulse<=Pul0_mid) || (bit_level && pulse>=Pul1_min && pulse<=Pul1_mid)) //pulse court à l'état bas ou pulse court à l'état haut { if (!half_bit) // Les pulses courts sont forcément par pairs. Seul le second de la paire est utilisé { if (cpt_bit%2==0) //payload[(cpt_bit-2)/2]=!bit_level; // Chaque bit étant doublé avec son complément, on ne sauvegarde qu'un seul bit { payload[(cpt_bit-2)/16]>>=1; payload[(cpt_bit-2)/16]=payload[(cpt_bit-2)/16] |!bit_level<<7; if (cpt_bit%16 == 0) //Octet complet { if (((cpt_bit-2)/16)<6) chk_14_cal+= (payload[(cpt_bit-2)/16]&0x0F) + ((payload[(cpt_bit-2)/16]&0xF0)>>4); //Calcul le chk d'un payload de 14 nibbles pour THN132 if (((cpt_bit-2)/16)<7) chk_17_cal+= (payload[(cpt_bit-2)/16]&0x0F) + ((payload[(cpt_bit-2)/16]&0xF0)>>4); //Calcul le chk d'un payload de 17 nibbles pour THGR122 } } cpt_bit++; } half_bit=!half_bit; } else if ((!bit_level && pulse>=Pul0_mid && pulse<=Pul0_max && half_bit) || (bit_level && pulse>=Pul1_mid && pulse<=Pul1_max && half_bit)) //pulse long à l'état bas ou pulse long à l'état haut { if (cpt_bit%2==0) //payload[(cpt_bit-2)/2]=!bit_level; // Chaque bit étant doublé avec son complément, on ne sauvegarde qu'un seul bit { payload[(cpt_bit-2)/16]>>=1; payload[(cpt_bit-2)/16]=payload[(cpt_bit-2)/16] |!bit_level<<7; if (cpt_bit%16 == 0) //Octet complet { if (((cpt_bit-2)/16)<6) chk_14_cal+= (payload[(cpt_bit-2)/16]&0x0F) + ((payload[(cpt_bit-2)/16]&0xF0)>>4); //Calcul le chk d'un payload de 14 nibbles pour THN132 if (((cpt_bit-2)/16)<7) chk_17_cal+= (payload[(cpt_bit-2)/16]&0x0F) + ((payload[(cpt_bit-2)/16]&0xF0)>>4); //Calcul le chk d'un payload de 17 nibbles pour THGR122 } } cpt_bit++; } else if (i == Plugin_199_number[buf_stream]); // Parfois le dernier bit est trop long mais on ignore l'erreur car on n'utilise pas ce bit else // erreur de timing du pulse { //Serial.printf("i: %d/%d - duree: %d \n",i,Plugin_199_number,pulse); decod_step=0; cpt_bit=0; i = Plugin_199_number[buf_stream] + 1; // Permet de sortir de la boucle for en cas d'erreur de timing //Serial.printf("OS err data\n"); addLog(LOG_LEVEL_INFO,"Oregon Sci: Erreur data => Preamble+Sync OK mais erreur timing pulse data"); } break; } } if (decod_step==2) { decod_step=0; sens_type=(payload[0]&0x0F)<<12 | (payload[0]&0xF0)<<4 | (payload[1]&0x0F)<<4 | (payload[1]&0xF0)>>4; sens_id=(payload[3]&0x0F)<<12|(payload[2]&0xF0)<<4|(payload[2]&0x0F); temperature=(payload[5]&0x0F)*10 + ((payload[4]&0xF0)>>4) + (payload[4]&0x0F)*0.1; //Conversion BCD en decimal temperature=(payload[5]&0xF0)==0 ? temperature : -temperature; //Température négative si le bit de signe est !0 if (sens_type==0x1D20) //Capteur THGR122NX { Hum_Bat= ((payload[6]&0xF0)>>4)*10 + (payload[6]&0x0F) + ((payload[3]&0xF0)>>4)*0.1; //L'humidité étant un entier, on utilise la 1er décimal pour le niveau de batterie chksum= ((payload[7]&0xF0)>>4) + ((payload[8]&0x0F)<<4) - ((payload[7]&0x0F)); //On soustrait le 14eme nibble "unknown" } else //Capteur THN132N { Hum_Bat=((payload[3]&0xF0)>>4)*0.1; //L'humidité étant un entier, on utilise la 1er décimal pour le niveau de batterie chksum= payload[6]; } if (((chksum==chk_14_cal) || (chksum==chk_17_cal)) && (UserVar[BaseVarIndex + 1] != sens_id || UserVar[BaseVarIndex + 2] != temperature || UserVar[BaseVarIndex + 3] != Hum_Bat) && Redondant_check==0) { //if (Nb_Preamble==16) Serial.printf("OSv2 HQ - Code: %X - T°: %.1f - Hum.bat: %.1f\n",sens_id,temperature,Hum_Bat); //else Serial.printf("OSv2 LQ - Code: %X - T°: %.1f - Hum.bat: %.1f\n",sens_id,temperature,Hum_Bat); /*Serial.printf("\nOSv2 HQ Type: %X - Code: %X - T°: %.1f - Hum.bat: %.1f - Chk: %d - Chk calc 14: %d - Chk calc 17: %d",sens_type,sens_id,temperature,Hum_Bat,chksum,chk_14_cal,chk_17_cal); else Serial.printf("\nOSv2 LQ Type: %X - Code: %X - T°: %.1f - Hum.bat: %.1f - Chk: %d - Chk calc 14: %d - Chk calc 17: %d",sens_type,sens_id,temperature,Hum_Bat,chksum,chk_14_cal,chk_17_cal); */ //Serial.printf("OSv2 LQ - Code: %X - T°: %.1f - Hum.bat: %.1f\n",sens_id,temperature,Hum_Bat); UserVar[BaseVarIndex] = 102; UserVar[BaseVarIndex + 1] = sens_id; UserVar[BaseVarIndex + 2] = temperature; UserVar[BaseVarIndex + 3] = Hum_Bat; String log = F("**Oregon Sci LQ** Adress: 0x"); if (Nb_Preamble==16) log = F("**Oregon Sci HQ** Adress: 0x"); log += String(sens_id,HEX); log += F(" ; Temperature: "); log += temperature; log += F(" ; Humidité.batterie: "); log += Hum_Bat; addLog(LOG_LEVEL_INFO, log); Plugin_199_ready = false; success = true; } else if (UserVar[BaseVarIndex + 1] == sens_id && UserVar[BaseVarIndex + 2] == temperature && UserVar[BaseVarIndex + 3] == Hum_Bat && Redondant_check==0); //{Serial.printf("OS cur red\n");} //Redondance du bitstream courant => pas d'action else if (UserVar[BaseVarIndex + 1] == sens_id && UserVar[BaseVarIndex + 2] == temperature && UserVar[BaseVarIndex + 3] == Hum_Bat && Redondant_check==1) Redondant_check=2; //Serial.printf("OS red\n"); //Confirmation redondance => Redondant_check=false else if ((chksum==chk_14_cal) || (chksum==chk_17_cal)) Redondant_check=3; //Serial.printf("OS no red\n"); //Pas de redondance : Nouvelle data => Pas d'action else Serial.printf("OS chk error\n"); //addLog(LOG_LEVEL_INFO, "Oregon Sci: Erreur Chksum");//Erreur de chksum } return success; } //******************************************************************************** // Decode NewKaku protocol (without code wheel) //******************************************************************************** #define NewKAKU_RawSignalLength 132 #define NewKAKUdim_RawSignalLength 148 #define NewKAKU_1T 275 // us #define NewKAKU_mT 650 // us #define NewKAKU_4T 1100 // us #define NewKAKU_8T 2200 // us boolean decodeNewKaku(byte BaseVarIndex) { boolean success = false; byte Par1 = 0; //unsigned long Par2 = 0; unsigned long bitstream = 0L; unsigned long address = 0L; byte channel = 0; byte command = 0; boolean Bit; int i; int P0, P1, P2, P3; Par1 = 0; if (Plugin_199_number[buf_stream] == NewKAKU_RawSignalLength || Plugin_199_number[buf_stream] == NewKAKUdim_RawSignalLength) { i = 3; // Plugin_199_pulses [3] is the first of a T, xT, T, xT combination do { P0 = Plugin_199_pulses[i][buf_stream] * PULSE_MULTIPLY; P1 = Plugin_199_pulses[i + 1][buf_stream] * PULSE_MULTIPLY; P2 = Plugin_199_pulses[i + 2][buf_stream] * PULSE_MULTIPLY; P3 = Plugin_199_pulses[i + 3][buf_stream] * PULSE_MULTIPLY; if (P0 < NewKAKU_mT && P1 < NewKAKU_mT && P2 < NewKAKU_mT && P3 > NewKAKU_mT)Bit = 0; // T,T,T,4T else if (P0 < NewKAKU_mT && P1 > NewKAKU_mT && P2 < NewKAKU_mT && P3 < NewKAKU_mT)Bit = 1; // T,4T,T,T else if (P0 < NewKAKU_mT && P1 < NewKAKU_mT && P2 < NewKAKU_mT && P3 < NewKAKU_mT) // T,T,T,T This should be on i = 111 because: 27th NewKAKU bit times 4 plus 2 positions for start bit { if (Plugin_199_number[buf_stream] != NewKAKUdim_RawSignalLength) // error if the dim bits are not there return false; } else return false; // other possibilities are not valid in NewKAKU signal. if (i < 130) // all bits belonging to the 32-bit pulse train 32bits * 4 positions per bit + pulse / space for start bit bitstream = (bitstream << 1) | Bit; else // the remaining four bits that belong to the dim level Par1 = (Par1 << 1) | Bit; i += 4; // next pulse quartet } while (i < Plugin_199_number[buf_stream] - 2); //-2 because the space / pulse of the stop bit is no longer part of the signal. if (i > 140) // Command and Dim part Par1++; // Dim level. +1 because user dim level starts at one. else Par1 = ((bitstream >> 4) & 0x01) ? 16 : 0; // Convert the On / Off bit to a Nodo value. address = bitstream >> 6; channel = (bitstream & 0x0f) + 1; command = (bitstream >> 4) & 0x03; if (command > 1) channel = 0; if ((UserVar[BaseVarIndex + 1] != address || UserVar[BaseVarIndex + 2] != channel || UserVar[BaseVarIndex + 3] != Par1) && Redondant_check==0) { UserVar[BaseVarIndex] = 103; UserVar[BaseVarIndex + 1] = address; UserVar[BaseVarIndex + 2] = channel; UserVar[BaseVarIndex + 3] = Par1; //Serial.printf("DIO Code: %X - Ch : %d - State : %d\n",address,channel,Par1); String log = F("**DIO protocol** Adress: 0x"); log += String(address,HEX); log += F(" ; Channel: "); log += channel; log += F(" ; State: "); log += Par1; addLog(LOG_LEVEL_INFO, log); Plugin_199_ready = false; success = true; } else if (UserVar[BaseVarIndex + 1] == address && UserVar[BaseVarIndex + 2] == channel && UserVar[BaseVarIndex + 3] == Par1 && Redondant_check==0); //{Serial.printf("DIO cur red\n");} //Redondance du bitstream courant => pas d'action else if (UserVar[BaseVarIndex + 1] == address && UserVar[BaseVarIndex + 2] == channel && UserVar[BaseVarIndex + 3] == Par1 && Redondant_check==1) Redondant_check=2; //Serial.printf("DIO red\n"); //Confirmation redondance => Redondant_check=false else Redondant_check=3; //Serial.printf("DIO no red\n"); //Pas de redondance : Nouvelle data => Pas d'action } return success; } //******************************************************************************** // Transmit pulses using pulse array //******************************************************************************** void RawSendRF(void) { if (Plugin_199_RXpin != -1) detachInterrupt(Plugin_199_RXpin); int x; //digitalWrite(PIN_RF_RX_VCC,LOW); // Turn off RF receiver //digitalWrite(PIN_RF_TX_VCC,HIGH); // Turn on RF sender delay(5); // small delay between switching send/receive Plugin_199_pulses[Plugin_199_number[buf_stream]][buf_stream] = 1; // force last duration as 1 msec for (byte y = 0; y < 7; y++) // repeats RF code { x = 1; noInterrupts(); while (x < Plugin_199_number[buf_stream]) { digitalWrite(Plugin_199_TXpin, HIGH); delayMicroseconds(Plugin_199_pulses[x++][buf_stream] * 25 - 5); digitalWrite(Plugin_199_TXpin, LOW); delayMicroseconds(Plugin_199_pulses[x++][buf_stream] * 25 - 7); } interrupts(); delay(20);// Delay must run outside interrupt blocked code. } delay(5); //digitalWrite(PIN_RF_TX_VCC,LOW); // turn off RF sender //digitalWrite(PIN_RF_RX_VCC,HIGH); // turn on RF receiver if (Plugin_199_RXpin != -1) attachInterrupt(Plugin_199_RXpin, RF_ISR, CHANGE); } //******************************************************************************** // Send DIO protocol (without code wheel) //******************************************************************************** void sendDIO(unsigned long address, byte channel, byte state) { unsigned long bitstream = 0L; byte i = 1; byte x; // number of positions for pulses / spaces in RawSignal bitstream = address << 6; bitstream |= (channel - 1); //RawSignal.Repeats = 7; // Number of repetitions of the signal. //RawSignal.Delay = 20; // Some time rest between each pulse series. if (state == 16 || state == 0) { bitstream |= (state == 16) << 4; // bit-5 is the on / off command in KAKU signal x = 130; // send start bit + 32 bits = 130 } else x = 146; // send start bit + 32 bits = 130 + 4dimbits = 146 // bitstream now contains the KAKU bits to be sent. for (i = 3; i <= x; i++)Plugin_199_pulses[i][buf_stream] = NewKAKU_1T / 25; // Most times in signal are T. Fill all pulse times with this value. Later the 4T values are put into place i = 1; Plugin_199_pulses[i++][buf_stream] = NewKAKU_1T / 25; // pulse of the start bit Plugin_199_pulses[i++][buf_stream] = NewKAKU_8T / 25; //space after the start bit byte y = 31; // bit from the bitstream while (i < x) { if ((bitstream >> (y--)) & 1) Plugin_199_pulses[i + 1][buf_stream] = NewKAKU_4T / 25; // Bit=1; // T,4T,T,T else Plugin_199_pulses[i + 3][buf_stream] = NewKAKU_4T / 25; // Bit=0; // T,T,T,4T if (x == 146) // als het een dim opdracht betreft { if (i == 111) // Plaats van de Commando-bit uit KAKU Plugin_199_pulses[i + 3][buf_stream] = NewKAKU_1T / 25; // moet een T,T,T,T zijn bij een dim commando. if (i == 127) // als alle pulsen van de 32-bits weggeschreven zijn { bitstream = (unsigned long)state; // nog vier extra dim-bits om te verzenden. y = 3; } } i += 4; } Plugin_199_pulses[i++][buf_stream] = NewKAKU_1T / 25; //pulse van de stopbit Plugin_199_pulses[i][buf_stream] = 0; //space van de stopbit Plugin_199_number[buf_stream] = i; // aantal bits*2 die zich in het opgebouwde RawSignal bevinden RawSendRF(); } #endif // USES_P199