From 344afee97aeecdfeac133953b3f9158a6dd6efd1 Mon Sep 17 00:00:00 2001 From: jgromes Date: Sat, 9 Feb 2019 09:38:50 +0100 Subject: [PATCH] [Morse] Added support for Morse code transmissions --- examples/Morse_Transmit/Morse_Transmit.ino | 98 +++++++ keywords.txt | 4 + src/RadioLib.h | 1 + src/protocols/Morse.cpp | 322 +++++++++++++++++++++ src/protocols/Morse.h | 51 ++++ 5 files changed, 476 insertions(+) create mode 100644 examples/Morse_Transmit/Morse_Transmit.ino create mode 100644 src/protocols/Morse.cpp create mode 100644 src/protocols/Morse.h diff --git a/examples/Morse_Transmit/Morse_Transmit.ino b/examples/Morse_Transmit/Morse_Transmit.ino new file mode 100644 index 00000000..4b0ab90e --- /dev/null +++ b/examples/Morse_Transmit/Morse_Transmit.ino @@ -0,0 +1,98 @@ +/* + RadioLib Morse Transmit Example + + This example sends Morse code message using + SX1278's FSK modem. + + Other modules that can be used for Morse: + - SX1272/73/76/77/79 + - RF69 + - SX1231 + - CC1101 +*/ + +// include the library +#include + +// SX1278 module is in slot A on the shield +SX1278 fsk = RadioShield.ModuleA; + +// create Morse client instance using the FSK module +MorseClient morse(&fsk); + +void setup() { + Serial.begin(9600); + + // initialize SX1278 + Serial.print(F("[SX1278] Initializing ... ")); + // carrier frequency: 434.0 MHz + // bit rate: 48.0 kbps + // frequency deviation: 50.0 kHz + // Rx bandwidth: 125.0 kHz + // output power: 13 dBm + // current limit: 100 mA + // sync word: 0x2D 0x01 + int state = fsk.beginFSK(); + if(state == ERR_NONE) { + Serial.println(F("success!")); + } else { + Serial.print(F("failed, code ")); + Serial.println(state); + while(true); + } + + // initialize Morse client + Serial.print(F("[Morse] Initializing ... ")); + // base frequency: 434.0 MHz + // speed: 20 words per minute + state = morse.begin(434.0); + if(state == ERR_NONE) { + Serial.println(F("success!")); + } else { + Serial.print(F("failed, code ")); + Serial.println(state); + while(true); + } +} + +void loop() { + Serial.print(F("[Morse] Sending Morse data ... ")); + + // MorseClient supports all methods of the Serial class + // NOTE: Characters that do not have ITU-R M.1677-1 + // representation will not be sent! Lower case + // letters will be capitalized. + + // send start signal first + morse.startSignal(); + + // Arduino String class + String aStr = "Arduino String"; + morse.print(aStr); + + // character array (C-String) + morse.print("C-String"); + + // character + morse.print('c'); + + // byte + // formatting DEC/HEX/OCT/BIN is supported for + // any integer type (byte/int/long) + morse.print(255, HEX); + + // integer number + int i = 1000; + morse.print(i); + + // floating point number + // NOTE: When using println(), the transmission will be + // terminated with cross signal (.-.-.). + float f = -3.1415; + morse.println(f, 3); + + Serial.println(F("done!")); + + // wait for a second before transmitting again + delay(1000); +} diff --git a/keywords.txt b/keywords.txt index c2187ae0..c464762d 100644 --- a/keywords.txt +++ b/keywords.txt @@ -32,6 +32,7 @@ XBeeSerial KEYWORD1 MQTTClient KEYWORD1 HTTPClient KEYWORD1 RTTYClient KEYWORD1 +MorseClient KEYWORD1 ####################################### # Methods and Functions (KEYWORD2) @@ -113,6 +114,9 @@ check KEYWORD2 idle KEYWORD2 byteArr KEYWORD2 +# Morse +startSignal KEYWORD2 + # TransportLayer openTransportConnection KEYWORD2 closeTransportConnection KEYWORD2 diff --git a/src/RadioLib.h b/src/RadioLib.h index b95891fd..0b432443 100644 --- a/src/RadioLib.h +++ b/src/RadioLib.h @@ -22,6 +22,7 @@ #include "modules/XBee.h" #include "protocols/PhysicalLayer.h" +#include "protocols/Morse.h" #include "protocols/RTTY.h" #include "protocols/TransportLayer.h" diff --git a/src/protocols/Morse.cpp b/src/protocols/Morse.cpp new file mode 100644 index 00000000..09e677a3 --- /dev/null +++ b/src/protocols/Morse.cpp @@ -0,0 +1,322 @@ +#include "Morse.h" + +// structure to save data about character Morse code +struct Morse_t { + char c; // ASCII character + const char* m; // Morse code representation +}; + +// array of all Morse code characters +static const Morse_t MorseTable[MORSE_LENGTH] = { + {.c = 'A', .m = ".-"}, + {.c = 'B', .m = "-..."}, + {.c = 'C', .m = "-.-."}, + {.c = 'D', .m = "-.."}, + {.c = 'E', .m = "."}, + {.c = 'F', .m = "..-."}, + {.c = 'G', .m = "--."}, + {.c = 'H', .m = "...."}, + {.c = 'I', .m = ".."}, + {.c = 'J', .m = ".---"}, + {.c = 'K', .m = "-.-"}, + {.c = 'L', .m = ".-.."}, + {.c = 'M', .m = "--"}, + {.c = 'N', .m = "-."}, + {.c = 'O', .m = "---"}, + {.c = 'P', .m = ".--."}, + {.c = 'Q', .m = "--.-"}, + {.c = 'R', .m = ".-."}, + {.c = 'S', .m = "..."}, + {.c = 'T', .m = "-"}, + {.c = 'U', .m = "..-"}, + {.c = 'V', .m = "...-"}, + {.c = 'W', .m = ".--"}, + {.c = 'X', .m = "-..-"}, + {.c = 'Y', .m = "-.--"}, + {.c = 'Z', .m = "--.."}, + {.c = '1', .m = ".----"}, + {.c = '2', .m = "..---"}, + {.c = '3', .m = "...--"}, + {.c = '4', .m = "....-"}, + {.c = '5', .m = "....."}, + {.c = '6', .m = "-...."}, + {.c = '7', .m = "--..."}, + {.c = '8', .m = "---.."}, + {.c = '9', .m = "----."}, + {.c = '0', .m = "-----"}, + {.c = '.', .m = ".-.-.-"}, + {.c = ',', .m = "--..--"}, + {.c = ':', .m = "---..."}, + {.c = '?', .m = "..--.."}, + {.c = '\'', .m = ".----."}, + {.c = '-', .m = "-....-"}, + {.c = '/', .m = "-..-."}, + {.c = '(', .m = "-.--."}, + {.c = ')', .m = "-.--.-"}, + {.c = '\"', .m = ".-..-."}, + {.c = '=', .m = "-...-"}, + {.c = '+', .m = ".-.-."}, + {.c = '@', .m = ".--.-."}, + {.c = ' ', .m = "_"}, // space is used to separate words + {.c = 0x01, .m = "-.-.-"}, // ASCII SOH (start of heading) is used as alias for start signal + {.c = 0x02, .m = ".-.-."} // ASCII EOT (end of transmission) is used as alias for stop signal +}; + +MorseClient::MorseClient(PhysicalLayer* phy) { + _phy = phy; +} + +int16_t MorseClient::begin(float base, uint8_t speed) { + // calculate 24-bit frequency + _base = (base * (uint32_t(1) << _phy->getDivExponent())) / _phy->getCrystalFreq(); + + // calculate dot length (assumes PARIS as typical word) + _dotLength = 1200 / speed; + + // set module frequency deviation to 0 + int16_t state = _phy->setFrequencyDeviation(0); + + return(state); +} + +size_t MorseClient::write(const char* str) { + if(str == NULL) { + return(0); + } + + return(MorseClient::write((uint8_t*)str, strlen(str))); +} + +size_t MorseClient::write(uint8_t* buff, size_t len) { + size_t n = 0; + for(size_t i = 0; i < len; i++) { + n += MorseClient::write(buff[i]); + } + return(n); +} + +size_t MorseClient::write(uint8_t b) { + // find the correct Morse code in array + uint8_t pos; + bool found = false; + for(pos = 0; pos < MORSE_LENGTH; pos++) { + if(MorseTable[pos].c == toupper(b)) { + found = true; + break; + } + } + Serial.print((char)b); + Serial.print('\t'); + // check if the requested code was found in the array + if(found) { + // iterate over Morse code representation and output appropriate tones + for(uint8_t i = 0; i < strlen(MorseTable[pos].m); i++) { + Serial.print(MorseTable[pos].m[i]); + switch(MorseTable[pos].m[i]) { + case '.': + _phy->transmitDirect(_base); + delay(_dotLength); + break; + case '-': + _phy->transmitDirect(_base); + delay(_dotLength * 3); + break; + case '_': + // do nothing (word space) + break; + } + + // symbol space + _phy->receiveDirect(); + delay(_dotLength); + } + Serial.println(); + + // letter space + delay(_dotLength * 3); + + return(1); + } + + return(0); +} + +size_t MorseClient::startSignal() { + return(MorseClient::write(0x01)); +} + +size_t MorseClient::print(const String& str) { + return(MorseClient::write((uint8_t*)str.c_str(), str.length())); +} + +size_t MorseClient::print(const char* str) { + return(MorseClient::write((uint8_t*)str, strlen(str))); +} + +size_t MorseClient::print(char c) { + return(MorseClient::write(c)); +} + +size_t MorseClient::print(unsigned char b, int base) { + return(MorseClient::print((unsigned long)b, base)); +} + +size_t MorseClient::print(int n, int base) { + return(MorseClient::print((long)n, base)); +} + +size_t MorseClient::print(unsigned int n, int base) { + return(MorseClient::print((unsigned long)n, base)); +} + +size_t MorseClient::print(long n, int base) { + if(base == 0) { + return(MorseClient::write(n)); + } else if(base == DEC) { + if (n < 0) { + int t = MorseClient::print('-'); + n = -n; + return(MorseClient::printNumber(n, DEC) + t); + } + return(MorseClient::printNumber(n, DEC)); + } else { + return(MorseClient::printNumber(n, base)); + } +} + +size_t MorseClient::print(unsigned long n, int base) { + if(base == 0) { + return(MorseClient::write(n)); + } else { + return(MorseClient::printNumber(n, base)); + } +} + +size_t MorseClient::print(double n, int digits) { + return(MorseClient::printFloat(n, digits)); +} + +size_t MorseClient::println(void) { + return(MorseClient::write(0x02)); +} + +size_t MorseClient::println(const String& str) { + size_t n = MorseClient::print(str); + n += MorseClient::println(); + return(n); +} + +size_t MorseClient::println(const char* str) { + size_t n = MorseClient::print(str); + n += MorseClient::println(); + return(n); +} + +size_t MorseClient::println(char c) { + size_t n = MorseClient::print(c); + n += MorseClient::println(); + return(n); +} + +size_t MorseClient::println(unsigned char b, int base) { + size_t n = MorseClient::print(b, base); + n += MorseClient::println(); + return(n); +} + +size_t MorseClient::println(int num, int base) { + size_t n = MorseClient::print(num, base); + n += MorseClient::println(); + return(n); +} + +size_t MorseClient::println(unsigned int num, int base) { + size_t n = MorseClient::print(num, base); + n += MorseClient::println(); + return(n); +} + +size_t MorseClient::println(long num, int base) { + size_t n = MorseClient::print(num, base); + n += MorseClient::println(); + return(n); +} + +size_t MorseClient::println(unsigned long num, int base) { + size_t n = MorseClient::print(num, base); + n += MorseClient::println(); + return(n); +} + +size_t MorseClient::println(double d, int digits) { + size_t n = MorseClient::print(d, digits); + n += MorseClient::println(); + return(n); +} + +size_t MorseClient::printNumber(unsigned long n, uint8_t base) { + char buf[8 * sizeof(long) + 1]; + char *str = &buf[sizeof(buf) - 1]; + + *str = '\0'; + + if(base < 2) { + base = 10; + } + + do { + char c = n % base; + n /= base; + + *--str = c < 10 ? c + '0' : c + 'A' - 10; + } while(n); + + return(MorseClient::write(str)); +} + +size_t MorseClient::printFloat(double number, uint8_t digits) { + size_t n = 0; + + char code[] = {0x00, 0x00, 0x00, 0x00}; + if (isnan(number)) strcpy(code, "nan"); + if (isinf(number)) strcpy(code, "inf"); + if (number > 4294967040.0) strcpy(code, "ovf"); // constant determined empirically + if (number <-4294967040.0) strcpy(code, "ovf"); // constant determined empirically + + if(code[0] != 0x00) { + return(MorseClient::write(code)); + } + + // Handle negative numbers + if (number < 0.0) { + n += MorseClient::print('-'); + number = -number; + } + + // Round correctly so that print(1.999, 2) prints as "2.00" + double rounding = 0.5; + for(uint8_t i = 0; i < digits; ++i) { + rounding /= 10.0; + } + number += rounding; + + // Extract the integer part of the number and print it + unsigned long int_part = (unsigned long)number; + double remainder = number - (double)int_part; + n += MorseClient::print(int_part); + + // Print the decimal point, but only if there are digits beyond + if(digits > 0) { + n += MorseClient::print('.'); + } + + // Extract digits from the remainder one at a time + while(digits-- > 0) { + remainder *= 10.0; + unsigned int toPrint = (unsigned int)(remainder); + n += MorseClient::print(toPrint); + remainder -= toPrint; + } + + return n; +} diff --git a/src/protocols/Morse.h b/src/protocols/Morse.h new file mode 100644 index 00000000..291688b5 --- /dev/null +++ b/src/protocols/Morse.h @@ -0,0 +1,51 @@ +#ifndef _RADIOLIB_MORSE_H +#define _RADIOLIB_MORSE_H + +#include "TypeDef.h" +#include "PhysicalLayer.h" + +#define MORSE_LENGTH 52 + +class MorseClient { + public: + MorseClient(PhysicalLayer* phy); + + // basic methods + int16_t begin(float base, uint8_t speed = 20); + size_t write(const char* str); + size_t write(uint8_t* buff, size_t len); + size_t write(uint8_t b); + + size_t startSignal(); + + size_t print(const String &); + size_t print(const char[]); + size_t print(char); + size_t print(unsigned char, int = DEC); + size_t print(int, int = DEC); + size_t print(unsigned int, int = DEC); + size_t print(long, int = DEC); + size_t print(unsigned long, int = DEC); + size_t print(double, int = 2); + + size_t println(void); + size_t println(const String &s); + size_t println(const char[]); + size_t println(char); + size_t println(unsigned char, int = DEC); + size_t println(int, int = DEC); + size_t println(unsigned int, int = DEC); + size_t println(long, int = DEC); + size_t println(unsigned long, int = DEC); + size_t println(double, int = 2); + + private: + PhysicalLayer* _phy; + uint32_t _base; + uint16_t _dotLength; + + size_t printNumber(unsigned long, uint8_t); + size_t printFloat(double, uint8_t); +}; + +#endif