// 433MHz code sending program // (c)2020 Max515 // License: GPL // DESCRIPTION // This program runs on ESP32 microcontroller. It's purpose is to transmit codes to radio remote controlled devices via a 433 MHz radio emitter connected to one of the GPIO pins. // The emitter will output 433MHz continuous radio wave for as long as the GPIO is high (1). // If you modify this program, make sure you DON'T LEAVE THE RADIO TURNED ON!!! You will jamm the neighborhood, if you do! The local ham-radio guys will come kick your a**. // PROGRAM FLOW // Randomly choose a mode, intensity and duration. // Send multiple MODE commands, then send multiple PLUS commands, then deep-sleep for the specified duration. // On my device, MODE command resets intensity to zero, so there's no need to bring it back down after the previous run. // Commands are hard-coded. Each user must determine its own codes and modify the program accordingly. // A digital one is a continuous radio signal of "signalcadence" duration. // A digital zero is a radio-silent interval of "signalcadence" duration. // Determine your own code by looking at a recorded signal. Convert the succession of ones and zeros into a hex number padding with zeroes to the right to form a 32bit value. // The header of the signal (a long radio "1" followed by a long "0" at the beginning of the signal) is not included in the code. The header is defined separately. // SETTINGS #define PIN 26 #define LED 10 // SIGNAL const unsigned int signalcadence=560; //Cadence of the signal (microseconds). Timer will be set to this value. const unsigned char signalheader[2]={8,4}; //Header radio-on / radio-off durations (cadence counts, not microseconds). //const unsigned char signallength=25; //Signal length (bits) const unsigned long int signalcode[6]={ //Binary-to-hex encoded commands 0xB6D6DB80, //0 Plus 0xB7ADAB80, //1 Minus 0xB756D780, //2 Mode 0xB6EDB580, //3 Numb 0xB7DB5580, //4 Auto 0xB6B6DD80}; //5 Off #include unsigned char selector; //Selected code to send unsigned char repeat; //Count how many code repeats are left unsigned char signalposbit; //Bit that is currently being sent. unsigned char signalposframe; //Frame that is currently being sent (the corresponding state has just been set at the beginning of this timer interrupt and is currently emitting). Frame counts down. unsigned char state=0; //State of GPIO / radio emitter (0=OFF, 1=ON). The GPIO is set to this state at the beginning of the IRQ function. Then, the variable will be set to the next required state, to be ready when the timer triggers the next interrupt. volatile unsigned char operation=0; //Part of code being sent: 0=idle, 1=header-ON, 2=header-OFF, 4=code, 5=finishing unsigned int i1,i2; unsigned char intensity; unsigned char length; unsigned char mode; void nextframe(){ //Determine state according to frame. if(signalposframe==0 && signalposbit!=0){ //The last frame of this bit is currently on-air, but it's not the last bit. signalposbit--; signalposframe=1;} else signalposframe--; //Next frame. switch(signalposframe){ case 2: break; //Keep state case 1: state=(signalcode[selector]>>signalposbit)&1; break; //Determine state: select the code from signalcode array, shift right to current bit, and isolate it from remaining higher bits. case 0: state=0; break;}} //Last frame is always GPIO-OFF. // TIMERS, IRQ hw_timer_t *timer; void IRAM_ATTR onTimer(){ if(state!=2){ digitalWrite(LED,state^1); //DEBUG (LED is inverted, OFF when 1) digitalWrite(PIN,state);} //Set radio to the next required state (0=off, 1=on). /* Serial.print (String(state)); Serial.print (" C="+String(selector)); Serial.print (" R="+String(repeat)); Serial.print (" O="+String(operation)); Serial.print (" B="+String(signalposbit)); Serial.println(" F="+String(signalposframe)); */ switch(operation){ case 1: // SENDING HEADER-ON if(signalposframe--==0){ //Header-ON ended. Set up for header-OFF. state=0; //radio-off operation=2; //Operation: Header-OFF signalposframe=signalheader[1];} //Header-OFF length (cadence intervals) else{state=2;} //Keep this state until the header ends. break; case 2: // SENDING HEADER-OFF if(signalposframe--==0){ //Header-OFF ended. Prepare first bit of the code. state=1; //First bit is always "1", otherwise it would just be part of a longer header-off. operation=4; signalposbit=31; //First bit is index 31 of the code, counting from 0. signalposframe=1;} else{state=2;} //Keep this state until the header ends. break; case 4: // CODE SENDING if(signalposbit==0){ //Nothing more to do, but it must end with radio OFF, to avoid jamming the neighborhood. state=0; //Don't forget to turn off the radio!!! operation=5;} else nextframe(); //Determine new state: shift right to current bit, then isolate it from remaining bits on the left. break; case 5: // FINISH operation=0; timerAlarmDisable(timer); //Stop the timer. break; case 0: // IDLE. Execution should not reach this point, since the timer was disabled at operation=5. break;}} // SETUP void setup() { Serial.begin(115200); //Init serial port, set speed M5.begin(); //Init M5StickC hardware components M5.Axp.ScreenBreath(7); //M5.Lcd.setRotation(1); //M5.Lcd.setCursor(0,0); //M5.Lcd.fillScreen(BLACK); //M5.Lcd.setTextSize(1); //1=normal, 2=double-size //M5.Lcd.setTextFont(2); //1=v.small, 2=small, 3=N/A, 4=large, 5=N/A //M5.Lcd.setTextColor(WHITE,BLACK); pinMode(LED,OUTPUT); //Set GPIO pin to output mode pinMode(PIN,OUTPUT); //Set GPIO pin to output mode timer=timerBegin(0,80,true); //Init timer, divisor 80 => 1us steps timerAttachInterrupt(timer,&onTimer,true); //Set timer interrupt function timerAlarmWrite(timer,signalcadence,true); //Trigger interrupt each CADENCE timer steps => selected CADENCE in microseconds vTaskDelay(1000/portTICK_PERIOD_MS); //Allow 1 second for serial init. M5.Lcd.println("Start!"); Serial.println("START"); mode=random(1,8); //Always set a mode! Otherwise, it will increse intensity over the current mode and intensity! M5.Lcd.print("Mode +"); M5.Lcd.println(mode); Serial.print("Mode +"); Serial.println(mode); i1=random(11); i2=random(11); intensity=(i1+i2)/2 ; //Average around 5 M5.Lcd.print("Intensity "); M5.Lcd.println(intensity); Serial.print("Intensity "); Serial.println(intensity); i1=random(10,90); i2=random(10,90); length=(i1+i2)/2; //Average around 55s M5.Lcd.print("Length "); M5.Lcd.println(length); Serial.print("Length "); Serial.println(length); repeat=mode; //Repeat MODE command for each mode change selector=2; //Select MODE signal operation=1; //Operation: Header-ON signalposframe=signalheader[0]; //Length of header-on (cadence intervals) state=1; //Prepare for header-ON (radio-on) timerAlarmEnable(timer); //Start timer } // MAIN LOOP void loop(){ if(operation==0){ //Radio is idle. Next op. vTaskDelay(500/portTICK_PERIOD_MS); //Wait time between codes if(repeat--){ //Must repeat current code (mode increment or intensity increment) operation=1; //Header-ON signalposframe=signalheader[0]; //Header-ON duration state=1; //Radio-ON timerAlarmEnable(timer);} //Start timer else{ //No more repeats if(intensity){ //Set intensity after mode repeat=intensity; //repeat PLUS command for each intensity level intensity=0; //Don't forget to remove intensity! selector=0; //select PLUS signal operation=1; //Header-ON signalposframe=signalheader[0]; //Header-ON duration state=1; //Radio-ON timerAlarmEnable(timer);} //Start timer else{ //Intensity was set. Prepare to sleep. M5.Lcd.println("Done!"); Serial.println("Done!"); M5.Lcd.print("Batt: "); M5.Lcd.print(M5.Axp.GetBatVoltage()); M5.Lcd.println("V"); //Print battery info here/now. Serial.print("Batt: "); Serial.print(M5.Axp.GetBatVoltage()); M5.Lcd.println("V"); esp_sleep_disable_wakeup_source(ESP_SLEEP_WAKEUP_ALL); esp_sleep_enable_ext0_wakeup((gpio_num_t)37,LOW); esp_sleep_enable_timer_wakeup(1000000*length); vTaskDelay(1000/portTICK_PERIOD_MS); //Wait for debug output esp_deep_sleep_start();}}} //Sleep. The CPU will wake up in a reset state (setup will run again). }