#include <LiquidCrystal_I2C.h>
#include "Keypad.h"
#include <cstring>
#define I2C_ADDR 0x27
#define LCD_COLUMNS 16
#define LCD_LINES 2
#define relayout 5
#define ledPin 19
#define audioInPin 18
#define speaker 17
#define DEFAULT_DELAY 300
const byte ROWS = 4; //four rows
const byte COLS = 4; //four columns
int x = 0; // Holds the LCD x position
int y = 0; // Holds the LCD y position
int minValue = 0; // Lower character location for T9 text entry
int maxValue = 0; // Max character location for T9 text entry
int keyPressTime = 100; // Number of loops check of the key
String msg = ""; // Holds the created message
//String num = ""; // Holds the mobile number
String alpha = "1/@_$%? ABC2 DEF3 GHI4 JKL5 MNO6 PQRS7 TUV8 WXYZ9 * 0# "; // Characters for T9 text entry
//String morseWord = "";
char hexaKeys[ROWS][COLS] = { // Character matrix for the keypad
{'1', '2', '3', 'A'},
{'4', '5', '6', 'B'},
{'7', '8', '9', 'C'},
{'*', '0', '#', 'D'}
};
byte rowPins[ROWS] = {27, 13, 16, 4}; // pin assignments for keypad rows
byte colPins[COLS] = {33, 25, 26, 14}; // pin assignments for keypad columns
Keypad customKeypad = Keypad( makeKeymap(hexaKeys), rowPins, colPins, ROWS, COLS);
LiquidCrystal_I2C lcd(I2C_ADDR, LCD_COLUMNS, LCD_LINES); // pin assignments for LCD
// Morse code conversion table
const char* morseCode[] = {
".-", "-...", "-.-.", "-..", ".", "..-.", "--.", "....", "..", ".---", "-.-", ".-..", "--", "-.",
"---", ".--.", "--.-", ".-.", "...", "-", "..-", "...-", ".--", "-..-", "-.--", "--..",
".----", "..---", "...--", "....-", ".....", "-....", "--...", "---..", "----.", "-----"
};
const int numChar = sizeof(morseCode) / sizeof(morseCode[0]);
// Buffer for storing the entered text
char buffer[100] ;
int bufferIndex = 0;
char receivedText[100];
int receivedTextIndex = 0;
///////////// prameter for receiving Morse code/////////////
int lcdindex = 0;
int line1[LCD_COLUMNS];
int line2[LCD_COLUMNS];
//int audioInPin = 18 ; // relayin 18
//int audioOutPin = 17; // speaker 17
//int ledPin = 19;
float magnitude ;
int magnitudelimit = 100;
int magnitudelimit_low = 100;
int realstate = LOW;
int realstatebefore = LOW;
int filteredstate = LOW;
int filteredstatebefore = LOW;
float coeff;
float Q1 = 0;
float Q2 = 0;
float sine;
float cosine;
float sampling_freq = 8928.0;
float target_freq = 558.0; /// adjust for your needs see above
float n = 48.0; //// if you change her please change next line also
int testData[48];
int nbtime = 6; /// ms noise blanker
long starttimehigh;
long highduration;
long lasthighduration;
long hightimesavg;
long lowtimesavg;
long startttimelow;
long lowduration;
long laststarttime = 0;
char code[100];
int codeIndex = 0;
int stop = LOW;
int wpm;
//////////////////////////////////////////////////////////////////////
byte charUp[8] = { // arrow up character for LCD
B00100,
B01110,
B11111,
B00000,
B00000,
B00000,
B00000,
B00000
};
byte charDown[8] = { // arrow down character for LCD
B00000,
B00000,
B00000,
B00000,
B00000,
B11111,
B01110,
B00100
};
byte charUpDown[8] = { // arrow up and down character for LCD
B00100,
B01110,
B11111,
B00000,
B00000,
B11111,
B01110,
B00100
};
byte menuLevel = 0; // Level 0: no menu display, display anything you like
// Level 1: display main menu
// Level 2: display sub menu
// Level 3: display sub menu of sub menu
byte menu = 1; // holds the menu level
byte sub = 1; // holds the sub menu level
void setup()
{
Serial.begin(115200);
lcd.init(); // Init
lcd.backlight();
lcd.createChar(0, charUp); // arrow up character
lcd.createChar(1, charDown); // arrow down character
lcd.createChar(2, charUpDown); // arrow up and down character
updateLevel_0(); // display the HOME screen
// Set up the relay pins
pinMode(relayout, OUTPUT); // Relay for sending Morse code
digitalWrite(relayout, LOW);
pinMode(audioInPin, INPUT); // audo input for receiving Morse code
pinMode(ledPin, OUTPUT);
digitalWrite(ledPin, LOW);
pinMode(speaker, OUTPUT); // Speaker for play sound
digitalWrite(speaker, LOW);
noTone(speaker);
/////// for recieving ... ////////
int k;
float omega;
k = (int) (0.5 + ((n * target_freq) / sampling_freq));
omega = (2.0 * PI * k) / n;
sine = sin(omega);
cosine = cos(omega);
coeff = 2.0 * cosine;
for (int index = 0; index < LCD_COLUMNS; index++) {
line1[index] = 32;
line2[index] = 32;
}
}
void loop() {
// Process the keys
processkey();
strcpy(buffer, msg.c_str()); //copy text in msg to buffer
// Do other stuffs here
}
void postPrint()
{
x++;
if ( x >= 16 )
{
if (y == 0)
{
y++;
y = 0;
} else
{
lcd.clear();
x = 0;
y = 0;
}
}
}
void processkey() {
char key = customKeypad.getKey();
if (isAlpha(key)) { // check if key press is a letter
processKeys(key); // process it according to keys
}
}
void parseKey(int minValue, int maxValue, char keyPress) {
int ch = minValue;
char key = keyPress;
if (keyPress == 'C') { // if C, means backspace
if ( (x > 0) || (y > 0) ) { // prevent backspace when no character yet
x = x - 1; // go back to previous character position
lcd.setCursor(x, y); // set the new lcd position
lcd.print(" "); // write _, which means for editing
msg.remove(msg.length() - 1); // remove the last character from the string
}
} else {
for (int i = 0; i < keyPressTime; i++) {
if (key == keyPress) { // make sure that same key is press
lcd.setCursor(x, y); // set the lcd position
lcd.print(alpha[ch]); // print the character according to the character position
ch++; // increment character position
if (ch > maxValue) { // if the character counter reached the max value
ch = minValue; // reset to min value
i = 0; // reset the loop counter
}
}
key = customKeypad.getKey(); // get the keypress
delay(10); // delay for some time
}
x++; // increment the x position
msg += alpha[ch - 1]; // add the character to the variable msg
if (x > 15) { // if the lcd reached the rightmost position
y = 1; // then wrap to the next line
x = 0; // in first character in the left
}
}
}
void enterMSG() {
char key;
lcd.clear(); // clear the LCD display
x = 0; // init the x position to zero
y = 0; // init the y position to zero
msg = ""; // clear the msg variable
clearBuffer(); // clear the buffer variable
do {
key = customKeypad.getKey();
if (key == '1') { // if a key is pressed,
parseKey(0, 7, key); // compare it to the alpha string array
} else if (key == '2') {
parseKey(8, 12, key);
} else if (key == '3') {
parseKey(13, 17, key);
} else if (key == '4') {
parseKey(18, 22, key);
} else if (key == '5') {
parseKey(23, 27, key);
} else if (key == '6') {
parseKey(28, 32, key);
} else if (key == '7') {
parseKey(33, 38, key);
} else if (key == '8') {
parseKey(39, 43, key);
} else if (key == '9') {
parseKey(44, 49, key);
} else if (key == '0') {
parseKey(52, 54, key);
} else if (key == 'C') {
parseKey(50, 51, key);
} else if (key == 'D') {
// do nothing
}
} while (key != 'D'); // exit the loop when D is pressed
}
// Function to send Morse code using a relay/////////////////////////////////////////////////////////////
void sendMorseCode(const char* text) {
for (int i = 0; text[i] != '\0'; i++) {
char ch = toLowerCase(text[i]);
if (ch >= 'a' && ch <= 'z') {
int index = ch - 'a';
sendMorseChar(morseCode[index]);
} else if (ch >= '0' && ch <= '9') {
int index = ch - '0' + 26;
sendMorseChar(morseCode[index]);
} else if (ch == '/') {
// Pause between words
delay(1400);
}
// Pause between characters
delay(700);
}
}
// Function to send a single Morse code character//////////////////////////////////////////////////
void sendMorseChar(const char* morseChar) {
for (int i = 0; morseChar[i] != '\0'; i++) {
if (morseChar[i] == '.') {
digitalWrite(relayout, HIGH); // activate the relay
digitalWrite(speaker, HIGH);
tone(speaker, 558.0);
digitalWrite(ledPin, HIGH);
delay(200);
digitalWrite(relayout, LOW); // deactivate the relay
digitalWrite(speaker, LOW);
noTone(speaker);
digitalWrite(ledPin, LOW);
} else if (morseChar[i] == '-') {
digitalWrite(relayout, HIGH); // activate the relay
digitalWrite(speaker, HIGH);
tone(speaker, 558.0);
digitalWrite(ledPin, HIGH);
delay(600);
digitalWrite(relayout, LOW); // deactivate the relay
digitalWrite(speaker, LOW);
noTone(speaker);
digitalWrite(ledPin, LOW);
}
// Pause between dots and dashes
delay(200);
}
}
//////////////for recieving/////////////////
void recieveMorseCode() {
lcd.clear(); // clear the LCD display
/////////////////////////////////////
// The basic where we get the tone //
/////////////////////////////////////
for (char index = 0; index < n; index++)
{
testData[index] = analogRead(audioInPin);
}
for (char index = 0; index < n; index++) {
float Q0;
Q0 = coeff * Q1 - Q2 + (float) testData[index];
Q2 = Q1;
Q1 = Q0;
}
float magnitudeSquared = (Q1 * Q1) + (Q2 * Q2) - Q1 * Q2 * coeff; // we do only need the real part //
magnitude = sqrt(magnitudeSquared);
Q2 = 0;
Q1 = 0;
//Serial.print(magnitude); Serial.println(); //// here you can measure magnitude for setup..
///////////////////////////////////////////////////////////
// here we will try to set the magnitude limit automatic //
///////////////////////////////////////////////////////////
if (magnitude > magnitudelimit_low) {
magnitudelimit = (magnitudelimit + ((magnitude - magnitudelimit) / 6)); /// moving average filter
}
if (magnitudelimit < magnitudelimit_low)
magnitudelimit = magnitudelimit_low;
////////////////////////////////////
// now we check for the magnitude //
////////////////////////////////////
if (magnitude > magnitudelimit * 0.6) // just to have some space up
realstate = HIGH;
else
realstate = LOW;
/////////////////////////////////////////////////////
// here we clean up the state with a noise blanker //
/////////////////////////////////////////////////////
if (realstate != realstatebefore) {
laststarttime = millis();
}
if ((millis() - laststarttime) > nbtime) { // note that ==> nbtime = 6 ms
if (realstate != filteredstate) {
filteredstate = realstate;
}
}
////////////////////////////////////////////////////////////
// Then we do want to have some durations on high and low //
////////////////////////////////////////////////////////////
if (filteredstate != filteredstatebefore) {
if (filteredstate == HIGH) {
starttimehigh = millis();
lowduration = (millis() - startttimelow);
}
if (filteredstate == LOW) {
startttimelow = millis();
highduration = (millis() - starttimehigh);
if (highduration < (2 * hightimesavg) || hightimesavg == 0) {
hightimesavg = (highduration + hightimesavg + hightimesavg) / 3; // now we know avg dit time ( rolling 3 avg)
}
if (highduration > (5 * hightimesavg) ) {
hightimesavg = highduration + hightimesavg; // if speed decrease fast ..
}
}
}
///////////////////////////////////////////////////////////////
// now we will check which kind of baud we have - dit or dah //
// and what kind of pause we do have 1 - 3 or 7 pause //
// we think that hightimeavg = 1 bit //
///////////////////////////////////////////////////////////////
if (filteredstate != filteredstatebefore) {
stop = LOW;
if (filteredstate == LOW) { //// we did end a HIGH
if (highduration < (hightimesavg * 2) && highduration > (hightimesavg * 0.6)) { /// 0.6 filter out false dits
strcat(code, ".");
Serial.print(".");
}
if (highduration > (hightimesavg * 2) && highduration < (hightimesavg * 6)) {
strcat(code, "-");
Serial.print("-");
wpm = (wpm + (1200 / ((highduration) / 3))) / 2; //// the most precise we can do ;o)
}
}
if (filteredstate == HIGH) { //// we did end a LOW
float lacktime = 1;
if (wpm > 25)lacktime = 1.0; /// when high speeds we have to have a little more pause before new letter or new word
if (wpm > 30)lacktime = 1.2;
if (wpm > 35)lacktime = 1.5;
if (lowduration > (hightimesavg * (2 * lacktime)) && lowduration < hightimesavg * (5 * lacktime)) { // letter space
//docode();
code[0] = '\0';
Serial.print("/");
}
if (lowduration >= hightimesavg * (5 * lacktime)) { // word space
//docode();
code[0] = '\0';
//printascii(32);
Serial.println();
}
}
}
//////////////////////////////
// write if no more letters //
//////////////////////////////
if ((millis() - startttimelow) > (highduration * 6) && stop == LOW) {
//docode();
code[0] = '\0';
stop = HIGH;
}
/////////////////////////////////////
// we will turn on and off the LED //
// and the speaker //
/////////////////////////////////////
if (filteredstate == HIGH) {
digitalWrite(ledPin, HIGH);
tone(speaker, target_freq);
}
else {
digitalWrite(ledPin, LOW);
noTone(speaker);
}
//////////////////////////////////
// the end of main loop clean up//
/////////////////////////////////
updateinfolinelcd();
realstatebefore = realstate;
lasthighduration = highduration;
filteredstatebefore = filteredstate;
}
void updateinfolinelcd() {
/////////////////////////////////////
// here we update the upper line //
// with the speed. //
/////////////////////////////////////
int place;
if (LCD_LINES == 4) {
place = LCD_COLUMNS / 2;
}
else {
place = 2;
}
if (wpm < 10) {
lcd.setCursor((place) - 2, 0);
lcd.print("0");
lcd.setCursor((place) - 1, 0);
lcd.print(wpm);
lcd.setCursor((place), 0);
lcd.print(" WPM");
}
else {
lcd.setCursor((place) - 2, 0);
lcd.print(wpm);
lcd.setCursor((place), 0);
lcd.print(" WPM ");
}
}
// Function to convert Morse code back to text
void convertToText(const char* code, char* receivedText) {
int index = 0;
char tempMorse[100];
strcpy(tempMorse, code); // Copy the content of code to tempMorse
char* token = strtok(tempMorse, " ");
while (token != NULL) {
for (int i = 0; i < numChar; i++) {
if (strcmp(token, morseCode[i]) == 0) {
if (i < 26) {
receivedText[index] = i + 'A';
} else {
receivedText[index] = i - 26 + '0';
}
index++;
break;
}
}
token = strtok(NULL, " ");
}
receivedText[index] = '\0';
}
/////////////////////////////////////////////////////////////////////////////
void processKeys(int keyPressed) {
switch (menuLevel) {
case 0: // Level 0, home screen
switch ( keyPressed ) {
case 'D': // Enter
menu = 1;
menuLevel = 2; // go to main menu
updateLevel_2(); // show main menu
delay(DEFAULT_DELAY);
break;
case 'A': // Up
break;
case 'B': // Down
menu = 2;
menuLevel = 2; // go to sub menu
updateLevel_2(); // show sub menu
delay(DEFAULT_DELAY);
break;
case 'C': // Back
menuLevel = 0; // go to home screen
updateLevel_0(); // show home screen (ElctroMorseCode E S O T)
delay(DEFAULT_DELAY * 2);
break;
default:
break;
}
break;
case 1: // Level 1, main menu
switch ( keyPressed ) {
case 'D': // Enter
sub = 1;
menuLevel = 2; // go to sub menu
updateLevel_2(); // show sub menu
delay(DEFAULT_DELAY);
break;
case 'A': // Up
menu--;
updateLevel_1(); // show main menu
delay(DEFAULT_DELAY);
break;
case 'B': // Down
menu++;
updateLevel_1(); // show main menu
delay(DEFAULT_DELAY);
break;
case 'C': // Back
menuLevel = 0; // hide menu, go back to level 0
updateLevel_0(); // show home screen
delay(DEFAULT_DELAY);
break;
default:
break;
}
break;
case 2: // Level 2, sub menu
switch ( keyPressed ) {
case 'D': // Enter
if (menu == 1) {
if (sub == 1) { // Create text to convert to Morse code
lcd.clear();
lcd.print("Enter Msg");
delay(1000);
enterMSG();
delay(500);
lcd.clear();
menuLevel = 2; // go to sub menu
updateLevel_2(); // show sub menu
} else if (sub == 2) { // Send Morse code
lcd.clear();
lcd.print("sending ...");
lcd.setCursor(0, 1);
lcd.print(buffer);
sendMorseCode(buffer);
menuLevel = 2; // go to sub menu
updateLevel_2(); // show sub menu
} else if (sub == 3) { // Read msg Morsecode
lcd.clear();
lcd.print("Sent/Msg");
lcd.setCursor(0, 1);
delay(500);
lcd.clear();
lcd.print(msg);
delay(1000);
//char key = customKeypad.getKey();
//if (key == 'C') { // exit the loop when C is pressed
menuLevel = 2; // go to sub menu
updateLevel_2(); // show sub menu
//}
} else if (sub == 4) { // Delete msg Morsecode
lcd.clear();
lcd.print("Snd/Msg Deleted");
lcd.setCursor(0, 1);
msg = "" ; // clear msg Morsecode
clearBuffer();
delay(1000);
menuLevel = 2; // go to sub menu
updateLevel_2(); // show sub menu
}
} else if (menu == 2) {
if (sub == 1) { // Receiving the Morse code
lcd.clear();
lcd.print("recieving ...");
delay(500);
recieveMorseCode();
convertToText(code, receivedText);
lcd.println(receivedText);
char key = customKeypad.getKey();
if (key == 'C') { // exit the loop when C is pressed
menuLevel = 2; // go to sub menu
updateLevel_2(); // show sub menu
}
} else if (sub == 2) { // read Morse code
lcd.clear();
lcd.print("Received Msg");
lcd.setCursor(0, 1);
delay(500);
lcd.clear();
lcd.print(receivedText);
char key = customKeypad.getKey();
if (key == 'C') { // exit the loop when C is pressed
menuLevel = 2; // go to sub menu
updateLevel_2(); // show sub menu
}
} else if (sub == 3) { // Read msg Morsecode
lcd.clear();
lcd.print("Rec/Msg Deleted");
lcd.setCursor(0, 1);
clearCode();
clearReceivedText();
delay(1000);
menuLevel = 2; // go to sub menu
updateLevel_2(); // show sub menu
}
delay(DEFAULT_DELAY);
break;
}
case 'A': // Up
sub--;
updateLevel_2();
delay(DEFAULT_DELAY);
break;
case 'B': // Down
sub++;
updateLevel_2(); // show main menu
delay(DEFAULT_DELAY);
break;
case 'C': // Back
menuLevel = 1; // go back to level 1
updateLevel_1(); // show main menu
delay(DEFAULT_DELAY);
break;
default:
break;
}
break;
default:
break;
}
}
void updateLevel_0() {
lcd.clear();
lcd.println("ELCTRO/MORSECODE");
lcd.setCursor(0, 1);
lcd.println("*** E S O T ***");
delay(DEFAULT_DELAY * 10);
lcd.clear();
lcd.println("> D for Snd/Morse ");
lcd.setCursor(0, 1);
lcd.println("> B for Rec/Morse ");
}
void updateLevel_1 () {
switch (menu) {
case 0:
menu = 1;
break;
case 1:
lcd.clear();
lcd.print(">Snd/Morse ");
lcd.setCursor(0, 1);
lcd.print("Rec/Morse ");
lcd.setCursor(15, 1);
lcd.write((byte)1); // down arrow
break;
case 2:
lcd.clear();
lcd.print("Snd/Morse ");
lcd.setCursor(0, 1);
lcd.print(">Rec/Morse ");
lcd.setCursor(15, 1);
lcd.write((byte)0); // up arrow
break;
case 3:
menu = 2;
break;
}
}
void updateLevel_2 () {
switch (menu) {
case 0:
break;
case 1: // Messages
switch (sub) {
case 0:
break;
case 1:
lcd.clear();
lcd.print("Send MorseCode");
lcd.setCursor(0, 1);
lcd.write((byte)1); // down arrow
lcd.print(" Create");
lcd.setCursor(15, 1);
break;
case 2:
lcd.clear();
lcd.print("Send MorseCode");
lcd.setCursor(0, 1);
lcd.write((byte)2); // up and down arrow
lcd.print(" Send");
lcd.setCursor(15, 1);
break;
case 3:
lcd.clear();
lcd.print("Send MorseCode");
lcd.setCursor(0, 1);
lcd.write((byte)2); // up and down arrow
lcd.print(" Read");
lcd.setCursor(15, 1);
break;
case 4:
lcd.clear();
lcd.print("Send MorseCode");
lcd.setCursor(0, 1);
lcd.write((byte)0); // up arrow
lcd.print(" Delete");
lcd.setCursor(15, 1);
break;
default:
break;
}
break;
case 2: // Relay 2
switch (sub) {
case 0:
break;
case 1:
lcd.clear();
lcd.print("Recieve MorseCode");
lcd.setCursor(0, 1);
lcd.write((byte)1); // down arrow
lcd.print(" Recieving");
lcd.setCursor(15, 1);
break;
case 2:
lcd.clear();
lcd.print("Recieve MorseCode");
lcd.setCursor(0, 1);
lcd.write((byte)2); // up and down arrow
lcd.print(" Read");
lcd.setCursor(15, 1);
break;
case 3:
lcd.clear();
lcd.print("Recieve MorseCode");
lcd.setCursor(0, 1);
lcd.write((byte)0); // up arrow
lcd.print(" Delete");
lcd.setCursor(15, 1);
break;
default:
break;
}
break;
case 3:
sub = 2;
break;
}
}
// Function to clear the buffer
void clearBuffer() {
for (int i = 0; i < bufferIndex; i++) {
buffer[i] = '\0';
}
bufferIndex = 0;
}
void clearCode() {
for (int i = 0; i < codeIndex; i++) {
code[i] = '\0';
}
codeIndex = 0;
}
void clearReceivedText() {
for (int i = 0; i < receivedTextIndex; i++) {
receivedText[i] = '\0';
}
receivedTextIndex = 0;
}