Wireless Morse Code Receiver and Transmitter Using Arduino
Wireless Morse Code Receiver and Transmitter Using Arduino
Project Report
Name: Gopal Parwani Roll No: 200260018
Project Title: Morse to Text
Project Abstract: The Morse code is named after one of its co-founders, Samuel Morse who
developed an early forerunner for the modern International Morse Code along with Joseph
Henry and Alfred Vail in around 1837 which used length of the signal and silence between
them to encode alphanumeric characters. It became one of the most used modes of radio
communication in the late 19th century and early 20th century and fell out of use in the
1990s. It’s still used by some navy intelligence specialists, aviators, and amateur radio
operators.
The aim of the project was to develop a simple morse-to-text converter using Arduino
which can show text on screen just by using a single push button.
The outcome of the project was a simple device that could write 38 alphanumeric
characters including space and full stop. However, this list can be extended, and
punctuation marks can be included, and it is trivial to do. International Morse Code
convention was followed as given in the 2009 handbook of the International
Telecommunication Union for radiocommunication to encode the alphanumerics in the
form of dot and dash, but the strict ratio of the length of dot and dash wasn’t imposed to
make the device convenient. The wireless functionality was also introduced so as to make it
possible to send messages to someone sitting away. This required the use of an extra
Arduino and two transceiver modules. However, the range of communication is limited to
100 meters, that too in free space.
Project Introduction: Such a compact device can be used by people suffering from paralysis
who can neither speak nor move their fingers much to use different keys to type different
alphanumeric characters. So the final goal of the project was to develop a simple and easy
to use morse-to-text converter.
It can be divided into two parts: a transmitter and a receiver.
Transmitter
The transmitter has a push-button which when pressed sends a HIGH signal to the Arduino
for the duration it’s pressed. This duration of pressing has been used as a differentiator of
dot and dash. When the button is pressed for a duration of more than th_dd (the threshold
for differentiating between dot(.) and dash(-)), it has to be interpreted as a dash, and when
pressed for less than that it has to be interpreted as a dot. When the button is pressed for
more than th_dd, a red LED will glow indicating that the input will be interpreted as a dash
for sure and we can release the button. If we intend to send a dot as an input, we need to
release the button, before the red LED starts to glow. As soon as the button is released, the
red LED turns off and the GREEN LED will start to glow. This green LED will glow for a
duration of th_new (the threshold time to wait before typing a new alphanumeric
character). If we press the button again while the green LED is on, the green LED will turn
off, and the input (either dot or dash) will be considered as a part of the ongoing letter
however, if we press the button after th_new from the last release, i.e. when the green LED
has turned off on its own, the input will be accounted for next alphanumeric character. In
case, we don’t release the button after pressing, the red LED will turn off after the pressing
PH435 Microprocessor
Project Report
duration has crossed th_new-buffer (we want to turn the red LED off before the th_new
time is crossed that’s why this buffer time is introduced). So we have a upper limit on the
press duration given by th_new-buffer.
As we are working with a push button, there will be effects of bouncing primarily at the
edges i.e. pushing or releasing. That duration is a maximum of 30ms for the button we are
working with so to be on the safe side, we want the press duration of at least th_dbc
(threshold for debouncing) which we have put 50 ms. So, for even a dot we need to push
for at least th_dbc. If we press for less than that the GREEN LED won’t turn ON when
released however the RED LED will turn ON showing that input is wrong.
These durations of the press are transmitted in the form of radio waves.
e.g. we want to write ‘AB’. The Morse code for A is dot-dash ( .- ) and for B is dash-dot-dot-
dot ( -… ). We will press the button momentarily (>th_dbc) and release it before the red LED
starts to glow and when released, the green LED starts to glow. We now have to give a dash
input before the green LED turns off as the dot and dash are part of the same letter i.e. A, so
we press the button again, the green LED goes OFF and we release after the red LED starts
to glow and before it turns off. Now on releasing, the green LED will turn on again and as we
want to start the next letter i.e. B, we will wait for it to turn off. After it turns off, we can
start giving input for B. B is dash-dot-dot-dot, so we push the button till the red LED starts to
glow and release it before it turns off, and on release the green LED will start to glow and
we need to push for dot again before it turns off, when we push it momentarily (>th_dbc),
the green LED goes off and release it before the red LED starts to glow, when released, the
green LED will start to glow. We need to do it two more times for two more dots and wait
for the green LED to go off on its own at the last so that new input goes after B has been
sent.
Receiver
The receiver receives the duration of the press. After it receives one duration which we call
as t_width, it checks whether it’s dot or dash based on its value i.e. if th_dbc< t_≤ th_dd it
will be taken as dot, if th_dd<t_width<(th_new-buffer) it will be taken as dash and then it
waits for th_new duration. If it receives another t_width during this, it will take as a part of
the same letter and it will wait again for th_new. If no t_width received in that duration, it
will check the Morse code of which alphanumeric character matches the pattern of dot and
dash received matches. If it matches some character, it will display that character and delete
that pattern that was received. If it doesn't match any morse code, it will just delete the
received pattern without displaying any character.
Hardware: The following components have been used:
1. Arduino UNO 2 pieces
2. USB cables 2 pieces
3. nRF24L01 transceiver module 2 pieces
This module can be set in both transmitting as well as receiving modes and will be used
wireless communication.
4. Battery 9V (for transmitter) 1 piece
5. Jumper wires
PH435 Microprocessor
Project Report
6. LED 1 green, 1 red
7. Push-button 1 piece
8. Resistors
150Ω to connect in series with LEDs 2 pieces
10kΩ as a pull-down resistor for the push-button 1-piece
If we take too value of this pull down resistor like in mΩ then the current it will draw into
Arduino will be too much and can damage it. If we take a value which is too high, a very
small current from the noise can also set the signal as high even If we have not pressed the
button.
9. Snap connector for 9V battery 1 piece
10. DC Jack 2.1 mm to connect battery to arduino 1 piece
11. (Optional) 10uF capacitor 2 pieces
To add in parallel to the transceiver power supply to avoid noise if you want a high
range.
12. Normal wires for breadboard or breadboard wires
nRF24L01 Module
There are 125 channels spread across 2.4-2.525GHz at a separation of 1MHz. This is also
called 2.4GHz ISM (Industrial, Scientific, and Medical) band. This channel i.e. the frequency
of communication should be the same for the transmitter and receiver and also the address
should be the same so as to ensure that there is no interference from any other device
communicating at the same frequency or channel. In one channel a total of 6 addresses can
be assigned to the entities known as pipes i.e. there we can create 6 pipes or in other words
we can communicate with 6 other modules within a channel and this address is unique to a
pipe in the channel. It is necessary but not sufficient for the transmitter and receiver to
operate in the same channel to communicate. When a transmitter and receiver are
operating on same channel, the address array is transmitted before the data and if it
PH435 Microprocessor
Project Report
matches the address array of the receiver, it accepts the data which is transmitted in the
form of binary. The data transaction between nRF24L01 and Arduino happens in binary.
That binary data is transmitted using radiowaves by GFSK (Gaussian Frequency Shift Keying).
When it’s 1, the frequency of the radiowaves increases by ∆f, and when it’s 0, the frequency
decreases by ∆f w.r.t. the channel frequency. This ∆f is in the range of kHz. For 1Mbps, it’s
160kHz and for 2Mbps it’s 320 kHz.
GND should be connected to the ground of Arduino and VCC should be in the range of 1.9-
3.6 V so we can connect it to 3.3V output of Arduino. However, the other pins can tolerate
5V logic. MISO, MOSI and SCK are SPI pins which have to be connected to the SPI pins of
Arduino
SPI stands for Serial Peripheral Interface. SPI communication has at least three connections
from master to slave :
1. MISO (Master In Slave Out) : This line carries data from the slave to master i.e. from
nRF24L01 to arduino
2. MOSI (Master Out Slave In) : This line carries data from Arduino to nRF24L01
3. SCK (Serial Clock) : This line provides a clock signal generated by Arduino to a slave which
is our nRF24L01 to synchronize data transmission between the devices.
4. SS (Slave Select) : This is an optional pin and is used when there are several slaves
connected to Arduino and it wants to communicate to specifically to one of them. This pin is
not there in the nRF24L01.
However, there is CSN or CNS pin which serves the purpose of SS. To initiate SPI data
transaction between microcontroller and transceiver, we need to set CSN (Chip Not Select)
to low. When CE (Chip Enable) is low, the module gets ready for data transmission or
PH435 Microprocessor
Project Report
reception. This means both CSN and CE follow logic-low. IRQ stands for Interrupt Request
and is an optional pin. It can be used to generate an interrupt signal to the Arduino. It is also
logic-low.
Methods:
Arduino Arduino
Transmitter Receiver
connected
connected to connected to
nRF24L01 nRF24L01
battery PC
Display Screen
Push-button
and LED
circuit on
breadboard
#include <avr/interrupt.h>
#include <TimerOne.h>
#include <SPI.h>
#include <nRF24L01.h>
#include <RF24.h>
avr/interrupt.h : For working with interrupts in avr microcontrollers that is used in our
Arduino
TimerOne.h : For timer interrupts.
SPI.h : This library provides support for serial communication between a master device
which is our arduino here with peripheral or slave devices such as our transceiver nRF24L01.
Serial communication means bit-by-bit communication.
There are other two libraries RF24.h and nRF24L01.h. Both of them are essential for
setting the parameters like channel, transmission rate, transmission power, etc. However,
RF24L01 is a high-level library and nRF24L01 is a low-level library i.e. RF24L01 is responsible
for the basic configuration of the module while nRF24L01 gives more control over can help
in configuring some things to non-default things.
This code snippet creates an RF24 object whose name is radio and the arguments are CE
(Chip Enable) and CSN (Chip Select Not) pins set to 7 and 8 respectively.
Then we create an array of byte datatype named address and set it to “00001”. This
address array is address[6] ={‘0’, ’0’, ’0’, ’0’, ’1’, ‘\0’} where last is the null character. this is
stored as hexadecimal so address[6] = {0x30, 0x30, 0x30, 0x30, 0x31, 0x00}. We are not
setting any channel by ourselves here so channel(76) is chosen by default in both
transmitter and receiver i.e. the channel of the 2.476 GHz band.
The variables x, y and z are created to see whether the bouncing is still affecting our device
or not. Ideally all of them should be equal and they are in fact found to be equal when we
press the button not in a hurry and when we press it rapidly, then the values of x, y and z
may not be equal because the interrupts might happen before we the values of some
variable is assigned but even then we don’t see any discrepancies in the transmission. The
rest of the variables are explained in the comments of the code snippet.
void setup() {
Serial.begin(9600);
radio.begin();
radio.openWritingPipe(address);
radio.setPALevel(RF24_PA_MIN);
radio.stopListening();
Timer1.initialize(1000);
Timer1.attachInterrupt(milisec);
digitalWrite(red_LED,red_LED_state);
digitalWrite(green_LED,green_LED_state);
}
Next, we do the initial setup, we radio.begin() initializes the nRF24L01 module and sets the
CE and CSN pins to low state and then the radio.openWritingPipe(address) assigns the
PH435 Microprocessor
Project Report
address to a transmitting pipe of the channel. radio.setPALevel(RF_24_PA_MIN) sets the
power amplification to the minimum level as we are placing the transmitter and receiver
nearby. There are other two possible levels RF_24_PA_HIGH and RF_24_PA_MAX. The latter
can provide a max range of 100m in free space but we will have to add capacitors to denoise
the transceiver modules. radio.stopListening () puts the transceiver into transmitter mode.
Then we initialize timer interrupt and attach interrupts for green LED i.e. GLED_ON at pin 2
and GLED_OFF at pin 3 of the arduino which will get triggered when on falling and rising
edges or when the button is pressed and released respectively. We enable the interrupts
and set the green and red LEDs to low initially.
void GLED_OFF() // This function is called when there is RISING edge at pin3
or the green LED has been glowing more than th_new
{
if(t){green_LED_state = LOW; t=0;}//check if it was genuine press or just
bouncing
else{t_push = i;if((t_push-t_pull)>th_dbc){t_push_real = t_push;
green_LED_state = LOW; z++;
}}
}
void milisec()
{
i++;
}
The code snippet above shows our functions used. When there is a falling edge on pin2
which can be both due to bouncing and release of the button by us, GLED_ON() is called and
it stores the timestamp in t_pull and then it checks whether the release was by us, it will use
an if-statement. If the interrupt was triggered by bouncing, t_pull-t_push will be smaller
than th_dbc since bouncing is a quick pulse of around at max 30ms and our th_dbc is 50ms
so one requirement for genuine push is t_pull – t_push>th_dbc and since we have put an
upper limit for our t_width, t_pull-t_push should be less than th_new-buffer. If these two
conditions are satisfied then only it is taken as a real push and t_push_real is assigned the
value of that timestamp which is t_pull, green_LED_state is set to high, t_width is assigned
value and the value of y is increased by 1.
PH435 Microprocessor
Project Report
When the function GLED_OFF() is called, it checks whether it has been called because the
green LED has been glowing for more than th_new and this is represented by t=1 which gets
assigned this value in the loop when it occurs just before the GLED_OFF() is called and if that
is the case it assigns low value to green_LED_state and puts t=0 again. Remember that t is a
boolian variable. If that’s not the case, it has been called by the interrupt and it assigns that
timestamp to t_pull and this could have occurred because of bouncing or pushing the
button. Since bouncing effects kick in on the change of the state of button, we are checking
only at the edges and we are right in doing so because when the switch is in steady state the
variation in the voltage is few mV which is far from enough to trigger the interrupts. So the
kind of bouncing which can trigger this interrupt will occur after we have released the push-
button whose for which t_pull is the mark. We check how long it took to rise again from
t_pull. If it took less than th_dbc it was bouncing as we have already advised the user to
have a gap of atleast th_dbc between a push and a release and also between a release and
push so nothing will happen but if this gap t_push-t_pull is greater than th_dbc then we can
say that it was a genuine push and t_push_real = t_push, green_LED_state is put low and z is
increased by 1.
millisec() is our timer and i is the count of milliseconds elapsed since the code started
working.
void loop() {
//turns on the RED LED if pressed for more than th_dd
if(t_push_real>t_pull_real && millis()-t_push_real>th_dd && !(millis()-
t_push_real>th_new-buffer))
{digitalWrite(red_LED,HIGH);}
else{digitalWrite(red_LED,LOW);}
#include <TimerOne.h>
#include <SPI.h>
#include <nRF24L01.h>
#include <RF24.h>
String input_array = "" ; //empty array to store the incoming pattern of dots
and dash for one alphanumeric character
PH435 Microprocessor
Project Report
Now we have i for timer of the Arduino at the receiver, t_last to store the timestamp when
t_width is received and t_width to store the value of received an empty input_array to store
the incoming data temporarily.
Then we have two arrays morse and alpha.
const String alpha[38] = {"A", "B", "C", "D", "E", "F", "G", "H", "I", "J",
"K", "L", "M", "N", "O","P", "Q", "R", "S", "T",
"U", "V", "W", "X", "Y", "Z", "1", "2", "3", "4",
"5", "6", "7", "8", "9", "0", " ", "."};
//Array of corresponding Morse codes in same order
const String morse[38] = {".-", "-...", "-.-.", "-..", ".", "..-.", "--.",
"....", "..", ".---", "-.-", ".-..", "--",
"-.", "---", ".--.", "--.-", ".-.", "...", "-",
"..-", "...-", ".--", "-..-", "-.--", "--..",
".----", "..---", "...--", "....-", ".....", "-
....", "--...", "---..", "----.", "-----",
".......", ".-.-.-"};
Then we have function dord which takes an array as the argument and updates the array
based on the value of t_width received and puts t_width = 0 again.
void setup() {
Serial.begin(9600);
radio.begin();
radio.openReadingPipe(0, address);
radio.setPALevel(RF24_PA_MIN);
radio.startListening();
Timer1.initialize(1000);
Timer1.attachInterrupt(millisec);
interrupts();
}
This is similar to transmitter the difference here is the we have
radio.openReadingPipe(0,address) which assigns the pipe 0 the address. There are six pipes
numbered from 0 to 5 and we put the transceiver in receiver mode by radio.startListening().
void loop() {
if (radio.available()) {
radio.read(&t_width, sizeof(t_width)); // receive the signal
PH435 Microprocessor
Project Report
dord(input_array);
//Serial.println(input_array);
}
if(i-t_last>=th_new) // after wait of th_new from the last received signal
{for(int k=0; k<38; k++)
{if(morse[k]==input_array){Serial.print(alpha[k]); break;} // check if
the input array matches
// any of the
elements of morse array
else{continue;}}
input_array = "" ; //make the array empty again for new letter
}
In the loop, we check if there is data to receive by radio.available(). If there is then the
t_width is given the value received. We call the dord function to update the input_array and
store the timestamp in t_last.
Now we check if th_new time has passed since the last t_width was received. If it has, we
will check whether the sequence of dot and dash matches any of the 38 elements of the
morse array. If input_array is same to morse[k], alpha[k] which is the corresponding
alphanumeric character is printed, the for loop breaks and the input_array is made empty
again.
At last we have millisec() for timer.
void millisec()
{
i++;
}
Conclusion: After the completion of the project, we have an easy-to-use device to convert
morse to text with just one push button. It can be useful for differently abled people who
may be suffering from paralysis or maybe some other type of motor disease that doesn’t
allow them to move their hands much. Further, there is also a possibility of sending the
input by the duration of closing the eyes because when eyes are closed the power of the
occipital alpha waves increases which can be fed into Arduino as high signal rather than
using a push button.
Author’s contribution: Lone wolf.
Acknowledgments: By doing this project as a part of the course PH 435, Microprocessors
Lab, I got an opportunity to learn and explore many things like wireless modules which
would not have been possible without Mr. Varad Mahashabde, one of the TAs of the course
and a very good friend of mine who motivated me to make the device wireless. I’m grateful
PH435 Microprocessor
Project Report
to my friends Mr. Reet Mhaske and Mr. Shashwat Chakraborty who donated the nRF24L01
modules to the Electronics Lab when they completed this course. Special thanks to Mr.
Shashwat Chkraborty who helped me in understanding the nRF24L01 modules. Thanks to
Nitin Pawar Sir who helped me get around the problem of bouncing and Swapnali Gharat
Mam suggested an LED to indicate the difference between dot and dash. Also, thanks to the
Snehal Mam and Nilesh Sir for providing the components on time. Finally, this would not
have been possible without the course instructor Prof. Pramod Kumar, and the TAs of the
course who put efforts in teaching and made stuff easy to understand. Special thanks to my
project TA Mr. Sanyam Singhal who at first helped me to converge on this project idea and
brainstormed with me and motivated me to find the solutions of the problems and
challenges faced in the way.
References:
Arduino reference
Arduino forum
nRF24L01 Datasheet
nRF24L01 Tutorial
Wikipedia
Tinkercad
Occipital alpha-band brain waves
Appendix:
Transmitter code
Receiver code