Wireless Joystick For Arduino Robot Car With nRF24L01

You might also like

Download as docx, pdf, or txt
Download as docx, pdf, or txt
You are on page 1of 23

Wireless Joystick for Arduino Robot Car with

nRF24L01+

Introduction
Arduinos are great for many reasons, one of them being their ability to wire up to
almost anything.  But there are times when you want to connect to your Arduino
without using any wires, and when you do get the desire to go wireless there are
several methods to choose from.

In this article (and in the corresponding video) I will examine one method of Arduino
wireless communications, the nRF24L01+ module.  This is an inexpensive module
that provides 2-way communications using the 2.4 GHz band. This band is free to use
for low power devices without a license and in some cases can be useful up to a
kilometer (although you should expect much shorter ranges without a special
antenna).
The nRF24L01+ is available in a number of different configurations, in this article I’ll
look at a couple of the more popular ones. We’ll hook them up and use a very
extensive library to facilitate communications between them.

1
After we get them working we will use them to build something fun – a wireless
joystick control for the Robot Car Base that we worked on earlier.
Before we begin let’s examine the nRF24L01+ and some of the factore you need to
take into account when designing wireless devices.
Wireless Communications
We have been sending information wirelessly since the late 1880’s, Thomas Edison
used a system of Electromagnetic Induction to send telegraph signals from a moving
train to a set of wires beside the track. Wireless devices are pretty well a part of our
lives and most work using one of the following methods:
 Signals are sent on beams of infrared light
 Signals are sent using radio waves
Radio waves have a number of advantages over beams of infrared light, the most
obvious is that radio waves can travel (to a degree) through walls and most other
obstructions.
Radio waves are by no means a perfect method of communication, they are subject to
interference from a number of sources and can be obstructed by metal or thick walls.
But they do serve their purpose in a number of low-speed data applications and are
thus perfect or Arduino and Raspberry Pi experimenters who want to build remote
controlled devices or who need to send data without wires.
Radio signals are based upon the concept of changing or “modulating” a “carrier
wave” that is transmitted wirelessly to a receiver. On the receiving end the carrier
wave is stripped off and the signal is “demodulated” to extract the original
information from it.

2
Since these carrier waves can interfere with one another the allotment of them is
strictly controlled, and every nation has a government department responsible for
regulating them. In the United States it’s the Federal Communications Commission
(FCC), in Canada it’s the Canadian Radio and Television Commission (CRTC) and in
the UK the radio waves are regulated by the Office of Communications (Ofcom).
Using radio carrier frequencies illegally can result in a very stiff fine and is also pretty
rude, so just don’t do it.
There are plenty of frequencies available to experimenters like us, one of the most
popular is the 2.4 GHz Band.
The 2.4 GHz Band
The 2.4 GHz Industrial, Scientific and Medical (ISM) band has been reserved for
unlicensed low-powered devices and this makes it perfect for building remotely
controlled Arduino devices. Of course it also makes it perfect for a number of other
devices in your house like wireless routers, cordless phones, Bluetooth gizmos and
other wireless dodadds.
The band in question runs from 2.400.0 GHz to 2.483.5 GHz and in order to make it
possible for multiple devices to coexist it’s broken down into channels. Although
there are 14 channels available not every one is legal in every area. Channels 1 to 11
are legal in most every part of the world so you’d be advised to contain your
experimenting to those channels.
As communicating on the 2.4 GHz band is a pretty common function there are a
number of modules made for exactly that purpose. The one we will be playing with
today is possibly the most popular, it’s available for a couple of dollars or less on
eBay and Amazon.
The nRF24L01+
The nRF24L01+ is the part number of a common chip used to construct 2.4 GHz
transmitters and receivers, or “transceivers”. This chip has been used to create some
simple and inexpensive modules that can be used to transmit and receive data using
the 2.4 GHz band.
There are a variety of modules available based upon the nRF24L01, I’m going to use
two very common ones in this article. If you have a different module it should work
fine, just be sure to observe the wiring and especially the power supply requirements.
<INSERT NRF24L01 2 Modules PICTURE>
The two modules I’m using are quite similar and are interchangeable, the difference
between them is that one of them has a built in Low Noise Amplifier (LNA) and an
external antenna connection.  I tend to prefer that one even though it’s a bit more
expensive as it can be used for reliable data communications over a pretty fair

3
distance. Unless you live in a castle it will probably be more than sufficient to cover
your entire house.
All of the experiments, including the wireless joystick, may be built with either
module (they share the same pinouts) but you’ll achieve better range with the model
with the external antenna.
nRF24L01 Connections
The nRF24L01 has an 8-pin connector that interfaces it with the outside world.  This
connector is common between both styles of nRF24L01 modules. Although the
nRF24L01 is powered by a 1.9 to 3.9 volt supply the logic pins are 5-volt tolerant so
they can be used directly with an Arduino or other 5-volt logic microcontroller.
SPI Bus
The nRF24L01 communicates using the Serial Peripheral Interface or SPI bus.  This
is a standard bus that is used by many microcontrollers and microcomputers including
the Arduino and the Raspberry Pi.
The SPI bus uses a concept of a Master and Slave, in most common applications the
microcontroller or microcomputer is the Master and the nRF24L01 is the Slave.
Unlike the I2C bus the number of slaves on the SPI bus is limited, on the Arduino
Uno you can use a maximum of two SPI slaves.
The SPI bus is a bidirectional bus, meaning that the master and slave can transmit and
receive simultaneously, however the library we will be using with the nRF24L01
doesn’t do that.
Each slave device needs to be selected by the master in order for it to communicate.
Only one slave can communicate at any given time. The nRF24L01 and other slave
devices have an Interrupt pin that can alert the master when they need to communicate
but the library we will be using today ignores that so in our applications we won’t be
connecting the interrupt pin to the Arduino.

4
Module Connections
The connections to the nRF24L01 module are as follows:

1. GND. This is the Ground Pin. It is usually marked by encasing the pin in a
square so it can be used as a reference for identifying the other pins.
2. VCC. The positive voltage. This can be anywhere from 1.9 to 3.9 volts. It os
NOT 5-volt tolerant!
3. CE. Chip Enable, an active-high pin. When selected the nRF24L01 will either
transmit or receive, depending upon which mode it is currently in.
4. CSN. Chip Select Not. Thi is an active-low pin, it is the pin that the SPI bus
uses to select the nRF24L01 slave.
5. SCK. The Clock pin, an external clock source provided by the SPI bus Master.
6. MOSI. Master Out Slave In. The input to the nRF24L01.
7. MISO. Master In Slave Out. The output from the nRF24L01.
8. IRQ. The Interrupt output pin.
The style of nRF24L01 that uses an external antenna also has an SMA connector for
attaching the antenna.
Power Supply Considerations
Because the nRF24L01 has a power supply range of 1.9 – 3.9 volts it can be battery
powered. It is also common to power the module with a 3.3 volt power supply.
When selecting a power supply it should be noted that the nRF24L01 can consume a
fair amount of current when transmitting at its highest power. Your power supply
should be capable of providing at least 300 mA of current.
Noise on the power supply can also cause problems with the nRF24L01. It is advised
to place a filter capacitor (100 microfarads is ideal) across the power supply lines as
physically close to the nRF24L01 module as possible to eliminate power supply
noise.

5
Another way to resolve the power supply issues, and the one I suggest you employ, is
to use an Adapter Module for your nRF24L01.
nRF24L01 Adapter Module
The nRF24L01 Adapter Module is a very inexpensive prototyping board that
simplifies working with the nRF24L01. I recommend you use one and I show it in all
the schematics included in this article.
The adapter module has an 8-pin female connector to allow you to plug in an
nRF24L01, it can accommodate either the module with the =integrated or external
antenna.  It also has a 6-pin male connector for the SPI and Interrupt connections and
a 2-pin connector for power input.
The adapter module has its own 3.3 volt voltage regulator and a set of filter
capacitors, so you can power it with a 5-volt power supply. Assuming your power
supply has the required current capability the adapter module will resolve all of the
power supply considerations mentioned above.
Since these modules are available for about a dollar a piece there is no real reason
why you shouldn’t use one. They can make the difference between success and failure
with your nRF24L01 design.
nRF24L01+ and Arduino
In our experiments we will be using two nRF24L01 modules with a couple of
Arduino Uno's. You could of course use another model of Arduino, if you do however
you may need to change the pinouts as different Arduino models use different pins for
the SPI bus.
I’ll describe the pinouts for both the Arduino Uno and the Arduino Mega 2560 here.
Going forward (in in the schematics) I’ll only be using an Arduino Uno so if you are
using a Mega 2560 you’ll need to substitute pin numbers accordingly. Our Robot Car
project was based around an Arduino Uno, this is the project that we will be
modifying to use the wireless joystick with.
Arduino Libraries and Connections
As with other radio modules there are a number of libraries available for the nRf24l)1.
Using a library will really simplify creating projects with these modules.  
The following libraries will all work with the nRF24L01+ modules:
 TMRh20 – This library has been around for several years.  It is great for
creating secure wireless communications devices. You can read more on the TMRh20
Project Blog and get the latest version on the TMRh20 GitHub repository fork for
Arduino devices.
 RF24 – This is an old standard and has been used in many nRF24L01
projects. It has been superseded by the RadioHead and TMRh20 libraries.
 Mirf Library – Based on the Tinkerer library. This is a much older library so
you won’t find too many projects based upon it anymore.

6
 RadioHead – This is a modern library with many advanced features, capable
of supporting many RF modules.
In the experiments we will be performing and for our wireless joystick project we will
be using the RadioHead library.
Please note that not all of the libraries listed above use the came connections to the
Arduino, and that the connections differ depending upon which type of Arduino you
are using.
RadioHead Library
RadioHead is a library written by Mike McCauley for the Airspayce company.  I used
it in a previous article, Using Inexpensive 433MHz Transmit and Receive Modules
with Arduino.
This is an advanced library which allows many methods of packet radio
communications between RF modules like the nRF24L01.  It contains many different
drivers for different RF modules, the driver for the nRF24L01 is the RH_NRF24
driver.
You can learn more about the RadioHead library and download the ZIP file that you
will need to install in in your Arduino IDE on the RadioHead website.  Look for the
link to the ZIP file near the top of the description on the page.
Once you download the ZIP file you will need to install it in your Arduino IDE.  This
is a very simple process:
1. Open the Arduino IDE.
2. Select Sketch from the top menu bar,
3. Select Include Library from the Sketch menu drop-down.
4. Select Add .ZIP Library from the Include Library sub-menu.
5. Use the dialog box to select the ZIP file you have downloaded.
6. The RadioHead library will be installed.
After you have the RadioHead library installed in your Arduino IDE you are ready to
begin the experiments with the nRF24L01.
Hooking up the Arduinos
The RadioHead library comes with a number of sample sketches that illustrate its use.
We will begin our experiments with a few of these sketches, then we’ll modify a
couple of them for our joystick project.
Here are the connections you will need to make for an Arduino Uno:

7
Note that you will need to make two of these circuits! We will refer to one of the
circuits as the Server and one as the Client. The wiring for both is identical.
Also note that I’m illustrating the schematic using a nRF24L01 Adapter Module
which has its own voltage regulator to supply the 3.3 volts to the nRF24L01.  You can
wire directly to the nRF24L01 itself if you wish but you’ll need to use the 3.3 volt
output from your Arduino and not the 5 volt output (which will likely destroy your
nRF24L01 module).
Note that many Arduino clones don’t have sufficient current on the 3.3 volt output for
the nRF24L01. Even if yours does it’s advisable to use a filter capacitor across the
power supply lines. But again your best bet is to simply use the nRF24L01 Adapter
Module and save yourself a lot of grief and frustration!
If you are using an Arduino Mega 2560 then the pinouts for the RadioHead library are
a bit different:
1. GND. This is still Ground of course so it goes to one of the Mega 2560 ground
pins.
2. VCC. Again this is still a power supply connection. See the notes above
regarding which voltage to use.
3. CE. This is the same as the Arduino Uno, it goes to pin 8 on the Mega 2560.
4. CSN. Connect this to output pin 53 on the Mega 2560.
5. SCK. Connect this to output pin 52 on the Mega 2560.
6. MOSI. This goes to pin 51 on the Mega 2560.
7. MISO. Finally this goes to pin 50 on the Mega 2560.
Note that the IRQ pin on the nRF24L01 is not used with either of the Arduino boards
as it’s ignored by the RadioHead library.
Once you have everything hooked up you are ready to run the first sketches.  
RadioHead Sample Sketch – Client & Server
The first sketches we will be running are the basic client and server examples
included with the RadioHead library. You can find and load them as follows:
8
1. Open the Arduino IDE (you may have already done this).
2. Open the File  menu from the top menu bar.
3. Select Examples. A sub-menu will be displayed.
4. Scroll down the Examples sub-menu to the section at the bottom
titled Examples from Custom Libraries.
5. Select RadioHead from the menu. Another sub-menu will appear beside this
one.
6. Select nrf24 from the RadioHead sub-menu.
7. A list of example sketches for the RadioHead RH_NRF24 Driver will be
displayed.
There are two sketches we need to load, one on each Arduino. If you don’t have the
luxury of having two computers then you can do these individually using one
computer.
The sketches we need are as follows:
 On the Server Arduino load the nrf24_server sketch.
 On the Client computer load the nrf24_client sketch.
The sketches are very well commented so I’ll just go over some of the essential
elements of the here.
Each sketch includes the RadioHead RH_NRF24 library as well as the Arduino SPI
library. They both then declare an instance of the radio driver. If you want to change
the wiring or use a different type of Arduino you can add optional parameters when
declaring the driver.
After that they both move into the Setup routine, which begins by setting up the serial
monitor and initializing the driver.  Afterwards the setChannel method is called to
change the radio channel from the default channel (which is channel 2) to channel 1.
You can experiment with different channels if you find any 2.4 GHz devices you have
(i.e wireless mice) interfere with the experiment.
You can also add parameters to change the data rate and transmit power of the
module. A slower data rate will result in a longer range of operation. Just be sure to
keep the data rate and channel the same between the server and client.
We then proceed to the loop.
On the server side the loop starts by looking for a message from the client.  If it is
received it is placed into a buffer and then printed to the serial monitor.  After that a
reply “And hello back to you” is placed into an array and is sent to the client.
On the client side the loop begins in the opposite fashion. A message “Hello World”
is placed into an array and is sent to the server.  We then wait to receive a message
from the client (the “And hello back to you” message). If/when we receive the
message it gets printed out to the serial monitor.
If you are lucky enough to have two computers you can open both serial monitor and
observe the interaction between the server and client. If you only have one computer

9
then I suggest you power one Arduino with a battery or USB power supply while you
use your computer to power and monitor the other one.
While this is a very basic sketch it does illustrate how the RadioHead library makes it
easy to work with the nRF24L01. And it also has a practical use – you can use it (with
one Arduino battery powered) to determine the range you can achieve with your two
modules. If you have both the modules with integrated antennas and the ones with
external antennas you’ll soon see how vastly superior the external antenna modules
really are.
RadioHead Sample Sketch – Reliable Datagram
The previous sketches work well and for many applications they are all you’ll ever
need. But if you have a situation where you’re transmitting data that simply must be
received without errors then you’ll want to look at another method.
This can be a requirement when you are breaking up a large file into several small
bits. The transmitting end needs to make sure that the receiving end has received
every bit intact. If it isn’t then the data needs to be resent.
Data transfer on the Internet works using this principle.
The RadioHead Reliable Datagram method of exchanging data also uses this method
of verifying data integrity. It doesn’t require any special coding on either end as the
Reliable Datagram library does it all for you in the background.  Let’s look at it now.
For this experiment you won’t need to make any wiring changes as the hookup is
identical to the previous experiment.
Go back into the RadioHead Library Example sketches for nrf24 and select the
following two sketches:
 On the Server Arduino load the nrf24_reliable_datagram_server sketch.
 On the Client Arduino load the nrf24_reliable_datagram_client sketch.
Again these sketches are well commented and they also have many similarities to the
sketches we just looked at, so I’ll mostly discuss the differences here.
Aside from the two libraries loaded in the previous sketches these sketches also load
the RadioHead RHReliableDatagram library.
Afterwards two constants are defined, a CLIENT_ADDRESS and
SERVER_ADDRESS. These addresses are not radio channels, instead they are
addresses used within the datagram packets exchanged between the server and client.
After creating an instance of the radio driver each sketch set up a datagram manager
using one of the addresses defined above (this is where the server and client differ).
In the setup routine the serial monitor is setup and the datagram manager is initialized.
Outside of the setup routine and before the loop an array is defined with the data to be
sent and a buffer is defined.

10
In the loop the operation is very similar to the previous sketches. The server waits for
a message to be received from the client, prints it to the serial monitor and then sends
a message of its own. The client does the same in reverse.
One thing you will notice in the serial monitor is that both sides print out the address
contained in the received packet.
Try out the demo and see the results. At far range you’ll occasionally notice a slight
delay in receiving data, this will occur when packets drop off and need to be resent.
As with the previous sketches this one works very well. We will modify this sketch
now to send some joystick data and then use it to build our wireless joystick.
Joystick Demo
The two previous sets of sketches illustrated how to exchange text data like “Hello
World” and “And hello back to you”, which in itself can be useful. But in many
situations you’ll want to exchange numerical data wirelessly between two Arduinos.
An example of this would be sending data from a remote sensor to a base station.
In our next experiment we will send data from a joystick to the remote receiver. Each
axis of the joystick will be sent as as single byte along with a “dummy byte” whose
use I will explain in due time. Depending upon the position of the joystick the values
of the axis data wil range from 0 to 255.
You can of course use this sketch to send other sensor data, it doesn’t need to be from
a joystick. Your imagination is the only limitation here.
Each sketch is a modified version of the Reliable Datagram sketches we saw earlier,
you’ll recognize a lot of the code.  No sense in reinventing the wheel!
Before we get started you’ll need to take one of the Arduinos and add a joystick to it.
The hookup is shown below:

11
As you can see the joystick hookup is very simple. If you don’t have a joystick just
use two potentiometers as that’s really all that an analog joystick is – a pot for the x-
axis and another one for the y-axis.  Each control is connected to one of the Arduino’s
analog inputs.
On the other end just leave the Arduino and nRF24L01 wired as they are, the only
thing you’ll need to change is the sketch.
Here is the sketch for the joystick side of things. Note that it is based upon the
RadioHead Reliable Datagram Client sketch, that was just an arbitrary choice on my
part as I could have just as easily used the server sketch.
Joystick Demo Code
1 /*
2   nRF24L01+ Joystick Transmitter
3   nrf24l01-joy-xmit-demo.ino
4   nRF24L01+ Transmitter with Joystick
5   Use with Joystick Receiver Demo
6   DroneBot Workshop 2018
7   https://dronebotworkshop.com
8 */
9  
10 // Include RadioHead ReliableDatagram & NRF24 Libraries
11 #include <RHReliableDatagram.h>
12 #include <RH_NRF24.h>
13  
14 // Include dependant SPI Library
15 #include <SPI.h>
16  
17 // Define Joystick Connections
18 #define JoyStick_X_PIN     A0
19 #define JoyStick_Y_PIN     A1
20  
21 // Define addresses for radio channels
22 #define CLIENT_ADDRESS 1  
23 #define SERVER_ADDRESS 2
24  
25 // Create an instance of the radio driver
26 RH_NRF24 RadioDriver;
27  
28 // Sets the radio driver to NRF24 and the client address to 1
29 RHReliableDatagram RadioManager(RadioDriver, CLIENT_ADDRESS);
30  
31 // Declare unsigned 8-bit joystick array
32 uint8_t joystick[3];
33  
34 // Define the Message Buffer
35 uint8_t buf[RH_NRF24_MAX_MESSAGE_LEN];
36  
37 void setup()
38 {
39   // Setup Serial Monitor
40   Serial.begin(9600);
41  
42   // Initialize RadioManager with defaults - 2.402 GHz (channel 2), 2Mbps, 0dBm
43   if (!RadioManager.init())
44     Serial.println("init failed");
45 }
46  
47 void loop()
48 {
49   // Print to Serial Monitor
50   Serial.println("Reading joystick values ");

12
51   
52   // Read Joystick values and map to values of 0 - 255
53   joystick[0] = map(analogRead(JoyStick_X_PIN), 0, 1023, 0, 255);
54   joystick[1] = map(analogRead(JoyStick_Y_PIN), 0, 1023, 0, 255);
55   joystick[2] = 100;
56  
57   //Display the joystick values in the serial monitor.
58   Serial.println("-----------");
59   Serial.print("x:");
60   Serial.println(joystick[0]);
61   Serial.print("y:");
62   Serial.println(joystick[1]);
63  
64   Serial.println("Sending Joystick data to nrf24_reliable_datagram_server");
65   
66   //Send a message containing Joystick data to manager_server
67   if (RadioManager.sendtoWait(joystick, sizeof(joystick), SERVER_ADDRESS))
68   {
69     // Now wait for a reply from the server
70     uint8_t len = sizeof(buf);
71     uint8_t from;
72     if (RadioManager.recvfromAckTimeout(buf, &len, 2000, &from))
73     {
74       Serial.print("got reply from : 0x");
75       Serial.print(from, HEX);
76       Serial.print(": ");
77       Serial.println((char*)buf);
78     }
79     else
80     {
81       Serial.println("No reply, is nrf24_reliable_datagram_server running?");
82     }
83   }
84   else
85     Serial.println("sendtoWait failed");
86  
87   delay(100);  // Wait a bit before next transmission
88 }

The sketch starts in the same way as the RadioHead Reliable Datagram sketches, it
loads the required libraries. We also define the inputs used by the joystick, as well as
client and server addresses for the reliable datagram packets.
We then define an 8-bit unsigned integer array called “joystick” with three elements:
 joystick[0] is the x-axis value.
 joystick[1] is the y-axis value.
 joystick[2] is the “dummy” value.
The only reason for sending the “dummy” value is to have a third byte as our final
wireless joystick sketch will use this byte to indicate the motor direction. By defining
it in this demonstration sketch we can use the receiver we build here to troubleshoot
the final product if necessary.  
I assigned a value of 100 to the dummy value, you can assign pretty well any value
between 0 and 255. It’s just there to test data integrity right now.
The setup routine is identical to the RadioHead Reliable Datagram example.

13
In the loop we start the serial monitor as we will use it to monitor joystick values.  We
then proceed to get those joystick values using an Arduino analogRead function on
each of the joystick analog inputs.
As the Arduino’s analog to digital converter is a 10-bit converter we will get a value
of 0 to 1023 back from each joystick. We use the Arduino map command to convert
this into a range of 0 to 255. Each of the hvalues is assigned to its respective element
in the  joystick array.
The remainder of the sketch is pretty well identical to the Reliable Datagram sketch.
The array is sent to the server end and we wait to see if we get a reply. Then we do it
all over again.
The other end (i.e. the “joystick receiver”) is even simpler.  Here is the sketch:
Joystick Demo Receiver Code

1 /*
2   nRF24L01+ Joystick Receiver Demo
3   nrf24l01-joy-rcv-demo.ino
4   nRF24L01+ Receiver with Joystick Decode
5   Use with Joystick Transmitter Demo
6   DroneBot Workshop 2018
7   https://dronebotworkshop.com
8 */
9  
10 // Include RadioHead ReliableDatagram & NRF24 Libraries
11 #include <RHReliableDatagram.h>
12 #include <RH_NRF24.h>
13  
14 // Include dependant SPI Library
15 #include <SPI.h>
16  
17 // Define addresses for radio channels
18 #define CLIENT_ADDRESS 1  
19 #define SERVER_ADDRESS 2
20  
21 // Create an instance of the radio driver
22 RH_NRF24 RadioDriver;
23  
24 // Sets the radio driver to NRF24 and the server address to 2
25 RHReliableDatagram RadioManager(RadioDriver, SERVER_ADDRESS);
26  
27 // Define a message to return if values received
28 uint8_t ReturnMessage[] = "JoyStick Data Received";
29  
30 // Define the Message Buffer
31 uint8_t buf[RH_NRF24_MAX_MESSAGE_LEN];
32  
33 void setup()
34 {
35   // Setup Serial Monitor
36   Serial.begin(9600);
37   
38   // Initialize RadioManager with defaults - 2.402 GHz (channel 2), 2Mbps, 0dBm
39   if (!RadioManager.init())
40     Serial.println("init failed");
41 }
42  
43 void loop()
44 {

14
45   if (RadioManager.available())
46   {
47 // Wait for a message addressed to us from the client
48     uint8_t len = sizeof(buf);
49     uint8_t from;
50     if (RadioManager.recvfromAck(buf, &len, &from))
51 //Serial Print the values of joystick
52     {
53       Serial.print("got request from : 0x");
54       Serial.print(from, HEX);
55       Serial.print(": X = ");
56       Serial.print(buf[0]);
57       Serial.print(" Y = ");
58       Serial.print(buf[1]);
59       Serial.print(" Z = ");
60       Serial.println(buf[2]);
61  
62       // Send a reply back to the originator client, check for error
63       if (!RadioManager.sendtoWait(ReturnMessage, sizeof(ReturnMessage), from))
64         Serial.println("sendtoWait failed");
65     }
66   }              
67 }

The only difference between this sketch and the RadioHead Reliable Datagram server
sketch is that we are displaying numerical values in the received data array instead of
text.  We label these as X, Y and Z:
 X is the x-axis reading.
 Y is the y-axis reading.
 Z is the “dummy value”.
After loading bnoth sketches run them, with at least the receiver connected to a
computer so you can observe the serial monitor. You should observe the values
change as you move the joystick.

Robot Car Remote


So now we can see how we can send joystick values. It’s time to put it all together
and create our Robot Car remote joystick.
On the joystick side you have already done the wiring, all you’ll need to change is the
sketch. On the other end though you’ll need a robot car!
If you followed the instructions in the previous article “Build a Robot Car with Speed
Sensors” then you already have a robot car. If not then go and see that article for
instructions on putting together a robot car using an inexpensive kit.
As this project doesn’t use the speed sensors you can ignore that part. If you already
have built the robot car then just leave it as it is, you don’t need to remove the speed
sensors.

15
If you did build the original car there are some changes you will need to make,
specifically in how the L298N H-Bridge motor controller is hooked up. The original
design uses some of the pins required by the nRF24L01 so it needs to be required.
Here is the new Robot Car schematic:

The connections to the nRF24L01 module are exactly the same as they have been in
our other experiments, no surprise there. You can use any type of nRF24L01 module
but I strongly recommend using the model with the external antenna for improved
performance, at least on the car side.
Using an Analog Pin as a Digital Pin
On the motor side you’ll notice that the pins for Motor A have been moved when
compared to the original design. One of them might surprise you – the L289N H-
Bridge IN1 pin is connected to the Arduino analog pin A0. Why an analog pin?
If you look at the specs of the ATMega328, which is the heart of the Arduino Uno,
you’ll understand why I used A0. It turns out the the “analog” pins on an Arduino can
also function quite well as digital I/O pins.
I needed an extra pin for my L298N motor controller and I had a few choices:
 I didn’t  want to use pins 2 or 3 as the Robot Car uses these for the speed
sensors. Even though the sensors are not part of this design I wanted to keep them
free.
 Pins 1 and 2 might look promising but they are also special pins – they are
used as the RX and TX lines for the serial interface. I wanted to keep them free, also
due to internal reasons they won’;t work very well in this application anyway.
 I chose to use pin A0. Its and analog pin but it’s also digital I/O pin #14 on the
Arduino Uno.
As long as the pin is defined as an output in code it will work fine.
Once you have the car wired up all that remains is to load the code. We will look at
the joystick code first.

16
Joystick Transmitter Sketch
As you might expect the remote joystick sketch is very similar to the joystick demo
sketch we looked at earlier. Her it is in all its glory:
Wireless Joystick Transmitter

1 /*
2   nRF24L01+ Joystick Transmitter
3   nrf24l01-joy-xmit-car.ino
4   nRF24L01+ Transmitter with Joystick for Robot Car
5   Use with Joystick Receiver for Robot Car
6   DroneBot Workshop 2018
7   https://dronebotworkshop.com
8 */
9  
10 // Include RadioHead ReliableDatagram & NRF24 Libraries
11 #include <RHReliableDatagram.h>
12 #include <RH_NRF24.h>
13  
14 // Include dependant SPI Library
15 #include <SPI.h>
16  
17 // Define Joystick Connections
18 #define joyVert    A0
19 #define joyHorz    A1
20  
21 // Define Joystick Values - Start at 512 (middle position)
22 int joyposVert = 512;
23 int joyposHorz = 512;
24  
25 // Define addresses for radio channels
26 #define CLIENT_ADDRESS 1  
27 #define SERVER_ADDRESS 2
28  
29 // Create an instance of the radio driver
30 RH_NRF24 RadioDriver;
31  
32 // Sets the radio driver to NRF24 and the client address to 1
33 RHReliableDatagram RadioManager(RadioDriver, CLIENT_ADDRESS);
34  
35 // Declare unsigned 8-bit motorcontrol array
36 // 2 Bytes for motor speeds plus 1 byte for direction control
37 uint8_t motorcontrol[3];
38  
39 // Define the Message Buffer
40 uint8_t buf[RH_NRF24_MAX_MESSAGE_LEN];
41  
42 void setup()
43 {
44   // Setup Serial Monitor
45   Serial.begin(9600);
46
47   // Initialize RadioManager with defaults - 2.402 GHz (channel 2), 2Mbps, 0dBm
48   if (!RadioManager.init())
49     Serial.println("init failed");
50   
51   // Set initial motor direction as forward
52   motorcontrol[2] = 0;
53  
54 }
55  
56 void loop()
57 {
58   // Print to Serial Monitor

17
59   Serial.println("Reading motorcontrol values ");
60   
61   // Read the Joystick X and Y positions
62   joyposVert = analogRead(joyVert);
63   joyposHorz = analogRead(joyHorz);
64  
65   // Determine if this is a forward or backward motion
66   // Do this by reading the Verticle Value
67   // Apply results to MotorSpeed and to Direction
68  
69   if (joyposVert < 460)
70   {
71     // This is Backward
72     // Set Motors backward
73     motorcontrol[2] = 1;
74  
75     //Determine Motor Speeds
76     // As we are going backwards we need to reverse readings
77     motorcontrol[0] = map(joyposVert, 460, 0, 0, 255);
78     motorcontrol[1] = map(joyposVert, 460, 0, 0, 255);
79  
80   }
81   else if (joyposVert > 564)
82   {
83     // This is Forward
84     // Set Motors forward
85     motorcontrol[2] = 0;
86  
87     //Determine Motor Speeds
88     motorcontrol[0] = map(joyposVert, 564, 1023, 0, 255);
89     motorcontrol[1] = map(joyposVert, 564, 1023, 0, 255);
90  
91   }
92   else
93   {
94     // This is Stopped
95     motorcontrol[0] = 0;
96     motorcontrol[1] = 0;
97     motorcontrol[2] = 0;
98  
99   }
100   
101   // Now do the steering
102   // The Horizontal position will "weigh" the motor speed
103   // Values for each motor
104  
105   if (joyposHorz < 460)
106   {
107     // Move Left
108     // As we are going left we need to reverse readings
109     // Map the number to a value of 255 maximum
110     joyposHorz = map(joyposHorz, 460, 0, 0, 255);
111  
112     motorcontrol[0] = motorcontrol[0] - joyposHorz;
113     motorcontrol[1] = motorcontrol[1] + joyposHorz;
114  
115     // Don't exceed range of 0-255 for motor speeds
116     if (motorcontrol[0] < 0)motorcontrol[0] = 0;
117     if (motorcontrol[1] > 255)motorcontrol[1] = 255;
118  
119   }
120   else if (joyposHorz > 564)
121   {
122     // Move Right
123     // Map the number to a value of 255 maximum
124     joyposHorz = map(joyposHorz, 564, 1023, 0, 255);
125   

18
126     motorcontrol[0] = motorcontrol[0] + joyposHorz;
127     motorcontrol[1] = motorcontrol[1] - joyposHorz;
128  
129     // Don't exceed range of 0-255 for motor speeds
130     if (motorcontrol[0] > 255)motorcontrol[0] = 255;
131     if (motorcontrol[1] < 0)motorcontrol[1] = 0;      
132  
133   }
134  
135   // Adjust to prevent "buzzing" at very low speed
136   if (motorcontrol[0] < 8)motorcontrol[0] = 0;
137   if (motorcontrol[1] < 8)motorcontrol[1] = 0;
138  
139   //Display the Motor Control values in the serial monitor.
140   Serial.print("Motor A: ");
141   Serial.print(motorcontrol[0]);
142   Serial.print(" - Motor B: ");
143   Serial.print(motorcontrol[1]);
144   Serial.print(" - Direction: ");
145   Serial.println(motorcontrol[2]);
146   
147   //Send a message containing Motor Control data to manager_server
148   if (RadioManager.sendtoWait(motorcontrol, sizeof(motorcontrol), SERVER_ADDRESS))
149   {
150     // Now wait for a reply from the server
151     uint8_t len = sizeof(buf);
152     uint8_t from;
153     if (RadioManager.recvfromAckTimeout(buf, &len, 2000, &from))
154     {
155       Serial.print("got reply from : 0x");
156       Serial.print(from, HEX);
157       Serial.print(": ");
158       Serial.println((char*)buf);
159     }
160     else
161     {
162       Serial.println("No reply, is nrf24_reliable_datagram_server running?");
163     }
164   }
165   else
166     Serial.println("sendtoWait failed");
167  
168   delay(100);  // Wait a bit before next transmission
169 }

Those of you who went through the “Controlling DC Motors with the L298N Dual H-
Bridge and an Arduino” article may recognize some of the code here as it’s taken
from the sketch I used to demo a joystick with a robot car (that one used a wire).
We start by defining our libraries as we did before. Then we define the analog pins
used for the joystick inputs as well as a couple of variables that hold values of those
inputs.
The real difference in this sketch is what we do with those values. We want the
joystick to operate as follows:
 If we push the joystick forward the car should go forward. The further we
push it the faster it should go.
 If we pull the joystick back towards us the car should run in reverse. The
further we pull it back the faster it should go.

19
 If we move it to the left the car should steer left.
 If we move it to the right the car should steer right.
 If we leave the joystick in the center position then the car should not move at
all.
I established the “middle” of both the horizontal and vertical travel of the joystick to
have values between 460 and 564. The exact middle would of course be 511.5 but we
are using integers and we also have to consider that two dollar joysticks are not the
most precision instruments available.
If you follow the sketch through you should see the logic in this. We determine if the
vertical joystick is above 564 and if it is we are going to drive forward. So we set
the motorcontrol[2] variable to a value of 0, which in this sketch means “forward”.
If our vertical joystick is below 460 then we set motorcontrol[2] to 1 to indicate we
want to go backwards.
We then use the Arduino map function to map the joystick values to a value in the 0
to 255 range for the motor speeds, which are assigned to
the motorcontrol[0] and motorcontrol[1]  variables.
The horizontal control functions in the same manner, except it applies an offset to the
speed values to make one motor spin faster than the other one.
We then send the three variables in the array off to the transmitter, just like we did in
the joystick demo sketch.
Joystick Receiver Sketch
The receiver sketch, the one that runs on the car itself, is actually pretty simple. Here
it is:
Robot Car
1 /*
2   nRF24L01+ Joystick Receiver for Robot Car
3   nrf24l01-joy-rcv-car.ino
4   nRF24L01+ Receiver and L298N driver for Robot Car
5   Use with Joystick Transmitter for Robot Car
6   DroneBot Workshop 2018
7   https://dronebotworkshop.com
8 */
9  
10 // Include RadioHead ReliableDatagram & NRF24 Libraries
11 #include <RHReliableDatagram.h>
12 #include <RH_NRF24.h>
13  
14 // Include dependant SPI Library
15 #include <SPI.h>
16  
17 // Define addresses for radio channels
18 #define CLIENT_ADDRESS 1  
19 #define SERVER_ADDRESS 2
20  
21 // Motor A Connections
22 int enA = 9;
23 int in1 = 14;
24 int in2 = 4;
25  
26 // Motor B Connections

20
27 int enB = 5;
28 int in3 = 7;
29 int in4 = 6;
30  
31 // Create an instance of the radio driver
32 RH_NRF24 RadioDriver;
33  
34 // Sets the radio driver to NRF24 and the server address to 2
35 RHReliableDatagram RadioManager(RadioDriver, SERVER_ADDRESS);
36  
37 // Define a message to return if values received
38 uint8_t ReturnMessage[] = "JoyStick Data Received";
39  
40 // Define the Message Buffer
41 uint8_t buf[RH_NRF24_MAX_MESSAGE_LEN];
42  
43 void setup()
44 {
45   // Setup Serial Monitor
46   Serial.begin(9600);
47   
48   // Set all the motor control pins to outputs
49   pinMode(enA, OUTPUT);
50   pinMode(enB, OUTPUT);
51   pinMode(in1, OUTPUT);
52   pinMode(in2, OUTPUT);
53   pinMode(in3, OUTPUT);
54   pinMode(in4, OUTPUT);
55   
56   // Initialize RadioManager with defaults - 2.402 GHz (channel 2), 2Mbps, 0dBm
57   if (!RadioManager.init())
58     Serial.println("init failed");
59 }
60  
61 void loop()
62 {
63   if (RadioManager.available())
64   {
65 // Wait for a message addressed to us from the client
66     uint8_t len = sizeof(buf);
67     uint8_t from;
68     if (RadioManager.recvfromAck(buf, &len, &from))
69  
70     {
71  
72       //Serial Print the values of joystick
73       //Serial.print("got request from : 0x");
74       //Serial.print(from, HEX);
75       //Serial.print(": MotorA = ");
76       //Serial.print(buf[0]);
77       //Serial.print(" MotorB = ");
78       //Serial.print(buf[1]);
79       //Serial.print(" Dir = ");
80       //Serial.println(buf[2]);
81       
82       // Set Motor Direction
83       if (buf[2] == 1)
84       {
85     // Motors are backwards
86     digitalWrite(in1, LOW);
87     digitalWrite(in2, HIGH);
88     digitalWrite(in3, LOW);
89     digitalWrite(in4, HIGH);
90     }else{
91     // Motors are forwards
92     digitalWrite(in1, HIGH);
93     digitalWrite(in2, LOW);

21
94     digitalWrite(in3, HIGH);
95     digitalWrite(in4, LOW);
96      }
97
98       
99       // Drive Motors
100       analogWrite(enA, buf[1]);
101       analogWrite(enB, buf[0]);
102     
103       // Send a reply back to the originator client, check for error
104       if (!RadioManager.sendtoWait(ReturnMessage, sizeof(ReturnMessage), from))
105         Serial.println("sendtoWait failed");
106     }
107   }              
108 }

Essentially this is the same receiver sketch we have used in the last demo with the
addition of variables to define the motor connections. Note that we use Arduino pin
“14” for variable in1, this is of course the analog A0 pin. You could actually
substitute an “A0” here if you wish and it will work just fine.
In the setup we define the motor controller pins as outputs. Note that the pins used for
the two L298N enable lines need to be capable of PWM as that’s how the motor speed
is regulated.
You may notice I have remarked out all of the serial monitor statements. They are not
really necessary and just get in the way when the car is operating but you can
“unremark” if you need to troubleshoot your car.
The buffer values correspond to the motor control values we used in the transmitter.
So  buf[2]  contolas the motor direction and buf[0] and buf[1] handle the motor
speeds.
The motors are controlled with PWM using the Arduino analogWrite function.
Load the sketches and fire everything up. You should now have a remote controlled
robot car!
Conclusion
As you can see the nRF24L01 can be used to create some very useful wireless
projects with very little code, thanks to the RadioHead library.  If building robot cars
isn’t your thing you can still find a lot of use for this powerful combination, and I’ll
be featuring some more projects using these devices very soon.
Until then enjoy your robot car and happy motoring!
 

Resources
Sketches for the Article.  All of the sketches used in this article in a very handy
ZIP file, just for you!

22
RadioHead Library. You will need the RadioHead Library for Arduino to run the
experiments in this article.
nRF24L01 How-To. This is an excellent resource from the Arduino Info blog
that tells you a LOT about the nRF24L01.

23

You might also like