int AudioThreshold = 700; // Word-per-minute speed int wpm = 13; // Used as a command character to adjust some settings via serial. const char MorseCommand = '<'; // the debounce time. Keep well below dotTime!! unsigned long debounceDelay = 20; // Other Morse variables unsigned long dotTime = 1200 / wpm; // morse dot time length in ms unsigned long dashTime = 3 * 1200 / wpm; unsigned long wordSpace = 7 * 1200 / wpm; const int analogPin = 0; // Analog input pin for audio morse code const int morseInPin = 7; // The Morse keyer button const int morseOutPin = 2; // For Morse code output unsigned long markTime = 0; // timers for mark and space in morse signal unsigned long spaceTime = 0; // E=MC^2 ;p boolean morseSpace = false; // Flag to prevent multiple received spaces boolean gotLastSig = true; // Flag that the last received morse signal is decoded as dot or dash const int morseTreetop = 31; // character position of the binary morse tree top. const int morseTableLength = (morseTreetop*2)+1; const int morseTreeLevels = log(morseTreetop+1)/log(2); int morseTableJumper = (morseTreetop+1)/2; int morseTablePointer = morseTreetop; // Morse code binary tree table (or, dichotomic search table) char morseTable[] = "5H4S?V3I?F?U??2E?L?R???A?P?W?J1 6B?D?X?N?C?K?Y?T7Z?G?Q?M8??O9?0"; int morseSignals; // nr of morse signals to send in one morse character char morseSignal[] = "......"; // temporary string to hold one morse character's signals to send int morseSignalPos = 0; int sendingMorseSignalNr = 0; unsigned long sendMorseTimer = 0; boolean morseEcho = true; // Echoes character to encode back to serial and Morse signal input to output pin boolean listeningAudio = false; boolean sendingMorse = false; boolean morseSignalState = false; boolean lastKeyerState = false; //unsigned long lastAudioSignalTime = 0; unsigned long lastDebounceTime = 0; // the last time the input pin was toggled void setup() { pinMode(morseInPin, INPUT); digitalWrite(morseInPin,HIGH); // internal pullup resistor on pinMode(morseOutPin, OUTPUT); Serial.begin(9600); Serial.println("Morse en-/de-coder by raron"); markTime = millis(); spaceTime = millis(); } void loop() { boolean morseKeyer = !digitalRead(morseInPin); // inverted for active-low input // If the switch changed, due to noise or pressing: if (morseKeyer != lastKeyerState) { lastDebounceTime = millis(); // reset timer listeningAudio = false; // disable listen to audio-mode } // debounce the morse keyer, unless listening to audio morse signal if (!listeningAudio && (millis() - lastDebounceTime) > debounceDelay) { // whatever the reading is at, it's been there for longer // than the debounce delay, so take it as the actual current state: morseSignalState = morseKeyer; // differentiante mark and space times if (morseSignalState) markTime = lastDebounceTime; else spaceTime = lastDebounceTime; } // If no manual morse keying the last second, enter audio listen mode if (!morseKeyer && millis() - lastDebounceTime > 1000) listeningAudio = true; // Filter audio morse signal if (listeningAudio) { int audioMorse = analogRead(analogPin); unsigned long currentTime = millis(); // Check for an audio signal... if (audioMorse > AudioThreshold) { // If this is a new morse signal, reset morse signal timer if (currentTime - lastDebounceTime > dotTime/2) { markTime = currentTime; morseSignalState = true; // there is currently a signal } lastDebounceTime = currentTime; } else { // if this is a new pause, reset space time if (currentTime - lastDebounceTime > dotTime/2 && morseSignalState == true) { spaceTime = lastDebounceTime; // not too far off from last received audio morseSignalState = false; // No more signal } } } // Morse output, or a feedback when keying. if (!sendingMorse && morseEcho) digitalWrite(morseOutPin, morseSignalState); // Encode Morse code or execute commands if (Serial.available() > 0 && !sendingMorse) { char encodeMorseChar = Serial.read(); // if a command instead, adjust some settings if (encodeMorseChar == MorseCommand) { // An extremely crude and simple command parser // Expects (and wait for) 2 or 4 characters (>i or Audio threshold:"); Serial.print (value,DEC); Serial.println(" <"); break; case 'd': // Debounce value case 'D': debounceDelay = (unsigned long) value; if (debounceDelay<0) debounceDelay = 0; Serial.print(" > Debounce (ms):"); Serial.print (value,DEC); Serial.println(" <"); break; case 'e': // Turn on / off Morse echo back to serial and Morse output pin case 'E': if (value > 0) { morseEcho = true; Serial.println(" > Echo: on <"); } else { morseEcho = false; Serial.println(" > Echo: off <"); } break; case 'w': // Morse speed setting in wpm case 'W': wpm = value; if (wpm <= 0) wpm = 1; dotTime = 1200 / wpm; dashTime = 3 * 1200 / wpm; wordSpace = 7 * 1200 / wpm; Serial.print(" > Morse speed (wpm):"); Serial.print (value,DEC); Serial.println(" <"); break; case 'i': // Display info (current settings). case 'I': Serial.print(" > Morse speed: "); Serial.print (wpm,DEC); Serial.print(" wpm (dot="); Serial.print (dotTime,DEC); Serial.print("ms, dash="); Serial.print (dashTime,DEC); Serial.print("ms) Debounce: "); Serial.print (debounceDelay,DEC); Serial.print(" ms. Audio threshold: "); Serial.print (AudioThreshold,DEC); Serial.println(" <"); break; default: Serial.print(" > Unrecognized command <"); } // Mark that we have executed a command (dont send morse) encodeMorseChar = MorseCommand; } if (encodeMorseChar != MorseCommand) { // change to capital letter if not if (encodeMorseChar > 'Z') encodeMorseChar -= 'z'-'Z'; // Scan for the character to send in the Morse table int i; for (i=0; i 0) { // build the morse signal (backwards from last signal to first) for (i = startLevel; i= dotTime) { digitalWrite(morseOutPin, LOW); sendMorseTimer = millis(); morseSignal[sendingMorseSignalNr] = 'x'; // Mark the signal as sent } break; case '-': // Send a dash (same here, stop sending after a dash worth of time) if (millis() - sendMorseTimer >= dashTime) { digitalWrite(morseOutPin, LOW); sendMorseTimer = millis(); morseSignal[sendingMorseSignalNr] = 'x'; // Mark the signal as sent } break; case 'x': // To make sure there is a pause between signals and letters if (sendingMorseSignalNr < morseSignals-1) { // Pause between signals in the same letter if (millis() - sendMorseTimer >= dotTime) { sendingMorseSignalNr++; digitalWrite(morseOutPin, HIGH); // Start sending the next signal sendMorseTimer = millis(); // reset the timer } } else { // Pause between letters if (millis() - sendMorseTimer >= dashTime) { sendingMorseSignalNr++; sendMorseTimer = millis(); // reset the timer } } break; case ' ': // Pause between words (minus pause between letters - already sent) default: // Just in case its something else if (millis() - sendMorseTimer > wordSpace - dashTime) sendingMorse = false; } if (sendingMorseSignalNr >= morseSignals) sendingMorse = false; // Ready to encode more letters } // Decode morse code if (!morseSignalState) { if (!gotLastSig) { if (morseTableJumper > 0) { // if pause for more than half a dot, get what kind of signal pulse (dot/dash) received last if (millis() - spaceTime > dotTime/2) { // if signal for more than 1/4 dotTime, take it as a valid morse pulse if (spaceTime-markTime > dotTime/4) { // if signal for less than half a dash, take it as a dot, else if not, take it as a dash // (dashes can be really really long...) if (spaceTime-markTime < dashTime/2) morseTablePointer -= morseTableJumper; else morseTablePointer += morseTableJumper; morseTableJumper /= 2; gotLastSig = true; } } } else { // error if too many pulses in one morse character Serial.println(""); gotLastSig = true; morseTableJumper = (morseTreetop+1)/2; morseTablePointer = morseTreetop; } } // Write out the character if pause is longer than 2/3 dash time (2 dots) and a character received if ((millis()-spaceTime >= (dotTime*2)) && (morseTableJumper < 16)) { char morseChar = morseTable[morseTablePointer]; Serial.print(morseChar); morseTableJumper = (morseTreetop+1)/2; morseTablePointer = morseTreetop; } // Write a space if pause is longer than 2/3rd wordspace if (millis()-spaceTime > (wordSpace*2/3) && morseSpace == false) { Serial.print(" "); morseSpace = true ; // space written-flag } } else { // while there is a signal, reset some flags gotLastSig = false; morseSpace = false; } // save last state of the morse signal for debouncing lastKeyerState = morseKeyer; }