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

1673176765438.jpg
816fsLUV-xL._AC_SL1500_.jpg
Wireless With Arduino.png

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

arduino-uno-r3.png
RF Module 433MHZ.png
1.jpg
61xWHXGsnsL._SL1000_.jpg
20x4-LCD-BLUE.jpg
Arduino-Nano-V3.0-CH340-Chip-Mini-USB-Cable.jpg
61tHo16P1ZL._AC_UF894,1000_QL80_.jpg
41nCh+ZtGxL._SL1004_.jpg
Switch.jpg

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

Wireless Communication Using Arduino

A fast video describing all steps in an easy way

Explaination

2.jpg
5.png
4.jpg
3.png

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

Reciever.png
Sender.png
IMG_20221213_014157.jpg

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


CAD Design

Reciver Enclosure v5.png
Transmitter Enclosure v4.1.png
Transmitter Enclosure v4.png
1.png
1673176765309.jpg

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

Reciver Algorithm.png
Sender Algorithm.png

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

1673176765323.jpg
1673176765335.jpg
1673176765348.jpg
1673176765361.jpg
1673176765374.jpg
1673176765386.jpg
1673176765399.jpg
1673176765411.jpg
1673176765424.jpg
1673176765438.jpg
1673176765450.jpg
1673176765463.jpg

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