Program to transmit strings as Morse code using the CC1200. Useful for amateur radio projects!

Dependencies:   CC1200 SerialStream

This project contains a class (CC1200Morse) to convert text into Morse code that can be transmitted over a CC1200 radio IC, and a test program for this class. This is useful for amateur radio projects which need to broadcast their call sign in order to operate legally, and just a cool demonstration of the flexibility of the CC1200.

How It Works

First, the input string is translated into Morse code using a conversion table. Nearly all ASCII characters contained in the Morse character set are supported. For example, the string "ha" would become " ···· · −". Then, the Morse code is converted into one and zero bits according to the standard Morse timing rules. " ···· · −" then becomes 10101010 00010111 000". Finally, these bits are enqueued into the radio's packet buffer and transmitted. The CC1200 is configured into OOK (On-Off Keying) mode, so a 1 bit is modulated as full transmit power, and a 0 bit is modulated as zero transmit power. The transmission will occur at the speed (specified as the time unit, or the length of a dot) that you configure. Note that the CC1200 does the transmission in the background, so your processor can enqueue up to 128 bytes worth of Morse and then go do other things while it transmits at a human readable speed. You can't enqueue two packets at a time though.

Demo Video

Hardware Setup

This program assumes that a CC1200 radio is connected to your processor's SPI bus. The CC1200's circuit board must be configured for the 900MHz band (though you could also change the frequency in the code to match your boards).

I used a custom circuit board for my testing, but you should also be able to use an Mbed board connected to an CC1200 eval kit to run the program. Make sure to edit the #defines at the top of main.cpp to match the pins that your equipment is plugged into!

Note: License free transmission on the 900MHz band is only legal in Region 2 countries (North and South America). Make sure to follow all local regulations covering radio transmissions!

Committer:
Jamie Smith
Date:
Sat Aug 29 03:06:11 2020 -0700
Revision:
0:9e9ede3ac523
Initial commit

Who changed what in which revision?

UserRevisionLine numberNew contents of line
Jamie Smith 0:9e9ede3ac523 1 //
Jamie Smith 0:9e9ede3ac523 2 // Created by jamie on 8/24/2020.
Jamie Smith 0:9e9ede3ac523 3 //
Jamie Smith 0:9e9ede3ac523 4
Jamie Smith 0:9e9ede3ac523 5 #include "CC1200Morse.h"
Jamie Smith 0:9e9ede3ac523 6
Jamie Smith 0:9e9ede3ac523 7 #include <cinttypes>
Jamie Smith 0:9e9ede3ac523 8
Jamie Smith 0:9e9ede3ac523 9 // Morse code tables.
Jamie Smith 0:9e9ede3ac523 10 // Covers ASCII ranges 0x41-0x5A and 0x61-0x7A
Jamie Smith 0:9e9ede3ac523 11 char const * const alphaMorse[] = {".-","-...","-.-.","-..",".","..-.","--.","....","..",".---",
Jamie Smith 0:9e9ede3ac523 12 "-.-",".-..","--","-.","---",".--.","--.-",".-.","...","-","..-",
Jamie Smith 0:9e9ede3ac523 13 "...-",".--","-..-","-.--","--.."};
Jamie Smith 0:9e9ede3ac523 14
Jamie Smith 0:9e9ede3ac523 15 // Covers ASCII range 0x30-0x39
Jamie Smith 0:9e9ede3ac523 16 char const * const numMorse[] = {"-----",".----","..---","...--","....-",".....","-....","--...","---..","----."};
Jamie Smith 0:9e9ede3ac523 17
Jamie Smith 0:9e9ede3ac523 18 // covers ASCII range 0x21-0x2F
Jamie Smith 0:9e9ede3ac523 19 char const * const punctuation1Morse[] = {"-.-.--", ".-..-.", nullptr, "...-..-", nullptr, ".-...", ".----.", "-.--.",
Jamie Smith 0:9e9ede3ac523 20 "-.--.-", nullptr, ".-.-.", "--..--", nullptr, ".-.-.-", "-..-."};
Jamie Smith 0:9e9ede3ac523 21
Jamie Smith 0:9e9ede3ac523 22 // covers ASCII range 0x3A-0x40
Jamie Smith 0:9e9ede3ac523 23 char const * const punctuation2Morse[] = {"---...", "-.-.-.", nullptr, "-...-", nullptr, "..--..", ".--.-."};
Jamie Smith 0:9e9ede3ac523 24
Jamie Smith 0:9e9ede3ac523 25 void CC1200Morse::configure(CC1200::Band band, float radioFrequency, float morseTimePeriod, float transmitPower)
Jamie Smith 0:9e9ede3ac523 26 {
Jamie Smith 0:9e9ede3ac523 27 radio.setPacketMode(CC1200::PacketMode::FIXED_LENGTH);
Jamie Smith 0:9e9ede3ac523 28 radio.setCRCEnabled(false);
Jamie Smith 0:9e9ede3ac523 29
Jamie Smith 0:9e9ede3ac523 30 // set frequency
Jamie Smith 0:9e9ede3ac523 31 radio.setSymbolRate(1/morseTimePeriod);
Jamie Smith 0:9e9ede3ac523 32 radio.setRadioFrequency(band, radioFrequency);
Jamie Smith 0:9e9ede3ac523 33
Jamie Smith 0:9e9ede3ac523 34 // disable anything getting sent before the data
Jamie Smith 0:9e9ede3ac523 35 radio.configureSyncWord(0x0, CC1200::SyncMode::SYNC_NONE, 8);
Jamie Smith 0:9e9ede3ac523 36 radio.configurePreamble(0, 0);
Jamie Smith 0:9e9ede3ac523 37
Jamie Smith 0:9e9ede3ac523 38 // configure OOK modulation
Jamie Smith 0:9e9ede3ac523 39 radio.setModulationFormat(CC1200::ModFormat::ASK);
Jamie Smith 0:9e9ede3ac523 40 radio.disablePARamping();
Jamie Smith 0:9e9ede3ac523 41 radio.setASKPowers(transmitPower, CC1200::ASK_MIN_POWER_OFF);
Jamie Smith 0:9e9ede3ac523 42 }
Jamie Smith 0:9e9ede3ac523 43
Jamie Smith 0:9e9ede3ac523 44 // helper function for convertToMorse
Jamie Smith 0:9e9ede3ac523 45 static bool appendBits(uint8_t *outputBuffer, size_t bufferLen, uint8_t & nextBit, size_t & currByte, uint8_t toAppend, size_t count)
Jamie Smith 0:9e9ede3ac523 46 {
Jamie Smith 0:9e9ede3ac523 47 //printf("appendBits(%" PRIu8 ", %zu)\n", toAppend, count);
Jamie Smith 0:9e9ede3ac523 48 for(size_t counter = 0; counter < count; ++counter)
Jamie Smith 0:9e9ede3ac523 49 {
Jamie Smith 0:9e9ede3ac523 50 outputBuffer[currByte] |= toAppend << nextBit;
Jamie Smith 0:9e9ede3ac523 51
Jamie Smith 0:9e9ede3ac523 52 if(nextBit == 0)
Jamie Smith 0:9e9ede3ac523 53 {
Jamie Smith 0:9e9ede3ac523 54 nextBit = 7;
Jamie Smith 0:9e9ede3ac523 55 currByte += 1;
Jamie Smith 0:9e9ede3ac523 56 if(currByte >= bufferLen)
Jamie Smith 0:9e9ede3ac523 57 {
Jamie Smith 0:9e9ede3ac523 58 // out of space
Jamie Smith 0:9e9ede3ac523 59 return false;
Jamie Smith 0:9e9ede3ac523 60 }
Jamie Smith 0:9e9ede3ac523 61 }
Jamie Smith 0:9e9ede3ac523 62 else
Jamie Smith 0:9e9ede3ac523 63 {
Jamie Smith 0:9e9ede3ac523 64 nextBit--;
Jamie Smith 0:9e9ede3ac523 65 }
Jamie Smith 0:9e9ede3ac523 66 }
Jamie Smith 0:9e9ede3ac523 67
Jamie Smith 0:9e9ede3ac523 68 return true;
Jamie Smith 0:9e9ede3ac523 69 }
Jamie Smith 0:9e9ede3ac523 70
Jamie Smith 0:9e9ede3ac523 71
Jamie Smith 0:9e9ede3ac523 72 CC1200Morse::EncodedMorse CC1200Morse::convertToMorse(const char *string, uint8_t *outputBuffer, size_t bufferLen)
Jamie Smith 0:9e9ede3ac523 73 {
Jamie Smith 0:9e9ede3ac523 74 memset(outputBuffer, 0, bufferLen);
Jamie Smith 0:9e9ede3ac523 75
Jamie Smith 0:9e9ede3ac523 76 // place in the output buffer where next items will be written
Jamie Smith 0:9e9ede3ac523 77 uint8_t nextBit = 7;
Jamie Smith 0:9e9ede3ac523 78 size_t currByte = 0;
Jamie Smith 0:9e9ede3ac523 79
Jamie Smith 0:9e9ede3ac523 80 EncodedMorse encoded;
Jamie Smith 0:9e9ede3ac523 81 encoded.buffer = outputBuffer;
Jamie Smith 0:9e9ede3ac523 82 encoded.valid = false;
Jamie Smith 0:9e9ede3ac523 83
Jamie Smith 0:9e9ede3ac523 84 if(!appendBits(outputBuffer, bufferLen, nextBit, currByte, 0, spaceBefore))
Jamie Smith 0:9e9ede3ac523 85 {
Jamie Smith 0:9e9ede3ac523 86 return encoded;
Jamie Smith 0:9e9ede3ac523 87 }
Jamie Smith 0:9e9ede3ac523 88
Jamie Smith 0:9e9ede3ac523 89 size_t stringLength = strlen(string);
Jamie Smith 0:9e9ede3ac523 90 for(size_t charIndex = 0; charIndex < stringLength; ++charIndex)
Jamie Smith 0:9e9ede3ac523 91 {
Jamie Smith 0:9e9ede3ac523 92 char currChar = string[charIndex];
Jamie Smith 0:9e9ede3ac523 93 char const * morseToAppend = nullptr;
Jamie Smith 0:9e9ede3ac523 94 if((currChar >= 'A' && currChar <= 'Z'))
Jamie Smith 0:9e9ede3ac523 95 {
Jamie Smith 0:9e9ede3ac523 96 morseToAppend = alphaMorse[currChar - 'A'];
Jamie Smith 0:9e9ede3ac523 97 }
Jamie Smith 0:9e9ede3ac523 98 else if(currChar >= 'a' && currChar <= 'z')
Jamie Smith 0:9e9ede3ac523 99 {
Jamie Smith 0:9e9ede3ac523 100 morseToAppend = alphaMorse[currChar - 'a'];
Jamie Smith 0:9e9ede3ac523 101 }
Jamie Smith 0:9e9ede3ac523 102 else if(currChar >= '0' && currChar <= '9')
Jamie Smith 0:9e9ede3ac523 103 {
Jamie Smith 0:9e9ede3ac523 104 morseToAppend = numMorse[currChar - '0'];
Jamie Smith 0:9e9ede3ac523 105 }
Jamie Smith 0:9e9ede3ac523 106 else if(currChar >= '!' && currChar <= '/')
Jamie Smith 0:9e9ede3ac523 107 {
Jamie Smith 0:9e9ede3ac523 108 morseToAppend = punctuation1Morse[currChar - '!'];
Jamie Smith 0:9e9ede3ac523 109 }
Jamie Smith 0:9e9ede3ac523 110 else if(currChar >= ':' && currChar <= '@')
Jamie Smith 0:9e9ede3ac523 111 {
Jamie Smith 0:9e9ede3ac523 112 morseToAppend = punctuation2Morse[currChar - ':'];
Jamie Smith 0:9e9ede3ac523 113 }
Jamie Smith 0:9e9ede3ac523 114 else if(currChar == '_') // underscore is off by itself in the ASCII chart
Jamie Smith 0:9e9ede3ac523 115 {
Jamie Smith 0:9e9ede3ac523 116 morseToAppend = "..--.-";
Jamie Smith 0:9e9ede3ac523 117 }
Jamie Smith 0:9e9ede3ac523 118
Jamie Smith 0:9e9ede3ac523 119 // append bit timings
Jamie Smith 0:9e9ede3ac523 120 //printf("currChar = '%c'\n", currChar);
Jamie Smith 0:9e9ede3ac523 121 if(currChar == ' ')
Jamie Smith 0:9e9ede3ac523 122 {
Jamie Smith 0:9e9ede3ac523 123 // space between words is 7 time units
Jamie Smith 0:9e9ede3ac523 124 if(!appendBits(outputBuffer, bufferLen, nextBit, currByte, 0, 7))
Jamie Smith 0:9e9ede3ac523 125 {
Jamie Smith 0:9e9ede3ac523 126 return encoded;
Jamie Smith 0:9e9ede3ac523 127 }
Jamie Smith 0:9e9ede3ac523 128 }
Jamie Smith 0:9e9ede3ac523 129 else if(morseToAppend != nullptr)
Jamie Smith 0:9e9ede3ac523 130 {
Jamie Smith 0:9e9ede3ac523 131 size_t morseLength = strlen(morseToAppend);
Jamie Smith 0:9e9ede3ac523 132 for(size_t morseIndex = 0; morseIndex < morseLength; ++morseIndex)
Jamie Smith 0:9e9ede3ac523 133 {
Jamie Smith 0:9e9ede3ac523 134 // dot is 1 time unit, dash is 3 time units
Jamie Smith 0:9e9ede3ac523 135 if(!appendBits(outputBuffer, bufferLen, nextBit, currByte, 1, morseToAppend[morseIndex] == '-' ? 3 : 1))
Jamie Smith 0:9e9ede3ac523 136 {
Jamie Smith 0:9e9ede3ac523 137 return encoded;
Jamie Smith 0:9e9ede3ac523 138 }
Jamie Smith 0:9e9ede3ac523 139
Jamie Smith 0:9e9ede3ac523 140 // space between symbols is 1 time unit
Jamie Smith 0:9e9ede3ac523 141 if(!appendBits(outputBuffer, bufferLen, nextBit, currByte, 0, 1))
Jamie Smith 0:9e9ede3ac523 142 {
Jamie Smith 0:9e9ede3ac523 143 return encoded;
Jamie Smith 0:9e9ede3ac523 144 }
Jamie Smith 0:9e9ede3ac523 145 }
Jamie Smith 0:9e9ede3ac523 146
Jamie Smith 0:9e9ede3ac523 147 // extra space between letters is 2 time units
Jamie Smith 0:9e9ede3ac523 148 if(!appendBits(outputBuffer, bufferLen, nextBit, currByte, 0, 2))
Jamie Smith 0:9e9ede3ac523 149 {
Jamie Smith 0:9e9ede3ac523 150 return encoded;
Jamie Smith 0:9e9ede3ac523 151 }
Jamie Smith 0:9e9ede3ac523 152 }
Jamie Smith 0:9e9ede3ac523 153 }
Jamie Smith 0:9e9ede3ac523 154
Jamie Smith 0:9e9ede3ac523 155 if(!appendBits(outputBuffer, bufferLen, nextBit, currByte, 0, spaceAfter))
Jamie Smith 0:9e9ede3ac523 156 {
Jamie Smith 0:9e9ede3ac523 157 return encoded;
Jamie Smith 0:9e9ede3ac523 158 }
Jamie Smith 0:9e9ede3ac523 159
Jamie Smith 0:9e9ede3ac523 160 encoded.valid = true;
Jamie Smith 0:9e9ede3ac523 161 encoded.byteLen = currByte;
Jamie Smith 0:9e9ede3ac523 162 encoded.bitLen = 7 - nextBit;
Jamie Smith 0:9e9ede3ac523 163 encoded.totalLength = currByte + (encoded.bitLen > 0 ? 1 : 0);
Jamie Smith 0:9e9ede3ac523 164
Jamie Smith 0:9e9ede3ac523 165 return encoded;
Jamie Smith 0:9e9ede3ac523 166 }
Jamie Smith 0:9e9ede3ac523 167
Jamie Smith 0:9e9ede3ac523 168 void CC1200Morse::transmit(const CC1200Morse::EncodedMorse &morse)
Jamie Smith 0:9e9ede3ac523 169 {
Jamie Smith 0:9e9ede3ac523 170 if(morse.totalLength > 128)
Jamie Smith 0:9e9ede3ac523 171 {
Jamie Smith 0:9e9ede3ac523 172 // too large to send in one packet
Jamie Smith 0:9e9ede3ac523 173 return;
Jamie Smith 0:9e9ede3ac523 174 }
Jamie Smith 0:9e9ede3ac523 175 radio.setPacketLength(morse.byteLen, morse.bitLen);
Jamie Smith 0:9e9ede3ac523 176 radio.enqueuePacket(reinterpret_cast<const char *>(morse.buffer), morse.totalLength);
Jamie Smith 0:9e9ede3ac523 177 }