LoRa and Raspberry Pi Pico W based Home Automation Project with Feedback
Table of Contents
LoRa and Raspberry Pi Pico:
LoRa and Raspberry Pi Pico W based Home Automation Project with Feedback– In this tutorial, you will learn how to make a Long Range Radio controlled wireless Home automation project using a pair of Raspberry Pi Pico boards, and it doesn’t matter if you use normal Raspberry Pi Pico boards or Raspberry Pi Pico W boards. You will also need a pair of SX1278 Lora Transceiver modules, an I2C-supported 16×2 LCD, some switches, a relay module, and 110/220Vac light bulbs.
Ever since I have started this website, since then I have built many home automation projects using different controller boards like Arduino Mega, Arduino Uno, Arduino Nano, Nodemcu ESP8266, ESP32 WiFi + Bluetooth Module, Raspberry Pi Pico and so on; with these controller boards I used desktop applications, android cell phone applications designed in Android Studio, different IoT platforms like Blynk application, Ubidots, Thingspeak, Arduino IoT Cloud, Google Spreadsheet, InfluxDB, Cayenne, Adafruit Io, and so many other IoT platforms. Just go ahead and check different categories.
Anyway, recently I built a Home automation project using Raspberry Pi Pico and HC-05 Bluetooth module. I used my designed android application for controlling 220Vac light bulbs. This Home automation project was insanely fast and personally, I never had such an amazing experience with any other controller board. This was absolutely error-free, there was no false triggering, and it won’t freeze.
So, keeping in mind this speed and performance, I decided to convert one of my previously designed Arduino and LoRa based Home automation project into Raspberry Pi Pico and LoRa based Home automation project with feedback message.
Anyway, here is a prototype model of my Raspberry Pi Pico and LoRa based Home automation project. I have connected everything as per the circuit diagrams which I will explain in a minute.
You can see 4 buttons on the transmitter side. Using these very 4 buttons, I am going to control certain loads on the receiver side. For demonstration purposes, I have put 4 bulbs on the receiver’s side, which are connected to 4 relays. Besides lights, you can use any other 110/220Vac loads or use any other DC type loads.
If you want to use 110/220Vac supply, you must not forget to use protective gloves, because 110/220Vac can prove fatal. So, as far as possible you must ensure the presence of a friend or any companion while carrying on work on such projects. When the AC supply is ON, do not touch the relay module.
I am using the same SX1278 Lora Transceiver modules on the receiver as well as the transmitter side.
The reason I am using this 16×2 LCD on the transmitter side is to display the loads status. This way I know exactly which loads are ON and which loads are OFF. So, whenever I turn ON/OFF any button on the transmitter side, the receiver side sends me a feedback whether that load has turned ON or not. You will find this feedback functionality quite handy in situations when you are away and the loads are not visible to you. Let’s go ahead and start a practical demonstration so that you can see everything for yourself.
Altium Designer + Altium 365 + Octopart:
Altium 365 lets you hold the fastest design reviews ever. Share your designs from anywhere and with anyone with a single click. it’s easy, leave a comment tagging your teammate and they’ll instantly receive an email with a link to the design. Anyone you invite can open the design using a web browser. Using the browser interface, you’re able to comment, markup, cross probe, inspect, and more. Comments are attached directly to the project, making them viewable within Altium designer as well as through the browser interface. Design, share, and manufacture, all in the same space with nothing extra to install or configure. Connect to the platform directly from Altium Designer without changing how you already design electronics. Altium 365 requires no additional licenses and comes included with your subscription plan.
Get real-time component insights as you design with Octopart built into Altium 365. Octopart is the fastest search engine for electronic parts and gives you the most up-to-date part data like specs, datasheets, cad models, and how much the part costs at different amounts etc. Right in the design environment so you can focus on your designs. Start with Altium Designer and Activate Altium 365. Search for electronic parts on Octopart.
I can randomly turn ON and turn OFF any light and it is working quite superbly. This small delay occurs because I have used timers on the transmitter and receiver sides. You can increase or decrease the timer value if you want.
This Raspberry Pi Pico and Lora based automation system presents quite a practical look. Whenever I turn ON/OFF any button on the transmitter side, the receiver side sends me a feedback whether that load has turned ON or not.
Now even if I do not see electrical appliances or loads, there is no problem, because I keep on receiving feedback from the receiver’s side. Now, if there is some issue on the receiver side, I’ll not receive any kind of feedback. Or if I get out of the range, even then I’ll receive no feedback. To explain this point, I am going to turn OFF the receiver side.
Now you can see, that I am not receiving any type of feedback. It’s still displaying the same message. While if you take a look at the switches; you can see switch2 and switch3 are ON. But on the LCD it shows that D1 and D2 are ON as this is the old message. Now if I turn ON the receiver side once again, I will start receiving the feedback. I have been testing this project and it is working exceptionally well. For the step-by-step explanation and practical demonstration watch my video tutorial available on Electronic Clinic or you can click on the video link given at the end of this article. I am sure by now, you might have got an idea of how does this system work. So, without any further delay let’s get started!!!
Amazon Links:
Sx1278 LoRa Transceiver modules
Other Tools and Components:
Super Starter kit for Beginners
PCB small portable drill machines
*Please Note: These are affiliate links. I may make a commission if you buy the components through these links. I would appreciate your support in this way!
Lora SX1278 Module:
The SX1276/77/78/79 transceivers feature the LoRa TM long range modem that provides ultra-long range spread spectrum communication and high interference immunity whilst minimizing current consumption.
Using Semtech’s patented LoRa modulation technique SX1276/77/78/79 can achieve a sensitivity of over -148dBm using a low cost crystal and bill of materials. The high sensitivity combined with the integrated +20 dBm power amplifier yields industry leading link budget making it optimal for any application requiring range or robustness.
LoRa also provides significant advantages in both blocking and selectivity over conventional modulation techniques, solving the traditional design compromise between range, interference immunity and energy consumption.
These devices also support high performance (G)FSK modes for systems including WMBus, IEEE802.15.4g. The SX1276/77/78/79 deliver exceptional phase noise, selectivity, receiver linearity and IIP3 for significantly lower current consumption than competing devices.
I have got these three LoRa SX1278 Modules, LoRa (Long-Range) is digital wireless data communication IoT technology. LoRa transmits over license-free megahertz radio frequency bands: 169 MHz, 433 MHz (Asia), 868 MHz (Europe) and 915 MHz (North America). LoRa enables very-long-range wireless data transmission. The type of the LoRa modules I am using supports 433MHz. Each LoRa module is also provided with an Antenna which we will need to solder to the boards to increase the wireless communication range.
The supply voltage is 1.8 to 3.7 volts, so it can be used with 3.3 Volts and 5 volts compatible controller boards without any problem. 3.3V compatible boards e.g. ESP8266, ESP32, Raspberry Pi Pico, etc. The operational temperature range is -40 Celsius to +85 Celsius.
It has a total of 12 via’s or holes which are clearly labeled and out of which we will be using only VCC, MISO, MOSI, SLCK, NSS, and GND.
As a beginner you may get scared when you first look at these LoRa modules which has holes as they are very close to each other because you cannot solder regular male or female headers. But no worries at all, you can simply start by soldering jumper wires, which of course will need some soldering practice.
As you can see I soldered the jumpers wires and the antennas. Next, I checked the short circuits using a digital Multimeter, and then to secure the wires I applied the silicon, this will help to keep the wires in place and will also protect the wires from getting disconnected.
LoRa Applications:
When it comes to the applications, you have almost infinite ways of controlling and monitoring things including,
Automated Meter Reading.
Home and Building Automation
Wireless Alarm and Security System
Industrial Monitoring and Control
Long range Irrigation Systems and so on…
RPI Pico & LoRa Tx Circuit:
3.3V and GND pins of the LoRa SX1278 are connected with the Raspberry Pi Pico 3.3V and GND pins. While the NSS, MISO, SCK, and MOSI pins of the LoRa SX1278 Transceiver module are connected with the Raspberry Pi Pico GPIO pins 8, 16, 18, and 19.
4 switches are connected with the GPIO pins 2, 4, 6, and 15.
The VCC pin of the I2C supported 16×2 LCD is connected with the VBUS which is the 5V pin. And obviously the grounds are connected together. The SDA and SCL pins of the I2C supported 16×2 LCD are connected with the GP0 and GP1. As per the Pins layout diagram GP0 is the SDA and GP1 is the SCL.
RPI Pico & LoRa Rx Circuit:
This is the receiver side circuit diagram. You can see the LoRa SX1278 connection with the Raspberry Pi Pico remains exactly the same. The 4 channels relay module inputs are connected with the Raspberry Pi Pico GPIO pins 2, 3, 4, and 5. If you are using a 5v relay module then you can connect the VCC of the relay module with the VBUS pin of the Raspberry Pi Pico to supply 5 volts. And if you are using a 12V relay module then you will need to connect an external 12v power supply. So, that’s all about the receiver side circuit diagram. Now, let’s go ahead and take a look at the programming.
Required Libraries:
Before, you start downloading all the required libraries, first you will need to install the Raspberry Pi Pico board in the Arduino IDE. For this you can read my getting started article on the Raspberry Pi Pico and Arduino IDE. After you have successfully installed the Raspberry Pi Pico board in the Arduino IDE then you can start downloading all the required libraries.
Download the liquidCrystal_I2C and unzip the folder. Open the folder and copy the .h and .cpp files and paste them in the receiver side programming folder. The receiver side main code and these .h and .cpp files are need to inside the same folder.
LoRa & Raspberry Pi Pico Tx Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 |
/* Transmitter code Home Automation project by https://www.electroniclinic.com/ */ #include <SPI.h> // include libraries #include <LoRa.h> #include "LiquidCrystal_I2C.h" #include <Wire.h> #define PCF8574 0x27 #define PCF8574A 0x3F TwoWire Wire_00(i2c0, 0,1); // SDA, SCL LiquidCrystal_I2C lcd(&Wire_00, PCF8574, 16, 2); #define nss 8 #define rst 9 #define dio0 7 int sw1 = 2; int sw2 = 4; int sw3 = 6; int sw4 = 15; int Sensor1 = 0; int Sensor2 = 0; int Sensor3 = 0; int Sensor4 = 0; boolean bflag = false; String outgoing; // outgoing message byte msgCount = 0; // count of outgoing messages byte localAddress = 0xBB; // address of this device byte destination = 0xFF; // destination to send to long lastSendTime = 0; // last send time int interval = 50; // interval between sends String Mymessage = ""; void setup() { Serial.begin(9600); // initialize serial LoRa.setPins(nss, rst, dio0); Wire_00.begin(); Wire_00.setClock(100000); // 100kHz lcd.begin(); lcd.backlight(); pinMode(sw1, INPUT_PULLUP); pinMode(sw2, INPUT_PULLUP); pinMode(sw3, INPUT_PULLUP); pinMode(sw4, INPUT_PULLUP); Serial.println("LoRa Duplex"); if (!LoRa.begin(433E6)) { // initialize ratio at 915 MHz Serial.println("LoRa init failed. Check your connections."); while (true); // if failed, do nothing } Serial.println("LoRa init succeeded."); } void loop() { if (millis() - lastSendTime > interval) { Sensor1 = digitalRead(sw1); Sensor2 = digitalRead(sw2); Sensor3 = digitalRead(sw3); Sensor4 = digitalRead(sw4); Mymessage = Mymessage + Sensor1 +"," + Sensor2 + "," + Sensor3 +"," + Sensor4; Serial.println(Mymessage); sendMessage(Mymessage); Mymessage = ""; //Serial.println("Sending " + message); lastSendTime = millis(); // timestamp the message interval = random(50) + 100; } // parse for a packet, and call onReceive with the result: onReceive(LoRa.parsePacket()); } void sendMessage(String outgoing) { LoRa.beginPacket(); // start packet LoRa.write(destination); // add destination address LoRa.write(localAddress); // add sender address LoRa.write(msgCount); // add message ID LoRa.write(outgoing.length()); // add payload length LoRa.print(outgoing); // add payload LoRa.endPacket(); // finish packet and send it msgCount++; // increment message ID } void onReceive(int packetSize) { if (packetSize == 0) return; // if there's no packet, return // read packet header bytes: int recipient = LoRa.read(); // recipient address byte sender = LoRa.read(); // sender address byte incomingMsgId = LoRa.read(); // incoming msg ID byte incomingLength = LoRa.read(); // incoming msg length String incoming = ""; while (LoRa.available()) { incoming += (char)LoRa.read(); } if (incomingLength != incoming.length()) { // check length for error //Serial.println("error: message length does not match length"); ; return; // skip rest of function } // if the recipient isn't this device or broadcast, if (recipient != localAddress && recipient != 0xFF) { // Serial.println("This message is not for me."); ; return; // skip rest of function } // if message is for this device, or broadcast, print details: //Serial.println("Received from: 0x" + String(sender, HEX)); //Serial.println("Sent to: 0x" + String(recipient, HEX)); //Serial.println("Message ID: " + String(incomingMsgId)); // Serial.println("Message length: " + String(incomingLength)); Serial.println("Message: " + incoming); // Serial.println("RSSI: " + String(LoRa.packetRssi())); // Serial.println("Snr: " + String(LoRa.packetSnr())); // Serial.println(); String device1 = getValue(incoming, ',', 0); String device2 = getValue(incoming, ',', 1); String device3 = getValue(incoming, ',', 2); String device4 = getValue(incoming, ',', 3); int d1 = device1.toInt(); int d2 = device2.toInt(); int d3 = device3.toInt(); int d4 = device4.toInt(); //clear display lcd.clear(); lcd.setCursor(0,0); lcd.print("D1:"); lcd.print(d1); lcd.print(" D2:"); lcd.print(d2); lcd.setCursor(0,1); lcd.print("D3:"); lcd.print(d3); lcd.print(" D4:"); lcd.print(d4); incoming = ""; } String getValue(String data, char separator, int index) { int found = 0; int strIndex[] = { 0, -1 }; int maxIndex = data.length() - 1; for (int i = 0; i <= maxIndex && found <= index; i++) { if (data.charAt(i) == separator || i == maxIndex) { found++; strIndex[0] = strIndex[1] + 1; strIndex[1] = (i == maxIndex) ? i+1 : i; } } return found > index ? data.substring(strIndex[0], strIndex[1]) : ""; } |
LoRa & Raspberry Pi Pico Rx Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 |
/* Receiver code Home Automation project by https://www.electroniclinic.com/ */ #include <SPI.h> // include libraries #include <LoRa.h> #define nss 8 #define rst 9 #define dio0 7 boolean flag1 = false; boolean flag2 = false; boolean flag3 = false; boolean flag4 = false; int RLY1=2; int RLY2=3; int RLY3=4; int RLY4=5; int Sensor1 = 0; int Sensor2 = 0; int Sensor3 = 0; int Sensor4 = 0; int relay1Status; int relay2Status; int relay3Status; int relay4Status; String outgoing; // outgoing message byte msgCount = 0; // count of outgoing messages byte localAddress = 0xFF; // address of this device byte destination = 0xBB; // destination to send to long lastSendTime = 0; // last send time int interval = 50; // interval between sends String statusmessage = ""; void setup() { Serial.begin(9600); // initialize serial pinMode(RLY1,OUTPUT); pinMode(RLY2,OUTPUT); pinMode(RLY3,OUTPUT); pinMode(RLY4,OUTPUT); LoRa.setPins(nss, rst, dio0); Serial.println("LoRa Duplex"); if (!LoRa.begin(433E6)) { // initialize ratio at 915 MHz Serial.println("LoRa init failed. Check your connections."); while (true); // if failed, do nothing } Serial.println("LoRa init succeeded."); } void loop() { if (millis() - lastSendTime > interval) { relay1Status = digitalRead(RLY1); relay2Status = digitalRead(RLY2); relay3Status = digitalRead(RLY3); relay4Status = digitalRead(RLY4); statusmessage = statusmessage + relay1Status + "," + relay2Status + "," + relay3Status + "," + relay4Status; sendMessage(statusmessage); delay(10); statusmessage = ""; lastSendTime = millis(); // timestamp the message interval = random(50) + 100; // 2-3 seconds } // parse for a packet, and call onReceive with the result: onReceive(LoRa.parsePacket()); } void sendMessage(String outgoing) { LoRa.beginPacket(); // start packet LoRa.write(destination); // add destination address LoRa.write(localAddress); // add sender address LoRa.write(msgCount); // add message ID LoRa.write(outgoing.length()); // add payload length LoRa.print(outgoing); // add payload LoRa.endPacket(); // finish packet and send it msgCount++; // increment message ID } void onReceive(int packetSize) { if (packetSize == 0) return; // if there's no packet, return // read packet header bytes: int recipient = LoRa.read(); // recipient address byte sender = LoRa.read(); // sender address byte incomingMsgId = LoRa.read(); // incoming msg ID byte incomingLength = LoRa.read(); // incoming msg length String incoming = ""; while (LoRa.available()) { incoming += (char)LoRa.read(); } if (incomingLength != incoming.length()) { // check length for error // Serial.println("error: message length does not match length"); ; return; // skip rest of function } // if the recipient isn't this device or broadcast, if (recipient != localAddress && recipient != 0xFF) { //Serial.println("This message is not for me."); ; return; // skip rest of function } // if message is for this device, or broadcast, print details: // Serial.println("Received from: 0x" + String(sender, HEX)); // Serial.println("Sent to: 0x" + String(recipient, HEX)); //Serial.println("Message ID: " + String(incomingMsgId)); // Serial.println("Message length: " + String(incomingLength)); Serial.println("Message: " + incoming); //Serial.println("RSSI: " + String(LoRa.packetRssi())); //Serial.println("Snr: " + String(LoRa.packetSnr())); Serial.println(); String q = getValue(incoming, ',', 0); String r = getValue(incoming, ',', 1); String s = getValue(incoming, ',', 2); String t = getValue(incoming, ',', 3); Sensor1 = q.toInt(); Sensor2 = r.toInt(); Sensor3 = s.toInt(); Sensor4 = t.toInt(); if((Sensor1 == 0)&&(flag1==true)) { digitalWrite(RLY1,HIGH); relay1Status = 1; Serial.println("Relay 1 is turned on"); flag1=false; } if((Sensor1 == 1)&&(flag1 ==false)) { digitalWrite(RLY1,LOW); relay1Status = 0; Serial.println("Relay 1 is turned off"); flag1=true; } if((Sensor2 == 0)&&(flag2==true)) { digitalWrite(RLY2,HIGH); relay2Status = 0; Serial.println("Relay 2 is turned on"); flag2=false; } if((Sensor2 == 1)&&(flag2 ==false)) { digitalWrite(RLY2,LOW); relay2Status = 0; Serial.println("Relay 2 is turned off"); flag2=true; } if((Sensor3 == 0)&&(flag3==true)) { digitalWrite(RLY3,HIGH); relay3Status = 1; Serial.println("Relay 3 is turned on"); flag3=false; } if((Sensor3 == 1)&&(flag3 ==false)) { digitalWrite(RLY3,LOW); relay3Status = 0; Serial.println("Relay 3 is turned off"); flag3=true; } if((Sensor4 == 0)&&(flag4==true)) { digitalWrite(RLY4,HIGH); relay4Status = 1; Serial.println("Relay 4 is turned on"); flag4=false; } if((Sensor4 == 1)&&(flag4 ==false)) { digitalWrite(RLY4,LOW); relay4Status = 0; Serial.println("Relay 4 is turned off"); flag4=true; } incoming = ""; } String getValue(String data, char separator, int index) { int found = 0; int strIndex[] = { 0, -1 }; int maxIndex = data.length() - 1; for (int i = 0; i <= maxIndex && found <= index; i++) { if (data.charAt(i) == separator || i == maxIndex) { found++; strIndex[0] = strIndex[1] + 1; strIndex[1] = (i == maxIndex) ? i+1 : i; } } return found > index ? data.substring(strIndex[0], strIndex[1]) : ""; } |
90% of the transmitter and receiver code is from my Arduino and LoRa based automation project. I made a few changes in the code, on the transmitter side I am using a 16×2 LCD instead of using an Oled display and the other changes are I am using different pin numbers. While everything else remains exactly the same. So, that’s all about the programming.
Watch Video Tutorial: