Hi.
Continuing on my Morse decoder from this thread: http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1289074596/15 and inspired by liudr in this thread: http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1290666645, I made it into a Morse encoder and decoder.
I just figured I should start a new thread about it.
I have to admit that the source code is in kind of a mess, but well commented I hope. I want to make classes or maybe just functions out of it sometime too (first attempt just messed up everything), but for now this is it.
Here is a youtube video where I demonstrate it, and also do some speed tests for the fun of it. I'm testing up to 400 wpm (which fails), but the 300 wpm (full duplex) worked, at least when I did the video Sounds like a mess with two morse codes simultaneously beeping away (but the one at 13 wpm was kind of musical).
My camera aiming is a bit off though, and the video editing isn't the best either...
And here is the code:
(part 1, too long to fit in one post)
/*
MORSE EN-DE-CODER 1.06
- A Morse encoder / decoder for the Arduino.
3 input methods for decoding and encoding:
- Audio Morse signals on an analog input (for decoding).
- Morse keying on digital input (for decoding).
- ASCII text via serial communication (for encoding. Also for adjusting settings).
2 outputs for encoded and decoded Morse:
- Morse-code toggled output pin (for encoded Morse).
- ASCII text via serial communication (for decoded Morse) .
It will enter audio listen mode if nothing is keyed in within a second.
Serial input:
Only International Morse Code (letters A-Z, numbers), and space, are
encoded and sent as Morse code automatically unless whatever the "command"
character is (default '<') is received.
No punctuation supported (but the morse table can be extended easily for that)
Morse en-/de-coder < commands via serial:
The ASCII "less than" character '<' denotes a command instead of encoding and
sending Morse. The format is:
<Lxxx
where L is a letter and xxx is a three-digit decimal number 000-999.
One exception is the <i command for info (displays current settings).
The letter can be upper or lower case W, A, D, E or I. Examples:
<w008 - sets speed to 8 wpm
<a900 - sets audio threshold to be 900
<D020 - sets debounce delay to be 20 milliseconds.
<e000 - turn off Morse echo back to serial as ASCII and output pin as Morse
<e001 - turn on Morse echo (any value above 0 will do)
<i - info - displays some settings
Full duplex:
It can both transmit and receive simultaneously, but serial output will
be a mixed mess unless echo is turned off.
NOTE: Values for tolerance (like dot time or dash time divided by 2 etc) are
more or less arbitrarily/experimentally chosen.
Sorry the source code is a big mess for now...
Based on Debounce example from the Arduino site for the morse keyer button
(http://www.arduino.cc/en/Tutorial/Debounce).
Loosely based on Morse Decoder 3.5 from 1992 for the Amiga by the same guy.
Contact: raronzen@gmail.com (not checked too often..)
Copyright (C) 2010 raron
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.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
History:
1992.01.06 - Morse decoder 3.5 - 68000 Assembler version for the Amiga 600
using a binary tree (dichotomic table) for Morse decoding
2010.11.11 - Rewrite for the Arduino
2010.11.27 - Added Morse encoding via reverse-dichotomic path tracing.
Thus using the same Morse table / binary tree for encoding and decoding.
2010.11.28 - Added a simple audio Morse signal filter. Added a simple command parser
2010.11.29 - Added echo on/off command
TODO: Make the timings signed long again, to avoid rollover issue
Avoid calling millis() all the time?
Make functions or classes out of it sometime...
Generally tidy it up
*/
// Simple analog input signal threshold (512 = middle-ish)
// Set high enough to avoid noise, low enough to get signal
// ( 512 + noise < AudioThreshold < 512 + signal )
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 = 13; // 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);