Introduction: A Morse Code Translator( Light -> Text & Text ->Light)

About: Hi eveyone, my name is Raed, I'm a computer science student from Jordan, I'm passionate about learning and developing mobile games in Unity3D. I love to explore new ways and methods such as sensors as inputs f…

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)

Morse Code

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;
        }
    }
}