Morse code generator using Arduino…

on , most recently modified on

‘Micro-controllers’ was a theory chapter in the Computer Science course I had in my pre-university years, and yet I hadn’t really seen one up close until the end of my first year at MIT, Manipal. Learning of micro-controllers was not a big deal; the real deal was writing a micro-controller program on your own without help(and I don’t mean simple programs that involve flashing LEDs in a sequence at best). Tens of my colleagues have made robots using micro-controllers, but only because they were parts of guided tours. They hadn’t much to do with the robots they owned. Not being rude here, just ask yourselves, “Am I capable of writing programs for the chip from my bot and incorporate it in a different bot?” Well, I am. Now, this is certainly not the first thing I’ve ever made; it’s just the first thing I have shared for everyone to see.

Foremost, you need to know my motivation for this project, so here goes. MIT had offered to take classes for people wanting to earn an Amateur Radio license. This seems like a great opportunity to be one amongst about half a million around the world with marginally special privileges. These people are actually hobbyists and follow a certain convention – one of them being, you guessed it, Morse code communication. Thus, people who wished to apply for the general license needed to know Morse Code.

As I see it, Morse Code is not that difficult to learn. What’s difficult, however, is that after five minutes of practising, people get exasperated and tend to stop. It is not the fault of anyone really; we’re just not naturally wired to tolerate monotony which is perhaps 90% of what Morse code sounds like. So, I decided to get a Morse code tutor, who would reward me every time I recognised a letter correctly. The only way I could do it, without paying anyone, is by setting up one of my micro-controller boards to spew Morse code of a randomly chosen alphabet and wait form me to get it right. Yeah, that’s far more complicated than what I ended up building. It is funny how I thought of something complex before something simple. I guess that’s sums up how I came upon the idea of the Morse code generator.

Let us familiarise ourselves with the concept of Morse Code, shall we?

Image taken from Wikipedia

Particulars of Morse Code communication:

  1. Basic time unit: This is the duration of the shortest pulse in a Morse code transmission, i.e. duration of the sound of the ‘dot’. This is what decides the speed and universality of your code. How universality? Well, the world has it’s fair share of slow readers, so, even though you’re following a convention you’re making it difficult for others to follow your train of Morse coded thought.
  2. Duration of a ‘dash’: This has been set to be equal to the duration of three dots.
  3. Intra-letter gaps: This is the time of silence(or relaxation time) between two consecutive pulses within a letter; it has to equal to the duration of a ‘dot’. (See the picture above, the spaces between dots and dashes are as much as a dot.)
  4. Inter-letters gaps in a word: This should be equal to duration of three dots, akin to a ‘dash of silence’.
  5. Gaps between words: This is a bit ambiguous. Generally it is defined as 7 dots, but people, who have reached a peak in their transmission speeds, use 6 dots (2 dashes).
  6. Nomenclature of the pulses based on sounds: The dots and dashes in Morse code have been named onomatopoeically to make it easier to learn by letting you speak the sounds out.
    • A ‘dot’ sounds like “dit”.
    • A ‘dash’ sounds like “dah”.
    • A ‘dot’, that is not the last dot in the sequence allotted to a letter, sound like “di”.

e.g. The letter ‘S’ in Morse code sounds like di-di-dit and ‘Q’ sounds like dah-dah-di-dah.

When you practice sounding the dots and dashes follow a fixed convention. Have fun with it once in a while. Stuck in traffic? Interpret the Morse codes people inadvertently transmit while sounding their horns. Go crazy with it. In a while, you’ll actually think in Morse code.

Morse Code circuit

Since the speaker is, by nature, an inductor I’ve skipped other impedances. If you plan on operating in lower frequencies (under 100Hz), attach a 10nF capacitor in cascade…

Things I’ve used in the making of the circuit:

  • An Arduino board(any Arduino board will do; I chose Uno, because it was ‘the one’ for me, literally) and the Arduino software.
  • A speaker (or a piezo).
  • Connecting wires.
  • A USB cable – male A to male B (for programming the Arduino and it’s serial communication with the PC).
  • Lots of patience.

Some of you already know this; Arduino is one of the best platforms for learning and implementing developer electronics for the following reasons:

  • It is open source; the hardware bound by Creative Commons licenses, much like the contents of this blog, and programming environment by GPL. This would mean that any derivative work you publish should at least mention the original equipment; other than that, you’re free to use it howsoever you’d see fit.
  • The programming environment is user friendly and the language for programming is similar to the C language. This makes it easy for people with no prior knowledge of micro-controllers to self learn this piece of awesomeness.
  • Engineers and hobbyists have already implemented a multitude of cool projects and put ’em all up online (yes, there do exist good people in this world). This means that you need not learn everything from scratch. Many have already done the hard work of implementing things you’ve thought of. So, you could pick up a project you found online and implement your own version of it with minor improvements.
  • Many Arduino projects have standard hardware requirements which means it is possible to make PCBs with certain fixed part on it and still make it run different programs. These standard boards called Shields (since they shield the main-board), can easily be attached and detached from the board to allow rapid-prototyping.

Back to the project now. We start programming here…

Programming

There are no hard steps to follow here. It’s mostly bit and pieces of advice you could use to build one of your own Morse code generators. First, you’ve got to decide the duration of a dot. Every other thing will be relative to this duration. You can either fix the duration to a specific value in milliseconds like so…

#define dotPeriod 100

or make it decide the base duration of the dot dynamically from an environment variable. Don’t be alarmed. We’ll just be taking real world input on analog pin 2 to decide the duration of the dot in real time.

#define dotPeriod analogRead(2)

Do either of the above two, never both. Now, let us define dash length, relaxation time, letter space time and word space time. A good way to do this is individually define them by multiplying the right values to the value of dotPeriod defined. The better way, my way, the elegant way, is to define them as multiples of dotPeriod itself…

#define dashPeriod (dotPeriod*3)
#define relaxTime (dotPeriod)
#define letterSpace (dotPeriod*2)
#define wordSpace (dotPeriod*4)

This ensures that those variables depend on the dotPeriod. The definitions of those variables do seem way off, don’t they? You’ll see later how everything falls in place.

So let us set the frequency and the tone pin…

#define buzz 1000
#define tonePin 3

Now, if you’re me, you’ll swap the buzz definition with the following…

#define buzz (analogRead(1)+200)

I added 200 to the analog input to make sure the value never drops below 200Hz, thus eliminating the need for the optional capacitor; this way it can take an upper limit of 1223Hz.

Here is something I like to do a lot – making stuff modular. Even though the code gets a bit longer this way, it is a good thing to make a program you’ll understand like a decade from now.

So we get to defining what we call dits and dahs…

void dit()
{
  tone(tonePin, buzz);
  delay(dotPeriod);
  noTone(tonePin);
  delay(relaxtime);
}

Define dahs the same way; just change the delay to dashPeriod.

void dah()
{
  tone(tonePin, buzz);
  delay(dahPeriod);
  noTone(tonePin);
  delay(relaxtime);
}

Now, this is absolutely unnecessary, but you can define a function called di to make it more appealing.

void di()
{
  dit();
}

Now, let us define a function that makes it play the right letter in Morse code…

void playLetter(char x)
{
  switch (x):
    case 'E':
      dit(); return;
    case 'T':
      dah(); return;
    case 'A':
      di();dah(); return;
    case 'O':
      dah();dah();dah(); return;
/* Insert more alphabets here */
    case ' ':
      delay(wordSpace);
/* We've inserted the wordSpace delay to make sure it relaxes when the letter is a space */
}

I’ve clearly not done this alphabetically and for a reason. Some alphabets are far more probable to appear than others and it makes sense to have the input scrutinised for those alphabets first. This helps to reduce compute load and latency resulting from it.

The letter E is the most frequent, so we first check for E, and then T and then A and so on. You could look up Morse code for various special characters and add them to the function as well. I haven’t.

Now let us define the mandatory setup function.

void setup()
{
  Serial.begin(9600);
/* Using the serial command I program the Arduino to communicate at a baud rate of 9600 bits per second. */

  pinMode(tonePin, OUTPUT);
// This command sets the pin to act as output
}

Let us now define some global scope variables.

int Index=0, endofstring, i;
//This creates dummy variables to index letters
 
char Input[255];
//This is required to store the input data temporarily

Now let us define loop function. This one is also mandatory for an Arduino sketch to run…

void loop()
{
  while(!Serial.available());
//Delay until serial data is available
  while(Serial.available())
  {
    Input[Index]=Serial.read();
//Stores the byte of data from the serial buffer
    Serial.print(Input[Index]);
//Prints out the same data
    if(Input[Index]>=97&&Input[Index]<=122)
    {
      Input[Index]=char(int(Input[Index])-32);
//Changes lowercase letters to uppercase
    }
//This condition is used to uppercase all letters
    Index++;
  }
//The loop runs until Serial data is completely transmitted
  Serial.println();
//Creates a new line
  endofstring=Index;
//Stores the address of the last byte of data
  for(Index=0;Index<=endofstring;Index++)
  {
    playLetter(Input[Index]);
//Executes the playLetter function defined earlier
    delay(letterSpace);
//Waits for 3 times unit duration between letters in words
  }
  Index=0;
//Sets index back to zero before the loop restarts
}

Now, lets understand what the code does. Steps as follows:

  1. It initialises the serial rate and sets tone pin as output.
  2. Waits for serial data.
  3. When the serial data becomes available, it grabs the data and stores them in an array; it also prints out the data as it enters and changes letters to uppercase.
  4. It plays the data, one letter at a time. The intraletter spacings are embedded in the dit and dah function itself. Spacing between letters are called after playing of the specific letter(space of 2 for letter and 1 for intraletter, giving 3 dot spaces. Word space is also defined in the letter playing function(4 for word space adds to 3 for the letterspaces, to give 7 spaces as intended).
  5. After all this is done, it goes back to step 3.

The source code

TheCodeTheCode#define dotPeriod 100 #define dashPeriod (dotPeriod*3) #define relaxTime (dotPeriod) #define letterSpace (dotPeriod*2) #define wordSpace (dotPeriod*4) #define buzz 1000 #define tonePin 3 void dit() { tone(tonePin, buzz); delay(dotPeriod); noTone(tonePin); delay(relaxtime); } void dah() { tone(tonePin, buzz); delay(dahPeriod); noTone(tonePin); delay(relaxtime); } void di() { dit(); } void playLetter(char x) { switch (x): case 'E': dit(); return; case 'T': dah(); return; case 'A': di();dah(); return; case 'O': dah();dah();dah(); return; case 'I': di();dit(); return; case 'N': dah();dit(); return; case 'S': dah();dah();dah(); return; case 'H': di();di();di();dit(); return; case 'R': di();dah();dit(); return; case 'D': dah();di();dit(); return; case 'L': di();dah();di();dit(); return; case 'U': di();di();dah(); return; case 'C': dah();di();dah();dit(); return; case 'M': dah();dah(); case 'F': di();di();dah();dit(); return; case 'W': di();dah();dah(); return; case 'Y': dah();di();dah();dah(); return; case 'P': di();dah();dah();dit(); return; case 'V': di();di();di();dah(); return; case 'B': dah();di();di();dit(); return; case 'G': dah();dah();dit(); return; case 'K': dah();di();dah(); return; case 'J': di();dah();dah();dah(); return; case 'Q': dah();dah();di();dah(); return; case 'X': dah();di();di();dah(); return; case 'Z': dah();dah();di();dit(); return; case ' ': delay(wordSpace); } void setup() { Serial.begin(9600); pinMode(tonePin, OUTPUT); } int Index=0, endofstring, i; char Input[255]; void loop() { while(!Serial.available()); while(Serial.available()) { Input[Index]=Serial.read(); Serial.print(Input[Index]); if(Input[Index]>=97&&Input[Index]<=122) { Input[Index]=char(int(Input[Index])-32); } Index++; } Serial.println(); endofstring=Index; for(Index=0;Index<=endofstring;Index++) { playLetter(Input[Index]); delay(letterSpace); } Index=0; } 1

Pretty cool, isn’t it? Well that’s not all. Here are some fascinating tidbits about Morse code you could experiment with.

  1. The tone labeled Special in Nokia phones is actually Morse code for “SMS SMS“.
  2. The tone labeled Ascending spells out “Connecting people“.
  3. The tone called Standard is just “M M” which probably is an initial for ‘message’.
  4. A very standard signal for help is SOS that sounds di-di-dit dah-dah-dah di-di-dit.

Here’s something you could try. With help of some creative electrical manipulation, you can feed the Morse code output directly into your computer via the mic jack and analyse the sound in your favourite audio manipulation software.

The fun thing about taking up projects like this is to, at the very least, test your potential. Of course, the occasional looking up information is allowed, since most of us, including me, are hobbyists; but believe me, engineering is fun only when you do it yourself.

Oh! Do take some time and suggest me ideas to implement in the future. I also accept requests for tutorials and I’ll always be there to personally advice you on electronics if you need me.

Bye for now…