Wednesday, March 21, 2018

Demo 41: ESP32 connects with nRF24L01 2.4 GHz wireless chip

1. Introduction
Figure: Module nRF24L01
The nRF24L01 2.4 GHz wireless chip from Nordic Semiconductor. It has:
- SPI interface, hardware link layer, multiple pipelines, ...
- The chip is very cheap.
Comparing to Wifi modules such as ESP8266, ESP32:
- It has less power consumption.
- It is cheaper (but need a MCU to control).
- It has lower data rate.
If your Wifi infrastructure is good, you can consider using Wifi modules for your application.
If power consumption is not your issue, you can consider using Wifi modules for your application.
If we use nrf24l01 and need to send data to cloud via Internet, consider using ESP or Raspberry as a Gateway.
Some main characteristics:
1.1 NRF24L01 Hardware Interface
- 8 pins Vcc, GND, IRQ, CE, SPI pins (CSN (chip select not), SCK, MISO, and MOSI).
- IO pins are 5V-tolerant and operating range of 1.9 to 3.6V for Vcc.
- CSN, SCK, MISO, and MOSI for data transmission and reception.
- The CSN pin is active-low, and is normally kept high. When this pin goes low, the 24L01 begins listening on its SPI port for data and processes it accordingly.
- CE is used to control data transmission and reception when in TX and RX modes.
- IRQ is the interrupt pin, and is active-low. There are three internal interrupts that can cause this pin to go low when they are active. Each of these bits can be masked out such that when the bit’s respective interrupt becomes active, the status of the IRQ pin is not changed.
1.2 Interfacing the nRF24L01 via SPI
- The SPI interface allows you to read/write registers, transmit data, receive data from and to the 24L01. For start step we use SPI at 2 Mbps. The high SPI data rates are used if you needed huge on-air data rates.
1.3 SPI Instruction Set Summary
- In order to send data to or receive data from the SPI port on the nRF24L01:
  + The CSN pin on the 24L01 must be high then bring the CSN pin low to receive SPI data. Note: this pin will stay low throughout the entire transaction.
  + You will transmit the command byte of the instruction you wish to send. If you are receiving data bytes for this instruction, you must then send one byte to the 24L01 for every one byte that you wish to get out of the 24L01. If you are just sending the 24L01 data, you simply send your data bytes and generally don’t worry about what it sends back to you.
  + Once you have transmitted and/or read all of the bytes that you need, you bring CSN back high.
  + When you send any command byte, the 24L01 always returns to you the STATUS register.
1.4 FIFO Info
- There are FIFOs for both TX and RX modes.
- Both of the FIFOs  will hold the three newest packets that have been put into them. If you receive three packets in RX mode and you don’t read the payloads, the first (oldest) packet received is pushed out by the newest packet received. The same goes for the TX payload – if you load three packets and don’t transmit them (by executing the aforementioned CE toggle), then the fourth packet will push out the first packet you loaded.
1.5 Data Packet Format
- The transceiver protocol has 2 modes: Shockburst and Enhanced Shockburst
- The message format for both Shockburst and Enhanced Shockburst modes are different.
- In both modes, the preamble is sent first (1 byte), is used to allow the receiver to know that what it is hearing is the beginning of a packet and not just on-air noise.
- The next bytes sent are the address bytes. This is set by the user, and is between three and five bytes long.
- The next bytes sent is different between the two modes. In Enhanced Shockburst only, a flag word of nine bits is sent to indicate the message status concerning re-transmissions. Only two bits are currently used (a count of resent packets), and the other seven are reserved for future use.
- The last half of the packet that is sent is the same in both modes. The first of the fields to be sent in both modes is the payload data. The length of the payload is also set by the user (1 to 32 bytes long). The final part of the packet to be sent is the CRC, which is user-settable (0, 1, or 2 byte(s)).
Let 's make 2 demos:
- Demo 1: ESP32 Nano will send "hello" message to Arduino.
- Demo 2: Arduino Nano will send "hello" message to ESP32.
If you have 2 ESP32 you can replace Arduino Nano with ESP32.
2. Hardware
You need 2 Arduino/ESP8266/ESP32/Raspberry boards for testing: 1 board will send/receive message and 1 board will receive/send message which was sent by first board.
I used Arduino for testing, just do wiring like below:
Arduino_D8      x   NRF24_CSN
Arduino_D7      x   NRF24_CE
Arduino_D13    x   NRF24_SCK
Arduino_D11    x   NRF24_MOSI
Arduino_D12    x   NRF24_MISO

Arduino_3.3      x   NRF24_Vcc
Arduino_GND  x   NRF24_GND
For ESP32, I modified the library RF24 so that the software can use SPI software instead of using SPI hardware. Do wiring like below:
NRF24_CE      x   ESP32_IO12
NRF24_CSN    x   ESP32_IO14
NRF24_SCK    x   ESP32_IO26
NRF24_MISO  x   ESP32_IO25
NRF24_MOSI  x   ESP32_IO27
NRF24_Vcc      x   ESP32_3.3V
NRF24_GND    x   ESP32_GND  
3. Software
For ESP32, I modified the library RF24 so that the software can use SPI software insted of using SPI hardware. The new API has new form:
RF24(uint16_t _cepin, uint16_t _cspin, uint16_t sck, uint16_t miso, uint16_t mosi)
and to create an instance: RF24 radio(12, 14, 26, 25, 27);
For Arduino, we use SPI hardware so just use form:  
RF24(uint16_t _cepin, uint16_t _cspin)
and the instance is RF24 radio(7,8);
- Demo 1: ESP32 Nano will send "hello" message to Arduino.
ESP32 transmitter code
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
#include  <SPI.h>
#include "nRF24L01.h"
#include "RF24.h"
char msg[6] = "hello";
RF24 radio(12, 14, 26, 25, 27);
const uint64_t pipe = 0xE8E8F0F0E1LL;

void setup(void) {
  Serial.begin(115200);
  radio.begin();
  radio.setChannel(2);
  radio.setPayloadSize(7);
  radio.setDataRate(RF24_250KBPS);
  radio.openWritingPipe(pipe);
}
void loop(void) {
  Serial.println("send ...");
  radio.write(msg, 6);
  delay(3000);
}
Arduino receiver 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
#include <SPI.h>
#include "nRF24L01.h"
#include "RF24.h"
char msg[6];
RF24 radio(7,8);
const uint64_t pipe = 0xE8E8F0F0E1LL;
void setup(void){
 Serial.begin(115200);
 radio.begin();
 radio.setChannel(2);
 radio.setPayloadSize(7);
 radio.setDataRate(RF24_250KBPS);
 radio.openReadingPipe(1,pipe);
 radio.startListening();
}

void loop(void){
 if (radio.available()){  
     radio.read(msg, 6);      
     Serial.println(msg);
     delay(10);
 }
 else{
  //Serial.println("No radio available");
 }
}
- Demo 2: Arduino Nano will send "hello" message to ESP32.
ESP32 receiver 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
#include <SPI.h>
#include "nRF24L01.h"
#include "RF24.h"
char msg[6];
RF24 radio(12, 14, 26, 25, 27);
const uint64_t pipe = 0xE8E8F0F0E1LL;
void setup(void){
 Serial.begin(115200);
 radio.begin();
 radio.setChannel(2);
 radio.setPayloadSize(7);
 radio.setDataRate(RF24_250KBPS);
 radio.openReadingPipe(1,pipe);
 radio.startListening();
}

void loop(void){
 if (radio.available()){  
     radio.read(msg, 6);      
     Serial.println(msg);
     delay(10);
 }
 else{
  //Serial.println("No radio available");
 }
}
Arduino transmitter code

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
#include  <SPI.h>
#include "nRF24L01.h"
#include "RF24.h"
char msg[6] = "hello";
RF24 radio(7,8);
const uint64_t pipe = 0xE8E8F0F0E1LL;

void setup(void) {
  Serial.begin(115200);
  radio.begin();
  radio.setChannel(2);
  radio.setPayloadSize(7);
  radio.setDataRate(RF24_250KBPS);
  radio.openWritingPipe(pipe);
}
void loop(void) {
  Serial.println("send ...");
  radio.write(msg, 6);
  delay(3000);
}
4. Result
Figure: ESP32 received "hello" message from Arduino

2 comments:

Michael Kirsch said...

Worked like a charm. Nice work thank you

Anonymous said...

very thanks for you from Iraq ..... :)