#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