Wireless Communication Using Arduino and RF Module
by ICARUS in Circuits > Arduino
2308 Views, 20 Favorites, 0 Comments
Wireless Communication Using Arduino and RF Module
Firstly let's talk about wireless transmission.
This is the era of wireless communication/technology. We can see the rise in the number of devices that are using Bluetooth and WiFi these days. With the IoT revolution in the electronics industry as well as the ever-increasing devices that needed internet connectivity we had to shift to IPv6 from IPv4.
With these technological advances, we are forgetting about some simpler and older radio frequency devices and schemes. More and more people are publishing sophisticated projects which use IoT, Lora-WAN, and stuff even when it is not required in that project. A small project doesn't need such powerful wireless technology. It is a waste of resources.
Now let's learn a bit about our RF Tx/Rx pair.
The Tx/Rx pair that is used in this project uses a frequency of 433MHz which falls in the ISM band. ISM stands for Industrial, Scientific, and Medical. This band of frequency is free to use and is unlicensed.
The Tx/Rx pair employs an ASK modulation scheme for transmitting and receiving data. ASK stands for Amplitude shift keying which is one of the digital modulation techniques others being Frequency shift keying (FSK) and Phase shift keying (PSK).
Let's learn more about ASK. As I have mentioned earlier that ASK is a digital modulation technique, which means it can be used to transmit digital data wirelessly over a carrier of 433 MHz. The carrier is a sine wave of 433MHz which is switched on or off depending on the digital bit that is to be transmitted. In this way, by switching the carrier on and off one can transmit 0 or 1 respectively.
For example, If I want to send 1010, I will first switch on the carrier, this carrier will be received on the receiver and it will interpret that a bit 1 has been transmitted. Similarly, if I now switch off the carrier, the receiver will not receive the signal and it will interpret that a 0 has been transmitted. See the picture for understanding the concept clearly.
Supplies
Name of Component | Quantity
- Arduino Uno --->1
- Arduino Nano --->1
- RF module (433MHZ) --->1
- LCD 20*4 --->1
- I2C --->1
- Tiny Breadboard --->1
- Jumpers --->1
- Switch --->1
- Battery 9v --->1
A Full Video
A fast video describing all steps in an easy way
Explaination
Amplitude Shift Keying
ASK
ASK is a type of Amplitude Modulation that represents the binary data in the form of variations in the amplitude of a signal.
Any modulated signal has a high frequency carrier. The binary signal when ASK modulated, gives a zero value for Low input while it gives the carrier output for High input.
The following figure represents ASK modulated waveform along with its input.
To find the process of obtaining this ASK modulated wave, let us learn about the working of the ASK modulator.
ASK Modulator
The ASK modulator block diagram comprises of the carrier signal generator, the binary sequence from the message signal and the band-limited filter. Following is the block diagram of the ASK Modulator.
The carrier generator, sends a continuous high-frequency carrier. The binary sequence from the message signal makes the unipolar input to be either High or Low. The high signal closes the switch, allowing a carrier wave. Hence, the output will be the carrier signal at high input. When there is low input, the switch opens, allowing no voltage to appear. Hence, the output will be low.
The band-limiting filter shapes the pulse depending upon the amplitude and phase characteristics of the band-limiting filter or the pulse-shaping filter.
ASK Demodulator
There are two types of ASK Demodulation techniques. They are −
● Asynchronous ASK Demodulation/Detection
● Synchronous ASK Demodulation/Detection
The clock frequency at the transmitter when matches with the clock frequency at the receiver, it is known as a Synchronous method, as the frequency gets synchronized. Otherwise, it is known as Asynchronous.
Asynchronous ASK Demodulator
The Asynchronous ASK detector consists of a half-wave rectifier, a low pass filter, and a comparator. Following is the block diagram for the same.
The modulated ASK signal is given to the half-wave rectifier, which delivers a positive half output. The low pass filter suppresses the higher frequencies and gives an envelope-detected output from which the comparator delivers a digital output.
Synchronous ASK Demodulator
Synchronous ASK detector consists of a Square law detector, a low pass filter, a comparator, and a voltage limiter. Following is the block diagram for the same.
The ASK-modulated input signal is given to the Square law detector. A square law detector is one whose output voltage is proportional to the square of the amplitude-modulated input voltage. The low pass filter minimizes the higher frequencies. The comparator and the voltage limiter help to get a clean digital output.
Wiring
For the Receiver (Arduino Nano)
- On the I2C adapter
- Device----------> Nano
- VCC 5v
- GND GND
- SDA A4
- SCL A5
- On the RF chip (Reciever)
- VCC 5v or D12
- GND GND
- Data* D11
- On the switch
- Middle pin Gnd
- Other pin Power Supply-GND
- Other
- Power Supply+ VIN
On the RF chip (Transmitter)
- Device--------> Uno
- GND D8
- VCC D9
- DATA D10
Downloads
CAD Design
I used Fusion 360 To make the Enclosure of the sender and receiver circuit
then I sent the DXF files to a laser-cutting machine that made the Enclosure parts
The Part is made with wood of 3 mm in thickness
Coding and Testing
For Coding, first, you need to test the I2C and take the address
you can make that by the following Code and uploading it on the Arduino Nano that connected to LCD
I2C Test
#include <Wire.h>
void setup()
{
Wire.begin();
Serial.begin(115200);
Serial.println("\nI2C Scanner");
}
void loop()
{
byte error, address;
int nDevices;
Serial.println("Scanning...");
nDevices = 0;
for(address = 0; address <= 127; address++ )
{
// The i2c_scanner uses the return value of
// the Write.endTransmisstion to see if
// a device did acknowledge to the address.
Wire.beginTransmission(address);
error = Wire.endTransmission();
if (error == 0)
{
Serial.print("I2C device found at address 0x");
if (address<16)
Serial.print("0");
Serial.print(address,HEX);
Serial.println(" !");
nDevices++;
}
else if (error==4)
{
Serial.print("Unknow error at address 0x");
if (address<16)
Serial.print("0");
Serial.println(address,HEX);
}
}
if (nDevices == 0)
Serial.println("No I2C devices found\n");
else
Serial.println("done\n");
delay(8000); // wait 8 seconds for next scan
}
Sender Code
that code will make you able to know whether the I2C is Connected with LCD and the address
then we need to upload the following code to the Arduino Uno, it's so simple you can understand it easily
#include <VirtualWire.h>
#include <elapsedMillis.h> //For the status on the reciever
String serialdata; //Holds data coming in
char msg[50]; //Char array to send data out with
String statustext="6"; //This is the text sent every interval
elapsedMillis packettimer;//this is the timer
#define interval 50//Lower this if you send long packets
void setup()
{
pinMode(8, OUTPUT);//Module GND
pinMode(9, OUTPUT); //Module VCC
digitalWrite(9, HIGH); // turn the VCC to HIGH, set to LOW for default off
digitalWrite(8, LOW); // turn the GND to LOW
vw_set_tx_pin(10);
vw_setup(2500); // Bits per sec
Serial.begin(9600);
packettimer = 0;//Reset the timer
Serial.println ("Serial Port Open");
}
void loop()
{
if (Serial.available()){
delay (50);
Serial.println ("I found Serial Data");// If you are having issues, these will help you find where your code doesnt work.
serialdata=Serial.readString();//put text from serial in serialdata string
if (serialdata.startsWith("off")) {
digitalWrite(9, LOW); // turn the VCC to LOW
digitalWrite(8, LOW); // turn the GND to LOW
Serial.println("");
Serial.println("Module OFF");
Serial.println("");
}
else if (serialdata.startsWith("on")) {
digitalWrite(9, HIGH); // turn the VCC to HIGH
digitalWrite(8, LOW); // turn the GND to LOW
Serial.println("");
Serial.println("Module ON");
Serial.println("");
}
else {
serialdata.toCharArray(msg, 50);//convert serialdat the the msg char array
Serial.println ("I converted it to a CHAR ARRAY");
Serial.println("Text to be Sent-");//debugging
Serial.println("");//debugging
Serial.print(msg);//debugging
Serial.println("");//debugging
vw_send((uint8_t *)msg, strlen(msg));
vw_wait_tx();
Serial.println ("MSG send request");
// driver.waitPacketSent();//wait until it is sent
Serial.println ("Data Sent");
}
}
if (packettimer > interval) {
statustext.toCharArray(msg, 50);//convert the statustext string to msg char array
vw_send((uint8_t *)msg, strlen(msg)); //line that sends the msg variable
vw_wait_tx();//wait untill sent
}
}
//Code mainly From Arduino.cc
Explain of Receiving Code
Here we go to the hard part
Uploading the Reciever code to the Arduino nano
Include Libraries and define the variables
#include <VirtualWire.h>
#include <Wire.h> //IIC Library
#include <LiquidCrystal_I2C.h>//For the screen
#include <elapsedMillis.h>//For the Status
LiquidCrystal_I2C lcd(0x3F,20,4); // set the parameters
boolean constat=1;//Status Variable 1=On at start, 0=off at start
int line;//Status code moves cursor, variable to store DESIRED line number
boolean splashcleared;//Bool to hold if splash screen has been cleared
String text = " ";//String to hold the text
elapsedMillis packettimer;//The timer for status
#define interval 1500//The threshold for how long it can go without "6"(The connection indicator)
I used http://maxpromer.github.io/LCD-Character-Creator/ to draw inside the Pixels
byte cchar[] = {
0x1F,
0x15,
0x0E,
0x04,
0x04,
0x04,
0x04,
0x04
};
byte bar1[8] = {
0x00,
0x00,
0x00,
0x03,
0x03,
0x03,
0x1B,
0x1B
};
byte bar2[8] = {
0x03,
0x03,
0x1B,
0x1B,
0x1B,
0x1B,
0x1B,
0x1B
};
//Thanks to http://maxpromer.github.io/LCD-Character-Creator/
In this part, we will initialize the symbols and prepare the LCD
void setup()
{
pinMode(12, OUTPUT); //Module VCC
digitalWrite(12, HIGH); // turn the VCC to High
vw_set_rx_pin(11);// set you rx pin
vw_setup(2500); // Bits per sec. Lower for further distance. Make sure this matches the number on the transmitter
vw_rx_start(); // Start the receiver PLL running
splashcleared=0;//make sure that this is 0
lcd.init();//start the screen
//initialize the symbols
//setCursor comands note- Counting starts at 0 NOT 1
lcd.createChar(0, cchar); //Rf symbol
lcd.createChar(1, bar1); //Bar 1 and 2
lcd.createChar(2, bar2); //Bar 3 and 4
Serial.begin(9600);//Open serial port, add the !serial thing for leonardo
lcd.backlight();//Turn on the backlight
lcd.clear();//Clear the screen
lcd.home();//go home
//display Home screen This will be cleared once The siglal indicator "6" is sent
lcd.setCursor (5,0);
lcd.print("RF--Screen");
lcd.setCursor (0,1);
lcd.print("By ICARUS");
lcd.setCursor (0,2);
lcd.print("Connection Status-");
lcd.setCursor (0,3);
lcd.print("Inactive");
delay (1200);//set this to the interval
}
The rest of the code is if conditions to know whether the upcoming signal is a word to show or a command for LCD
void loop()
{
uint8_t buf[VW_MAX_MESSAGE_LEN];
uint8_t buflen = VW_MAX_MESSAGE_LEN;
if (packettimer< interval){//It the status of the connection is good and "6" was recieved in the last 1.5 secs.
if (splashcleared==0){//if screen hasn't been cleared of status
lcd.clear();//clear it
splashcleared=1;//make sure it won't be again
}
}//make sure the screen wont clear again
if (constat==1){ //If the status bar is enabled
if (packettimer< interval){//It the status of the connection is good and "6" was recieved in the last 1.5 secs.
lcd.setCursor (17,0); //go to 3rd last position
lcd.write(0); //RF symbol
lcd.write(1);//bar one
lcd.write(2);//Bar two
}
else if (packettimer > interval) {//It the status of the connection is bad and "6" was not recieved in the last 1.5 secs.
//we will blink a X by the rf symbol
lcd.setCursor (17,0);//go to 3rd last position
lcd.write(0); //RF symbol
lcd.setCursor (18,0);//go to 2nd last position
lcd.print ("X ");//To add the X
delay (450);// wait
lcd.setCursor (18,0);////go to 2nd last position
lcd.print (" ");//To erase the X
delay (400);//wait
}
}
if (vw_get_message(buf, &buflen)) // Non-blocking
{
packettimer = 0;//Reset timer when data is recieved
text.remove(0);//clear all text from string: text
text=(char*)buf;//put new data into string "text"
text.remove(buflen-1);//Remove garbage after the end of the text
lcd.setCursor(0,line);//Restore cursor to original position
//Most of these commands are self explanitory...
if (text.startsWith("1")) {
lcd.setCursor(0,0);
line=0;
}
else if (text.startsWith("2")) {
lcd.setCursor(0,1);
line=1;
}
else if (text.startsWith("3")) {
lcd.setCursor(0,2);
line=2;
}
else if (text.startsWith("4")) {
lcd.setCursor(0,3);
line=3;
}
else if (text.startsWith("clear")) {
lcd.clear();
}
else if (text.startsWith("6")) {//"6" is sent every 300 ms to check the signal, and we dont want it to display
packettimer = 0;//Reset timer when data is recieved
if (splashcleared==0){//if screen hasn't been cleared of status
lcd.clear();
splashcleared=1;//make sure the screen wont clear again
}
}
else if (text.startsWith("backlight")) {
lcd.backlight();
}
else if (text.startsWith("nobacklight")) {
lcd.noBacklight();
}
else if (text.startsWith("status")) {
lcd.setCursor (17,0);//Go to the third last spot
lcd.write(0);//Show the Rf symbol
lcd.write(1);//Show bar set 1
lcd.write(2);//Show bar set 2
constat=1;//(ConnectionStatus)this variable will make the signal show
}
else if (text.startsWith("nostatus")) {
lcd.setCursor (17,0);//Go to the third last spot
lcd.print (" ");//write it over with 3 spaces
constat=0;//(ConnectionStatus)this variable will block the signal from showing
}
else if (text.startsWith("{")) {//if we want to sen a command word to display on screen, we precede it with{
text.remove (1); //remove the"{", This must be second last of if statements
}
else {//this MUST be last statement
packettimer = 0;//Reset timer when data is put onto the screen
lcd.setCursor(0,line);//Go to the specified line
lcd.print(text);//If word isn't a command print it
}
}
}
the Reciever Code
#include <VirtualWire.h>
#include <Wire.h> //IIC Library
#include <LiquidCrystal_I2C.h>//For the screen
#include <elapsedMillis.h>//For the Status
LiquidCrystal_I2C lcd(0x3F,20,4); // set the parameters
boolean constat=1;//Status Variable 1=On at start, 0=off at start
int line;//Status code moves cursor, variable to store DESIRED line number
boolean splashcleared;//Bool to hold if splash screen has been cleared
String text = " ";//String to hold the text
elapsedMillis packettimer;//The timer for status
#define interval 1500//The threshold for how long it can go without "6"(The connection indicator)
//initialize the symbols
void setup()
{
pinMode(12, OUTPUT); //Module VCC
digitalWrite(12, HIGH); // turn the VCC to High
vw_set_rx_pin(11);// set you rx pin
vw_setup(2500); // Bits per sec. Lower for further distance. Make sure this matches the number on the transmitter
vw_rx_start(); // Start the receiver PLL running
splashcleared=0;//make sure that this is 0
lcd.init();//start the screen
//initialize the symbols
//setCursor comands note- Counting starts at 0 NOT 1
lcd.createChar(0, cchar); //Rf symbol
lcd.createChar(1, bar1); //Bar 1 and 2
lcd.createChar(2, bar2); //Bar 3 and 4
Serial.begin(9600);//Open serial port, add the !serial thing for leonardo
lcd.backlight();//Turn on the backlight
lcd.clear();//Clear the screen
lcd.home();//go home
//display Home screen This will be cleared once The siglal indicator "6" is sent
lcd.setCursor (5,0);
lcd.print("RF--Screen");
lcd.setCursor (0,1);
lcd.print("By ICARUS");
lcd.setCursor (0,2);
lcd.print("Connection Status-");
lcd.setCursor (0,3);
lcd.print("Inactive");
delay (1200);//set this to the interval
}
void loop()
{
uint8_t buf[VW_MAX_MESSAGE_LEN];
uint8_t buflen = VW_MAX_MESSAGE_LEN;
if (packettimer< interval){//It the status of the connection is good and "6" was recieved in the last 1.5 secs.
if (splashcleared==0){//if screen hasn't been cleared of status
lcd.clear();//clear it
splashcleared=1;//make sure it won't be again
}
}//make sure the screen wont clear again
if (constat==1){ //If the status bar is enabled
if (packettimer< interval){//It the status of the connection is good and "6" was recieved in the last 1.5 secs.
lcd.setCursor (17,0); //go to 3rd last position
lcd.write(0); //RF symbol
lcd.write(1);//bar one
lcd.write(2);//Bar two
}
else if (packettimer > interval) {//It the status of the connection is bad and "6" was not recieved in the last 1.5 secs.
//we will blink a X by the rf symbol
lcd.setCursor (17,0);//go to 3rd last position
lcd.write(0); //RF symbol
lcd.setCursor (18,0);//go to 2nd last position
lcd.print ("X ");//To add the X
delay (450);// wait
lcd.setCursor (18,0);////go to 2nd last position
lcd.print (" ");//To erase the X
delay (400);//wait
}
}
if (vw_get_message(buf, &buflen)) // Non-blocking
{
packettimer = 0;//Reset timer when data is recieved
text.remove(0);//clear all text from string: text
text=(char*)buf;//put new data into string "text"
text.remove(buflen-1);//Remove garbage after the end of the text
lcd.setCursor(0,line);//Restore cursor to original position
//Most of these commands are self explanitory...
if (text.startsWith("1")) {
lcd.setCursor(0,0);
line=0;
}
else if (text.startsWith("2")) {
lcd.setCursor(0,1);
line=1;
}
else if (text.startsWith("3")) {
lcd.setCursor(0,2);
line=2;
}
else if (text.startsWith("4")) {
lcd.setCursor(0,3);
line=3;
}
else if (text.startsWith("clear")) {
lcd.clear();
}
else if (text.startsWith("6")) {//"6" is sent every 300 ms to check the signal, and we dont want it to display
packettimer = 0;//Reset timer when data is recieved
if (splashcleared==0){//if screen hasn't been cleared of status
lcd.clear();
splashcleared=1;//make sure the screen wont clear again
}
}
else if (text.startsWith("backlight")) {
lcd.backlight();
}
else if (text.startsWith("nobacklight")) {
lcd.noBacklight();
}
else if (text.startsWith("status")) {
lcd.setCursor (17,0);//Go to the third last spot
lcd.write(0);//Show the Rf symbol
lcd.write(1);//Show bar set 1
lcd.write(2);//Show bar set 2
constat=1;//(ConnectionStatus)this variable will make the signal show
}
else if (text.startsWith("nostatus")) {
lcd.setCursor (17,0);//Go to the third last spot
lcd.print (" ");//write it over with 3 spaces
constat=0;//(ConnectionStatus)this variable will block the signal from showing
}
else if (text.startsWith("{")) {//if we want to sen a command word to display on screen, we precede it with{
text.remove (1); //remove the"{", This must be second last of if statements
}
else {//this MUST be last statement
packettimer = 0;//Reset timer when data is put onto the screen
lcd.setCursor(0,line);//Go to the specified line
lcd.print(text);//If word isn't a command print it
}
}
}
Final
Command List (Case-sensitive)
- status, shows status icon in top right corner (change the boolean constat to 0 to disable by default)
- clear, will clear the screen
- on, will turn on the transmitter
- off, will turn off the transmitter
- {, If you precede a command with this, it will display the command on the LCD
- nostatus, hides status icon (default)
- backlight, activates backlight (default)
- nobacklight, deactivates backlight (to set as default comment out lcd.backlight() is setup)
- 1, set line number to one
- 2, set line number to two
- 3, set line number to three
- 4, set line number to four