#ifdef USES_P250 //####################################################################################################### //#################################### Plugin 250: Somfy RTS receiver ################################### //####################################################################################################### void SOMFY_ISR() ICACHE_RAM_ATTR; // Microseconds volatile const word k_tempo_synchro_hw_min = 2416*0.9; //initialement *0.7 mais *0.9 pour reduire les fausses detections 2179µs volatile const word k_tempo_synchro_hw_max = 2416*1.3; //3140 µs volatile const word k_tempo_synchro_sw_min = 4550*0.7; //3185 µs volatile const word k_tempo_synchro_sw_max = 4550*1.3; //5915 µs volatile const word k_tempo_half_symbol_min = 604*0.7; //422 µs volatile const word k_tempo_half_symbol_max = 604*1.3; //785 µs volatile const word k_tempo_symbol_min = 1208*0.7; volatile const word k_tempo_symbol_max = 1208*1.3; //avancement décodage volatile byte decod_step=0; //0=Waiting_synchro, 1=Receiving_data, 2=k_display ,3=k_complete //Flag debug volatile byte cpt_progress_hw=0; volatile byte cpt_progress_sw=0; volatile byte cpt_progress_err=0; volatile byte cpt_synchro_hw; //Compteur de pulses de synchro HW. Une trame valide commence par 4 pulses de synchro HW volatile byte cpt_bits; // 56 bits de data volatile byte previous_bit; volatile bool waiting_half_symbol; volatile byte payload[7]; // Tableau avec les 7 champs de 8bits volatile int8_t Plugin_250_RXpin = -1; // To store pulse length, in microseconds volatile word pulse; volatile byte frame[7]; volatile byte order=0; volatile unsigned long rolling_code =0; volatile unsigned long RemoteID =0; volatile boolean SOMFYdebugframe = false; volatile unsigned int HWpulse; #define PLUGIN_250 #define PLUGIN_ID_250 250 #define PLUGIN_NAME_250 "Somfy Receiver" #define PLUGIN_VALUENAME1_250 "Remote" #define PLUGIN_VALUENAME2_250 "Order" #define PLUGIN_VALUENAME3_250 "Rolling_Code" #define PLUGIN_VALUENAME4_250 "ChkSum" #define P250_debugframe PCONFIG(1) #define P250_HWpulse PCONFIG(2) // Not enough HW pulses are considered to be noise... boolean Plugin_250(byte function, struct EventStruct *event, String& string) { boolean success = false; switch (function) { case PLUGIN_DEVICE_ADD: { Device[++deviceCount].Number = PLUGIN_ID_250; Device[deviceCount].Type = DEVICE_TYPE_SINGLE; Device[deviceCount].VType = Sensor_VType::SENSOR_TYPE_QUAD; //SENSOR_TYPE_TRIPLE Device[deviceCount].Ports = 0; Device[deviceCount].PullUpOption = false; Device[deviceCount].InverseLogicOption = false; Device[deviceCount].FormulaOption = true; Device[deviceCount].ValueCount = 4; Device[deviceCount].DecimalsOnly = false; Device[deviceCount].SendDataOption = true; Device[deviceCount].TimerOption = true; Device[deviceCount].TimerOptional = false; Device[deviceCount].GlobalSyncOption = true; break; } case PLUGIN_GET_DEVICENAME: { string = F(PLUGIN_NAME_250); break; } case PLUGIN_GET_DEVICEVALUENAMES: { strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[0], PSTR(PLUGIN_VALUENAME1_250)); strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[1], PSTR(PLUGIN_VALUENAME2_250)); strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[2], PSTR(PLUGIN_VALUENAME3_250)); strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[3], PSTR(PLUGIN_VALUENAME4_250)); break; } case PLUGIN_GET_DEVICEGPIONAMES: { event->String1 = formatGpioName_input(F("Somfy-RX_Pin")); break; } case PLUGIN_SET_DEFAULTS: { PCONFIG(1) = 0; // Desactive debug PCONFIG(2) = 1; // Nb pulse HW mini pour début trame valide success = true; break; } case PLUGIN_WEBFORM_LOAD: { addFormSubHeader(F("General parameters")); addFormCheckBox(F("Debug mode: raw frame"), F("P250_debugframe"),P250_debugframe); addFormNumericBox(F("Nombre de pulse de synchro HW (typ 2, norme RTS=4)"), F("P250_HWpulse"), P250_HWpulse, 0, 4); success = true; break; } case PLUGIN_WEBFORM_SAVE: { P250_debugframe = isFormItemChecked(F("P250_debugframe")); P250_HWpulse = getFormItemInt(F("P250_HWpulse")); success = true; break; } case PLUGIN_INIT: { if (Settings.TaskDevicePin1[event->TaskIndex] != -1){ Plugin_250_RXpin = Settings.TaskDevicePin1[event->TaskIndex]; pinMode(Plugin_250_RXpin, INPUT_PULLUP); attachInterrupt(Plugin_250_RXpin, SOMFY_ISR, CHANGE); success = true; } SOMFYdebugframe = P250_debugframe; HWpulse = P250_HWpulse; break; } case PLUGIN_ONCE_A_SECOND: { if (SOMFYdebugframe) { String log = F("Progression SW = "); log+= cpt_progress_sw; addLog(LOG_LEVEL_INFO, log); log = F("Error Frame = "); log+= cpt_progress_err; addLog(LOG_LEVEL_INFO, log); } success = true; break; } case PLUGIN_TEN_PER_SECOND: { if (decod_step==2) { frame[0] = payload[0]; for(int i = 1; i < 7; ++i) frame[i] = payload[i] ^ payload[i-1]; // Verification du checksum byte cksum = 0; for(int i = 0; i < 7; ++i) cksum = cksum ^ frame[i] ^ (frame[i] >> 4); cksum = cksum & 0x0F; // Touche de controle order=frame[1] & 0xF0; // Rolling code rolling_code = (frame[2] << 8) + frame[3]; // Adresse RemoteID = ((unsigned long)frame[4] << 16) + (frame[5] << 8) + frame[6]; UserVar[event->BaseVarIndex] = RemoteID; UserVar[event->BaseVarIndex + 1] = order; UserVar[event->BaseVarIndex + 2] = rolling_code; UserVar[event->BaseVarIndex + 3] = cksum; sendData(event); //Permet d'envoyer à l'exterieur du plugin (vers controller ou vers trigger de rules) decod_step =0; } success = true; break; } } return success; } //******************************************************************************** // Interrupt handler for RF messages //******************************************************************************** void SOMFY_ISR(){ static word last; // determine the pulse length in microseconds, for either polarity pulse = micros() - last; last += pulse; switch(decod_step) { case 0: // Waiting_synchro: Attente de 4 pulses de synchro HW if (pulse > k_tempo_synchro_hw_min && pulse < k_tempo_synchro_hw_max) ++cpt_synchro_hw; // Si reception d'un pulse HW, incrémente compteur else if (pulse > k_tempo_synchro_sw_min && pulse < k_tempo_synchro_sw_max && cpt_synchro_hw >= HWpulse) { cpt_bits = 0; // Le protocole est 4 pulses HW mais souvent il n'y en a que 2. Une fois au moins 1 pulse HW recus et le pulse SW, on init pour reception des datas previous_bit = 0; waiting_half_symbol = false; for(int i=0; i<7; ++i) payload[i] = 0; ++cpt_progress_sw; decod_step = 1; //Passage en Receiving_data } else { cpt_synchro_hw = 0; } break; //Receiving_data: Codage Manchester. Rising edge=1 / Falling edge=0 //1bit=1208µs soit 604µs low + 604µs high = Rising edge ou 604µs high + 604µs low = Falling edge case 1: if (pulse > k_tempo_symbol_min && pulse < k_tempo_symbol_max && !waiting_half_symbol) { //Cas où on a 10 ou 01 previous_bit = 1 ^ previous_bit; payload[cpt_bits/8] += previous_bit << (7 - cpt_bits%8); ++cpt_bits; } else if (pulse > k_tempo_half_symbol_min && pulse < k_tempo_half_symbol_max) { //Cas où on a 00 ou 01 if (waiting_half_symbol) { waiting_half_symbol = false; payload[cpt_bits/8] += previous_bit << (7 - cpt_bits%8); ++cpt_bits; } else { waiting_half_symbol = true; } } else { // Trame perdue cpt_synchro_hw = 0; ++cpt_progress_err; decod_step = 0; } break; } if (decod_step == 1 && cpt_bits == 56) { // Une fois tous les bits recus decod_step = 2; } } #endif // USES_P250