Radio doorbell on an Arduino Uno
Two Unos, an nRF24L01 radio each and no wire between them: the button on one rings the lamp on the other, and the sender knows it arrived. The whole build, an Arduino Uno and 5 more parts, runs here in your browser on the firmware below; open it in the editor to change the wiring or the code and run it again.
Teaching? Assign it to your class in one click.
Advanced About 40 minutes to build and run. Runs in your browser. Free, and no account needed.
The parts retail for about $62. On Mokxi they're free.
Step by step, with the wiring and the common mistakes: Arduino nRF24L01: Wiring, Code and a Radio Doorbell
What you'll learn
- Wireless messages with an nRF24L01 radio
- SPI wiring: SCK, MISO, MOSI, CSN
- Matching address, channel and data rate
- Acknowledgments to confirm delivery
- Running two sketches on two boards
- Difficulty: Advanced
- Time: About 40 minutes
- Board: Arduino Uno R3
- Parts: 8
How it works
Two Unos each carry an nRF24L01 radio. When you press the button on the first, it sends a packet that says ding dong and a number. The second hears it, prints it and flashes its yellow LED for 300 ms.
Both radios must agree on the channel, data rate and the address, 00001. The receiver's radio acknowledges each packet by itself. That acknowledgment is what makes the sender's write return true, so the Serial Monitor can print delivered or no answer.
Parts list
8 parts, plus the jumper wires. They retail for about $62; on Mokxi they're free.
- 2 × Arduino Uno R3In Starter Kit, Claw Machine Kit, Vending Machine Kit.
- 1 × Full-size breadboard
- 2 × 2.4 GHz radio, nRF24L01+
- 1 × PushbuttonIn Starter Kit, Christmas Lights Kit, Logic Lab Kit.
- 1 × Resistor, 220 ΩIn Starter Kit, Claw Machine Kit, Halloween Prop Kit.
- 1 × LED, yellowIn Starter Kit, Halloween Prop Kit, Logic Lab Kit.
Wiring table
Every connection, pin by pin, read from the circuit itself. Each row joins the pin on the left to the pin on the right, by a jumper wire or a breadboard strip.
| Part and pin | Connects to |
|---|---|
| 2.4 GHz radio, nRF24L01+ (1) pin GND | Arduino Uno R3 (1) pin GND |
| 2.4 GHz radio, nRF24L01+ (1) pin SCK | Arduino Uno R3 (1) pin 13 |
| 2.4 GHz radio, nRF24L01+ (1) pin MISO | Arduino Uno R3 (1) pin 12 |
| 2.4 GHz radio, nRF24L01+ (1) pin MOSI | Arduino Uno R3 (1) pin 11 |
| 2.4 GHz radio, nRF24L01+ (1) pin CSN | Arduino Uno R3 (1) pin 8 |
| 2.4 GHz radio, nRF24L01+ (1) pin CE | Arduino Uno R3 (1) pin 7 |
| Pushbutton pin 1a | Arduino Uno R3 (1) pin 2 |
| 2.4 GHz radio, nRF24L01+ (1) pin VCC | Arduino Uno R3 (1) pin 3V3 |
| Arduino Uno R3 (2) pin GND | Arduino Uno R3 (1) pin GND |
| Pushbutton pin 2a | Arduino Uno R3 (1) pin GND |
| LED, yellow pin C | Arduino Uno R3 (1) pin GND |
| 2.4 GHz radio, nRF24L01+ (2) pin SCK | Arduino Uno R3 (2) pin 13 |
| 2.4 GHz radio, nRF24L01+ (2) pin MISO | Arduino Uno R3 (2) pin 12 |
| 2.4 GHz radio, nRF24L01+ (2) pin MOSI | Arduino Uno R3 (2) pin 11 |
| 2.4 GHz radio, nRF24L01+ (2) pin CSN | Arduino Uno R3 (2) pin 8 |
| 2.4 GHz radio, nRF24L01+ (2) pin CE | Arduino Uno R3 (2) pin 7 |
| Resistor, 220 Ω pin 1 | Arduino Uno R3 (2) pin 5 |
| 2.4 GHz radio, nRF24L01+ (2) pin VCC | Arduino Uno R3 (2) pin 3V3 |
| 2.4 GHz radio, nRF24L01+ (2) pin GND | Arduino Uno R3 (2) pin GND |
| LED, yellow pin A | Resistor, 220 Ω pin 2 |
The code
The firmware exactly as the editor opens it. Change a line there and press Run: it compiles in the browser.
// Radio doorbell, the sending end.
//
// Press the button and this Uno sends a packet over its nRF24L01 to the other
// Uno, which rings. radio.write() waits for the other radio's acknowledgement
// and says whether it came: take the receiving board's program away, or put
// its radio on another channel, and every press prints "no answer".
//
// Both radios have to agree on three things: the channel (76 is RF24's
// default), the data rate, and the address, "00001" here. Change any one of
// them on one side only and the packets go nowhere.
#include <SPI.h>
#include <nRF24L01.h>
#include <RF24.h>
RF24 radio(7, 8); // CE on 7, CSN on 8
const byte address[6] = "00001";
const int BUTTON = 2;
int presses = 0;
void setup()
{
Serial.begin(9600);
pinMode(BUTTON, INPUT_PULLUP);
radio.begin();
radio.openWritingPipe(address);
radio.setPALevel(RF24_PA_MIN);
radio.stopListening();
Serial.println("Press the button to ring.");
}
void loop()
{
if (digitalRead(BUTTON) == HIGH) {
return;
}
presses++;
char message[32] = "ding dong ";
message[10] = (char)('0' + presses % 10);
bool heard = radio.write(&message, sizeof(message));
Serial.print(message);
Serial.println(heard ? ": delivered" : ": no answer");
// Wait for the button to come back up, so one press is one ring.
while (digitalRead(BUTTON) == LOW) {
delay(10);
}
delay(50);
}
// Radio doorbell, the receiving end.
//
// This Uno listens on the same channel and address as the sender. Each packet
// that arrives is printed and flashes the LED. The radio acknowledges every
// packet on its own, in hardware, before the sketch has even looked at it:
// that acknowledgement is what makes the sender's radio.write() return true.
#include <SPI.h>
#include <nRF24L01.h>
#include <RF24.h>
RF24 radio(7, 8); // CE on 7, CSN on 8
const byte address[6] = "00001";
const int LED = 5;
void setup()
{
Serial.begin(9600);
pinMode(LED, OUTPUT);
radio.begin();
radio.openReadingPipe(0, address);
radio.setPALevel(RF24_PA_MIN);
radio.startListening();
Serial.println("Listening.");
}
void loop()
{
if (!radio.available()) {
return;
}
char message[32] = "";
radio.read(&message, sizeof(message));
Serial.print("heard: ");
Serial.println(message);
digitalWrite(LED, HIGH);
delay(300);
digitalWrite(LED, LOW);
}
Code walkthrough
The key functions of the sketch, and what each one does.
setup sketch.ino
In both sketches it starts the radio with the same address. The sender calls openWritingPipe and stopListening. The receiver calls openReadingPipe and startListening. Both use a low power level, RF24_PA_MIN.
void setup()
{
Serial.begin(9600);
pinMode(BUTTON, INPUT_PULLUP);
radio.begin();
radio.openWritingPipe(address);
radio.setPALevel(RF24_PA_MIN);
radio.stopListening();
Serial.println("Press the button to ring.");
}loop sketch.ino
The sender waits for the button, builds a message with a counting digit, and sends it with write. It prints whether it was delivered. The receiver checks available, reads the message, prints it and flashes the LED.
void loop()
{
if (digitalRead(BUTTON) == HIGH) {
return;
}
presses++;
char message[32] = "ding dong ";
message[10] = (char)('0' + presses % 10);
bool heard = radio.write(&message, sizeof(message));
Serial.print(message);
Serial.println(heard ? ": delivered" : ": no answer");
// Wait for the button to come back up, so one press is one ring.
while (digitalRead(BUTTON) == LOW) {
delay(10);
}
delay(50);
}Troubleshooting
The usual mistakes with this circuit, and the fix for each.
- The sender prints no answer This project
The other Uno may not be running, or its address or channel is different. Both sides must match. - The radio does not work at all This project
Power it from 3V3, not 5V. Check CE on pin 7, CSN on pin 8 and the SPI pins 11, 12 and 13. - The lamp never lights This project
The LED is on the second Uno's pin 5, through the 220 ohm resistor, with the cathode on ground. - The module resets, drops packets or never works on 5 V. 2.4 GHz radio
Power it from 3.3 V, never 5 V. Add a 10 uF capacitor across VCC and GND close to the module, since the Uno 3.3 V pin is weak. - Two radios do not hear each other. 2.4 GHz radio
Both need the same channel, address, data rate and payload size. One writes, the other listens. Check CE and CSN pins and the SPI wires (SCK, MOSI, MISO). - The input reads random HIGH and LOW when the button is not pressed. Pushbutton
A floating pin picks up noise. Add a 10 k pull-down or pull-up resistor, or use INPUT_PULLUP and wire the button between the pin and GND. Then pressed reads LOW. - The button seems always pressed, or never changes. Pushbutton
A 4-leg tactile switch has legs joined in pairs. Place it across the breadboard center gap so the pairs sit on different rows. Wired along one side, the legs are already connected. - The LED is dim, burns out, or the pin reads wrong because the resistor value is off. Resistor
Read the color bands again. A 220 ohm to 330 ohm resistor suits a typical LED on 5 V. 10k is a common pull-up or pull-down value. Check with a multimeter if unsure.
Change it and keep it
Open it in the editor, change the circuit or the code, and keep your version in a free account.