The ForceTronics blog provides tutorials on creating fun and unique electronic projects. The goal of each project will be to create a foundation or jumping off point for amateur, hobbyist, and professional engineers to build on and innovate. Please use the comments section for questions and go to forcetronics.com for information on forcetronics consulting services.
In this video we look at an easy way with not very much code to setup a wireless network using the nRF24L01 Transceiver and Arduino.
//***************************Master or Receiver code***************** /*This code was used for a video tutorial on the ForceTronics YouTube Channel * This code is free and open for anybody to use and modify at your own risk */ #include <SPI.h> //Call SPI library so you can communicate with the nRF24L01+ #include <nRF24L01.h> //nRF2401 libarary found at https://github.com/tmrh20/RF24/ #include <RF24.h> //nRF2401 libarary found at https://github.com/tmrh20/RF24/ const uint8_t pinCE = 9; //This pin is used to set the nRF24 to standby (0) or active mode (1) const uint8_t pinCSN = 10; //This pin is used for SPI comm chip select RF24 wirelessSPI(pinCE, pinCSN); // Declare object from nRF24 library (Create your wireless SPI) const uint64_t rAddress = 0xB00B1E50C3LL; //Create pipe address for the network and notice I spelled boobies because I am mature, the "LL" is for LongLong type const uint8_t rFChan = 89; //Set channel frequency default (chan 84 is 2.484GHz to 2.489GHz) //Create a structure to hold fake sensor data and channel data struct PayLoad { uint8_t chan; uint8_t sensor; }; PayLoad payload; //create struct object void setup() { wirelessSPI.begin(); //Start the nRF24 module wirelessSPI.setChannel(rFChan); //set communication frequency channel wirelessSPI.openReadingPipe(1,rAddress); //This is receiver or master so we need to be ready to read data from transmitters wirelessSPI.startListening(); // Start listening for messages Serial.begin(115200); //serial port to display received data Serial.println("Network master is online..."); } void loop() { if(wirelessSPI.available()){ //Check if recieved data wirelessSPI.read(&payload, sizeof(payload)); //read packet of data and store it in struct object Serial.print("Received data packet from node: "); Serial.println(payload.chan); //print node number or channel Serial.print("Node sensor value is: "); Serial.println(payload.sensor); //print node's sensor value Serial.println(); } }
//***************************Node or Transmitter code***************** /*This code was used for a video tutorial on the ForceTronics YouTube Channel * This code is free and open for anybody to use and modify at your own risk */ #include <SPI.h> //Call SPI library so you can communicate with the nRF24L01+ #include <nRF24L01.h> //nRF2401 libarary found at https://github.com/tmrh20/RF24/ #include <RF24.h> //nRF2401 libarary found at https://github.com/tmrh20/RF24/ const uint8_t pinCE = 9; //This pin is used to set the nRF24 to standby (0) or active mode (1) const uint8_t pinCSN = 10; //This pin is used to tell the nRF24 whether the SPI communication is a command RF24 wirelessSPI(pinCE, pinCSN); // Declare object from nRF24 library (Create your wireless SPI) const uint64_t wAddress = 0xB00B1E50C3LL; //Create pipe address to send data, the "LL" is for LongLong type const uint8_t rFChan = 89; //Set channel default (chan 84 is 2.484GHz to 2.489GHz) const uint8_t rDelay = 7; //this is based on 250us increments, 0 is 250us so 7 is 2 ms const uint8_t rNum = 5; //number of retries that will be attempted const uint8_t chan1 = 2; //D2 pin for node channel check const uint8_t chan2 = 3; //D3 pin for node channel check const uint8_t chan3 = 4; //D4 pin for node channel check //stuct of payload to send fake sensor data and node channel struct PayLoad { uint8_t chan; uint8_t sensor; }; PayLoad payload; //create struct object void setup() { pinMode(chan1,INPUT_PULLUP); //set channel select digital pins to input pullup pinMode(chan2,INPUT_PULLUP); pinMode(chan3,INPUT_PULLUP); wirelessSPI.begin(); //Start the nRF24 module wirelessSPI.setChannel(rFChan); wirelessSPI.setRetries(rDelay,rNum); //if a transmit fails to reach receiver (no ack packet) then this sets retry attempts and delay between retries wirelessSPI.openWritingPipe(wAddress); //open writing or transmit pipe wirelessSPI.stopListening(); //go into transmit mode randomSeed(analogRead(0)); //set random seed for fake sensor data setChannel(); //checks current channel setting for transceiver } void loop() { delay(3000); //send data every 3 seconds payload.sensor = random(0,255); //get made up sensor value if (!wirelessSPI.write(&payload, sizeof(payload))){ //send data and remember it will retry if it fails delay(random(5,20)); //as another back up, delay for a random amount of time and try again if (!wirelessSPI.write(&payload, sizeof(payload))){ //set error flag if it fails again } } } //check for low digital pin to set node address void setChannel() { if(!digitalRead(chan1)) payload.chan = 1; else if(!digitalRead(chan2)) payload.chan = 2; else if(!digitalRead(chan3)) payload.chan = 3; else payload.chan = 0; }
In this three part series we look at how to create a wireless sensor mesh network that stores data on the cloud using the Arduino platform. In part three we look at how to access the sensor data from the cloud with a PC or Android device.
In this three part series we look at how to create a wireless sensor mesh network that stores data on the cloud using the Arduino platform. In part two we look at how to add time stamps to our sensor data and track the battery state of our nodes.
In this three part series we look at how to create a wireless sensor mesh network that stores data on the cloud using the Arduino platform. In part one we will look at the architecture of the network and how to get started sending sensor data to the cloud.
In this video we look at how to use the pulse signal from an nRF24L01+ transceiver module's IRQ pin to trigger an interrupt on your Arduino.
Arduino code from video, receiver code first and then transmitter code: #include <SPI.h> //Call SPI library so you can communicate with the nRF24L01+ #include <nRF24L01.h> //nRF2401 libarary found at https://github.com/tmrh20/RF24/ #include <RF24.h> //nRF2401 libarary found at https://github.com/tmrh20/RF24/ #include <avr/sleep.h> //library needed to use AVR based sleep API const int pinCE = 9; //This pin is used to set the nRF24 to standby (0) or active mode (1) const int pinCSN = 10; //This pin is used to tell the nRF24 whether the SPI communication is a command or message to send out byte gotByte = 0; //used to store payload from transmit module volatile int count = 0; //tracks the number of interrupts from IRQ int pCount = 0; //tracks what last count value was so know when count has been updated RF24 wirelessSPI(pinCE, pinCSN); // Declare object from nRF24 library (Create your wireless SPI) const uint64_t pAddress = 0xB00B1E5000LL; //Create a pipe addresses for the 2 nodes to communicate over, the "LL" is for LongLong type void setup() { wirelessSPI.begin(); //Start the nRF24 module wirelessSPI.setAutoAck(1); // Ensure autoACK is enabled so rec sends ack packet to let you know it got the transmit packet payload wirelessSPI.enableAckPayload(); //allows you to include payload on ack packet wirelessSPI.maskIRQ(1,1,0); //mask all IRQ triggers except for receive (1 is mask, 0 is no mask) wirelessSPI.setPALevel(RF24_PA_LOW); //Set power level to low, won't work well at higher levels (interfer with receiver) wirelessSPI.openReadingPipe(1,pAddress); //open pipe o for recieving meassages with pipe address wirelessSPI.startListening(); // Start listening for messages attachInterrupt(1, interruptFunction, FALLING); //Create interrupt: 0 for pin 2 or 1 for pin 3, the name of the interrupt function or ISR, and condition to trigger interrupt } void loop() { if(pCount < count) { //If this is true it means count was interated and another interrupt occurred Serial.begin(57600); //start serial to communicate process Serial.print("Receive packet number "); Serial.println(count); Serial.end(); //have to end serial since it uses interrupts pCount = count; } } //This is the function called when the interrupt occurs (pin 2 goes high) //this is often referred to as the interrupt service routine or ISR //This cannot take any input arguments or return anything void interruptFunction() { count++; //up the receive counter while(wirelessSPI.available()) { //get data sent from transmit wirelessSPI.read( &gotByte, 1 ); //read one byte of data and store it in gotByte variable }
} //********************Transmitter code****************************#include <SPI.h> //Call SPI library so you can communicate with the nRF24L01+ #include <nRF24L01.h> //nRF2401 libarary found at https://github.com/tmrh20/RF24/ #include <RF24.h> //nRF2401 libarary found at https://github.com/tmrh20/RF24/ const int pinCE = 9; //This pin is used to set the nRF24 to standby (0) or active mode (1) const int pinCSN = 10; //This pin is used to tell the nRF24 whether the SPI communication is a command or message to send out byte counter = 1; //used to count the packets sent RF24 wirelessSPI(pinCE, pinCSN); // Create your nRF24 object or wireless SPI connection const uint64_t pAddress = 0xB00B1E5000LL; // Radio pipe addresses for the 2 nodes to communicate. void setup() { Serial.begin(57600); //start serial to communicate process wirelessSPI.begin(); //Start the nRF24 module wirelessSPI.setAutoAck(1); // Ensure autoACK is enabled so rec sends ack packet to let you know it got the transmit packet payload wirelessSPI.enableAckPayload(); // Allow optional ack payloads wirelessSPI.setPALevel(RF24_PA_LOW); wirelessSPI.openWritingPipe(pAddress); // pipe address that we will communicate over, must be the same for each nRF24 module wirelessSPI.stopListening(); //transmitter so stop listening for data randomSeed(analogRead(0)); //use random ADC value to seed random number algorithm } void loop() { delay(random(100,5000)); //Generate delay time between 100msec and 5 sec Serial.println("Sending packet"); if (!wirelessSPI.write( &counter, 1 )){ //if the send fails let the user know over serial monitor Serial.println("packet delivery failed"); } Serial.println(); }
In part 6 we look at the final hardware design, we switch to the TMRh20 library for the nRF24L01, and we look at a library wrapper that makes getting started with your own wireless sensor network real easy. Go to ForceTronics.com to purchase a wireless flex node and go to Github to access the code and PCB design files.
In Part 5 of building a wireless sensor network with Arduino and the nRF24L01+ transceiver we take a look at our brand new PCB boards and look at the code for adding the DS18S20 and the STTS751 temperature sensors to the design. You can access the PCB Eagle files and the Arduino code from GitHub: https://github.com/ForceTronics/nRF24L01_Wireless_Sensor_Dev_Board
In part 4 of Building a Wireless Sensor Network with the nRF24L01 we take a look at the design's PCB layout in Eagle software as well as cover some software and hardware updates to the design.
In part three we take a look at the updated hardware schematic of the router / end device design, how the router / end device settings work, and we go over the initial software of the router / end device. You can find the code from this video in GitHub at https://github.com/ForceTronics/nRF24L01_Wireless_Sensor_Dev_Board
In part 2 we focus on powering our wireless sensor node. We talk about batteries, battery sizing, estimating battery life, and battery monitoring. If you have any feedback or questions use the comments section below.
This is part 1 in a series where we look at how to build a large wireless network using Arduino and the nRF24L01+ Transceiver Modules. At the end of this series you will have a reference design for a wireless sensor development board and the code needed to turn the wireless sensor developments boards into a network. You will be able purchase all the hardware for this project at my site: www.forcetronics.com
The nRF24L01+ Transceiver is a great low cost way to add wireless capability to any project. But the down side of the nRF24L01+ is it can be a hassle to prototype with. In this video we look at how to build an Arduino shield and a mini proto board for the nRF24L01+. You can also purchase the shield and mini proto board covered in the video at forcetronics.com.
//*****************************Arduino Code for Transmitter***********************
//This sketch is from a tutorial video on the ForceTronics YouTube Channel. The tutorial discusses how to build a
//shield and a prototyping board for the nRF24L01 Transceiver Module.
//the code was leverage from Ping pair example at http://tmrh20.github.io/RF24/pingpair_ack_8ino-example.html
//This sketch is free to the public to use and modify at your own risk
#include <SPI.h> //Call SPI library so you can communicate with the nRF24L01+
#include <nRF24L01.h> //nRF2401 libarary found at https://github.com/tmrh20/RF24/
#include <RF24.h> //nRF2401 libarary found at https://github.com/tmrh20/RF24/
const int pinCE = 9; //This pin is used to set the nRF24 to standby (0) or active mode (1)
const int pinCSN = 10; //This pin is used to tell the nRF24 whether the SPI communication is a command or message to send out
RF24 wirelessSPI(pinCE, pinCSN); // Create your nRF24 object or wireless SPI connection
const uint64_t pAddress = 0xB00B1E5000LL; // Radio pipe addresses for the 2 nodes to communicate.
void setup()
{
Serial.begin(57600); //start serial to communicate process
wirelessSPI.begin(); //Start the nRF24 module
wirelessSPI.setAutoAck(1); // Ensure autoACK is enabled so rec sends ack packet to let you know it got the transmit packet payload
wirelessSPI.setRetries(5,15); // Sets up retries and timing for packets that were not ack'd, current settings: smallest time between retries, max no. of retries
wirelessSPI.openWritingPipe(pAddress); // pipe address that we will communicate over, must be the same for each nRF24 module
wirelessSPI.stopListening();
}
void loop()
{
byte t = analogRead(0);//note that we can cast the ADC value to a byte because we know the temp sensor is not going to return a value higher than 255
if (!wirelessSPI.write(&t, 1 )){ //if the send fails let the user know over serial monitor
Serial.println("packet delivery failed");
}
delay(1000);
}
//*****************************Arduino Code for Receiver***********************
//This sketch is from a tutorial video on the ForceTronics YouTube Channel. The tutorial discusses how to build a
//shield and a prototyping board for the nRF24L01 Transceiver Module.
//the code was leverage from Ping pair example at http://tmrh20.github.io/RF24/pingpair_ack_8ino-example.html
//This sketch is free to the public to use and modify at your own risk
#include <SPI.h> //Call SPI library so you can communicate with the nRF24L01+
#include <nRF24L01.h> //nRF2401 libarary found at https://github.com/tmrh20/RF24/
#include <RF24.h> //nRF2401 libarary found at https://github.com/tmrh20/RF24/
const int pinCE = 9; //This pin is used to set the nRF24 to standby (0) or active mode (1)
const int pinCSN = 10; //This pin is used to tell the nRF24 whether the SPI communication is a command or message to send out
byte bVal; //used to store ADC value payload from transmit module, the ADC value will be < 256 so it will fit in a byte
RF24 wirelessSPI(pinCE, pinCSN); // Declare object from nRF24 library (Create your wireless SPI)
const uint64_t pAddress = 0xB00B1E5000LL; //Create a pipe addresses for the 2 nodes to communicate over, the "LL" is for LongLong type
void setup()
{
Serial.begin(57600); //start serial to communicate process
wirelessSPI.begin(); //Start the nRF24 module
wirelessSPI.setAutoAck(1); // Ensure autoACK is enabled, this means rec send acknowledge packet to tell xmit that it got the packet with no problems
wirelessSPI.openReadingPipe(1,pAddress); //open pipe o for recieving meassages with pipe address
wirelessSPI.startListening(); // Start listening for messages
}
void loop()
{
//loop until all of the payload data is recieved, for this example loop should only run once
while(wirelessSPI.available()){
wirelessSPI.read( &bVal, 1 ); //read one byte of data and store it in bVal variable
Serial.print("Temperature at transmitter is ");
Serial.print(calculateTempF(calculateArduinoVolt(bVal))); //convert the ADC value to a voltage value and than to a temperature value in F
Serial.println(" F");
}
delay(200);
}
//this function calculates temp in F from TMP36 temp sensor
float calculateTempF(float v1) {
float temp = 0;
//calculate temp in C, .75 volts is 25 C. 10mV per degree
if (v1 < .75) { temp = 25 - ((.75-v1)/.01); } //if below 25 C
In this video we take a look at the power needs or power profile of the nRF24L01+ Transceiver. We discuss how much power it draws in each mode and how to reduce or optimize its power consumption for battery powered projects or designs. Finally we pair the nRF24L01 with an Arduino utilizing sleep mode and look at their combined power profile.
************Arduino and nRF24L01 Low Power Example Sketch************* #include <SPI.h> //Call SPI library so you can communicate with the nRF24L01+ #include <nRF24L01.h> //nRF2401 libarary found at https://github.com/tmrh20/RF24/ #include <RF24.h> //nRF2401 libarary found at https://github.com/tmrh20/RF24/ #include <avr/sleep.h> #include <avr/wdt.h> /*WDT BYTE variables for setting timer value WDTO_15MS, WDTO_30MS, WDTO_60MS, WDTO_120MS, WDTO_250MS, WDTO_500MS, WDTO_1S, WDTO_2S, WDTO_4S, WDTO_8S */ const int pinCE = 9; //This pin is used to set the nRF24 to standby (0) or active mode (1) const int pinCSN = 10; //This pin is used to tell the nRF24 whether the SPI communication is a command or message to send out RF24 wirelessSPI(pinCE, pinCSN); // Create your nRF24 object or wireless SPI connection const uint64_t wAddress = 0xB00B1E50D2LL; // Pipe to write or transmit on const uint64_t rAddress = 0xB00B1E50B1LL; //pipe to recive data on void setup() { randomSeed(analogRead(0)); //create unique seed value for random number generation wirelessSPI.begin(); //Start the nRF24 module wirelessSPI.setRetries(15,10); wirelessSPI.openWritingPipe(wAddress); //open writing or transmit pipe wirelessSPI.openReadingPipe(1,rAddress); //open reading or recieve pipe wirelessSPI.stopListening(); //go into transmit mode } void loop() { byte randNumber = (byte)random(11); //generate random guess between 0 and 10 if (!wirelessSPI.write(&randNumber, 1)){ //if the write fails // delivery failed } delay(30); //delay for short time in normal mode wirelessSPI.powerDown(); //put nRF24L01 into power down mode delayWDT(WDTO_30MS); // Use WDT sleep delay function, argument is byte variable from WDT Library wirelessSPI.powerUp(); //power up the nRF24 } //This function serves as a power saving delay function. The argument is a Byte type variable that is used to set the delay time //The function sets up sleep mode in power down state. The function then sets up the WDT timer in interrupt mode and sets it. //It then puts the Arduino to sleep for the set time. Upon wake up the WDT and sleep mode are shut off void delayWDT(byte timer) { sleep_enable(); //enable the sleep capability set_sleep_mode(SLEEP_MODE_PWR_DOWN); //set the type of sleep mode. Default is Idle ADCSRA &= ~(1<<ADEN); //Turn off ADC before going to sleep (set ADEN bit to 0) WDTCSR |= 0b00011000; //Set the WDE bit and then clear it when set the prescaler, WDCE bit must be set if changing WDE bit WDTCSR = 0b01000000 | timer; //Or timer prescaler byte value with interrupt selectrion bit set wdt_reset(); //Reset the WDT sleep_cpu(); //enter sleep mode. Next code that will be executed is the ISR when interrupt wakes Arduino from sleep sleep_disable(); //disable sleep mode ADCSRA |= (1<<ADEN); //Turn the ADC back on } //This is the interrupt service routine for the WDT. It is called when the WDT times out. //This ISR must be in your Arduino sketch or else the WDT will not work correctly ISR (WDT_vect) { wdt_disable(); MCUSR = 0; //Clear WDT flag since it is disabled, this is optional } // end of WDT_vect
In this video we will look at how to create an nRF24L01 Transceiver module network (more than two). This is useful if you want to build a wireless sensor network or some type of wireless automation system that has multiple wireless nodes.
***************************Arduino Code for Receiver******************************* //This sketch is from a tutorial video for networking more than two nRF24L01 tranciever modules on the ForceTronics YouTube Channel //the code was leverage from the following code http://maniacbug.github.io/RF24/starping_8pde-example.html //This sketch is free to the public to use and modify at your own risk #include <SPI.h> //Call SPI library so you can communicate with the nRF24L01+ #include <nRF24L01.h> //nRF2401 libarary found at https://github.com/tmrh20/RF24/ #include <RF24.h> //nRF2401 libarary found at https://github.com/tmrh20/RF24/ const int pinCE = 9; //This pin is used to set the nRF24 to standby (0) or active mode (1) const int pinCSN = 10; //This pin is used to tell the nRF24 whether the SPI communication is a command or message to send out byte daNumber = 0; //The number that the transmitters are trying to guess RF24 wirelessSPI(pinCE, pinCSN); // Declare object from nRF24 library (Create your wireless SPI) const uint64_t rAddress[] = {0xB00B1E50D2LL, 0xB00B1E50C3LL}; //Create pipe addresses for the 2 nodes to recieve data, the "LL" is for LongLong type const uint64_t wAddress[] = {0xB00B1E50B1LL, 0xB00B1E50A4LL}; //Create pipe addresses for the 2 nodes to transmit data, the "LL" is for LongLong type void setup() { randomSeed(analogRead(0)); //create unique seed value for random number generation daNumber = (byte)random(11); //Create random number that transmitters have to guess Serial.begin(57600); //start serial to communication Serial.print("The number they are trying to guess is: "); Serial.println(daNumber); //print the number that they have to guess Serial.println(); wirelessSPI.begin(); //Start the nRF24 module wirelessSPI.openReadingPipe(1,rAddress[0]); //open pipe o for recieving meassages with pipe address wirelessSPI.openReadingPipe(2,rAddress[1]); //open pipe o for recieving meassages with pipe address wirelessSPI.startListening(); // Start listening for messages } void loop() { byte pipeNum = 0; //variable to hold which reading pipe sent data byte gotByte = 0; //used to store payload from transmit module while(wirelessSPI.available(&pipeNum)){ //Check if recieved data wirelessSPI.read( &gotByte, 1 ); //read one byte of data and store it in gotByte variable Serial.print("Recieved guess from transmitter: "); Serial.println(pipeNum); //print which pipe or transmitter this is from Serial.print("They guess number: "); Serial.println(gotByte); //print payload or the number the transmitter guessed if(gotByte != daNumber) { //if true they guessed wrong Serial.println("Fail!! Try again."); } else { //if this is true they guessed right if(sendCorrectNumber(pipeNum)) Serial.println("Correct! You're done."); //if true we successfully responded else Serial.println("Write failed"); //if true we failed responding } Serial.println(); } delay(200); } //This function turns the reciever into a transmitter briefly to tell one of the nRF24s //in the network that it guessed the right number. Returns true if write to module was //successful bool sendCorrectNumber(byte xMitter) { bool worked; //variable to track if write was successful wirelessSPI.stopListening(); //Stop listening, stop recieving data. wirelessSPI.openWritingPipe(wAddress[xMitter-1]); //Open writing pipe to the nRF24 that guessed the right number if(!wirelessSPI.write(&daNumber, 1)) worked = false; //write the correct number to the nRF24 module, and check that it was recieved else worked = true; //it was recieved wirelessSPI.startListening(); //Switch back to a reciever return worked; //return whether write was successful }
***************************Arduino Code for Transmitter 1****************************
//This sketch is from a tutorial video for networking more than two nRF24L01 tranciever modules on the ForceTronics YouTube Channel
//the code was leverage from the following code http://maniacbug.github.io/RF24/starping_8pde-example.html
//This sketch is free to the public to use and modify at your own risk
#include <SPI.h> //Call SPI library so you can communicate with the nRF24L01+
#include <nRF24L01.h> //nRF2401 libarary found at https://github.com/tmrh20/RF24/
#include <RF24.h> //nRF2401 libarary found at https://github.com/tmrh20/RF24/
const int pinCE = 9; //This pin is used to set the nRF24 to standby (0) or active mode (1)
const int pinCSN = 10; //This pin is used to tell the nRF24 whether the SPI communication is a command or message to send out
byte counter = 1; //used to count the packets sent
bool done = false; //used to know when to stop sending packets
RF24 wirelessSPI(pinCE, pinCSN); // Create your nRF24 object or wireless SPI connection
const uint64_t wAddress = 0xB00B1E50D2LL; // Pipe to write or transmit on
const uint64_t rAddress = 0xB00B1E50B1LL; //pipe to recive data on
void setup()
{
Serial.begin(57600); //start serial to communicate process
randomSeed(analogRead(0)); //create unique seed value for random number generation
wirelessSPI.begin(); //Start the nRF24 module
wirelessSPI.openWritingPipe(wAddress); //open writing or transmit pipe
wirelessSPI.openReadingPipe(1,rAddress); //open reading or recieve pipe
wirelessSPI.stopListening(); //go into transmit mode
}
void loop()
{
if(!done) { //true once you guess the right number
byte randNumber = (byte)random(11); //generate random guess between 0 and 10
if (!wirelessSPI.write( &randNumber, 1 )){ //if the write fails let the user know over serial monitor
Serial.println("Guess delivery failed");
}
else { //if the write was successful
Serial.print("Success sending guess: ");
Serial.println(randNumber);
wirelessSPI.startListening(); //switch to recieve mode to see if the guess was right
unsigned long startTimer = millis(); //start timer, we will wait 200ms
bool timeout = false;
while ( !wirelessSPI.available() && !timeout ) { //run while no recieve data and not timed out
if (millis() - startTimer > 200 ) timeout = true; //timed out
}
if (timeout) Serial.println("Last guess was wrong, try again"); //no data to recieve guess must have been wrong
else { //we recieved something so guess must have been right
byte daNumber; //variable to store recived value
wirelessSPI.read( &daNumber,1); //read value
if(daNumber == randNumber) { //make sure it equals value we just sent, if so we are done
Serial.println("You guessed right so you are done");
done = true; //signal to loop that we are done guessing
}
else Serial.println("Something went wrong, keep guessing"); //this should never be true, but just in case
}
wirelessSPI.stopListening(); //go back to transmit mode
}
}
delay(1000);
}
***************************Arduino Code for Transmitter 2****************************
//This sketch is from a tutorial video for networking more than two nRF24L01 tranciever modules on the ForceTronics YouTube Channel
//the code was leverage from the following code http://maniacbug.github.io/RF24/starping_8pde-example.html
//This sketch is free to the public to use and modify at your own risk
#include <SPI.h> //Call SPI library so you can communicate with the nRF24L01+
#include <nRF24L01.h> //nRF2401 libarary found at https://github.com/tmrh20/RF24/
#include <RF24.h> //nRF2401 libarary found at https://github.com/tmrh20/RF24/
const int pinCE = 9; //This pin is used to set the nRF24 to standby (0) or active mode (1)
const int pinCSN = 10; //This pin is used to tell the nRF24 whether the SPI communication is a command or message to send out
bool done = false; //used to know when to stop sending guesses
RF24 wirelessSPI(pinCE, pinCSN); // Create your nRF24 object or wireless SPI connection
const uint64_t wAddress = 0xB00B1E50C3LL; //pipe for writing or transmitting data
const uint64_t rAddress = 0xB00B1E50A4LL; //pipe for reading or recieving data
void setup()
{
Serial.begin(57600); //start serial to communicate process
randomSeed(analogRead(0)); //create unique seed value for random number generation
wirelessSPI.begin(); //Start the nRF24 module
wirelessSPI.openWritingPipe(wAddress); // setup pipe to transmit over
wirelessSPI.openReadingPipe(1,rAddress); //set up pipe to recieve data
wirelessSPI.stopListening(); //turn off recieve capability so you can transmit
}
void loop()
{
if(!done) { //true once you guess the right number
byte randNumber = (byte)random(11); //generate random guess between 0 and 10
if (!wirelessSPI.write( &randNumber, 1 )){ //if the write fails let the user know over serial monitor
Serial.println("Guess delivery failed");
}
else { //if the write was successful
Serial.print("Success sending guess: ");
Serial.println(randNumber);
wirelessSPI.startListening(); //switch to recieve mode to see if the guess was right
unsigned long startTimer = millis(); //start timer, we will wait 200ms
bool timeout = false;
while ( !wirelessSPI.available() && !timeout ) { //run while no recieve data and not timed out
if (millis() - startTimer > 200 ) timeout = true; //timed out
}
if (timeout) Serial.println("Last guess was wrong, try again"); //no data to recieve guess must have been wrong
else { //we recieved something so guess must have been right
byte daNumber; //variable to store recived value
wirelessSPI.read( &daNumber,1); //read value
if(daNumber == randNumber) { //make sure it equals value we just sent, if so we are done
Serial.println("You guessed right so you are done");
done = true; //signal to loop that we are done guessing
}
else Serial.println("Something went wrong, keep guessing"); //this should never be true, but just in case
}
wirelessSPI.stopListening(); //go back to transmit mode
In this video we build a remote control car using Arduino and the nRF24L01+ transceiver for wireless communication / control of the car. To control this car we won't be using the classic joystick, but instead a glove! The glove will use an accelerometer (MPU-6050) to control the car's direction and speed based on the position of your hand.
//********************Arduino Code for the RC Car******************************** //This code is for a remote control car using the nRF24L01 for wireless communication. The tutorial on this project can be found on the ForceTronics Youtube channel //This code is free and open for anybody to use or modify at your own risk #include <Wire.h> //This library is needed for I2C communication (motor shield uses this) #include <Adafruit_MotorShield.h> //Library for the adafruit motor shield #include "utility/Adafruit_PWMServoDriver.h" //needed for motor shield, file is found in the library folder of Adafruit_MotorShield.h #include <SPI.h> //Call SPI library so you can communicate with the nRF24L01+ #include <nRF24L01.h> //nRF2401 libarary found at https://github.com/tmrh20/RF24/ #include <RF24.h> //nRF2401 libarary found at https://github.com/tmrh20/RF24/ // Create the motor shield object with the default I2C address Adafruit_MotorShield AFMS = Adafruit_MotorShield(); // create an object for each motor and assign it to a port on the shield Adafruit_DCMotor *M1 = AFMS.getMotor(1); Adafruit_DCMotor *M2 = AFMS.getMotor(2); Adafruit_DCMotor *M3 = AFMS.getMotor(3); Adafruit_DCMotor *M4 = AFMS.getMotor(4); const int pinCE = 9; //This pin is used to set the nRF24 to standby (0) or active mode (1) const int pinCSN = 10; //This pin is used to tell the nRF24 whether the SPI communication is a command or message //The controller sends a "packet" to control the speed and direction of the car. The first and last byte just signal the start and end of the packet //The second and third bytes represent the speed and direction of the car. The second byte is for forward and backwards //The third byte is for right and left directions byte bArray[] = {255, 125, 125, 254}; //This array holds the speed and direction packet RF24 wirelessSPI(pinCE, pinCSN); // Declare object from nRF24 library (Create your wireless SPI) const uint64_t pAddress = 0xB00B1E5000LL; //Create a pipe addresses for the 2 nodes to communicate over, my address spells boobies :-) int sCount = 0; //variable to track timer to stop motor, if communication is lost this will shut off motor void setup() { AFMS.begin(); //Start motor shield object, create with the default frequency 1.6KHz wirelessSPI.begin(); //Start the nRF24 module wirelessSPI.setAutoAck(1); // Ensure autoACK is enabled, this means rec send acknowledge packet to tell xmit that it got the packet with no problems wirelessSPI.enableAckPayload(); // Allow optional payload or message on ack packet, even though we are not using this wirelessSPI.setRetries(5,10); // Defines packet retry behavior: first arg is delay between retries at 250us x 5 and max no. of retries wirelessSPI.openReadingPipe(1,pAddress); //open pipe o for recieving meassages with pipe address wirelessSPI.startListening(); // Start listening for messages, because we are the reciever motorStop(); //ensure motor is at stop to start } void loop() { if(wirelessSPI.available()){ //check to see if a data packet is available from transmitter wirelessSPI.read( bArray, 4 ); //read 4 bytes of data and store it in array if(verifyPacket) { //verify it is a valid packet to control car setMotorSpeed(bArray[1], bArray[2]); //get the forward / backward and right / left speeds sCount = 0; //reset count } else { //do something here if a bad packet was recieved } } delay(1); //short delay before looping again sCount++; //increment the loop count if(sCount > 60) { motorStop(); } //if we do not get a packet from the controller } //This function makes sure a packet is valid by checking it has the correct start and end byte //it also checks to see if the forward / backward and right / left speeds are valid bool verifyPacket() { if(bArray[0] == 255 & bArray[3] == 254 & bArray[1] < 251 & bArray[2] < 251) return true; else return false; } //This function is used to set the direction and speed based on the two bytes from the controller //125 means stop, above 125 means right or forward, below 125 means left or backwards void setMotorSpeed(int upDown, int leftRight) { int lR = 0; //left and right direction variable, zero is stop int bF = 0; //forward and backward direction variable, zero is stop if(leftRight == 125) { //if true no left or right turn (stop) lR = 0; } else if(leftRight > 125) { //if this is true right turn lR = 1; leftRight = leftRight - 125; //scale variable from 0 to 125 } else { //else this is a left lR = 2; } if(upDown == 125) { //if true no forward or back (stop) bF = 0; } else if(upDown > 125) { //if this is true go forward bF = 1; upDown = upDown - 125; //scale variable from 0 to 125 } else { //this is go backwards bF = 2; } //We have direction now set speed //scale turn and back / forward if(lR == 0 && bF == 0) { //stop all motors if no forward / backward and right / left direction motorStop(); } else if (bF==1) { //Go forward if(lR == 0) { //go straight forward goForward(scaleSpeed(upDown)); //Send forward speed } else if(lR == 1) { //go forward and right goTurn(scaleSpeed(scaleTurn(upDown,leftRight)), scaleSpeed(upDown), 1); //send forward and right turn speeds } else { goTurn(scaleSpeed(upDown),scaleSpeed(scaleTurn(upDown,leftRight)), 1); //send forward and left turn speeds } } else if (bF==2) { //same thing but this is backwards if(lR == 0) { //go straight backwards goBackward(scaleSpeed(upDown)); } else if(lR == 1) { //go forward and right goTurn(scaleSpeed(scaleTurn(upDown,leftRight)), scaleSpeed(upDown), 0); } else { goTurn(scaleSpeed(upDown),scaleSpeed(scaleTurn(upDown,leftRight)), 0); } } else { //No forward or backwards direction so just do a turn if(lR==1) { //Right turn goRight(scaleSpeed(leftRight)); } else { //left turn goLeft(scaleSpeed(leftRight)); } } } //This function scales the speed value from controller to a value for the motor //max motor speed is 250 and max value from controller is 125 int scaleSpeed(int scale) { float r = ((float)scale/125)*250; //scale to value between 1 and 250 return int(r); //covert to int value and return } //Used to scale turn value, based on forward or backward speed as well as turn speed int scaleTurn(int fBSp, int lRSp) { float r =(float)fBSp*(1 - (float)lRSp/125); return int(r); } //function to stop the motors void motorStop() { M2->run(RELEASE); M4->run(RELEASE); M1->run(RELEASE); M3->run(RELEASE); } //function to tell motors to go forward, input is speed void goForward(int mSpeed) { M1->setSpeed(mSpeed); M2->setSpeed(mSpeed); M3->setSpeed(mSpeed); M4->setSpeed(mSpeed); M2->run(FORWARD); M4->run(FORWARD); M1->run(FORWARD); M3->run(FORWARD); } //function to tell motors to go backward, input is speed void goBackward(int mSpeed) { M1->setSpeed(mSpeed); M2->setSpeed(mSpeed); M3->setSpeed(mSpeed); M4->setSpeed(mSpeed); M2->run(BACKWARD); M4->run(BACKWARD); M1->run(BACKWARD); M3->run(BACKWARD); } //function for left or right turn. inputs are speed for left tires and speed for right tires //and whether we are going forward or backwards void goTurn(int rTire, int lTire, int forward) { M1->setSpeed(rTire); M2->setSpeed(lTire); M3->setSpeed(rTire); M4->setSpeed(lTire); //code to turn Right if(forward) { M2->run(FORWARD); //M2 and M4 are left tires M4->run(FORWARD); M1->run(FORWARD); //M1 and M3 are right tires M3->run(FORWARD); } else { M2->run(BACKWARD); M4->run(BACKWARD); M1->run(BACKWARD); M3->run(BACKWARD); } } //right turn function, no forward or backwards motion void goRight(int tSpeed) { M1->setSpeed(tSpeed); M2->setSpeed(tSpeed); M3->setSpeed(tSpeed); M4->setSpeed(tSpeed); //code to turn Right M2->run(FORWARD); //left tires M4->run(FORWARD); M1->run(BACKWARD); //right tires M3->run(BACKWARD); } //left turn function, no forward or backwards motion void goLeft(int tSpeed) { M1->setSpeed(tSpeed); M2->setSpeed(tSpeed); M3->setSpeed(tSpeed); M4->setSpeed(tSpeed); //code to turn Right M2->run(BACKWARD); //left tires M4->run(BACKWARD); M1->run(FORWARD); //right tires M3->run(FORWARD); }
//********************Arduino Code for the Glove Controller**************************** //This code is for a remote control car using the nRF24L01 for wireless communication //and accel to dictate the cars direction and speed based of hand postion. //The tutorial on this project can be found on the ForceTronics Youtube channel //This code is free and open for anybody to use or modify at your own risk #include <SPI.h> //Call SPI library so you can communicate with the nRF24L01+ #include <nRF24L01.h> //nRF2401 libarary found at https://github.com/tmrh20/RF24/ #include <RF24.h> //nRF2401 libarary found at https://github.com/tmrh20/RF24/ #include "I2Cdev.h" //the MPU6050 Accel uses I2C communication #include "MPU6050.h" // Arduino Wire library is required if I2Cdev I2CDEV_ARDUINO_WIRE implementation // is used in I2Cdev.h #if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE #include "Wire.h" #endif const int pinCE = 9; //This pin is used to set the nRF24 to standby (0) or active mode (1) const int pinCSN = 10; //This pin is used to tell the nRF24 whether the SPI communication is a command or message to send out MPU6050 accelgyro; //declare the object to access and cotrol the accel (we don't use the gyro) RF24 wirelessSPI(pinCE, pinCSN); // Create your nRF24 object or wireless SPI connection const uint64_t pAddress = 0xB00B1E5000LL; // Radio pipe addresses for the 2 nodes to communicate. The address spells boobies :-) //The controller sends a "packet" to control the speed and direction of the car. The first and last byte just signal the start and end of the packet //The second and third bytes represent the speed and direction of the car. The second byte is for forward and backwards //The third byte is for right and left directions byte bArray[] = {255, 125, 125, 254}; //This array holds the speed and direction packet void setup() { // join I2C bus (I2Cdev library doesn't do this automatically) #if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE Wire.begin(); #elif I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_FASTWIRE Fastwire::setup(400, true); #endif accelgyro.initialize(); //initialize the accel object wirelessSPI.begin(); //Start the nRF24 module wirelessSPI.setAutoAck(1); // Ensure autoACK is enabled so rec sends ack packet to let you know it got the transmit packet payload wirelessSPI.enableAckPayload(); // Allow optional ack payloads (we don't use this) wirelessSPI.setRetries(5,15); // Sets up retries and timing for packets that were not ack'd, current settings: smallest time between retries, max no. of retries wirelessSPI.openWritingPipe(pAddress);// pipe address that we will communicate over, must be the same for each nRF24 module (this is a transmitter) wirelessSPI.stopListening(); //we are the transmitter so don't need to listen } void loop() { int x, y, z; //create variables to hold accel values (we don't use z direction) accelgyro.getAcceleration(&x, &y, &z); //get accel values, note variables are sent by reference buildArray(buildXValue(x), buildYValue(y)); //build speed and direction array or packet, this is what we send to the car to control if (!wirelessSPI.write( bArray, 4 )){ //if the send fails let the user know over serial monitor //put code here to do something if sending the packet fails } delay(5); //delay a before sending the next packet } //This function is used to build the forward / backwards direction and speed value //The X direction of the accel is used for the forward / backwards direction and speed //Note that the accel value has to be scaled to fit in a byte of data byte buildXValue(int xV) { if(xV <= 1000 & xV >= -1000) { //This creates a cushion for the stop value so the car is not constantly moving return 125; //this is the stop value } else if (xV > 1000) { //if positive value then car is being directed forward xV = xV - 1000; if(xV > 15000) { xV = 15000; } //ceiling value for forward speed return (scaleSpeed(xV,15000) + 125); //scale speed to send, add 125 since this is forward } else { //Negative x value is to tell the car to go backwards xV = xV * -1; //conver negative value to positive xV = xV - 1000; if(xV > 15000) { xV = 15000; } //set ceiling on accel value return scaleSpeed(xV,15000); //scale to 1 to 125 } } //This function is used to build the right and left direction and speed value //The Y direction of the accel is used for the right and left direction and speed //Note that the accel value has to be scaled to fit in a byte of data byte buildYValue(int yV) { if(yV <= 1000 & yV >= -1000) { //This creates a cushion for the stop value so the car is not constantly moving return 125; //this is the stop value } else if (yV > 1000) { //if positive value then car is being directed right yV = yV - 1000; if(yV > 11000) { yV = 11000; } //ceiling value for right speed return scaleSpeed(yV,11000); } else { //Negative x value is to tell the car to go backwards yV = yV * -1; yV = yV - 1000; if(yV > 11000) { yV = 11000; } //ceiling value for left speed return (scaleSpeed(yV,11000)+125); //scale speed to send, add 125 since this is left } } //This function scales the accel speed value to a value that can fit in a byte byte scaleSpeed(int scale, int sVal) { float r = ((float)scale/sVal)*125; //speed is between 0 to 125 return (byte)r; } //This function builds the packet that sends the speed and direction //The first and last byte is used to represent the start and end of the packet void buildArray(byte xV, byte yV) { bArray[0] = 255; bArray[1] = xV; bArray[2] = yV; bArray[3] = 254; }