In mid 2015 I decided to start dabbling with Arduino, and programming in C++. After doing a few introductory exercises (flashing an LED, using a button etc), I decided to tackle making a remote control, using a radio transmitter module called nRF24.
I was naive and didn’t expect it to be so hard! But after a few months I finally worked it out and achieved a working prototype. This was eventually used by one of my students, to animate a sculptural design they’d created for an exhibition.
Here’s two videos of the prototype in action. There’s a bit of a delay in the signal, as well as perhaps the starting-torque for the motors that means you have to turn the dial at least ~half way before the motor starts turning. But, it works!
Some Technical Details
Why Radio Control, RF?
Initially I tried using infrared (IR) remote control. I used an infrared sensor and an Apple remote. This was fine to start the motor running (from 0 rpm). But once the motor was spinning it generated so much interference that the IR signal from the remote couldn’t get through anymore (this was with a high rpm DC hobby motor that did have a capacitor connected across the motor terminals). Interference doesn’t seem to be an issue with radio control via the nRF24L01 module. The radio signal also has much better range — works through walls and upstairs/downstairs.
The connections are different for Arduino Uno and Mega boards because SPI pins are in different positions on these boards.
nRF24L01 module pin — UNO — MEGA — my wire colour
GND — GND — GND — brown (ground)
VCC — 3.3v — 3.3v — red (module doesn’t like 5v)
MOSI — 11–51 — blue (Master Out Slave In)
MISO — 12–50 — purple (Master In Slave Out)
SCK — 13–52 — green (Clock)
CSN — 10–53 — yellow (Slave Select)
CE — 9–40 — orange (Chip Enable)
IRQ not used
(Thanks to Matthew for this info)
A Metaphor for how nRF24L01+ wireless communication works
‘Pipes’ and ‘Addresses’ and radio syntax was pretty confusing. Understanding this could save a lot of time and frustration in coding.
Open a window (a ‘pipe’)
Imagine you and your friend live in high-rise buildings and your apartments face each other. You want to yell out to your friend without going over to their apartment. You want to transmit information for him to receive.
You have 6 windows in your apartment — you have to choose one window to use, to call out to them. Similarly the nRF24L01 has 6 windows (or ‘pipes’, numbered 0,1,2,3,4 and 5) through which it can communicate. For the nRF24L01 radio to send or receive information it needs to be told which ‘pipe’ it will use to send/receive.
Once you’ve chosen which window to use, you have to open the window before you can yell out to your friend. The transmitting radio needs to open a pipe in order to send (‘write’) information. The transmitting pipe is pipe 0, so pipe number doesn’t need to be specified. It just needs to be opened. Use openWritingPipe()
On the other side your friend has to choose one of their own windows and open it, in order to hear you. Also your friend has to be ready, ‘listening’, for you. Use openReadingPipe() and startListening(). The receiving radio waits (‘listens’) and collects (‘reads’) the information.
Identify what to notice, what to ignore (using ‘addresses’)
Lastly, there’s a lot of noise around in the city and your friend needs some way of knowing what to listen to and what to ignore. You help them by calling his name “John!” before you say what you want to say. For this to work both you and your friend need to know his name is John. Similarly we need to tell both the transmitter and receiver what name (‘address’) to use. Create the address by declaring a const uint64_t type variable.
const uint64_t pipeAddressA = 0xE8E8F0F0E1LL
Note about the address variable definition:
- the ‘uint64_t’ tells the processor the pipeAddressA variable is a 64bit integer
- ‘pipeAddressA’ is the name of the address variable (can name it anything and it doesn’t have to be the same in transmitter and receiver code).
- The hexadecimal number E8E8F0F0E1 is the address value.
- the prefix ‘0x’ (zero x) indicates the number is hexademical
- the suffix ‘LL’ tells the processor how to handle the hexadecimal number of this length (it’s quite long for Arduino apparently).
- The address can be any value whatsoever, as long as only one radio is writing to it and only one other radio is listening to it. Coordinate these pipe addresses amongst nodes on the network. (http://maniacbug.github.io/RF24/classRF24.html)
- Note: Pipe 0 is the same as the transmitting pipe, and pipes 1 to 5 should have addresses with the same first 32 bits. Only the last 8 bits can be different (eg ‘E1’ in the above address). So if we wanted to use multiple pipes we’d need multiple addresses. Another address we use in the sketch below is and E8E8F0F0D2 (same as first one except ‘E1’ is replaced by ‘D2’).
- Some addresses: 0xF0F0F0F0D2LL and 0xF0F0F0F0E1LL. 0xE8E8F0F0E1LL and 0xE8E8F0F0D2LL
So, the complete syntax for opening pipes is:
- for Transmitter: radio.openWritingPipe(address to use)
- for Receiver: radio.openReadingPipe(pipe number, address to use)
Download here for Arduino.
Other links I found useful
- Wireless Transceiver nRF24L01 (best and most dummy-friendly single explanation I found) — thanks Adrian
- Electrolytic Capacitors — thanks Sparkfun
- nRF24L01 transmit joystick position — thanks Charles W
- How To — https://arduino-info.wikispaces.com/Nrf24L01-2.4GHz-HowTo
- 1-Day Project (Youtube) — thanks Julian
- RF24 technical code stuff (it looks like a good resource but I struggled) — thanks oxygen
- Decimal Hex Converter
- nRF24L01 datasheet