#include <LiquidCrystal.h>
#define BUZ_PIN 10
#define BUTTON_PIN 9
#define LED_PIN 13
#define NOTE_C6 1047
#define DOT 0b01
#define DASH 0b11
class CharQueue
{
private:
char *queue;
int queueSize;
int front;
int rear;
int count;
public:
CharQueue(int size) : queueSize(size), front(0), rear(0), count(0)
{
queue = new char[queueSize];
}
~CharQueue()
{
delete[] queue;
}
bool enqueue(char c)
{
if (count >= queueSize)
dequeue();
queue[rear] = c;
rear = (rear + 1) % queueSize;
count++;
return true;
}
char dequeue()
{
if (count <= 0)
return '\0';
char c = queue[front];
front = (front + 1) % queueSize;
count--;
return c;
}
bool isEmpty()
{
return count == 0;
}
void clear()
{
front = rear = count = 0;
}
char *getQueueAsString()
{
char *str = new char[count + 1]; // Allocate memory for the string plus null terminator
int tmpFront = front;
for (int i = 0; i < count; i++)
{ // Copy the queue contents
str[i] = queue[tmpFront];
tmpFront = (tmpFront + 1) % queueSize;
}
str[count] = '\0'; // Append the null terminator
return str;
}
};
class DebouncedButton
{
private:
int pin;
unsigned long debounceDelay;
unsigned long lastDebounceTime;
int lastButtonState;
int buttonState;
public:
DebouncedButton(int buttonPin, unsigned long delay) : pin(buttonPin), debounceDelay(delay)
{
pinMode(pin, INPUT_PULLUP); // Use INPUT_PULLUP if your button is connected to ground
lastDebounceTime = 0;
lastButtonState = HIGH; // Assuming INPUT_PULLUP
buttonState = HIGH;
}
int read()
{
int reading = digitalRead(pin);
if (reading != lastButtonState)
{
lastDebounceTime = millis();
}
if ((millis() - lastDebounceTime) > debounceDelay)
{
buttonState = reading;
}
lastButtonState = reading;
return buttonState;
}
};
class MorseDecoder
{
private:
DebouncedButton &button;
unsigned long pressStartTime;
unsigned int morseCode; // 16-bit variable to store dots and dashes
unsigned long lastPressTime;
bool nextMorseSymbolAvailable=false;
char lastMorseSymbol = ' ';
CharQueue *messageQueue;
const unsigned long dotDuration = 200; // Max duration of a dot press in milliseconds
const unsigned long dashDuration = 600; // Min duration of a dash press in milliseconds
const unsigned long letterPause = 1000; // Pause duration to consider end of a letter
const unsigned long wordPause = 2000;
// Function to add a dot or dash to the 16-bit morseCode variable
void addMorseSymbol(byte morseSymbol)
{
morseCode <<= 2;
morseCode |= morseSymbol;
}
char decodeMorse(unsigned int morseCode)
{
switch (morseCode)
{
case 0b0111:
return 'A'; // .-
case 0b11010101:
return 'B'; // -...
case 0b11011101:
return 'C'; // -.-.
case 0b110101:
return 'D'; // -..
case 0b01:
return 'E'; // .
case 0b01011101:
return 'F'; // ..-.
case 0b111101:
return 'G'; // --.
case 0b01010101:
return 'H'; // ....
case 0b0101:
return 'I'; // ..
case 0b01111111:
return 'J'; // .---
case 0b110111:
return 'K'; // -.-
case 0b01110101:
return 'L'; // .-..
case 0b1111:
return 'M'; // --
case 0b1101:
return 'N'; // -.
case 0b111111:
return 'O'; // ---
case 0b01111101:
return 'P'; // .--.
case 0b11110111:
return 'Q'; // --.-
case 0b011101:
return 'R'; // .-.
case 0b010101:
return 'S'; // ...
case 0b11:
return 'T'; // -
case 0b010111:
return 'U'; // ..-
case 0b01010111:
return 'V'; // ...-
case 0b011111:
return 'W'; // .--
case 0b11010111:
return 'X'; // -..-
case 0b11011111:
return 'Y'; // -.--
case 0b11110101:
return 'Z'; // --..
// Numbers
case 0b0111111111:
return '1'; // .----
case 0b0101111111:
return '2'; // ..---
case 0b0101011111:
return '3'; // ...--
case 0b0101010111:
return '4'; // ....-
case 0b0101010101:
return '5'; // .....
case 0b1101010101:
return '6'; // -....
case 0b1111010101:
return '7'; // --...
case 0b1111110101:
return '8'; // ---..
case 0b1111111101:
return '9'; // ----.
case 0b1111111111:
return '0'; // -----
// Special Characters
case 0b01110111:
return 'Ä'; // .-.- (A with umlaut)
case 0b11111101:
return 'Ö'; // ---. (O with umlaut)
case 0b0111110111:
return 'Å'; // .--.- (A with ring)
default:
return '?'; // Unknown code
}
}
public:
MorseDecoder(DebouncedButton &btn, int queueSize) : button(btn)
{
pressStartTime = 0;
morseCode = 0;
lastPressTime = 0;
messageQueue = new CharQueue(queueSize);
}
~MorseDecoder() {
delete messageQueue;
}
void update()
{
int state = button.read();
unsigned long currentTime = millis();
// Button pressed
if (state == LOW && pressStartTime == 0)
{
pressStartTime = currentTime;
if (currentTime - lastPressTime > wordPause && lastPressTime > 0) {
messageQueue->enqueue(' ');
}
}
// Button released
if (state == HIGH && pressStartTime > 0)
{
unsigned long pressDuration = currentTime - pressStartTime;
pressStartTime = 0;
lastPressTime = currentTime;
// Determine if the press was a dot or a dash, and add it to morseCode
if (pressDuration < dotDuration)
{
addMorseSymbol(DOT);
lastMorseSymbol = '.';
}
else if (pressDuration >= dotDuration)
{
addMorseSymbol(DASH);
lastMorseSymbol = '_';
}
nextMorseSymbolAvailable=true;
}
// Check for letter pause
if (morseCode != 0 && currentTime - lastPressTime > letterPause)
{
char decodedChar = decodeMorse(morseCode);
messageQueue->enqueue(decodedChar);
morseCode = 0;
}
}
bool hasCharacter()
{
return !messageQueue->isEmpty();
}
char dequeueCharacter()
{
return messageQueue->dequeue();
}
bool hasMorseSymbol()
{
return nextMorseSymbolAvailable;
}
char dequeueMorseSymbol()
{
nextMorseSymbolAvailable = false;
return lastMorseSymbol;
}
};
class TonePress {
private:
DebouncedButton &button;
int tonePin;
int frequency;
bool isTonePlaying;
public:
TonePress(DebouncedButton &debouncedButton, int toneOutputPin, int freq)
: button(debouncedButton), tonePin(toneOutputPin), frequency(freq), isTonePlaying(false) {
pinMode(tonePin, OUTPUT);
}
void update() {
int buttonState = button.read();
if (buttonState == LOW && !isTonePlaying) {
tone(tonePin, frequency);
isTonePlaying = true;
} else if (buttonState == HIGH && isTonePlaying) {
noTone(tonePin);
isTonePlaying = false;
}
}
};
class LightPress {
private:
DebouncedButton &button;
int ledPin;
bool isLedOn;
public:
LightPress(DebouncedButton &debouncedButton, int ledPin)
: button(debouncedButton), ledPin(ledPin), isLedOn(false) {
pinMode(ledPin, OUTPUT);
}
void update() {
int buttonState = button.read();
if (buttonState == LOW && !isLedOn) {
digitalWrite(ledPin, HIGH);
isLedOn = true;
} else if (buttonState == HIGH && isLedOn) {
digitalWrite(ledPin, LOW);
isLedOn = false;
}
}
};
DebouncedButton debouncedButton(BUTTON_PIN, 50);
MorseDecoder morseDecoder(debouncedButton, 16);
LiquidCrystal lcd(A2, A3, 4, 5, 6, 7);
TonePress tonePress(debouncedButton, BUZ_PIN, NOTE_C6);
LightPress lightPress(debouncedButton, LED_PIN);
CharQueue textQueue(16);
CharQueue morseQueue(5);
int prev = millis();
void showMorseQueue() {
lcd.setCursor(0, 1);
if (morseQueue.isEmpty()) {
lcd.print(" ");
} else {
lcd.print(morseQueue.getQueueAsString());
}
}
void showTextQueue() {
lcd.setCursor(0, 0);
lcd.print(textQueue.getQueueAsString());
}
void setup() {
Serial.begin(9600);
lcd.begin(16,2);
}
void loop() {
tonePress.update();
lightPress.update();
morseDecoder.update();
if (morseDecoder.hasMorseSymbol())
{
morseQueue.enqueue(morseDecoder.dequeueMorseSymbol());
showMorseQueue();
}
if (morseDecoder.hasCharacter())
{
char c = morseDecoder.dequeueCharacter();
textQueue.enqueue(c);
showTextQueue();
morseQueue.clear();
showMorseQueue();
Serial.print(c);
}
}