Introduction: A Morse Code Translator( Light -> Text & Text ->Light)
this is for my entry in the automation contest, I wanted to try something that has a little bit of code in it.
the project is divided into two main states(Read Light and Write Light) which are transitionable by a button
1-converting Light to Text, this was the most challenging part; using an LDR i needed to differentiate between a dot and a dash so I had to keep track of time the moment the light goes on till it goes off, and also I needed to differentiate between the end of letters and Words which is also done by keeping track of time the moment the light goes off till it's on again.
the most exciting part however, is that I made a Binary tree and Node class to represent the Morse code graph
so I could save a lot of time and processing power traversing through the tree finding letters from a dot and dash sequence with the simple logic
dot= left Child
dash= right Child
instead of linearly testing them in an array and comparing them character by character
2- converting Text to Light, for this I turned my 4X4 keypad into 4X4X2 so that I can represent all english alphabet (numbers not included unfortunately) you can view how to do so in my previous instructble. then I needed to convert my text to a dot and dash sequence which expressed with an LED light
Unity3D was used to visualize the code and pproject, it could have been easily replaced with an LCD screen and be 100% arduino :)
Step 1: Lets Get Our Components
1- LDR
2- LED
3- 2 10Kohm resistors
4-push Button
5-Keypad 4X4
6- a bunch of Jumper Wires(male to male)
Step 2: Wiring
The LDR needs to have a constant 5V through a 10Kohm resistor on One end and the other is connected to ground
the LED however is connected to A digitalPin(4) also through a resistor on One end and the other is to ground
as for the button I used Pull up resistor technique and connected it with ground and digitalPin(4)
the Keypad has 8 Wires connected from digital pin 5-12
Step 3: Build Unity Interface
We will be using the UI Canvas to create 3 images,each with it's own text component, an LCD to show out text and two Lights one indicating reading state(Light to Text) is active the other the write state(Text To Light).
I incorporated the two sprites I used and a video tutorial is available to those who are intrested
Step 4: Code:Arduino
the final code can be found here(make sure to add BinaryTree and Node classes to MorseCode.ino file)
the code is divided into 3 main steps:
1- switch between Reading state and Writing state through a push Button
2- (Writing state) take text input from the Keypad and convert to a sequence of Dots and Dashes expressed as LED light
3-(Reading state) monitor the LDR for light input, distinguish between Dot,Dash and spacing between letters and Words, once I have a Queue of my dot and dash sequence I want to use a Binary Tree to find out the letter representation .
#include <Keypad.h>
#include "BinaryTree.h" #include "Node.h" #include
const byte rows = 4; const byte columns = 4; const int LedPin = 4; int holdDelay = 500; //how long each hold will last int dotDelay = 1000; int n = 2; // how many letters we want each key to represent int state = 0; //used to indicate if hold occured and for how long, state =0 no hold ,state =1 short hold , state = 2 long hold char key = 0; String text = ""; String Array[26] = {".-", "-...", "-.-.", "-..", ".", "..-.", "--.", "....", "..", ".---", "-.-", ".-..", "--", "-.", "---", ".--.", "--.-", ".-.", "...", "-", "..-", "...-", ".--", "-..-", "-.--", "--.." };
//we will definne the key map as on the key pad: * notice each letter is spaced by 3 which our n char keys[rows][columns] = { {'a', 'c', 'e', 'g'}, {'i', 'k', 'm', 'o'}, {'q', 's', 'u', 'w'}, {'y', ' ', '#', '#'} };
byte rowPins[rows] = {12, 11, 10, 9}; byte columnPins[columns] = {8, 7, 6, 5};
Keypad keypad = Keypad(makeKeymap(keys), rowPins, columnPins, rows, columns);
const int ButtonPIN = 3; bool isReading = false;
QueueList words; bool sendData = false; bool lightOn = false; unsigned long elapsedTime = 0; unsigned long elapsedTimeOff = 0; unsigned long previousTime = 0; unsigned long previousTimeOff = 0; char data; BinaryTree* tree; void setup() { // put your setup code here, to run once: Serial.begin(9600); pinMode(ButtonPIN, INPUT); pinMode(LedPin, OUTPUT); digitalWrite(ButtonPIN, HIGH);
tree = NULL; tree = new BinaryTree(); }
void loop() { // put your main code here, to run repeatedly: if (digitalRead(ButtonPIN) == LOW) { isReading = !isReading; Serial.println("0"); delay(500); }
if (isReading) { char temp = keypad.getKey();
if ((int)keypad.getState() == PRESSED) {
if (temp != 0) { key = temp; } } if ((int)keypad.getState() == HOLD) { state++; state = constrain(state, 1, n - 1); delay(holdDelay); }
if ((int)keypad.getState() == RELEASED) {
if (key == '#') { textToLight(text); text = ""; Serial.println("#"); } else if (key == ' ') { text += " "; Serial.println(" "); } else {
key += state; int index = key - 97; text += Array[index]; Serial.println(key); } state = 0;
}
} else { int data = analogRead(A0); if ( data < 100 && !lightOn) {//the moment the light went On elapsedTimeOff = millis() - previousTimeOff; previousTime = millis(); lightOn = true; if ( elapsedTimeOff > (7 - 1) * dotDelay ) { words.push('*'); } else if (elapsedTimeOff > 3 * dotDelay) { words.push(' '); }
/*else if (elapsedTimeOff >(3-1)*dotDelay) {
}*/ }
if (lightOn) {//while light is On
if (data > 70) {//the moment the light went off lightOn = false; previousTimeOff = millis(); elapsedTime = millis() - previousTime;
if (elapsedTime > (7 - 1)*dotDelay) { readData(); } else if (elapsedTime > (3 - 1)*dotDelay) { words.push('-');
} else { words.push('.'); } }
}
}
delay(100);
} void textToLight(String text) {
for (int i = 0; i < text.length(); i++) { if (text.charAt(i) == '.') { digitalWrite(LedPin, HIGH); delay(dotDelay); digitalWrite(LedPin, LOW); delay(dotDelay * 3);
} else if ( text.charAt(i) == '-') { digitalWrite(LedPin, HIGH); delay(dotDelay * 3); digitalWrite(LedPin, LOW); delay(dotDelay * 3); } else { delay(dotDelay * 3);
} }
}
void readData() {
Node* focus = tree->root; while (!words.isEmpty()) { char letter = words.pop(); if (letter == '.') { focus = focus->right;
} else if (letter == '-') { focus = focus->left;
} else if (letter == ' ') { char letter = focus->get_Key(); Serial.println(letter); focus = tree->root;
} else { char letter = focus->get_Key(); Serial.println(letter); focus = tree->root; Serial.println(" "); } } char letter = focus->get_Key(); Serial.println(letter);
}
BinaryTree class
#include "arduino.h"
#include "Node.h" #include
class BinaryTree {
public: BinaryTree(); void initialize();
char letters[32] = {' ', 'e', 't', 'i', 'a', 'n', 'm', 's', 'u', 'r', 'w', 'd', 'k', 'g', 'o', 'h', 'v', 'f', ' ', 'l', ' ', 'p', 'j', 'b', 'x', 'c', 'y', 'z', 'q', ' ', ' ' }; QueueList words; Node* root;
};
BinaryTree::BinaryTree() {
root =NULL; root = new Node(letters[0]); initialize();
}
void BinaryTree::initialize() { QueueList nodes ; int counter = 0; nodes.push(root);
for (int i = 0; i < 15; i++) { Node* focus = nodes.pop(); focus->right =new Node(letters[i + 1 + counter]); focus->left =new Node(letters[i + 2 + counter]);
nodes.push(focus->right); nodes.push(focus->left);
counter++;
} }
Node class
#ifndef Node_h
#define Node_h
#include "arduino.h" class Node; class Node {
public: Node(char _key); Node* right; Node* left; char get_Key(); private: char key; //Node child; };
Node::Node(char _key) { key = _key;}
char Node::get_Key() {
return key; }
#endif
Step 5: Unity Code
1-Make sure to enable SerialPort communication by
Edit -> project Settings -> player -> optimization -> api compatability level ->.NET 2.0
2- create an empty game object and attach following script to it
using UnityEngine;
using System.Collections; using UnityEngine.UI; using System.IO.Ports; public class Controller : MonoBehaviour {
public Sprite onSprite, offSprite; public Image readImage, writeImage; public Text lcd;
private SerialPort port = new SerialPort(@"\\.\" + "COM11", 9600); private bool isReading = false; // Use this for initialization void Start () { port.Open(); port.ReadTimeout = 25; StartCoroutine(readString()); } // Update is called once per frame void Update () {
if (!isReading) { readImage.sprite = onSprite; writeImage.sprite = offSprite;
}
else { readImage.sprite = offSprite; writeImage.sprite = onSprite; } }
IEnumerator readString() {
while (true) {
if (port.IsOpen) {
try {
string data = port.ReadLine(); Debug.Log(data); if (data == "0") { isReading = !isReading; lcd.text = ""; } else if (data == "#") { lcd.text = ""; } else { lcd.text += data;
}
} catch (System.Exception) {
}
}
yield return null; }
} }