In this video, we learn how to wirelessly control real devices line fand, motor, doors using a relay and turn on a buzzers using the Heltec WiFi LoRa 32 (v3) module packed inside the Meshnology N32 Case, powered by a 3000mAh battery. This system uses LoRa (Long Range Radio) communication, allowing you to send and receive signals up to 21km (13 miles) for free.
The code below is the transmission signal when the user push button is pressed. This is a simple code to send signal when the key is pressed. When the key is released, the OFF signal is send and the relay will be turned OFF.
Project of this video:
Transmitter code: Simple ON/OFF (this code)
Transmitter code: Relay Toggle
Receiver Code: display the relay state, turn ON relay or buzzer
Topics in this lesson
Use Chapters from timeline or click on the time
00:00 Start
1:56 Project Explained
4:55 LoRa and Meshnology N32 package
9:26 Putting N32 together
13:51 Wiring a relay
16:43 Wiring a buzzer
21:47 Board and library
26:08 Transmission Code 1
28:08 Transmission Code 2
29:01 Receiver Code
29:57 Demo: Relay ON/OFF
30:45 Demo: Toggle
31:32 Demo: Buzzer Toggle
23:26 Demo: Buzzer ON/OFF
33:04 Range Demonstration
37:02 Conclusion
Trannsmitter Code to turn ON/OFF a relay over LoRa band
/*
file name: Relay_Secure_TX_Simple
Written on May 13, 2025
This sketch will send the encrypted ON or OFF over LoRa RF signal
when the USER push button (pin 0) is pressed or released.
The user push switch needed to be keept pressed to send ON signal.
Watch Full video instruction: https://youtu.be/lhLQqG5H8_M
This code has been part of Robojax_HeltecLoRa32 library
and can be downloaded from robojax.com
*/
#include
#include
#include "LoRaWan_APP.h"
#include
bool debug = false;
#define PUSH_BUTTON_PIN 0 //the pin connected a push button
#define BATTERY_PIN 1 //do not change
#define ADC_CTRL_PIN 37 //do not change
const char *displayTexttitle = "Relay:"; //shown on the screen
const char *displayTexTX = "(TX)"; //shown on the screen
const char *displayTexRelayON = "ON"; //shown on the screen
const char *displayTexRelayOFF = "OFF"; //shown on the screen
mbedtls_aes_context aes;
const char *userKey = "6tfDs$wEq3!"; //Security key.
SSD1306Wire oledDisplay(0x3c, 500000, SDA_OLED, SCL_OLED, GEOMETRY_128_64, RST_OLED);
Robojax_HeltecLoRa32 robojaxDisplay(&oledDisplay);
bool relayState;
int lastButtonState = LOW; //because of PULLDOWN
String displayTextStateValue= displayTexRelayOFF;
volatile bool buttonPressed = false; // Flag set by interrupt
unsigned long lastInterruptTime = 0;
const unsigned long debounceDelay = 50; // 50ms debounce time
#define RF_FREQUENCY 915000000 // Hz
#define TX_OUTPUT_POWER 2 // dBm from 2 to 20. when powered via battery 2 to 14dBm
#define LORA_BANDWIDTH 0 // [0: 125 kHz,
// 1: 250 kHz,
// 2: 500 kHz,
// 3: Reserved]
#define LORA_SPREADING_FACTOR 7 // [SF7..SF12]
#define LORA_CODINGRATE 1 // [1: 4/5,
// 2: 4/6,
// 3: 4/7,
// 4: 4/8]
#define LORA_PREAMBLE_LENGTH 8 // Same for Tx and Rx
#define LORA_SYMBOL_TIMEOUT 0 // Symbols
#define LORA_FIX_LENGTH_PAYLOAD_ON false
#define LORA_IQ_INVERSION_ON false
#define RX_TIMEOUT_VALUE 1000
#define BUFFER_SIZE 30 // Define the payload size here
char txpacket[BUFFER_SIZE];
char rxpacket[BUFFER_SIZE];
bool lora_idle=true;
static RadioEvents_t RadioEvents;
void VextON() {
pinMode(Vext, OUTPUT);
digitalWrite(Vext, LOW);
}
void OnTxDone(void);
void OnTxTimeout(void);
void IRAM_ATTR handleButtonInterrupt();
void readPushSwitch() ;
void decryptAES(uint8_t *data, const char *key);
void encryptAES(uint8_t *data, const char *key);
void processKey(const char *userKey, uint8_t *processedKey, size_t keySize);
void setup() {
Serial.begin(115200);
VextON();
delay(100); // OLED power on delay
pinMode(PUSH_BUTTON_PIN, INPUT_PULLDOWN);//pin for push button
attachInterrupt(digitalPinToInterrupt(PUSH_BUTTON_PIN), handleButtonInterrupt, CHANGE);
pinMode(ADC_CTRL_PIN, OUTPUT);
// Set ADC_Ctrl to HIGH to enable the ADC circuit.
digitalWrite(ADC_CTRL_PIN, HIGH);
robojaxDisplay.begin();
//robojaxDisplay.displayText("Hello", 0, 0, 16, TEXT_ALIGN_LEFT);
//delay(1000);
//LoRa stuff
Mcu.begin(HELTEC_BOARD,SLOW_CLK_TPYE);
RadioEvents.TxDone = OnTxDone;
RadioEvents.TxTimeout = OnTxTimeout;
Radio.Init( &RadioEvents );
Radio.SetChannel( RF_FREQUENCY );
Radio.SetTxConfig( MODEM_LORA, TX_OUTPUT_POWER, 0, LORA_BANDWIDTH,
LORA_SPREADING_FACTOR, LORA_CODINGRATE,
LORA_PREAMBLE_LENGTH, LORA_FIX_LENGTH_PAYLOAD_ON,
true, 0, 0, LORA_IQ_INVERSION_ON, 3000 );
}
void loop() {
//read the GPIO4
readPushSwitch();
if(debug)
{
Serial.print(displayTexttitle);Serial.print(":");
Serial.println(displayTextStateValue);
}
robojaxDisplay.displayLineText(displayTexttitle, 0, 0, 24, false);//size 24 font
robojaxDisplay.displayText(displayTexTX, 127, 5, 10, TEXT_ALIGN_RIGHT);//display TX on the right side
robojaxDisplay.displayLineText(displayTextStateValue, 0, 30, 24, true);//size 24 font
//now send the value
robojaxDisplay.sendStringSecure(displayTextStateValue);
delay(100);
}
// Interrupt service routine
void IRAM_ATTR handleButtonInterrupt() {
unsigned long interruptTime = millis();
if (interruptTime - lastInterruptTime > debounceDelay) {
buttonPressed = true;
}
lastInterruptTime = interruptTime;
}
void readPushSwitch() {
if (buttonPressed) {
buttonPressed = false; // Reset flag
int currentButtonState = digitalRead(PUSH_BUTTON_PIN);
if(debug)
{
Serial.print("Button state: ");
Serial.println(currentButtonState);
}
// if pressed ==LOW
if (currentButtonState == LOW) {
displayTextStateValue = displayTexRelayON ;
if(debug)
{
Serial.print("State updated: ");
Serial.println(displayTextStateValue);
}
}else{
displayTextStateValue = displayTexRelayOFF;
}
}
}//readPushSwitch() end
void OnTxDone( void )
{
if(debug)
Serial.println("TX done......");
lora_idle = true;
}
void OnTxTimeout( void )
{
Radio.Sleep( );
if(debug)
Serial.println("TX Timeout......");
lora_idle = true;
}