/*********************************************************************** WB7FHC's Simple Morse Code Decoder v. 2.0 [03/15/15] (c) 2015, Budd Churchward - WB7FHC This is an Open Source Project http://opensource.org/licenses/MIT Search YouTube for 'WB7FHC' to see several videos of this project as it was developed. MIT license, all text above must be included in any redistribution ********************************************************************** The program will automatically adjust to the speed of code that is being sent. The first few characters may come out wrong while it homes in on the speed. If you are not seeing solid copy, press the restart button on your Arduino. The software tracks the speed of the sender's dahs to make its adjustments. The more dahs you send at the beginning the sooner it locks into solid copy. This version of the software originally written by Budd Churchward has been modified by Chet Thayer, WA3I, to eliminate all LCD operations, the noise blanker and other unnecessary features. These were elimintated to allow the software to operate accurately at over 50 words per minute. The maximum speed with the orginal software is limited to 39 wpm. It has also been modified to include the prosigns used by Flex Radio and for the added feature of sending the ASCII character to the computer COM port. The basic Morse code decoding structure developed by Budd Churchward is intact. *********************************************************************/ #define SIGNALPIN 8 // Key UP/DOWN is read here from both tone decoder chip and telegraph key // Hook the key up between D8 and GND int signalPinState = 1; // will store the value we read on this pin ///////////////////////////////////////////////////////////////////////////////////////// // The following variables store values collected and calculated from millis() // For various timing functions long downTime = 0; // How long the tone was on in milliseconds long upTime = 0; // How long the tone was off in milliseconds long startDownTime = 0; // Arduino's internal timer when tone first comes on long startUpTime = 0; // Arduino's internal timer when tone first goes off long lastDahTime = 0; // Length of last dah in milliseconds long lastDitTime = 0; // Length of last dit in milliseconds // The following values will auto adjust to the sender's speed long averageDah = 100; // A dah should be 3 times as long as a dit long fullWait = 6000; // The time between letters long waitWait = 6000; // The time between dits and dahs long newWord = 0; // The time between words long dit = 10; // We start by defining a dit as 10 milliseconds boolean ditOrDah = true; // We have either a full dit or a full dah boolean characterDone = true; // A full character has been received boolean justDid = true; // Makes sure we only print one space during long gaps int myBounce = 2; // Used as a short delay between key up and down int myCount = 0; int wpm = 0; // We'll print the sender's speed on the LCD when we scroll int myNum = 0; // We will turn dits and dahs into a binary number stored here ///////////////////////////////////////////////////////////////////////////////// // Now here is the 'Secret Sauce' // The Morse Code is embedded into the binary version of the numbers from 2 - 63 // The place a letter appears here matches myNum that we parsed out of the code // #'s are miscopied characters char mySet[] = "##TEMNAIOGKDWRUS##QZYCXBJP#L+FVH09#8###7#(###/=61####+#)2###3#45"; // ^ ^ ^ ^ These are the positions of the prosign characters // ^ KN prosign // ^ BT prosign // ^ AR prosign // ^ AS prosign (not used by Flex Radio) char lcdGuy = ' '; // We will store the actual character decoded here ////////////////////////////////////////////////////////// void setup() { Serial.begin(9600); pinMode(SIGNALPIN, INPUT); // this reads our key and the tone decoder chip digitalWrite(SIGNALPIN, 1); // turn on internal pull up resistor } void loop() { // put your main code here, to run repeatedly: signalPinState = digitalRead(SIGNALPIN); // What is the tone decoder doing? if (!signalPinState) keyIsDown(); // LOW, or 0, means tone is being decoded if (signalPinState) keyIsUp(); // HIGH, or 1, means no tone is there } void keyIsDown() { // The LEDs on the decoder and Arduino will blink on in unison //digitalWrite(13,1); // turn on Arduino's LED signalPinState = digitalRead(SIGNALPIN); // What is the tone decoder doing? if (signalPinState) return; if (startUpTime > 0) { // We only need to do once, when the key first goes down startUpTime = 0; // clear the 'Key Up' timer } // If we haven't already started our timer, do it now if (startDownTime == 0) { startDownTime = millis(); // get Arduino's current clock time } characterDone = false; // we're still building a character ditOrDah = false; // the key is still down we're not done with the tone delay(myBounce); // Take a short breath here if (myNum == 0) { // myNum will equal zero at the beginning of a character myNum = 1; // This is our start bit - it only does this once per letter } } void keyIsUp() { // The LED on the Arduino will blink on and off with the code //digitalWrite(13,0); // turn off Arduino's LED signalPinState = digitalRead(SIGNALPIN); // What is the tone decoder doing? if (!signalPinState) return; // If we haven't already started our timer, do it now if (startUpTime == 0) { startUpTime = millis(); } // Find out how long we've gone with no tone // If it is twice as long as a dah print a space upTime = millis() - startUpTime; if (upTime < 20) return; if (upTime > (averageDah * 1.3)) { printSpace(); } // Only do this once after the key goes up if (startDownTime > 0) { downTime = millis() - startDownTime; // how long was the tone on? startDownTime = 0; // clear the 'Key Down' timer } if (!ditOrDah) { // We don't know if it was a dit or a dah yet shiftBits(); // let's go find out! And do our Magic with the bits } // If we are still building a character ... if (!characterDone) { // Are we done yet? if (upTime > dit) { // BINGO! we're done with this one printCharacter(); // Go figure out what character it was and print it characterDone = true; // We got him, we're done here myNum = 0; // This sets us up for getting the next start bit } downTime = 0; // Reset our keyDown counter } } void shiftBits() { // we know we've got a dit or a dah, let's find out which // then we will shift the bits in myNum and then add 1 or not add 1 if (downTime < dit / 3) return; // ignore my keybounce myNum = myNum << 1; // shift bits left ditOrDah = true; // we will know which one in two lines // If it is a dit we add 1. If it is a dah we do nothing! if (downTime < dit) { myNum++; // add one because it is a dit } else { // The next three lines handle the automatic speed adjustment: averageDah = (downTime + averageDah) / 2; // running average of dahs dit = averageDah / 3; // normal dit would be this dit = dit * 2; // double it to get the threshold between dits and dahs } } void printCharacter() { justDid = false; // OK to print a space again after this // Punctuation marks will make a BIG myNum if (myNum > 63) { printPunctuation(); // The value we parsed is bigger than our character array // It is probably a punctuation mark so go figure it out. return; // Go back to the main loop(), we're done here. } lcdGuy = mySet[myNum]; // Find the letter in the character set sendToLCD(); // Go figure out where to put in on the display } void printSpace() { if (justDid) return; // only one space, no matter how long the gap justDid = true; // so we don't do this twice lcdGuy = ' '; // this is going to go to the LCD Serial.print(lcdGuy); } void printPunctuation() { // Punctuation marks are made up of more dits and dahs than // letters and numbers. Rather than extend the character array // out to reach these higher numbers we will simply check for // them here. This function only gets called when myNum is greater than 63 // Thanks to Jack Purdum for the changes in this function // The original uses if then statements and only had 3 punctuation // marks. Then as I was copying code off of web sites I added // characters we don't normally see on the air and the list got // a little long. Using 'switch' to handle them is much better. switch (myNum) { case 71: lcdGuy = ':'; break; case 76: lcdGuy = ','; break; case 84: lcdGuy = '!'; break; case 94: lcdGuy = '-'; break; case 97: lcdGuy = 39; // Apostrophe break; case 101: lcdGuy = '@'; break; case 106: lcdGuy = '.'; break; case 115: lcdGuy = '?'; break; case 246: lcdGuy = '$'; //This is where the prosign for SK will go. For the Flex 6400 the prosign is $ break; case 122: lcdGuy = '$'; break; default: lcdGuy = '#'; // Should not get here break; } sendToLCD(); // go figure out where to put it on the display } void sendToLCD() { Serial.print(lcdGuy); }