March Madness - Morse Code Training

This program USES the previous Morse Code library to offer training. It randomly sends a letter in morse code and you have to type it in. A space allows you to give up on it and it will display the morese code chart.

In doing this program I have enhanced the Morse Code library as well. Added a fucntion to get the text represntation of the code and a routine to print out the entire table.

Morse Code Trainer
                     0  _ _ _ _ _         @  . . . . . .       P  . _ _ .           
!                    1  . _ _ _ _         A  . _               Q  _ _ . _           
"  . _ . . _ .       2  . . _ _ _         B  _ . . .           R  . _ .             
#                    3  . . . _ _         C  _ . _ .           S  . . .             
$  . . . _ . . _     4  . . . . _         D  _ . .             T  _                 
%                    5  . . . . .         E  .                 U  . . _             
&                    6  _ . . . .         F  . . _ .           V  . . . _           
'  . _ _ _ _ .       7  _ _ . . .         G  _ _ .             W  . _ _             
(  _ . _ _ .         8  _ _ _ . .         H  . . . .           X  _ . . _           
)  _ . _ _ . _       9  _ _ _ _ .         I  . .               Y  _ . _ _           
*  _ . . _           :  _ _ _ . . .       J  . _ _ _           Z  _ _ . .           
+  . _ . _ .         ;  _ . _ . _ .       K  _ . _             [  _ . _ _ .         
,  _ _ . . _ _       <  _ . _ _ .         L  . _ . .           \                    
-  _ . . . . _       =  _ . . . _         M  _ _               ]  _ . _ _ . _       
.  . _ . _ . _       >  _ . _ _ . _       N  _ .               ^                    
/  _ . . _ .         ?  . . _ _ . .       O  _ _ _             _  . . _ _ . _       


?  = Help
P  = Practice
T  = Print Table
ESC= Quite Practice





//****************************************************************************

//*	Morse code practice
#ifdef __MWERKS__
//	int	random(...);

	#define	PROGMEM
	#define boolean char
#else
#endif


#include	<MorseCode.h>
#include	"HardwareSerial.h"

	#define	kPin_Buzzer				6

short			gMode;
char			gCurrentPracticeChar;
unsigned long	gTimeOfLastCode;
int				gTimesSent;

enum
{
	kMode_Waiting	=	0,
	kMode_Practice,
	
	kMode_Last
};

//****************************************************************************
void PrintHelp()
{
	Serial.println("?  = Help");
	Serial.println("P  = Practice");
	Serial.println("T  = Print Table");
	Serial.println("ESC= Quite Practice");
}



//****************************************************************************
void Waiting(char theChar)
{

	switch(theChar)
	{
		case '?':
			PrintHelp();
			break;
	
		case 'P':
			gMode		=	kMode_Practice;
			gTimesSent	=	0;
			break;
	
		
		case 'T':
			DisplayMorseCodeTable();
			break;
	
		
	
	}
}


//****************************************************************************
void Practice(char theChar)
{

	if (theChar == 0x1b)
	{
		gMode		=	kMode_Waiting;
		Serial.println();
		Serial.println("Exit practice mode");
	}
	else if (theChar == 0x20)
	{
		//*	give up on this char
		Serial.println(gCurrentPracticeChar);
		gTimesSent	=	0;
	}
	else if (theChar > 0)
	{
		//*	the user typed a char, lets see if it matches
		if (theChar == gCurrentPracticeChar)
		{
			//*	we have a correct answer
			Serial.print(theChar);
			Serial.print(" ");
			gTimesSent	=	0;
		}
	}


	if (gTimesSent == 0)
	{
		gCurrentPracticeChar	=	random(0x41, 0x5a);
		
		SendMorseCode(gCurrentPracticeChar, 13, kPin_Buzzer);
		gTimesSent++;
	}
}




//****************************************************************************
void setup()
{
//	SendMorseCodeString("Hello World", 13, kPin_Buzzer);
	
	Serial.begin(9600);
	Serial.println("Morse Code Trainer");
	
	DisplayMorseCodeTable();
	

	PrintHelp();
	
	gCurrentPracticeChar	=	0;
	gMode					=	kMode_Waiting;

}


//****************************************************************************
void loop()
{
char	theChar;

	// when characters arrive over the serial port...
	if (Serial.available())
	{
		theChar		=	Serial.read();
		if (theChar > 0x5f)
		{
			theChar	=	theChar & 0x5f;	//*	force uper case
		}
	}
	else
	{
		theChar		=	0;
	}

	switch(gMode)
	{
		case kMode_Waiting:
			Waiting(theChar);
			break;
		
		case kMode_Practice:
			Practice(theChar);
			break;
		default:
			break;
	}

}



//#include	<MorseCode.h>



#ifdef __cplusplus
extern "C" {
#endif

void	SendMorseCode(char theChar, int wpm, int buzzerPinNum);
void	SendMorseCodeString(char *theString, int wpm, int buzzerPinNum);
short	GetMorseCodePattern(char theChar, char *codePattern);
void	DisplayMorseCodeTable(void);


#ifdef __cplusplus
}
#endif


//************************************************************************
//*	Morse code transmit
//*	
//*	This code is (C) by Mark Sproul
//*	
//*	This program is free software: you can redistribute it and/or modify
//*	it under the terms of the GNU General Public License as published by
//*	the Free Software Foundation, either version 3 of the License, or
//*	(at your option) any later version.
//*	
//*	This program is distributed in the hope that it will be useful,
//*	but WITHOUT ANY WARRANTY; without even the implied warranty of
//*	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//*	GNU General Public License for more details.
//*	
//************************************************************************


#ifdef __MWERKS__
	#define	PROGMEM
	short	pgm_read_byte_near(...);

#else
	#include	
#endif

#include	
#include	"WProgram.h"
#include	"HardwareSerial.h"


#include	<MorseCode.h>


//************************************************************************
//*	in order to make this work in prgram memory, it is a char array
//*	2 bytes per entry, the first is the pattern, the 2nd is the length
uint8_t	gMorseData[] PROGMEM	=
{
//	B00000000,		0,	//	0x20	space	we dont need this in the table
	B00000000,		0,	//	0x21	!
	B01001000,		6,	//	0x22	"
	B00000000,		0,	//	0x23	#
	B00010010,		7,	//	0x24	$
	B00000000,		0,	//	0x25	%
	B00000000,		0,	//	0x26	&
	B01111000,		6,	//	0x27	'
	B10110000,		5,	//	0x28	(
	B10110100,		6,	//	0x29	)
	B10010000,		4,	//	0x2A	*
	B01010000,		5,	//	0x2B	+
	B11001100,		6,	//	0x2C	,
	B10000100,		6,	//	0x2D	-
	B01010100,		6,	//	0x2E	.
	B10010000,		5,	//	0x2F	/
	
	B11111000,		5,	//	0x30	0
	B01111000,		5,	//	0x31	1
	B00111000,		5,	//	0x32	2
	B00011000,		5,	//	0x33	3
	B00001000,		5,	//	0x34	4
	B00000000,		5,	//	0x35	5
	B10000000,		5,	//	0x36	6
	B11000000,		5,	//	0x37	7
	B11100000,		5,	//	0x38	8
	B11110000,		5,	//	0x39	9
	B11100000,		6,	//	0x3A	:
	B10101000,		6,	//	0x3B	;
	B10110000,		5,	//	0x3C	<	same as (
	B10001000,		5,	//	0x3D	=
	B10110100,		6,	//	0x3E	>	same as )
	B00110000,		6,	//	0x3F	?

	B00000000,		6,	//	0x40	@
	B01000000,		2,	//	A
	B10000000,		4,	//	B
	B10100000,		4,	//	C
	B10000000,		3,	//	D
	B00000000,		1,	//	E
	B00100000,		4,	//	F
	B11000000,		3,	//	G
	B00000000,		4,	//	H
	B00000000,		2,	//	I
	B01110000,		4,	//	J
	B10100000,		3,	//	K
	B01000000,		4,	//	L
	B11000000,		2,	//	M
	B10000000,		2,	//	N
	B11100000,		3,	//	O
	B01100000,		4,	//	P
	B11010000,		4,	//	Q
	B01000000,		3,	//	R
	B00000000,		3,	//	S
	B10000000,		1,	//	T
	B00100000,		3,	//	U
	B00010000,		4,	//	V
	B01100000,		3,	//	W
	B10010000,		4,	//	X
	B10110000,		4,	//	Y
	B11000000,		4,	//	Z
	B10110000,		5,	//	0x5B	[	same as (
	B00000000,		0,	//	0x5C	
	B10110100,		6,	//	0x5D	]	same as )
	B00000000,		0,	//	0x5E	^
	B00110100,		6,	//	0x5F	_

};


//************************************************************************
static int	GetTableIndexFromChar(byte theChar)
{
int	tableIdx;

	tableIdx	=	0;
	if (theChar >= 0x21)
	{
		
		if (theChar >= 0x60)
		{
			theChar	=	theChar & 0x5f;	//*	force upper case
		}
		tableIdx	=	(theChar - 0x21);	//*	0x21 is the first entry
		tableIdx	=	tableIdx * 2;
	}
	
	return(tableIdx);
}

/*
http://www.kent-engineers.com/codespeed.htm

The word PARIS is the standard for determing CW code speed. Each dit is one element, each dah is three elements, 
intra-character spacing is one element, inter-character spacing is three elements and inter-word spacing is 
seven elements. The word PARIS is exactly 50 elements.
Note that after each dit/dah of the letter P -- one element spacing is used except the last one. (Intra-Character).
After the last dit of P is sent, 3 elements are added (Inter-Character). After the word PARIS - 7 elements are used.
Thus:
P = di da da di = 1 1 3 1 3 1 1 (3) = 14 elements
A = di da = 1 1 3 (3) = 8 elements
R = di da di = 1 1 3 1 1 (3) = 10 elements
I = di di = 1 1 1 (3) = 6 elements
S = di di di = 1 1 1 1 1 [7] = 12 elements
Total = 50 elements
() = intercharacter
[] = interword

If you send PARIS 5 times in a minute (5WPM) you have sent 250 elements (using correct spacing). 
250 elements into 60 seconds per minute = 240 milliseconds per element.

13 words-per-minute is one element every 92.31 milliseconds.

The Farnsworth method sends the dits and dahs and intra-character spacing at a higher speed, then 
increasing the inter-character and inter-word spacing to slow the sending speed down to the overall speed. 
For example, to send at 5 wpm with 13 wpm characters in Farnsworth method, the dits and intra-character 
spacing would be 92.3 milliseconds, the dah would be 276.9 milliseconds, the inter-character spacing 
would be 1.443 seconds and inter-word spacing would be 3.367 seconds.


*/


//************************************************************************
//*	Calculate the dot length in millseconds
//*		1 minute = 60,000 milliseconds
//*
//*		dotLength	=	(60,000 / 50) / wpm
//*		dotLength	=	1200 / wpm
//************************************************************************

//************************************************************************
void	SendMorseCode(char theChar, int wpm, int buzzerPinNum)
{
int		tableIdx;
byte	theCode;
byte	bitLen;
int		ii;
int		myDotLength;

//cq cq cq cq cq the quick brown fox jumped over the lazy dogs back


	//************************************************************************
	//*	Calculate the dot length in millseconds
	//*		1 minute = 60,000 milliseconds
	//*
	//*		dotLength	=	(60,000 / 50) / wpm
	//*		dotLength	=	1200 / wpm
	//************************************************************************
	myDotLength	=	1200 / wpm;

	if (theChar < 0x20)
	{
		//*	dont do anything
	}
	else if (theChar == 0x20)
	{
		delay(myDotLength * 7);
	}
	else
	{
		tableIdx	=	GetTableIndexFromChar(theChar);

		//*	get the dits and dahs pattern
		theCode		=	pgm_read_byte_near(gMorseData + tableIdx);
		//*	get the overal bit length
		bitLen		=	pgm_read_byte_near(gMorseData + tableIdx + 1);
		
		if (bitLen > 0)
		{
			//*	step through the bits
			for (ii = 0; ii < bitLen; ii++)
			{
				//*	turn the buzzer on
				analogWrite(buzzerPinNum, 128);
				if (theCode & 0x80)
				{
					//*	dash time
					delay(myDotLength * 3);
				}
				else
				{
					//*	dot time
					delay(myDotLength);
				}
				//*	turn the buzzer OFF
				analogWrite(buzzerPinNum, 0);
				delay(myDotLength);
				
				theCode	=	theCode << 1;
			}
			delay(myDotLength * 3);
		}
	}
}


//************************************************************************
void	SendMorseCodeString(char *theString, int wpm, int buzzerPinNum)
{
int	ii;

	ii	=	0;
	while (theString[ii] != 0)
	{
		SendMorseCode(theString[ii], wpm, buzzerPinNum);
		
		ii++;
	}
}


//************************************************************************
//*	returns number of bits, zero means no entry for that char
short	GetMorseCodePattern(char theChar, char *codePattern)
{
short	tableIdx;
byte	theCode;
byte	bitLen;
int		ii;


	codePattern[0]	=	0;
	bitLen			=	0;
	if (theChar >= 0x21)
	{
			
		tableIdx	=	GetTableIndexFromChar(theChar);
		
		//*	get the dits and dahs pattern
		theCode		=	pgm_read_byte_near(gMorseData + tableIdx);
		//*	get the overal bit length
		bitLen		=	pgm_read_byte_near(gMorseData + tableIdx + 1);
		
		if (bitLen > 0)
		{
			//*	step through the bits
			for (ii = 0; ii < bitLen; ii++)
			{
				if (theCode & 0x80)
				{
					//*	dash time
					strcat(codePattern, "_ ");
				}
				else
				{
					strcat(codePattern, ". ");
				}
				theCode	=	theCode << 1;
			}
		}
	}
	return(bitLen);
}


//************************************************************************
void	DisplayMorseCodeTable(void)
{
int		jj;
int		ii;
int		theChar;
int		spaceCnt;
int		bitLen;
char	codePattern[16];

	for (jj = 0; jj < 16; jj++)
	{
		for (ii=0; ii<4; ii++)
		{
			theChar	=	0x20 + (ii * 16) + jj;
			
			bitLen	=	GetMorseCodePattern(theChar, codePattern);
			Serial.print(theChar, BYTE);
			Serial.print("  ");
			Serial.print(codePattern);
			for (spaceCnt=0; spaceCnt < (8 - bitLen) ; spaceCnt++)
			{
				Serial.print("  ");
			}
			Serial.print("  ");
		}
		
		Serial.println();
	}
}