Designing a Morse Code Sender - Arduino
User Rating: / 5
PoorBest 
Written by Bryce Ringwood   

Revised 22 October, 2013 Incorrect const char* assignments would not compile with 1.0.5 of the Arduino IDE

Download morse.zip

This is a program to convert text into morse code for the Arduino.

There are quite a number of code examples for automatic morse code senders on the internet and elsewhere. Generally , they strive for efficiency or if they are written for the Arduino, they follow a 'C' style of coding rather than a "C++" design - but, let it be said, are more efficient in terms of speed than the design presented here. (At least they ought to be.)

This design had a number of different objectives.

  1. It had to be short and very easy to follow,
  2. It had to demonstrate the power of object-oriented programming,
  3. It had to be an example of how to make an Arduino library for version 1.0
  4. It had to have an example of pointers, and
  5. The  thing had to work.

The Design

In this design, there are two classes. There is a "Morse" class and a "MorseChar" class. To use the library, the user must first of all instantiate a Morse class. A Message is sent to the Morse object, which converts it to upper case and allocates memory where it can be stored until transmission is complete. The memory is then free'ed. Each character in a morse message is compared to the character in a MorseChar object. If it matches, the MorseChar object sends the character to the output pin. If no match is found, the Morse object tries to match the next character, until there are no more characters left in the message.

There are 41 MorseChar" objects. These are instantiated (come into being) as soon as the Morse object is created. You can have an array of objects, in the same way you can have an array of characters or numbers.

Creating the Library

There have been some chages between this version and earlier versions of the Arduino programming environment.  My first step was to take an existing library and copy it, renaming it to "Morse". The next step was to rename the files to Morse.cpp and Morse.h.

In the Morse.h file, delete everything except:

#ifndef Morse_h (Change whatever was there_h to this and in the line that follows)
#define Morse_h

#include <inttypes.h>
#endif

 

In the Morse.cpp file, delete everything except:

#include <stdio.h>
#include <string.h>
#include <inttypes.h>

#include "Arduino.h"

Now make two new files, MorseChar.h and MorseChar.cpp, with the same contents, respectively.

Finally, alter the keywordss.txt file to:

#######################################
# Syntax Coloring Map For Morse
#######################################

#######################################
# Datatypes (KEYWORD1)
#######################################

Morse KEYWORD1

#######################################
# Methods and Functions (KEYWORD2)
#######################################

sendMessage KEYWORD2
setMessage KEYWORD2


#######################################
# Constants (LITERAL1)
#######################################

You don't have to have syntax 'coloring' for MorseChar.h, because nobody is going to see it.

Now complete the project by filling it in as follows:-

Morse.h

#ifndef Morse_h
#define Morse_h

#include <inttypes.h>
#include "MorseChar.h"

class Morse 
{
public:
    void setMessage(const char* _icray);
    void sendMessage();
    Morse();
    virtual ~Morse();
private:
    MorseChar MCray[45]; 
    const char *icray;
};

#endif

Morse.cpp

#include "MorseChar.h"
#include "Morse.h"

#include <stdio.h>
#include <string.h>
#include <inttypes.h>

#include "Arduino.h"

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

Morse::Morse()
{
    MCray[0].init('A',".-");
    MCray[1].init('B',"-...");
    MCray[2].init('C',"-.-.");
    MCray[3].init('D',"-..");
    MCray[4].init('E',".");
    MCray[5].init('F',"..-.");
    MCray[6].init('G',"--.");
    MCray[7].init('H',"....");
    MCray[8].init('I',"..");
    MCray[9].init('J',".---");
    MCray[10].init('K',"-.-");
    MCray[11].init('L',".-..");
    MCray[12].init('M',"--");
    MCray[13].init('N',"-.");
    MCray[14].init('O',"---");
    MCray[15].init('P',".--.");
    MCray[16].init('Q',"--.-");
    MCray[17].init('R',".-.");
    MCray[18].init('S',"...");
    MCray[19].init('T',"-");
    MCray[20].init('U',"..-");
    MCray[21].init('V',"...-");
    MCray[22].init('W',".--");
    MCray[23].init('X',"-..-");
    MCray[24].init('Y',"-.--");
    MCray[25].init('Z',"--..");
    MCray[26].init('1',".---");
    MCray[27].init('2',"..---");
    MCray[28].init('3',"...--");
    MCray[29].init('4',"....-");
    MCray[30].init('5',".....");
    MCray[31].init('6',"-....");
    MCray[32].init('7',"--...");
    MCray[33].init('8',"---..");
    MCray[34].init('9',"----.");
    MCray[35].init('0',"-----");
    MCray[36].init('/',"-..-.");
    MCray[37].init('*',".-.-.");
    MCray[38].init('.',".-.-.-");
    MCray[39].init('-',"-....-");
    MCray[40].init(',',"--..--");
    MCray[41].init('?',"..--..");
    MCray[42].init(' '," ");
    MCray[43].init(0,"      ");
}

Morse::~Morse()
{

}


void Morse::sendMessage()
{  
    for(unsigned i=0; i < strlen(icray)+1;i++)
    {  
        for( int j = 0; j < 44; ++j)
        {   
           if( toupper(*(icray+i)) == MCray[j].getChar())
           {
              MCray[j].send();    
              break;
           }   
        }  
    } 

}

void Morse::setMessage(const char *_icray)

    icray=_icray;
    sendMessage();
}

MorseChar.h

#ifndef MorseChar_h
#define MorseChar_h

#include <inttypes.h>

class MorseChar 
{
public:
    void init(char ichar,const char* _icray); 
    char getChar();
    void send();
    MorseChar();
    virtual ~MorseChar();
private: 
    const char *icray;
    char ichar;
};

#endif

MorseChar.cpp

#include "Morse.h"
#include "MorseChar.h"
#include <stdio.h>
#include <string.h>
#include <inttypes.h>

#include "Arduino.h"

#define wpm 8
#define __pin 13
#define dot_len 1200/wpm
#define dot digitalWrite(__pin,HIGH);delay(dot_len);digitalWrite(__pin,LOW);delay(dot_len)
#define dash digitalWrite(__pin,HIGH); delay(3*dot_len);digitalWrite(__pin,LOW);delay(dot_len)
#define space digitalWrite(__pin,LOW);delay(2*dot_len)

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

MorseChar::MorseChar()
{
 
}

MorseChar::~MorseChar()
{

}

void MorseChar::send()
{
    const char* p=icray;
    while(*p)
    {
        if(*p == '.')
        {
           dot;
        }
        else if(*p == '-')
        {
           dash;
        }
        else
        {
           space;
        }
        p++;  
    }
    space; 
}

char MorseChar::getChar()
{
    return ichar;
}

void MorseChar::init(char ichar,const char *_icray)
{
    icray=_icray;
    this->ichar = ichar; 
}

Finally, do the sketch:-

Beacon.ino

 

#include <Morse.h>

/* Beacon*/

Morse MorseMessage;
void setup()
{               
     // initialize the digital pin as an output.
     // Pin 13 has an LED connected on most Arduino boards:
     pinMode(13, OUTPUT);   
}
void loop()
{
     MorseMessage.setMessage("VVV Frequency 11.17 MHZ Hello World");
     delay(1000); 
}

 

You will see the code all prettily coloured up.

Follow the Code

This started out as a fairly complicated program. All it consists of now seems to be a handful of almost trivial functions.

Morse.h and MorseChar.h

These two files contain the class definitions for the Morse and MorseChar classes. Note the first two lines and the last line of the file. If you think about it, this is just a nice way of avoiding inadvertent redefinition of the classes.

Note the array of MorseChar objects defined in the Morse.h class.

Morse.cpp

This has three functions. The first is the constructor, which initiallises all the MCray objects with their Character value and Morse equivalent. The getMessage function and the sendMessage fuction (which, I think I should have declared as "Private".) 

Morse::setMessage(const char *_icray)

This is the only call that should be accessible to the user. The const keyword tells the compiler that what follows can't be modified. The pointers assigned to _icray should also be constant. (Correction 22 Oct 2013)

Morse::sendMessage()

The function sendMessage goes through the message one character at a time. It compares the uppercase equivalent of the character in the message to a MorseChar and then if a match is found gets the MorseChar to send itself.

MorseChar.cpp

The init function stores the alphabetic character and its morse equivalent in member variables. The send function uses the dot, dash and space macros to set the state of the require pin high or low for a specified time. BEWARE - the dot macros etc. have to have curly brackets round them. One of the dangers of macros is that you think of them as functions. They're not.

Fire up the Beacon

Complete Morse Code BeaconHere is the complete Morse Code Beacon (The croc clip on the crystal can is the antenna). After about 5 minutes, it becomes very annoying.

Uses

You could use it as a proper beacon. This might be the first stage in a more powerful radio transmitter. You could fit a GPS to the Arduino and transmit your position in Morse code. It could be used to transmit the temperature of something. It could be connected to a radiation monitor and transmit the dosage level in milliSieverts ...

 
Joomla template by a4joomla