Part #2:
// 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 <Cxxx)
int digits;
int value = 0;
do
{
digits = Serial.available();
} while (digits < 1);
// Read what setting
char morseSet = Serial.read();
// Read 3 digits unless setting is i for info
if (morseSet != 'i' && morseSet != 'I')
{
do
{
digits = Serial.available();
} while (digits < 3);
// convert value from ASCII
for (int i=0; i<digits; i++)
{
encodeMorseChar = Serial.read();
value *= 10;
value += (encodeMorseChar - '0');
}
}
Serial.flush(); // just in case
// Adjust and print the new setting
Serial.println();
switch (morseSet)
{
case 'a': // Audio input threshold value
case 'A':
AudioThreshold = value;
if (AudioThreshold<0) AudioThreshold = 0; // not recommended
Serial.print(" > 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<morseTableLength; i++) if (morseTable[i] == encodeMorseChar) break;
int morseTablePos = i+1; // 1-based position
// Reverse dichotomic / binary tree path tracing
// Find out what level in the binary tree the character is
int test;
for (i=0; i<morseTreeLevels; i++)
{
test = (morseTablePos + (0x0001 << i)) % (0x0002 << i);
if (test == 0) break;
}
int startLevel = i;
morseSignals = morseTreeLevels - i; // = the number of dots and/or dashes
morseSignalPos = 0;
// Travel the reverse path to the top of the morse table
if (morseSignals > 0)
{
// build the morse signal (backwards from last signal to first)
for (i = startLevel; i<morseTreeLevels; i++)
{
int add = (0x0001 << i);
test = (morseTablePos + add) / (0x0002 << i);
if (test & 0x0001 == 1)
{
morseTablePos += add;
// Add a dot to the temporary morse signal string
morseSignal[morseSignals-1 - morseSignalPos++] = '.';
} else {
morseTablePos -= add;
// Add a dash to the temporary morse signal string
morseSignal[morseSignals-1 - morseSignalPos++] = '-';
}
}
} else { // unless it was on the top to begin with (A space character)
morseSignal[0] = ' ';
morseSignalPos = 1;
morseSignals = 1; // cheating a little; a wordspace for a "morse signal"
}
morseSignal[morseSignalPos] = '\0';
// Echo back the letter with morse signal as ASCII to serial output
if (morseEcho)
{
Serial.print(encodeMorseChar);
Serial.print(morseSignal);
Serial.print(" ");
}
if (morseTablePos-1 != morseTreetop)
{
Serial.println();
Serial.print("..Hm..error? MorseTablePos = ");
Serial.println(morseTablePos);
}
// start sending the the character
sendingMorse = true;
sendingMorseSignalNr = 0;
sendMorseTimer = millis();
if (morseSignal[0] != ' ') digitalWrite(morseOutPin, HIGH);
}
}
// Send Morse signals to output
if (sendingMorse)
{
switch (morseSignal[sendingMorseSignalNr])
{
case '.': // Send a dot (actually, stop sending a signal after a "dot time")
if (millis() - sendMorseTimer >= 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("<ERROR: unrecognized signal!>");
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;
}