Getting Simple Morse Code working with IR
So now I turned my attention to getting the send and receive
working. First, I needed to get the serial monitor
working so I could debug. What I quickly realized was
that the board we'd produced didn't have the FTDI TX/RX
wired up. That meant I had to just use that for the
sending side while I worked on programming the receive (and
debugging through the serial port on the receive.
ATTINY84:
For this I just had the loop run the following line.
This meant that it just sent a 1 and then waited a
second. That's all it did and it just kept send a 1
over and over. My theory was that I would work on the
receive code.
charBlink(49);
delay(1000);
ARDUINO:
This is what I used to read and debug the code. My
hole goal was to get the Arduino to read when the light when
on and off with special attention being paid to the length
of time the light was lit. I did find some good code
online that was setup for the matching of the DIT/DOT
sequences. So using those I tried to get them
talking....
...this didn't go well.
For some reason, the "pulse lengths" (aka the time the light
was turned on), never seemed to match the times that the IR
light was on/off. The IR reading never seemed to track
with when the light was on. If you look at the
"EXAMPLE OUTPUT" on the right, you'll see that the pulse
lengths are not even close.
/*
// Following is for Arduino
#define RledPin 13 //
This is the pin the LED is connected to
#define buttonPin 10 // This is the
pin that will read the button
#define IRtxPin 8
// This is the pin that will send IR messages
#define IRrxPin 11 //
This is the pin that will READ the IR messages
#define
DEBUG_PRINT(x) Serial.print
(x)
#define DEBUG_PRINTDEC(x)
Serial.print (x, DEC)
#define
DEBUG_PRINTLN(x) Serial.println (x)
*/
// following is for attiny
#include <SoftwareSerial.h>
#define RledPin 5
// This is the pin the LED is connected to
#define buttonPin 10 // This is the
pin that will read the button
#define IRtxPin 8
// This is the pin that will send IR messages
#define IRrxPin 7
// This is the pin that will READ the IR messages
#define SWrxPin 255 //
this maps to the TX on the FDDI board
#define SWtxPin 9 //
this maps to the RX on the FDDI board
#define
DEBUG_PRINT(x) SWSerial.print
(x)
#define DEBUG_PRINTDEC(x)
SWSerial.print (x, DEC)
#define
DEBUG_PRINTLN(x) SWSerial.println
(x)
// The following variable will set how verbose we want
the Serial debugging to be
const int debug_level = 2; // 1 = high level
debugging only; 2 = verbose logging
// These are the values for the short and long
blinks. Rules suggest dash should be 3 times
longer than dot
// We should try to get this down to the shortest
possible values.
const int dot = 50; // One unit
const int dash = dot * 3; // Three units
const boolean DOT = false;
const boolean DASH = true;
const struct MorseTree *tree = NULL;
int button = -1;
int pulseThreshold = dot; // the threshold between a
dot and a dash, in milliseconds.
int letterSeparation = dash; // the length of time, in
milliseconds, that must pass with no input between
letters.
int wordSeparation = dot * 7; //the length of time, in
milliseconds, that must pass with no input between
words.
#define MAX_CHAR_LENGTH 6
// Here are some constants that will make the code
more readable
const int wantToPlay = 1; // the ASCII value for
"1"...meaning..."I am player one and am ready to go"
const int yesWantToPlay = 2; // the ASCII value for
"2", meaning..."I will be player two"
const int rock = 3; // the ASCII value for
R...meaning...I am playing "Rock"
const int paper = 4; // the ASCII value for
P...meaning...I am playing "Paper"
const int scissor = 5; // the ASCII value for
S...meaning...I am playing "Scissor"
SoftwareSerial SWSerial(SWrxPin, SWtxPin); // RX, TX
void setup(){
pinMode(RledPin, OUTPUT);
pinMode(IRtxPin, OUTPUT);
pinMode(IRrxPin, INPUT);
pinMode(buttonPin, INPUT);
// This line is only needed if planning on
doing morse code
// NOTE: It takes about 10 seconds to start up
//initMorse(IRrxPin, pulseThreshold,
letterSeparation, wordSeparation);
SWSerial.begin(9600);
}
void
loop()
{
// Uncomment this if you want to program
the sending...
// charBlink(49);
findPartner();
delay(1000);
// When get here, another IR receiver has
been found and communicated with
//playGame();
}
//
++++++++++++++++++++++++++++++++++++++++++++++++++++++
//
++++++++++++++++++++++++++++++++++++++++++++++++++++++
//
++++++++++++++++++++++++++++++++++++++++++++++++++++++
//
++++++++++++++++++++++++++++++++++++++++++++++++++++++
//
++++++++++++++++++++++++++++++++++++++++++++++++++++++
//
++++++++++++++++++++++++++++++++++++++++++++++++++++++
//
++++++++++++++++++++++++++++++++++++++++++++++++++++++
//
++++++++++++++++++++++++++++++++++++++++++++++++++++++
findPartner
// This is the routine that runs at the start of the
game. It is how each of the two devices
// will initially discover each other. This
routine will run until it has found a partner to
// play with. While this routine runs the LED
will blink yellow.
void findPartner(){
debugLine(3, "Running findPartner");
boolean haveAPartner = false;
do {
// This will send
out a request for someone to play. This
// will be done
by sending a "1" (as in..."I am player 1").
// After the "1"
is sent, this will loop for two seconds waiting to see
if a "2"
// is sent back
as a "2" will indicate "I am in and ready to play as
player 2"
// The only
problem I can see here is...what if someone also tries
to communicate in the
// same time that
the 1 is transmitted. This will only be an issue
if the 1 takes too long.
// If that is the
case, we will need to wait a random amount of
time.
debugLine(1, "Looking
for a partner to play");
//charBlink(49);
char c = getNextChar();
debugLine(2,
String(c));
if (int(c) == 1) {
digitalWrite(RledPin, HIGH);
delay(1000);
digitalWrite(RledPin, LOW);
debugLine(2, "Game On");
haveAPartner == true;
}
} while (haveAPartner == false);
}
//
++++++++++++++++++++++++++++++++++++++++++++++++++++++
playGame
// This is the routine is going to be the bulk of the
game. It will negotiate who's playing
// and what they have choosen for a move. The
hard part will be doing this so that the players
// don't step on each other's move. You want
players to be able to communicate their moves at the
// same time rather than having to sit and wait.
void playGame(){
boolean myMoveMade = false; // This will keep
track of whether P1 has made their move
boolean otherMoveMade = false; // This will
keep track of whether P2 has made their move
int myMove = 0;
int otherMove = 0;
do { // Loop until both players have made
their move
// Check to see if current
user made a move
// Check to see if other user
transmitting a move
} while ((myMoveMade == false) &&
(otherMoveMade == false));
declareWinner(myMove, otherMove);
}
//
++++++++++++++++++++++++++++++++++++++++++++++++++++++
declareWinner
// This routine will declare the winner...
int declareWinner(int myMove, int otherMove){
int winner = 0;
switch (myMove) {
case rock:
if (otherMove == rock)
winner = 0;
else if (otherMove == paper)
winner = 2;
else
winner = 1;
break;
case paper:
if (otherMove == rock)
winner = 1;
else if (otherMove == paper)
winner = 0;
else
winner = 2;
break;
case scissor:
if (otherMove == rock)
winner = 2;
else if (otherMove == paper)
winner = 1;
else
winner = 0;
break;
default:
//
statements
break;
}
if (winner == 1) { // This
player one
// Turn LED to Green
and turn it on for 2 seconds
}
if (winner == 2) { // The
other player won
// Turn LED to Red and
turn it on for 2 seconds
}
if (winner == 0) { // It's a
tie
// Turn LED to Yellow
for one second and reset
}
}
//
++++++++++++++++++++++++++++++++++++++++++++++++++++++
debugLine
// This is a general routine that will be used for
debugging.
// It will use the SoftwareSerial library to send back
debugging
// through the FDDI cable. It will be passed two
variables...
// level This will
determine how verbose a debugging line the line is
//
and the line will only be printed to the screen if
that level of debugging is included
// message This is the message that
will be printed out.
void debugLine(int level, String message) {
if (debug_level >= level) {
SWSerial.println(message);
}
}
//
++++++++++++++++++++++++++++++++++++++++++++++++++++++
blink
// This is at the core of the entire send
routine. It is what will light the IR light.
// It is passed one value...an integer that
determines the length of time it should be lit.
void blink(int L){
digitalWrite(IRtxPin, HIGH); // set
the LED on
delay(L);
// wait for l/100 of a second
digitalWrite(IRtxPin, LOW);
// set the LED off
delay(dot);
}
//
++++++++++++++++++++++++++++++++++++++++++++++++++++++
charBlink
void charBlink(int c){
// This routine lights the LED using the morse code
pattern. It takes in integeger that represents
// the ASCII code for the specific character.
switch (c) {
case 1: // Player 1
blink(dot);
blink(dot);
blink(dot);
blink(dash);
delay(letterSeparation); // After each letter, three
dots of silence is observed.
debugLine(2, "Sent
Player 1");
return;
case 2: // Player 2
blink(dot);
blink(dot);
blink(dash);
blink(dot);
delay(letterSeparation); // After each letter, three
dots of silence is observed.
debugLine(2, "Sent
Player 2");
return;
case 3: // Rock
blink(dot);
blink(dot);
blink(dash);
blink(dash);
delay(letterSeparation); // After each letter, three
dots of silence is observed.
debugLine(2, "Sent
Rock");
return;
case 4: // Paper
blink(dot);
blink(dash);
blink(dot);
blink(dot);
delay(letterSeparation); // After each letter, three
dots of silence is observed.
debugLine(2, "Sent
Paper");
return;
case 5: // Scissor
blink(dot);
blink(dash);
blink(dot);
blink(dash);
delay(letterSeparation); // After each letter, three
dots of silence is observed.
debugLine(2, "Sent
Paper");
return;
case 48: // 0
blink(dash);
blink(dash);
blink(dash);
blink(dash);
blink(dash);
delay(letterSeparation); // After each letter, three
dots of silence is observed.
debugLine(2, "Sent 0");
return;
case 49: // 1
blink(dot);
blink(dash);
blink(dash);
blink(dash);
blink(dash);
delay(letterSeparation); // After each letter, three
dots of silence is observed.
debugLine(2, "Sent 1");
return;
case 50: // 2
blink(dot);
blink(dot);
blink(dash);
blink(dash);
blink(dash);
delay(letterSeparation); // After each letter, three
dots of silence is observed.
return;
case 51: // 3
blink(dot);
blink(dot);
blink(dot);
blink(dash);
blink(dash);
delay(letterSeparation); // After each letter, three
dots of silence is observed.
return;
case 52: // 4
blink(dot);
blink(dot);
blink(dot);
blink(dot);
blink(dash);
delay(letterSeparation); // After each letter, three
dots of silence is observed.
return;
case 53: // 5
blink(dot);
blink(dot);
blink(dot);
blink(dot);
blink(dot);
delay(letterSeparation); // After each letter, three
dots of silence is observed.
return;
case 54: // 6
blink(dash);
blink(dot);
blink(dot);
blink(dot);
blink(dot);
delay(letterSeparation); // After each letter, three
dots of silence is observed.
return;
case 55: // 7
blink(dash);
blink(dash);
blink(dot);
blink(dot);
blink(dot);
delay(letterSeparation); // After each letter, three
dots of silence is observed.
return;
case 56: // 8
blink(dash);
blink(dash);
blink(dash);
blink(dot);
blink(dot);
delay(letterSeparation); // After each letter, three
dots of silence is observed.
return;
case 57: // 9
blink(dash);
blink(dash);
blink(dash);
blink(dash);
blink(dot);
delay(letterSeparation); // After each letter, three
dots of silence is observed.
return;
case 65: // A
blink(dot);
blink(dash);
delay(letterSeparation); // After each letter, three
dots of silence is observed.
return;
case 66: // B
blink(dash);
blink(dot);
blink(dot);
blink(dot);
delay(letterSeparation); // After each letter, three
dots of silence is observed.
return;
case 67: // C
blink(dash);
blink(dot);
blink(dash);
blink(dot);
delay(letterSeparation); // After each letter, three
dots of silence is observed.
return;
case 68: // D
blink(dash);
blink(dot);
blink(dot);
delay(letterSeparation); // After each letter, three
dots of silence is observed.
return;
case 69: // E
blink(dot);
delay(letterSeparation); // After each letter, three
dots of silence is observed.
return;
case 70: // F
blink(dot);
blink(dot);
blink(dash);
delay(letterSeparation); // After each letter, three
dots of silence is observed.
return;
case 71: // G
blink(dash);
blink(dash);
blink(dot);
delay(letterSeparation); // After each letter, three
dots of silence is observed.
return;
case 72: // H
blink(dot);
blink(dot);
blink(dot);
blink(dot);
delay(letterSeparation); // After each letter, three
dots of silence is observed.
return;
case 73: // I
blink(dot);
blink(dot);
delay(letterSeparation); // After each letter, three
dots of silence is observed.
return;
case 74: // J
blink(dot);
blink(dash);
blink(dash);
blink(dash);
delay(letterSeparation); // After each letter, three
dots of silence is observed.
return;
case 75: // K
blink(dash);
blink(dot);
blink(dash);
delay(letterSeparation); // After each letter, three
dots of silence is observed.
return;
case 76: // L
blink(dot);
blink(dash);
blink(dot);
blink(dot);
delay(letterSeparation); // After each letter, three
dots of silence is observed.
return;
case 77: // M
blink(dash);
blink(dash);
delay(letterSeparation); // After each letter, three
dots of silence is observed.
return;
case 78: // N
blink(dash);
blink(dot);
delay(letterSeparation); // After each letter, three
dots of silence is observed.
return;
case 79: // O
blink(dash);
blink(dash);
blink(dash);
delay(letterSeparation); // After each letter, three
dots of silence is observed.
return;
case 80: // P
blink(dot);
blink(dash);
blink(dash);
blink(dot);
delay(letterSeparation); // After each letter, three
dots of silence is observed.
return;
case 81: // Q
blink(dash);
blink(dash);
blink(dot);
blink(dash);
delay(letterSeparation); // After each letter, three
dots of silence is observed.
return;
case 82: // R
blink(dot);
blink(dash);
blink(dot);
delay(letterSeparation); // After each letter, three
dots of silence is observed.
return;
case 83: // S
blink(dot);
blink(dot);
blink(dot);
delay(letterSeparation); // After each letter, three
dots of silence is observed.
return;
case 84: // T
blink(dash);
delay(letterSeparation); // After each letter, three
dots of silence is observed.
return;
case 85: // U
blink(dot);
blink(dot);
blink(dash);
delay(letterSeparation); // After each letter, three
dots of silence is observed.
return;
case 86: // V
blink(dot);
blink(dot);
blink(dot);
blink(dash);
delay(letterSeparation); // After each letter, three
dots of silence is observed.
return;
case 87: // W
blink(dot);
blink(dash);
blink(dash);
delay(letterSeparation); // After each letter, three
dots of silence is observed.
return;
case 88: // X
blink(dash);
blink(dot);
blink(dot);
blink(dash);
delay(letterSeparation); // After each letter, three
dots of silence is observed.
return;
case 89: // Y
blink(dash);
blink(dot);
blink(dash);
blink(dash);
delay(letterSeparation); // After each letter, three
dots of silence is observed.
return;
case 90: // Z
blink(dash);
blink(dash);
blink(dot);
blink(dot);
delay(letterSeparation); // After each letter, three
dots of silence is observed.
return;
default:
debugLine(2, "Sorry, i
didn't recognize that character ");
}
}
//
***************************************************************************
// THE FOLLOWING CAME FROM SOMEONE ELSE
//
http://bitbucket.natmote.net/arduino/src/c8866226c649/morse_decoder/?at=default
void initMorse(int lbutton, int lpulseThreshold, int
lletterSeparation, int lwordSeparation)
{
tree = generateMorseTree();
button = lbutton;
pulseThreshold = lpulseThreshold;
letterSeparation = lletterSeparation;
wordSeparation = lwordSeparation;
}
struct MorseTree
{
// '-' unless this is a leaf node
char character;
struct MorseTree *dotChild;
struct MorseTree *dashChild;
};
struct MorseData
{
char character;
bool code[MAX_CHAR_LENGTH];
byte codeLength;
};
void initMorseTree(struct MorseTree *tree)
{
tree -> character = '-';
tree -> dotChild = NULL;
tree -> dashChild = NULL;
}
struct MorseTree *newMorseTree()
{
struct MorseTree *tree = (struct MorseTree *)
malloc(sizeof(struct MorseTree));
initMorseTree(tree);
return tree;
}
void addTreeMember(struct MorseTree *tree, struct
MorseData &data)
{
struct MorseTree *current = tree;
for (byte i = 0; i < data.codeLength; i++)
{
boolean currentSymbol =
data.code[i];
if (currentSymbol == DOT)
{
if (current ->
dotChild == NULL)
current
-> dotChild = newMorseTree();
current = current ->
dotChild;
}
else
{
if (current ->
dashChild == NULL)
current
-> dashChild = newMorseTree();
current = current ->
dashChild;
}
}
// now current must be a leaf node
current -> character = data.character;
}
void addTreeMembers(struct MorseTree *tree, struct
MorseData data[], byte dataLength)
{
for (byte i = 0; i < dataLength; i++)
addTreeMember(tree, data[i]);
}
void addAlphabet(struct MorseTree *tree)
{
struct MorseData data[] = {
{'A', {DOT, DASH}, 2},
{'B', {DASH, DOT, DOT, DOT}, 4},
{'C', {DASH, DOT, DASH, DOT}, 4},
{'D', {DASH, DOT, DOT}, 3},
{'E', {DOT}, 1},
{'F', {DOT, DOT, DASH, DOT}, 4},
{'G', {DASH, DASH, DOT}, 3},
{'H', {DOT, DOT, DOT, DOT}, 4},
{'I', {DOT, DOT}, 2},
{'J', {DOT, DASH, DASH, DASH}, 4},
{'K', {DASH, DOT, DASH}, 3},
{'L', {DOT, DASH, DOT, DOT}, 4},
{'M', {DASH, DASH}, 2},
{'N', {DASH, DOT}, 2},
{'O', {DASH, DASH, DASH}, 3},
{'P', {DOT, DASH, DASH, DOT}, 4},
{'Q', {DASH, DASH, DOT, DASH}, 4},
{'R', {DOT, DASH, DOT}, 3},
{'S', {DOT, DOT, DOT}, 3},
{'T', {DASH}, 1},
{'U', {DOT, DOT, DASH}, 3},
{'V', {DOT, DOT, DOT, DASH}, 4},
{'W', {DOT, DASH, DASH}, 3},
{'X', {DASH, DOT, DOT, DASH}, 4},
{'Y', {DASH, DOT, DASH, DASH}, 4},
{'Z', {DASH, DASH, DOT, DOT}, 4},
};
addTreeMembers(tree, data, 26);
}
void addNumbers(struct MorseTree *tree)
{
struct MorseData data[] = {
{'1', {DOT, DASH, DASH, DASH,
DASH}, 5},
{'2', {DOT, DOT, DASH, DASH, DASH},
5},
{'3', {DOT, DOT, DOT, DASH, DASH},
5},
{'4', {DOT, DOT, DOT, DOT, DASH},
5},
{'5', {DOT, DOT, DOT, DOT, DOT},
5},
{'6', {DASH, DOT, DOT, DOT, DOT},
5},
{'7', {DASH, DASH, DOT, DOT, DOT},
5},
{'8', {DASH, DASH, DASH, DOT, DOT},
5},
{'9', {DASH, DASH, DASH, DASH,
DOT}, 5},
{'0', {DASH, DASH, DASH, DASH,
DASH}, 5},
};
addTreeMembers(tree, data, 10);
}
void addPunctuation(struct MorseTree *tree)
{
struct MorseData data[] = {
{'.', {DOT, DASH, DOT, DASH, DOT,
DASH}, 6},
{',', {DASH, DASH, DOT, DOT, DASH,
DASH}, 6},
{'?', {DOT, DOT, DASH, DASH, DOT,
DOT}, 6},
};
addTreeMembers(tree, data, 3);
}
struct MorseTree *generateMorseTree()
{
// since this will live for the life of the
program anyway, we won't even bother to have a
strategy for freeing it.
struct MorseTree *tree = newMorseTree();
initMorseTree(tree);
addAlphabet(tree);
addNumbers(tree);
addPunctuation(tree);
return tree;
}
// ---------------------------------------
// Arduino related stuff:
// ---------------------------------------
// waits for the given pin to go to the given state.
To reduce noise,
// the pin must read the required value twice, with a
short delay
// between the readings, to count as a reading.
void waitFor(int pin, int state)
{
debugLine(3, "Running waitFor on pin");
debugLine(3, String(pin));
debugLine(3, " ...and am waiting
for");
debugLine(3, String(state));
do
{
// spin until the pin reads the
given state
while (digitalRead(pin) != state) {
}
// delay, to verify the reading
delay(30);
// continue if the reading has
changed
} while (digitalRead(pin) != state);
}
boolean getNextSymbol()
{
debugLine(3, "Running getNextSymbol");
waitFor(button, HIGH);
debugLine(3, "Got a high");
unsigned long start = millis();
waitFor(button, LOW);
debugLine(3, "Went low");
unsigned long ending = millis();
unsigned long pulseLength = ending - start;
//Serial.print("Pulse length = ");
//Serial.print(pulseLength);
//Serial.print(" which is a ");
if (pulseLength > pulseThreshold) {
//Serial.println("DASH");
return DASH;
}
else {
//Serial.println("DOT");
return DOT;
}
}
boolean shouldTimeOut()
{
unsigned long start = millis();
while (digitalRead(button) == LOW)
{
if (millis() - start >
letterSeparation)
return true;
}
return false;
}
boolean shouldBeSpace()
{
unsigned long start = millis();
while (digitalRead(button) == LOW)
{
if (millis() - start >
wordSeparation)
return true;
}
return false;
}
char getNextChar()
{
debugLine(3, "Running getNextChar");
static boolean lastCharWasSpace = false;
const struct MorseTree *current = tree;
byte symbolCount = 0;
if (!lastCharWasSpace &&
shouldBeSpace())
{
lastCharWasSpace = true;
return ' ';
}
lastCharWasSpace = false;
while (true)
{
symbolCount++;
boolean currentSymbol =
getNextSymbol();
debugLine(3, (currentSymbol == DOT
? "DOT" : "DASH"));
if (currentSymbol == DOT)
current = current ->
dotChild;
else
current = current ->
dashChild;
if (current == NULL)
return '-';
if (shouldTimeOut() || symbolCount
>= MAX_CHAR_LENGTH) {
debugLine(2, "Got a
character!");
return current ->
character;
}
}
// should never get here
return '!';
}
|
+++++++++++++++++++++++++++++++++++++++++
++++++++++++ EXAMPLE OUTPUT ++++++++++++
+++++++++++++++++++++++++++++++++++++++++
+++ Output showing variant pulse lengths
Pulse length = 10736 which is a DASH
Pulse length = 568 which is a DASH
Pulse length = 1933 which is a DASH
Pulse length = 31 which is a DOT
Pulse length = 65 which is a DASH
-
Pulse length = 31 which is a DOT
Pulse length = 8360 which is a DASH
Pulse length = 30 which is a DOT
Pulse length = 31 which is a DOT
Pulse length = 63 which is a DASH
-
Pulse length = 3789 which is a DASH
Pulse length = 31 which is a DOT
Pulse length = 122 which is a DASH
Pulse length = 61 which is a DASH
Pulse length = 31 which is a DOT
-
Pulse length = 9574 which is a DASH
Pulse length = 3347 which is a DASH
Pulse length = 158 which is a DASH
Pulse length = 6831 which is a DASH
Pulse length = 122 which is a DASH
Pulse length = 3010 which is a DASH
|