In this project, I will show you one way of controlling equipment over a wireless interface.
I have used the cheap and commonly available nRF24L01 unit in this project.
This project uses many different components, connected to two different Arduino compatible boards.
The system operates in a Master-Slave configuration, with the Master being the receiver and the Slave the transmitter. It may be possible to have many Masters, with one Slave controlling all of them by sending an address byte to identify the master for which the message is meant. This is however not implemented at the moment.
The slave is a Maker Nano, with an Ultrasonic Module and nRF24 module connected to it.
The pin connections is a follows:
Nano nRF24
13 SCK 5 SCK - Brown
12 MISO 7 MISO - White
11 MOSI 6 MOSI - Purple
10 CSN 4 CSN - Green
9 CE 3 CE - Yellow
8 D8 8 IRQ - Grey
HC-SR04 Ultrasonic Sensor
7 Echo 2 Echo - Yellow
6 Trigger 3 Trigger - Green
2 Push Button configured as INPUT_PULLUP.
To solve problems with stability, a 10uf electrolytic capacitor was soldered to Vcc (2) and gnd(1) on the nRF24 Module. This prevents power spikes on the 3v3 line during transmission to cause problems.
As far as inputs for control purposes are concerned, I have programmed a simple "mode" system, where every press of the button advances the mode and transmits different instructions to the Master. This can be improved upon by adding a keypad connected to an I2C Port extender or similar.
The Ultrasonic Sensor data is transmitted continuously.
The modes are as follows:
0 All relays off and motors stopped.
1 Relay 1 On, Relay 2 Off and status displayed on LCD.
2 Relay 2 On, Relay 1 Off and status on LCD.
3 Both relays On status on LCD.
4 All relays Off, Motor 1 at slow speed, Motor 2 off, status on LCD.
5 All relays Off, Motor 1 Off, Motor 2 at slow speed, status on LCD.
6 Both motors On at slow speed, All relays Off, status on LCD.
7. Both Motors On at medium speed, All relays Off status on LCD.
8. Both Motors On at full speed, All relays Off status on LCD.
9. Both Motors On, Motor 1 full speed, Motor 2 slow speed, All relays Off status on LCD.
10. Both Motors On, Motor 1 slow speed, Motor 2 full speed, All relays Off status on LCD.
Only eight(8) bytes out of the available 32 bytes in the transmit buffer is used to send the data, leaving 24 bytes for other purposes. This is done as follows:
Ultrasonic Sensor's pulse duration and the detected distance to an object is sent as 2 bytes ( 1 word ) each by using the highByte(0 and lowByte() functions.
<code>
pulse = pulseIn(echo,HIGH);
distance = pulse/29/2;
tx_buf[10] = highByte(distance);
tx_buf[11] = lowByte(distance);
tx_buf[12] = highByte(pulse);
tx_buf[13] = lowByte(pulse);
</code>
Using a case statement, the respective modes sets various bits in another 4 bytes to control the relays and motors (4 bits in one byte) and PWM speed settings ( 2 bytes).
The PWM speed can be improved by using variable resistors on two analog inputs and scaling that to a value between 0 and 255 with the map function.
On the Receiver ( Master) I have made the following connections:
UNO nRF24
13 SCK 5 SCK - Brown
12 MISO 7 MISO - White
11 MOSI 6 MOSI - Purple
10 CSN 4 CSN - Green
9 CE 3 CE - Yellow
8 D8 8 IRQ - Grey
A 16x2 I2C LCD connected to SDA and SCL ( I2C pins )
A Relay Module is connected as follows
Uno Relay Module
7 D7 Relay1 - Grey
4 D4 Relay2 - White
The two motors are controlled via PWM on pins 5 and 6
The data is unpacked from the receive buffer and appropriate actions taken as programmed. At this stage, bi-directional data transfer has not yet been implemented.
The receiver code that handles the data looks like this
<code>
Serial.print("[0x");
Serial.print(rx_buf[10],HEX);
Serial.print(rx_buf[11],HEX);
distance = word(rx_buf[10],rx_buf[11]);
Serial.print("] ");
Serial.print(distance);
Serial.print(" cm [0x");
lcd.setCursor(0,0);
lcd.print(distance);
lcd.print(" cm");
Serial.print(rx_buf[12],HEX);
Serial.print(rx_buf[13],HEX);
pulse = word(rx_buf[12],rx_buf[13]);
Serial.print("] ");
Serial.print(pulse);
Serial.print(" ms [0b");
Serial.print(rx_buf[14],BIN);
Serial.print("] Relay 1=");
if (bitRead(rx_buf[14],1) == HIGH)
{
Serial.print(" OFF");
digitalWrite(relay1,HIGH);
lcd.setCursor(12,0);
lcd.print(" ");
} else {
Serial.print(" ON");
digitalWrite(relay1,LOW);
lcd.setCursor(12,0);
lcd.print("RL2");
}
Serial.print(" Relay 2=");
if (bitRead(rx_buf[14],0) == HIGH)
{
Serial.print(" OFF");
digitalWrite(relay2,HIGH);
lcd.setCursor(8,0);
lcd.print(" ");
} else {
Serial.print(" ON");
digitalWrite(relay2,LOW);
lcd.setCursor(8,0);
lcd.print("RL1");
}
Serial.print(" Mode: ");
Serial.print(rx_buf[15],DEC);
Serial.print(" M1= ");
if(bitRead(rx_buf[14],3) == HIGH)
{
motor1_status = HIGH;
Serial.print("ON");
} else {
motor1_status = LOW;
Serial.print("OFF");
}
if (motor1_status == HIGH) {
analogWrite(motor1,rx_buf[16]);
Serial.print(" Speed ");
Serial.print(rx_buf[16],DEC);
lcd.setCursor(0,1);
lcd.print("M1 ");
lcd.print(rx_buf[16]);
} else {
analogWrite(motor1,0);
lcd.setCursor(0,1);
lcd.print("M1 OFF");
}
Serial.print(" M2= ");
if(bitRead(rx_buf[14],2) == HIGH)
{
motor2_status = HIGH;
Serial.print("ON");
} else {
motor2_status = LOW;
Serial.print("OFF");
}
if (motor2_status == HIGH) {
analogWrite(motor2,rx_buf[17]);
Serial.print(" Speed ");
Serial.print(rx_buf[17],DEC);
lcd.setCursor(8,1);
lcd.print("M2 ");
lcd.print(rx_buf[17]);
} else {
analogWrite(motor2,0);
lcd.setCursor(8,1);
lcd.print("M2 OFF");
}
</code>
I have included the .cpp and .h files for the SPI functions that I have used in this post.
The source-code for the master as well as the slave node is also included.
The Transmitter Node - Code
<code>
#include "NRF24L01.h"
#define TX_ADR_WIDTH 5 // 5 unsigned chars TX(RX) address width
#define TX_PLOAD_WIDTH 32 // 3 unsigned chars TX payload
unsigned char TX_ADDRESS[TX_ADR_WIDTH] =
{
0x34,0x43,0x10,0x10,0x01
};
unsigned char rx_buf[TX_PLOAD_WIDTH] = {0}; // initialize value
unsigned char tx_buf[TX_PLOAD_WIDTH] = {0};
//Ultrasonic Sensor
long pulse; // duration of the received pulse in ms
int distance; // the distance of the object
const int trigger = 6 ; // pin for the trigger
const int echo = 7; // pin for the echo return
// Button and Mode
int mode = 0;
const int button = 2;
void setup()
{
Serial.begin(115200);
// Set pins to correct mode for ultrasonic sensor to work
pinMode(trigger,OUTPUT);
pinMode(echo,INPUT);
// Set pins for Button
pinMode(button,INPUT_PULLUP);
NRF_Init();
Serial.println("TX_Mode Start");
}
void loop()
{
switch(mode)
{
case 0: // All Relays OFF
tx_buf[14] = 0x03;
tx_buf[16] = 0x00;
tx_buf[17] = 0x00;
break;
case 1: // Relay One On, Two Off
tx_buf[14] = 0x02;
tx_buf[16] = 0x00;
tx_buf[17] = 0x00;
break;
case 2:
tx_buf[14] = 0x01; // Relay Two On, One Off
tx_buf[16] = 0x00;
tx_buf[17] = 0x00;
break;
case 3:
tx_buf[14] = 0x00; // Both Relays On
tx_buf[16] = 0x00;
tx_buf[17] = 0x00;
break;
case 4:
tx_buf[14] = 0x0b; //motor1 on slow
tx_buf[16] = 0x96;
break;
case 5:
tx_buf[14] = 0x07; //motor2 on slow
tx_buf[17] = 0x96;
break;
case 6:
tx_buf[14] = 0x0f; //both motors on slow
tx_buf[16] = 0x96;
tx_buf[17] = 0x96;
break;
case 7:
tx_buf[14] = 0x0f; //both motors on medium
tx_buf[16] = 0xDC;
tx_buf[17] = 0xDC;
break;
case 8:
tx_buf[14] = 0x0f; //both motors on fast
tx_buf[16] = 0xff;
tx_buf[17] = 0xff;
break;
case 9:
tx_buf[14] = 0x0f; //both motors on 1 slow, 2 fast
tx_buf[16] = 0x96;
tx_buf[17] = 0xff;
break;
case 10:
tx_buf[14] = 0x0f; //both motors on 1 fast 2 slow
tx_buf[16] = 0xff;
tx_buf[17] = 0x96;
break;
case 11:
break;
}
tx_buf[15] = mode;
if(digitalRead(button)==LOW)
{
while(digitalRead(button)==LOW);
mode++;
if (mode==11)
mode=0;
}
NRF_SeTxMode();
get_distance();
/*
Serial.print("Distance : ");
Serial.print(distance);
Serial.print(" cm. Pulse Duration : ");
Serial.print(pulse);
Serial.println(" ms ");
*/
do
{
NRF_Send(tx_buf);
}
while(!NRF_CheckAck());
delay(100);
}
void get_distance()
{
digitalWrite(trigger,LOW);
delayMicroseconds(2);
digitalWrite(trigger,HIGH);
delayMicroseconds(10);
digitalWrite(trigger,LOW);
pulse = pulseIn(echo,HIGH);
distance = pulse/29/2;
tx_buf[10] = highByte(distance);
tx_buf[11] = lowByte(distance);
tx_buf[12] = highByte(pulse);
tx_buf[13] = lowByte(pulse);
delay(25);
}
</code>
The Master (Receiver Code)
<code>
/*********************************************************************
** SPI-compatible **
** CE - to digital pin 9 **
** CSN - to digital pin 10 (SS pin) **
** SCK - to digital pin 13 (SCK pin) **
** MOSI - to digital pin 11 (MOSI pin) **
** MISO - to digital pin 12 (MISO pin) **
** IRQ - to digital pin 8 (MISO pin) **
*********************************************************************/
#include "NRF24L01.h"
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27,2,1,0,4,5,6,7,3,POSITIVE);
#define TX_ADR_WIDTH 5 // 5 unsigned chars TX(RX) address width
#define TX_PLOAD_WIDTH 32 // 32 unsigned chars TX payload
unsigned char RX_ADDRESS[TX_ADR_WIDTH] =
{
0x34,0x43,0x10,0x10,0x01
};
unsigned char rx_buf[TX_PLOAD_WIDTH] = {0};
unsigned char tx_buf[TX_PLOAD_WIDTH] = {0};
//Ultrasonic
long pulse;
long distance;
// Relays
const int relay1 = 7;
const int relay2 = 4;
const int motor1 = 6;
const int motor2 = 5;
int relay1_status = HIGH;
int relay2_status = HIGH;
int motor1_status = LOW;
int motor2_status = LOW;
void setup()
{
Serial.begin(115200);
lcd.begin(16,2);
lcd.backlight();
lcd.setCursor(0,0);
// Pinmode for Relays
// Our Relays are ACTIVE LOW, so Write Pin High to prevent Switching
// on on startup / reset
digitalWrite(relay1,HIGH);
digitalWrite(relay2,HIGH);
pinMode(relay1,OUTPUT);
pinMode(relay2,OUTPUT);
digitalWrite(motor1,LOW);
digitalWrite(motor2,LOW);
pinMode(motor1,OUTPUT);
pinMode(motor2,OUTPUT);
NRF_Init(); // Initialize IO
NRF_SetRxMode();
Serial.println("RX_Mode start...");
}
void loop()
{
NRF_SetRxMode();
lcd.clear();
if(NRF_Receive(rx_buf))
{
Serial.print("RX = ");
for(int i = 0; i < 10; i++)
{
Serial.write((char)rx_buf[i]);
}
Serial.print("[0x");
Serial.print(rx_buf[10],HEX);
Serial.print(rx_buf[11],HEX);
distance = word(rx_buf[10],rx_buf[11]);
Serial.print("] ");
Serial.print(distance);
Serial.print(" cm [0x");
lcd.setCursor(0,0);
lcd.print(distance);
lcd.print(" cm");
Serial.print(rx_buf[12],HEX);
Serial.print(rx_buf[13],HEX);
pulse = word(rx_buf[12],rx_buf[13]);
Serial.print("] ");
Serial.print(pulse);
Serial.print(" ms [0b");
Serial.print(rx_buf[14],BIN);
Serial.print("] Relay 1=");
if (bitRead(rx_buf[14],1) == HIGH)
{
Serial.print(" OFF");
digitalWrite(relay1,HIGH);
lcd.setCursor(12,0);
lcd.print(" ");
} else {
Serial.print(" ON");
digitalWrite(relay1,LOW);
lcd.setCursor(12,0);
lcd.print("RL2");
}
Serial.print(" Relay 2=");
if (bitRead(rx_buf[14],0) == HIGH)
{
Serial.print(" OFF");
digitalWrite(relay2,HIGH);
lcd.setCursor(8,0);
lcd.print(" ");
} else {
Serial.print(" ON");
digitalWrite(relay2,LOW);
lcd.setCursor(8,0);
lcd.print("RL1");
}
Serial.print(" Mode: ");
Serial.print(rx_buf[15],DEC);
Serial.print(" M1= ");
if(bitRead(rx_buf[14],3) == HIGH)
{
motor1_status = HIGH;
Serial.print("ON");
} else {
motor1_status = LOW;
Serial.print("OFF");
}
if (motor1_status == HIGH) {
analogWrite(motor1,rx_buf[16]);
Serial.print(" Speed ");
Serial.print(rx_buf[16],DEC);
lcd.setCursor(0,1);
lcd.print("M1 ");
lcd.print(rx_buf[16]);
} else {
analogWrite(motor1,0);
lcd.setCursor(0,1);
lcd.print("M1 OFF");
}
Serial.print(" M2= ");
if(bitRead(rx_buf[14],2) == HIGH)
{
motor2_status = HIGH;
Serial.print("ON");
} else {
motor2_status = LOW;
Serial.print("OFF");
}
if (motor2_status == HIGH) {
analogWrite(motor2,rx_buf[17]);
Serial.print(" Speed ");
Serial.print(rx_buf[17],DEC);
lcd.setCursor(8,1);
lcd.print("M2 ");
lcd.print(rx_buf[17]);
} else {
analogWrite(motor2,0);
lcd.setCursor(8,1);
lcd.print("M2 OFF");
}
// clear buffer
Serial.println();
for(int i = 0; i < 32; i++)
rx_buf[i] = 0;
}
delay(250);
}
</code>
The code is available for download from git hub, -> here.
I hope that you enjoyed this tutorial, and found it useful.