I’ve recently written a morse code generator.
It does use delay, but the delay is short enough that you could check a button in there somewhere too. Or convert it from using delay to something else.
And of course, instead of connecting an LED, connect a buzzer…
/*
* The duration (in milliseconds) of one tick.
* A tick can be a morse code "dot" or a pause.
*/
#define TICK_DURATION 200
/*
* The basic morse elements "DOT" and "DASH".
* These constants define how many ticks each are in length.
*/
#define DOT 1
#define DASH 3
/*
* Parameters for the pulse() function.
* Specify wheter to output a "mark" or a pause (gap).
*/
#define MARK 0x08 /* Digital port 3 */
#define GAP 0
/*
* The default gap duration as measured in ticks.
*/
#define GAP_BETWEEN_MARKS 1
#define GAP_BETWEEN_LETTERS 2 // It's 3 - GAP_BETWEEN_MARKS
#define GAP_BETWEEN_WORDS 7
/*
* Some pre-compiler magic to encode a morse
* code character into a 2-byte unsigned integer.
*/
#define ENC1(n0) (n0)
#define ENC2(n0, n1) (n0 + (n1 << 2))
#define ENC3(n0, n1, n2) (n0 + (n1 << 2) + (n2 << 4))
#define ENC4(n0, n1, n2, n3) (n0 + (n1 << 2) + (n2 << 4) + (n3 << 6))
#define ENC5(n0, n1, n2, n3, n4) (n0 + (n1 << 2) + (n2 << 4) + (n3 << 6) + (n4 << 8))
/*
* The international morse code representatino of
* the letters of the alphabet.
*/
PROGMEM const prog_uint16_t MORSE_ALPHA[] = {
ENC2( DOT, DASH ), /* A */
ENC4( DASH, DOT, DOT, DOT ), /* B */
ENC4( DASH, DOT, DASH, DOT ), /* C */
ENC3( DASH, DOT, DOT ), /* D */
ENC1( DOT ), /* E */
ENC4( DOT, DOT, DASH, DOT ), /* F */
ENC3( DASH, DASH, DOT ), /* G */
ENC4( DOT, DOT, DOT, DOT ), /* H */
ENC2( DOT, DOT ), /* I */
ENC4( DOT, DASH, DASH, DASH ), /* J */
ENC3( DASH, DOT, DASH ), /* K */
ENC4( DOT, DASH, DOT, DOT ), /* L */
ENC2( DASH, DASH ), /* M */
ENC2( DASH, DOT ), /* N */
ENC3( DASH, DASH, DASH ), /* O */
ENC4( DOT, DASH, DASH, DOT ), /* P */
ENC4( DASH, DASH, DOT, DASH ), /* Q */
ENC3( DOT, DASH, DOT ), /* R */
ENC3( DOT, DOT, DOT ), /* S */
ENC1( DASH ), /* T */
ENC3( DOT, DOT, DASH ), /* U */
ENC4( DOT, DOT, DOT, DASH ), /* V */
ENC3( DOT, DASH, DASH ), /* W */
ENC4( DASH, DOT, DOT, DASH ), /* X */
ENC4( DASH, DOT, DASH, DASH ), /* Y */
ENC4( DASH, DASH, DOT, DOT ) /* Z */
};
/*
* Same thing, but digits this time...
*/
PROGMEM const prog_uint16_t MORSE_NUM[] = {
ENC5( DASH, DASH, DASH, DASH, DASH ), /* 0 */
ENC5( DOT, DASH, DASH, DASH, DASH ), /* 1 */
ENC5( DOT, DOT, DASH, DASH, DASH ), /* 2 */
ENC5( DOT, DOT, DOT, DASH, DASH ), /* 3 */
ENC5( DOT, DOT, DOT, DOT, DASH ), /* 4 */
ENC5( DOT, DOT, DOT, DOT, DOT ), /* 5 */
ENC5( DASH, DOT, DOT, DOT, DOT ), /* 6 */
ENC5( DASH, DASH, DOT, DOT, DOT ), /* 7 */
ENC5( DASH, DASH, DASH, DOT, DOT ), /* 8 */
ENC5( DASH, DASH, DASH, DASH, DOT ) /* 9 */
};
/*
* Outputs either a MARK or GAP for the given number of ticks.
*/
static void pulse(char ticks, const char mark)
{
PORTD = mark;
while (ticks-- > 0)
delay(TICK_DURATION);
PORTD = 0;
}
/*
* Outputs a morse code sequence, including any required
* gaps between marks.
*/
static void pulseChar(prog_uint16_t seq)
{
while ((seq & DASH) != 0)
{
pulse((seq & DASH), MARK);
pulse(GAP_BETWEEN_MARKS, GAP);
seq >>= 2;
}
}
/*
* Converts a message to morse code and sends the resulting pulses.
*/
static void morseMessage(const char* ch)
{
/* Iterate over all characters in the message and convert letters to upper-case */
while (*ch)
{
if (*ch >= 'A' && *ch <= 'Z')
pulseChar(pgm_read_word(&MORSE_ALPHA[*ch - 'A']));
else if (*ch >= '0' && *ch <= '9')
pulseChar(pgm_read_word(&MORSE_ALPHA[*ch - '0']));
if (*ch++ == ' ')
pulse(GAP_BETWEEN_WORDS, GAP);
else
pulse(GAP_BETWEEN_LETTERS, GAP);
}
}
void setup()
{
/* Direct port access, setting the port associcated with "MARK" to OUTPUT */
DDRD = MARK;
}
void loop()
{
morseMessage("SOS 123\0"); /* !!! WARNING: LETTERS MUST BE CAPITALIZED !!! */
delay(5000);
}