
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!
CC1200Morse.cpp@0:9e9ede3ac523, 2020-08-29 (annotated)
- Committer:
- Jamie Smith
- Date:
- Sat Aug 29 03:06:11 2020 -0700
- Revision:
- 0:9e9ede3ac523
Initial commit
Who changed what in which revision?
User | Revision | Line number | New 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 | } |