LoRa RC Controller using Arduino
Table of Contents
LoRa RC Controller:
LoRa RC Controller using Arduino- If you want to make a long-range RC controller ranging from 1km to 5km using LoRa and Arduino, then this article is for you. Because after reading this article, you will be able to make RC Controllers for RC planes, RC cars, RC trucks, RC boats, RC helicopters, RC robots, and more – basically, anything you wish to wirelessly control over a long distance.
For the past 3 months, I have been using the Ai-thinker’s 433Mhz Ra-02 LoRa SX1278 transceiver modules, and I am completely amazed by their performance. I used the same LoRa transceiver modules to make a long-range water level indicator system, and besides that, I also built a LoRa-based Home Energy Monitoring system. I am very satisfied with the performance and range of these LoRa modules.
By the way, I have already written an article on LoRa range test in which I used three different types of antennas to determine the maximum wireless communication distance. If you need a distance of up to 1 kilometer, you can use LoRa whip antennas. For distances between 2 to 3 kilometers, you should use Flexible PCB antennas. And if you require a range of 5 kilometers or even more, I suggest using a combination of Suction cup antenna and Flexible PCB antenna.
But just long distance is not enough because Rylr896 LoRa modules by Reyax can send and receive data over a long distance of 15 kilometers, but they are quite slow and can’t be used for making RC Controller for RC planes, RC cars, etc.
So today, we are going to test if the Ai-thinker’s Ra-02 LoRa transceiver modules are fast enough to build an RC controller for controlling planes, cars, trucks, boats, etc.
We need to see if, along with the long-distance capability, its communication speed is also fast. This is crucial because data communication speed matters the most in radio controllers. RC planes, RC cars, and RC boats require control sticks to be highly responsive. If there is any delay or lag in communication, then you won’t be able to fly an RC plane or drive an RC car or a boat effectively.
For the demonstration purposes, I have built this fully functional prototype model. It’s a 2 channels RC controller, but you can increase the number of channels as per your needs, I will explain this in the programming.
Anyways, I am using one channel for controlling the speed of 775 Dc motor and the other channel for controlling the Servo motor. Instead of using the 775 DC motor you can also use a Brushless DC motor.
On the Transmitter side, a 2-axis analog joystick and LoRa module are connected to the Arduino. For now, forget about the relays and buzzer on the development boards.
On the Receiver side, along with the Lora module, I have also connected a 10kg high torque Servo motor, and a 12V 775 Motor through a 320A Brushed DC Motor controller. I am going to use my created 4S lithium Ion battery to power up the receiver side. Read my article on 775 DC Motor and 320A Brushed DC Motor Controller.
Even this setup is more than enough for building an RC Boat or an RC Car. Because, you can use the servo motor for the steering and the 775 DC motor as the engine. On this development board I have added a 5V and 3A power supply which can power up the Arduino and multiple Servos.
if you remove the relays, terminal blocks, transistors, resistors, female headers, and the Buzzer. You can reduce the circuit size, as we don’t need these components. You can read my article on the Arduino and Lora based development board.
And if you want to use this setup for controlling a Brushless DC Motor then you will have to replace this controller with this ESC. I am already planning on building an RC Plane so consider subscribing if you don’t want to miss my videos and articles on the LoRa based long range RC Controller. Anyway, let’s start the practical demonstration and afterwards I will explain the circuit diagrams and programming.
I have powered up the Transmitter and receiver side. When the Joystick is in the middle position, the servo Motor stays at 90 Degrees and the DC motor is completely stopped. “For the practical demonstration, watch the video tutorial given at the end of this article”.
This is insanely responsive. I have been flying RC Planes and Drones and right now it reminds me of the Throttle and Yaw stick. I can control the motor speed and Servo position both at the same time and I don’t see any delay or lagging issues.
You might be thinking the servo is slow or lagging but it’s not like that; it’s just because of its size; it’s bigger and that’s why to you it might appear a little bit slow. But in reality it’s quite responsive. Normally we use micro servo motors on RC Planes. The reason I am using this Large Servo motor is to let you know, that my RC controller is powerful enough to be used in RC Cars, RC Boats, RC Trucks, and RC Planes etc. In fact, I have also tested it with a 25Kg torque motor. With such high torque servos you can control almost any RC machine.
So, I am pretty satisfied with this first test, and now I can design my own Flight Controller for an RC Plane and radio controlled system for Cars and Boats etc. Now, let’s go ahead and take a look at the Transmitter and Receiver side wiring.
Amazon Links:
Arduino Nano USB-C Type (Recommended)
*Disclosure: These are affiliate links. As an Amazon Associate I earn from qualifying purchases.
LoRa RC Controller Transmitter Circuit:
The +5V and GND pins of the analog Joystick are connected to the Arduino 5V and GND pins. The VRX and VRY pins are connected to the Arduino Analog pins A0 and A1.
The LoRa Ra-02 433Mhz transceiver module NSS, SCK, MOSI, and MISO pins are connected to the Arduino pins 10, 13, 11, and 12. The reset pin of the LoRa module is connected to the Arduino pin 9. And its 3.3V and GND pins are connected to the Arduino 3.3V and GND pins.
The VIN and GND wires can be connected to an external Regulated 5V power supply.
On my board I have this 5V and 3A power supply and if you too want to make this power supply then you can follow this circuit diagram.
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.
LoRa RC Controller Receiver Circuit:
The Servo motor VCC and GND wires are connected to the Arduino VIN and GND pins which are connected to the 5V and 3A power supply. The Servo Motor Signal Wire is connected to the Arduino PWM Pin 9.
The VCC and GND wires of the Brushed Motor Controller are connected to the same Vin and GND pins. The signal Wire is connected to the Arduino PWM Pin 3.
These Red and Black wires with the XT60 connector are used to connect a 3S, 4S, or 5S Lipo or Lithium Ion battery to power up the DC Motor. In my case, I have connected the 775 Brushed DC motor with the controller output wires.
The LoRa Module and 5V Power supply wiring remain exactly the same. Now, let’s go ahead and take a look at the programming.
Required Library:
LoRa RC Controller Transmitter Programming:
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 |
/* Transmitter Side https://www.electroniclinic.com/ */ #include <SPI.h> // include libraries #include <Wire.h> #include <LoRa.h> int joyX = A0; int joyY = A1; int joyVal; int joyValy; 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 String Mymessage = ""; void setup() { Serial.begin(9600); // initialize serial if (!LoRa.begin(433E6)) { Serial.println("LoRa init failed. Check your connections."); while (true); // if failed, do nothing } Serial.println("LoRa init succeeded."); } void loop() { joyVal = analogRead(joyX); // read value from joystick joyValy = analogRead(joyY); // read value from joystick // Serial.print("Vx value = "); // Serial.print(joyVal); // Serial.println(); // Serial.print("Vy value = "); // Serial.print(joyValy); // Serial.println(); Mymessage = Mymessage + joyVal +"," + joyValy; // 2-channels sendMessage(Mymessage); Serial.println("Sending " + Mymessage); Mymessage = ""; //Serial.println("Sending " + message); } 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 } |
The Joystick two pins VRX and VRY are connected to the Arduino Pins A0 and A1. I have defined two variables for storing the values of VRX and VRY.
And I am using the same localAddress and destination address. These are the same addresses I have been using in majority of my LoRa based projects. By using addresses you get a full control over which LoRa module you want to send Data. You can define multiple LoRa nodes if in case you want to send control signals to multiple receivers. Anyway, you can read my article on how to use multiple loRa Nodes.
Code inside the setup function is exactly the same.
Inside the loop() function, we simply read the Joystick X-axis and Y-axis values and store them in variables joyVal and joyValy.
Then we create a message and use comma to separate those values. On the receiver side, I am going to use the comma as the delimiter to split and retrieve those values. So, right now, it’s a two channel RC controller. You can add another joystick, some potentiometers, and buttons. Read values from the desired components, store them in variables, and then add those variables in the message and don’t forget to add commas.
1 |
Mymessage = Mymessage + joyVal +"," + joyValy; // 2-channels |
So, this is how, you can add more channels. Anyway, once the message is ready, then we simply send it to the receiver side LoRa. Now, let’s go ahead and take a look at the receiver side programming.
LoRa RC Controller Receiver Programming:
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 |
/* Receiver Side https://www.electroniclinic.com/ */ #include <SPI.h> // include libraries #include <Wire.h> #include <LoRa.h> #include <Servo.h> // include servo library Servo ESC; // create ESC object to control the ESC Servo servo; // create servo object to control the Servo int mspeed = 0; // motor speed int servoangle; int joyVal; int joyValy; 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 void setup() { Serial.begin(9600); // initialize serial; ESC.attach(3);///DC Motor servo.attach(9); if (!LoRa.begin(433E6)) { Serial.println("LoRa init failed. Check your connections."); while (true); // if failed, do nothing } Serial.println("LoRa init succeeded."); } void loop() { // parse for a packet, and call onReceive with the result: onReceive(LoRa.parsePacket()); } 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 motorspeed = getValue(incoming, ',', 0); String sangle = getValue(incoming, ',', 1); joyVal = motorspeed.toInt(); joyValy = sangle.toInt(); if ((joyVal> 0) && (joyVal < 490)) { mspeed = map(joyVal, 0, 489, 2000,1501); ESC.writeMicroseconds(mspeed); Serial.println("Motor Forward"); } else if ((joyVal>490) && (joyVal < 540)) { ESC.writeMicroseconds(1500); // Send the signal to the ESC Serial.println("Motor stop"); } if ((joyVal> 540) && (joyVal < 1022)) { mspeed = map(joyVal, 540, 1022, 1501, 2000); // scale it to use it with the servo library (value between 0 and 180) ESC.writeMicroseconds(mspeed); Serial.println("Motor Reverse"); } ////////////////////////////////////////////////// if ((joyValy >= 0) && (joyValy <= 545)) { servoangle = map(joyValy, 0, 545, 0,90); servo.write(servoangle); //delay(100); } else if ((joyValy>545) && (joyValy <= 560)) { servo.write(90); //delay(100); } else if ((joyValy > 560) && (joyValy <= 1023)) { servoangle= map(joyValy, 560, 1023, 90, 180); servo.write(servoangle); //delay(100); } } 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]) : ""; } |
On the receiver side, things are pretty straightforward and easy, and I am sure you already know about how to use Servos if not then you can watch my videos on Servo motors. Anyways, you can see I have defined some variables and I am using the same addresses. But this time the localAddress is 0xFF and the destination address is 0xBB.
Inside the setup() function we activated the ESC and Servo which are connected to pins 3 and 9. These are PWM pins. Now, let’s go to the loop function.
Inside the loop() function we simply read the entire message and then using the getValue function we split the entire message using comma as the delimiter and store the corresponding values in variables to control the motor speed and servo angle. And then there are if conditions to control the speed of DC Motor and Angle of the Servo as per the Joystick movement. So, that’s all for now.
Watch Video Tutorial:
Hello!
Help please. How to add more channels, I need 4-channels
I set here:
Mymessage = Mymessage + button_1 +”,” + button_2 + “,” + pinX_1 + “,” + pinY_1;
Where edit else?
Thanks