Last active
July 29, 2024 19:24
-
-
Save flybd5/27fd8bfbf66f409ad3150b6a8d1a6765 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <serial-readline.h> | |
#include <BluetoothSerial.h> | |
// This code will implement an international morse code keyer using bluetooth. | |
// It requires an Arduino device and a relay (can be mechanical or solid state). | |
// The relay is powered from the Arduino. It is set as normally open. The | |
// trigger comes from one of the pio pins. On the relay you connect the NO | |
// and center contacts to a 1/4" male plug and connect that to the CW key | |
// jack on your radio. That's it. | |
// Written by Juan Jimenez, K1CPR, June 2023 | |
// Released to the public domain. Use as you wish but please give credit. | |
const int relay_pin = 26; // Change to the PIO pin you use to trigger the relay | |
const int dotTime = 75; // Time for a dot in milliseconds, dashes/space are 3x this value | |
bool flag195 = false; // Flags for special characters | |
bool flag196 = false; | |
bool flag197 = false; | |
// BluetoothSerial SerialBT; | |
SerialLineReader reader(Serial); | |
void setup() { | |
Serial.begin(115200); | |
Serial.setTimeout(1000); | |
pinMode(relay_pin, OUTPUT); | |
// This will be the Bluetooth device name that will show up on your computer | |
// Change as you see fit. | |
// SerialBT.begin("JJCWKeyBT"); //Bluetooth device name | |
Serial.println("JJCWKeyBT running. Enter text to transmit."); | |
// If you want to require a PIN to pair to this device, define USE_PIN and put it here | |
#ifdef USE_PIN | |
SerialBT.setPin(pin); | |
Serial.println("Using PIN"); | |
#endif | |
} | |
// For dot and dash the relay is activated to close, triggering the key on your radio | |
// Spacing between letters same as a dash. | |
void space() { | |
delay(dotTime * 3); | |
} | |
// Spacing between words is 7x the dot time | |
void wordSpace() { | |
delay(dotTime * 7); | |
} | |
// Define a dot | |
void dot() { | |
digitalWrite(relay_pin, HIGH); | |
delay(dotTime); | |
digitalWrite(relay_pin, LOW); | |
delay(dotTime); | |
} | |
// Define a dash | |
void dash() { | |
digitalWrite(relay_pin, HIGH); | |
delay(dotTime * 3); | |
digitalWrite(relay_pin, LOW); | |
delay(dotTime); | |
} | |
// Figure out which symbol to key and do it. | |
// This code handles everything, including international, except one symbol. | |
// The digraph ch was removed from the Spanish language but there are other | |
// languages that still use it. Shame on you, Microsoft! | |
void symbol(wchar_t theSymbol) { | |
if (isAlpha(theSymbol)) { | |
switch (toupper(theSymbol)) { | |
case 'A': | |
dot(); | |
dash(); | |
break; | |
case 'B': | |
dash(); | |
dot(); | |
dot(); | |
dot(); | |
break; | |
case 'C': | |
dash(); | |
dot(); | |
dash(); | |
dot(); | |
break; | |
case 'D': | |
dash(); | |
dot(); | |
dot(); | |
break; | |
case 'E': | |
dot(); | |
break; | |
case 'F': | |
dot(); | |
dot(); | |
dash(); | |
dot; | |
break; | |
case 'G': | |
dash(); | |
dash(); | |
dot(); | |
break; | |
case 'H': | |
dot(); | |
dot(); | |
dot(); | |
dot(); | |
break; | |
case 'I': | |
dot(); | |
dot(); | |
break; | |
case 'J': | |
dot(); | |
dash(); | |
dash(); | |
dash(); | |
break; | |
case 'K': | |
dash(); | |
dot(); | |
dash(); | |
break; | |
case 'L': | |
dot(); | |
dash(); | |
dot(); | |
dot(); | |
break; | |
case 'M': | |
dash(); | |
dash(); | |
break; | |
case 'N': | |
dash(); | |
dot(); | |
break; | |
case 'O': | |
dash(); | |
dash(); | |
dash(); | |
break; | |
case 'P': | |
dot(); | |
dash(); | |
dash(); | |
dot(); | |
break; | |
case 'Q': | |
dash(); | |
dash(); | |
dot(); | |
dash(); | |
break; | |
case 'R': | |
dot(); | |
dash(); | |
dot(); | |
break; | |
case 'S': | |
dot(); | |
dot(); | |
dot(); | |
break; | |
case 'T': | |
dash(); | |
break; | |
case 'U': | |
dot(); | |
dot(); | |
dash(); | |
break; | |
case 'V': | |
dot(); | |
dot(); | |
dot(); | |
dash(); | |
break; | |
case 'W': | |
dot(); | |
dash(); | |
dash(); | |
break; | |
case 'X': | |
dash(); | |
dot(); | |
dot(); | |
dash(); | |
break; | |
case 'Y': | |
dash(); | |
dot(); | |
dash(); | |
dash(); | |
break; | |
case 'Z': | |
dash(); | |
dash(); | |
dot(); | |
dot(); | |
default: | |
// Do nothing | |
break; | |
} | |
} else if (isdigit(theSymbol)) { | |
switch (theSymbol) { | |
case '1': | |
dot(); | |
dash(); | |
dash(); | |
dash(); | |
dash(); | |
break; | |
case '2': | |
dot(); | |
dot(); | |
dash(); | |
dash(); | |
dash(); | |
break; | |
case '3': | |
dot(); | |
dot(); | |
dot(); | |
dash(); | |
dash(); | |
break; | |
case '4': | |
dot(); | |
dot(); | |
dot(); | |
dot(); | |
dash(); | |
break; | |
case '5': | |
dot(); | |
dot(); | |
dot(); | |
dot(); | |
dot(); | |
break; | |
case '6': | |
dash(); | |
dot(); | |
dot(); | |
dot(); | |
dot(); | |
break; | |
case '7': | |
dash(); | |
dash(); | |
dot(); | |
dot(); | |
dot(); | |
break; | |
case '8': | |
dash(); | |
dash(); | |
dash(); | |
dot(); | |
dot(); | |
break; | |
case '9': | |
dash(); | |
dash(); | |
dash(); | |
dash(); | |
dot(); | |
break; | |
case '0': | |
dash(); | |
dash(); | |
dash(); | |
dash(); | |
dash(); | |
break; | |
default: | |
// Do nothing | |
break; | |
} | |
} else if (wcschr(L".,?\\!/()&:;=+-_\"$@", theSymbol) != NULL) { | |
switch (theSymbol) { | |
case '.': | |
dot(); | |
dash(); | |
dot(); | |
dash(); | |
dot(); | |
dash(); | |
break; | |
case ',': | |
dash(); | |
dash(); | |
dot(); | |
dot(); | |
dash(); | |
dash(); | |
break; | |
case '?': | |
dot(); | |
dot(); | |
dash(); | |
dash(); | |
dot(); | |
dot(); | |
break; | |
case '\'': | |
dot(); | |
dash(); | |
dash(); | |
dash(); | |
dash(); | |
dot(); | |
break; | |
case '!': | |
dash(); | |
dot(); | |
dash(); | |
dot(); | |
dash(); | |
dash(); | |
break; | |
case '/': | |
dash(); | |
dot(); | |
dot(); | |
dash(); | |
dot(); | |
break; | |
case '(': | |
dash(); | |
dot(); | |
dash(); | |
dash(); | |
dot(); | |
break; | |
case ')': | |
dash(); | |
dot(); | |
dash(); | |
dash(); | |
dot(); | |
dash(); | |
break; | |
case '&': | |
dot(); | |
dash(); | |
dot(); | |
dot(); | |
dot(); | |
break; | |
case ':': | |
dash(); | |
dash(); | |
dash(); | |
dot(); | |
dot(); | |
dot(); | |
break; | |
case ';': | |
dash(); | |
dot(); | |
dash(); | |
dot(); | |
dash(); | |
dot(); | |
break; | |
case '=': | |
dash(); | |
dot(); | |
dot(); | |
dot(); | |
dash(); | |
break; | |
case '+': | |
dot(); | |
dash(); | |
dot(); | |
dash(); | |
dot(); | |
break; | |
case '-': | |
dash(); | |
dot(); | |
dot(); | |
dot(); | |
dot(); | |
dash(); | |
break; | |
case '_': | |
dot(); | |
dot(); | |
dash(); | |
dash(); | |
dot(); | |
dash(); | |
break; | |
case '"': | |
dot(); | |
dash(); | |
dot(); | |
dot(); | |
dash(); | |
dot(); | |
break; | |
case '$': | |
dot(); | |
dot(); | |
dot(); | |
dash(); | |
dot(); | |
dot(); | |
dash(); | |
break; | |
case '@': | |
dot(); | |
dash(); | |
dash(); | |
dot(); | |
dash(); | |
dot(); | |
break; | |
default: | |
// Do nothing | |
break; | |
} | |
} else if (flag195) { | |
switch (int(theSymbol)) { | |
// Don't forget the non-latin symbols | |
// Special case digraph ch is not implemented due to no input code possible | |
// Ä and ä | |
case 164: | |
case 132: | |
dot(); | |
dash(); | |
dot(); | |
dash(); | |
break; | |
// À and à | |
case 128: | |
case 160: | |
dot(); | |
dash(); | |
dash(); | |
dot(); | |
dash(); | |
break; | |
// Ç and ç | |
case 135: | |
case 167: | |
dash(); | |
dot(); | |
dash(); | |
dot(); | |
dot(); | |
break; | |
// Ð | |
case 144: | |
dot(); | |
dot(); | |
dash(); | |
dash(); | |
dot(); | |
break; | |
// È and è | |
case 136: | |
case 168: | |
dot(); | |
dash(); | |
dot(); | |
dot(); | |
dash(); | |
break; | |
// É and é | |
case 137: | |
case 169: | |
dot(); | |
dot(); | |
dash(); | |
dot(); | |
dot(); | |
break; | |
// Ĵ and ĵ | |
case 180: | |
case 181: | |
dot(); | |
dash(); | |
dash(); | |
dash(); | |
dot(); | |
break; | |
// Ñ and ñ | |
case 145: | |
case 177: | |
dash(); | |
dash(); | |
dot(); | |
dash(); | |
dash(); | |
break; | |
// Ö and ö | |
case 150: | |
case 182: | |
dash(); | |
dash(); | |
dash(); | |
dot(); | |
break; | |
// þ | |
case 190: | |
dot(); | |
dash(); | |
dash(); | |
dot(); | |
dot(); | |
break; | |
// Ü and ü | |
case 156: | |
case 188: | |
dot(); | |
dot(); | |
dash(); | |
dash(); | |
break; | |
default: | |
// Do nothing | |
break; | |
} | |
} else if (flag197) { | |
switch (int(theSymbol)) { | |
// Š and š | |
case 160: | |
case 161: | |
dot(); | |
dot(); | |
dot(); | |
dash(); | |
dot(); | |
break; | |
default: | |
// Do nothing | |
break; | |
} | |
} else if (flag196) { | |
switch (int(theSymbol)) { | |
// Ĝ and ĝ | |
case 156: | |
case 157: | |
dash(); | |
dash(); | |
dot(); | |
dash(); | |
dot(); | |
break; | |
default: | |
// Do nothing | |
break; | |
} | |
} | |
space(); | |
} | |
// The main loop. | |
void loop() { | |
reader.poll(); | |
if (reader.available()) { | |
int theLen = reader.len(); | |
char text[theLen]; | |
reader.read(text); | |
Serial.println(text); | |
// If the next character is int 195, 196 or 197 it indicates a special char | |
// It is skipped and the next char is sent for transmission. The flags tell | |
// symbol() in what switch statement to handle it. | |
// Otherwise, the char is sent for transmission. | |
for (int i = 0; i < theLen; i++) { | |
if (int(text[i]) == 195) { | |
flag195 = true; | |
symbol(text[++i]); | |
flag195 = false; | |
} else if (int(text[i]) == 197) { | |
flag197 = true; | |
symbol(text[++i]); | |
flag197 = false; | |
} else if (int(text[i]) == 196) { | |
flag197 = true; | |
symbol(text[++i]); | |
flag197 = false; | |
} else { | |
symbol(text[i]); | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Corrected problem with international and special chars. Code handles them correctly now, but I can only guarantee it works with Windows.