Morse encoder/decoder w/audio input, full duplex

Hi.

Continuing on my Morse decoder from this thread: http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1289074596/15 and inspired by liudr in this thread: http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1290666645, I made it into a Morse encoder and decoder.
I just figured I should start a new thread about it.

I have to admit that the source code is in kind of a mess, but well commented I hope. I want to make classes or maybe just functions out of it sometime too (first attempt just messed up everything), but for now this is it.

Here is a youtube video where I demonstrate it, and also do some speed tests for the fun of it. I'm testing up to 400 wpm (which fails), but the 300 wpm (full duplex) worked, at least when I did the video :slight_smile: Sounds like a mess with two morse codes simultaneously beeping away (but the one at 13 wpm was kind of musical).

My camera aiming is a bit off though, and the video editing isn't the best either...

And here is the code:
(part 1, too long to fit in one post)

/*
               MORSE EN-DE-CODER 1.06

  - A Morse encoder / decoder for the Arduino.


3 input methods for decoding and encoding:
 -  Audio Morse signals on an analog input (for decoding).
 -  Morse keying on digital input (for decoding).
 -  ASCII text via serial communication (for encoding. Also for adjusting settings).
 
2 outputs for encoded and decoded Morse:
 -  Morse-code toggled output pin (for encoded Morse).
 -  ASCII text via serial communication (for decoded Morse) .

It will enter audio listen mode if nothing is keyed in within a second.

Serial input:
  Only International Morse Code (letters A-Z, numbers), and space, are
  encoded and sent as Morse code automatically unless whatever the "command"
  character is (default '<') is received.
  No punctuation supported (but the morse table can be extended easily for that)

Morse en-/de-coder < commands via serial:
  The ASCII "less than" character '<' denotes a command instead of encoding and 
  sending Morse. The format is:
  
    <Lxxx
  
  where L is a letter and xxx is a three-digit decimal number 000-999.
  One exception is the <i command for info (displays current settings).
  The letter can be upper or lower case W, A, D, E or I. Examples:

    <w008 - sets speed to 8 wpm
    <a900 - sets audio threshold to be 900
    <D020 - sets debounce delay to be 20 milliseconds.
    <e000 - turn off Morse echo back to serial as ASCII and output pin as Morse
    <e001 - turn on Morse echo (any value above 0 will do)
    <i    - info - displays some settings

Full duplex:
  It can both transmit and receive simultaneously, but serial output will
  be a mixed mess unless echo is turned off.


NOTE: Values for tolerance (like dot time or dash time divided by 2 etc) are
      more or less arbitrarily/experimentally chosen.
      
      Sorry the source code is a big mess for now...

Based on Debounce example from the Arduino site for the morse keyer button
(http://www.arduino.cc/en/Tutorial/Debounce).
Loosely based on Morse Decoder 3.5 from 1992 for the Amiga by the same guy.
Contact: raronzen@gmail.com  (not checked too often..)


Copyright (C) 2010 raron


    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.


History:
 1992.01.06 - Morse decoder 3.5 - 68000 Assembler version for the Amiga 600
                using a binary tree (dichotomic table) for Morse decoding
 2010.11.11 - Rewrite for the Arduino
 2010.11.27 - Added Morse encoding via reverse-dichotomic path tracing.
                Thus using the same Morse table / binary tree for encoding and decoding.
 2010.11.28 - Added a simple audio Morse signal filter. Added a simple command parser
 2010.11.29 - Added echo on/off command
 


TODO: Make the timings signed long again, to avoid rollover issue
      Avoid calling millis() all the time?
      Make functions or classes out of it sometime...
      Generally tidy it up
*/



// Simple analog input signal threshold (512 = middle-ish)  
// Set high enough to avoid noise, low enough to get signal
// ( 512 + noise < AudioThreshold < 512 + signal )
int AudioThreshold = 700;

// Word-per-minute speed
int wpm = 13;

// Used as a command character to adjust some settings via serial.
const char MorseCommand = '<';

// the debounce time. Keep well below dotTime!!
unsigned long debounceDelay = 20;

// Other Morse variables
unsigned long dotTime = 1200 / wpm;   // morse dot time length in ms
unsigned long dashTime = 3 * 1200 / wpm;
unsigned long wordSpace = 7 * 1200 / wpm;


const int analogPin = 0;       // Analog input pin for audio morse code
const int morseInPin = 7;      // The Morse keyer button
const int morseOutPin =  13;   // For Morse code output
unsigned long markTime = 0;    // timers for mark and space in morse signal
unsigned long spaceTime = 0;   // E=MC^2 ;p
boolean morseSpace = false;    // Flag to prevent multiple received spaces
boolean gotLastSig = true;     // Flag that the last received morse signal is decoded as dot or dash

const int morseTreetop = 31;   // character position of the binary morse tree top.
const int morseTableLength = (morseTreetop*2)+1;
const int morseTreeLevels = log(morseTreetop+1)/log(2);
int morseTableJumper = (morseTreetop+1)/2;
int morseTablePointer = morseTreetop;

// Morse code binary tree table (or, dichotomic search table)
char morseTable[] = "5H4S?V3I?F?U??2E?L?R???A?P?W?J1 6B?D?X?N?C?K?Y?T7Z?G?Q?M8??O9?0";

int morseSignals;              // nr of morse signals to send in one morse character
char morseSignal[] = "......"; // temporary string to hold one morse character's signals to send
int morseSignalPos = 0;
int sendingMorseSignalNr = 0;
unsigned long sendMorseTimer = 0;

boolean morseEcho = true; // Echoes character to encode back to serial and Morse signal input to output pin
boolean listeningAudio = false;
boolean sendingMorse = false;
boolean morseSignalState = false;
boolean lastKeyerState = false;

//unsigned long lastAudioSignalTime = 0;
unsigned long lastDebounceTime = 0;  // the last time the input pin was toggled



void setup()
{
  pinMode(morseInPin, INPUT);
  digitalWrite(morseInPin,HIGH); // internal pullup resistor on
  pinMode(morseOutPin, OUTPUT);
  Serial.begin(9600);
  Serial.println("Morse en-/de-coder by raron");
  markTime = millis();
  spaceTime = millis();
}



void loop()
{
  boolean morseKeyer = !digitalRead(morseInPin); // inverted for active-low input



  // If the switch changed, due to noise or pressing:
  if (morseKeyer != lastKeyerState)
  {
    lastDebounceTime = millis(); // reset timer
    listeningAudio = false;      // disable listen to audio-mode
  }
  


  // debounce the morse keyer, unless listening to audio morse signal
  if (!listeningAudio && (millis() - lastDebounceTime) > debounceDelay)
  {
    // whatever the reading is at, it's been there for longer
    // than the debounce delay, so take it as the actual current state:
    morseSignalState = morseKeyer;
    // differentiante mark and space times
    if (morseSignalState) markTime = lastDebounceTime; else spaceTime = lastDebounceTime;
  }



  // If no manual morse keying the last second, enter audio listen mode
  if (!morseKeyer && millis() - lastDebounceTime > 1000)  listeningAudio = true;



  // Filter audio morse signal
  if (listeningAudio)
  {
    int audioMorse = analogRead(analogPin);
    unsigned long currentTime = millis();

    // Check for an audio signal...
    if (audioMorse > AudioThreshold)
    {
      // If this is a new morse signal, reset morse signal timer
      if (currentTime - lastDebounceTime > dotTime/2)
      {
        markTime = currentTime;
        morseSignalState = true; // there is currently a signal
      }
      lastDebounceTime = currentTime;
    } else {
      // if this is a new pause, reset space time
      if (currentTime - lastDebounceTime > dotTime/2 && morseSignalState == true)
      {
        spaceTime = lastDebounceTime; // not too far off from last received audio
        morseSignalState = false;        // No more signal
      }
    }
  }



  // Morse output, or a feedback when keying.
  if (!sendingMorse && morseEcho) digitalWrite(morseOutPin, morseSignalState);

Part #2:

  // Encode Morse code or execute commands
  if (Serial.available() > 0 && !sendingMorse)
  {
    char encodeMorseChar = Serial.read();
    
    // if a command instead, adjust some settings
    if (encodeMorseChar == MorseCommand)
    {
      // An extremely crude and simple command parser
      // Expects (and wait for) 2 or 4 characters (>i or <Cxxx)
      int digits;
      int value = 0;
      do
      {
        digits = Serial.available();
      } while (digits < 1); 
      // Read what setting
      char morseSet = Serial.read();
      // Read 3 digits unless setting is i for info
      if (morseSet != 'i' && morseSet != 'I')
      {
        do
        {
          digits = Serial.available();
        } while (digits < 3); 
        // convert value from ASCII
        for (int i=0; i<digits; i++)
        {
          encodeMorseChar = Serial.read();
          value *= 10;
          value += (encodeMorseChar - '0');
        }
      }
      Serial.flush(); // just in case

      // Adjust and print the new setting
      Serial.println();
      switch (morseSet)
      {
        case 'a': // Audio input threshold value
        case 'A':
          AudioThreshold = value;
          if (AudioThreshold<0) AudioThreshold = 0; // not recommended
          Serial.print(" > Audio threshold:");
          Serial.print (value,DEC);
          Serial.println(" <");
          break;
        case 'd': // Debounce value
        case 'D':
          debounceDelay = (unsigned long) value;
          if (debounceDelay<0) debounceDelay = 0;
          Serial.print(" > Debounce (ms):");
          Serial.print (value,DEC);
          Serial.println(" <");
          break;
        case 'e': // Turn on / off Morse echo back to serial and Morse output pin
        case 'E':
          if (value > 0)
          {
             morseEcho = true;
             Serial.println(" > Echo: on <");
          } else {
            morseEcho = false;
            Serial.println(" > Echo: off <");
          }
          break;
        case 'w': // Morse speed setting in wpm
        case 'W':
          wpm = value;
          if (wpm <= 0) wpm = 1;
          dotTime = 1200 / wpm;
          dashTime = 3 * 1200 / wpm;
          wordSpace = 7 * 1200 / wpm;
          Serial.print(" > Morse speed (wpm):");
          Serial.print (value,DEC);
          Serial.println(" <");
          break;
        case 'i': // Display info (current settings).
        case 'I':
          Serial.print(" > Morse speed: ");
          Serial.print (wpm,DEC);
          Serial.print(" wpm (dot=");
          Serial.print (dotTime,DEC);
          Serial.print("ms, dash=");
          Serial.print (dashTime,DEC);
          Serial.print("ms) Debounce: ");
          Serial.print (debounceDelay,DEC);
          Serial.print(" ms. Audio threshold: ");
          Serial.print (AudioThreshold,DEC);
          Serial.println(" <");
          break;
        default:
          Serial.print(" > Unrecognized command <");
      }
      // Mark that we have executed a command (dont send morse)
      encodeMorseChar = MorseCommand; 
    }

    if (encodeMorseChar != MorseCommand)
    {
      // change to capital letter if not
      if (encodeMorseChar > 'Z') encodeMorseChar -= 'z'-'Z';
  
      // Scan for the character to send in the Morse table
      int i;
      for (i=0; i<morseTableLength; i++) if (morseTable[i] == encodeMorseChar) break;
      int morseTablePos = i+1;  // 1-based position
      
      // Reverse dichotomic / binary tree path tracing
      
      // Find out what level in the binary tree the character is
      int test;
      for (i=0; i<morseTreeLevels; i++)
      {
        test = (morseTablePos + (0x0001 << i)) % (0x0002 << i);
        if (test == 0) break;
      }
      int startLevel = i;
      morseSignals = morseTreeLevels - i; // = the number of dots and/or dashes
      morseSignalPos = 0;
      
      // Travel the reverse path to the top of the morse table
      if (morseSignals > 0)
      {
        // build the morse signal (backwards from last signal to first)
        for (i = startLevel; i<morseTreeLevels; i++)
        {
          int add = (0x0001 << i);
          test = (morseTablePos + add) / (0x0002 << i);
          if (test & 0x0001 == 1)
          {
            morseTablePos += add;
            // Add a dot to the temporary morse signal string
            morseSignal[morseSignals-1 - morseSignalPos++] = '.';
          } else {
            morseTablePos -= add;
            // Add a dash to the temporary morse signal string
            morseSignal[morseSignals-1 - morseSignalPos++] = '-';
          }
        }
      } else {  // unless it was on the top to begin with (A space character)
        morseSignal[0] = ' ';
        morseSignalPos = 1;
        morseSignals = 1; // cheating a little; a wordspace for a "morse signal"
      }
      morseSignal[morseSignalPos] = '\0';
  
      // Echo back the letter with morse signal as ASCII to serial output
      if (morseEcho)
      {
        Serial.print(encodeMorseChar);
        Serial.print(morseSignal);
        Serial.print(" ");
      }
      if (morseTablePos-1 != morseTreetop)
      {
        Serial.println();
        Serial.print("..Hm..error? MorseTablePos = ");
        Serial.println(morseTablePos); 
      }
      // start sending the the character
      sendingMorse = true;
      sendingMorseSignalNr = 0;
      sendMorseTimer = millis();
      if (morseSignal[0] != ' ') digitalWrite(morseOutPin, HIGH);
    }
  }



  // Send Morse signals to output
  if (sendingMorse)
  {
    switch (morseSignal[sendingMorseSignalNr])
    {
      case '.': // Send a dot (actually, stop sending a signal after a "dot time")
        if (millis() - sendMorseTimer >= dotTime)
        {
          digitalWrite(morseOutPin, LOW);
          sendMorseTimer = millis();
          morseSignal[sendingMorseSignalNr] = 'x'; // Mark the signal as sent
        }
        break;
      case '-': // Send a dash (same here, stop sending after a dash worth of time)
        if (millis() - sendMorseTimer >= dashTime)
        {
          digitalWrite(morseOutPin, LOW);
          sendMorseTimer = millis();
          morseSignal[sendingMorseSignalNr] = 'x'; // Mark the signal as sent
        }
        break;
      case 'x': // To make sure there is a pause between signals and letters
        if (sendingMorseSignalNr < morseSignals-1)
        {
          // Pause between signals in the same letter
          if (millis() - sendMorseTimer >= dotTime)
          {
            sendingMorseSignalNr++;
            digitalWrite(morseOutPin, HIGH); // Start sending the next signal
            sendMorseTimer = millis();       // reset the timer
          }
        } else {
          // Pause between letters
          if (millis() - sendMorseTimer >= dashTime)
          {
            sendingMorseSignalNr++;
            sendMorseTimer = millis();       // reset the timer
          }
        }
        break;
      case ' ': // Pause between words (minus pause between letters - already sent)
      default:  // Just in case its something else
        if (millis() - sendMorseTimer > wordSpace - dashTime) sendingMorse = false;
    }
    if (sendingMorseSignalNr >= morseSignals) sendingMorse = false; // Ready to encode more letters
  }



  // Decode morse code
  if (!morseSignalState)
  {
    if (!gotLastSig)
    {
      if (morseTableJumper > 0)
      {
        // if pause for more than half a dot, get what kind of signal pulse (dot/dash) received last
        if (millis() - spaceTime > dotTime/2)
        {
          // if signal for more than 1/4 dotTime, take it as a valid morse pulse
          if (spaceTime-markTime > dotTime/4)
          {
            // if signal for less than half a dash, take it as a dot, else if not, take it as a dash
            // (dashes can be really really long...)
            if (spaceTime-markTime < dashTime/2) morseTablePointer -= morseTableJumper;
            else morseTablePointer += morseTableJumper;
            morseTableJumper /= 2;
            gotLastSig = true;
          }
        }
      } else { // error if too many pulses in one morse character
        Serial.println("<ERROR: unrecognized signal!>");
        gotLastSig = true;
        morseTableJumper = (morseTreetop+1)/2;
        morseTablePointer = morseTreetop;
      }
    }
    // Write out the character if pause is longer than 2/3 dash time (2 dots) and a character received
    if ((millis()-spaceTime >= (dotTime*2)) && (morseTableJumper < 16))
    {
      char morseChar = morseTable[morseTablePointer];
      Serial.print(morseChar);
      morseTableJumper = (morseTreetop+1)/2;
      morseTablePointer = morseTreetop;
    }
    // Write a space if pause is longer than 2/3rd wordspace
    if (millis()-spaceTime > (wordSpace*2/3) && morseSpace == false)
    {
      Serial.print(" ");
      morseSpace = true ; // space written-flag
    }
    
  } else {
    // while there is a signal, reset some flags
    gotLastSig = false;
    morseSpace = false;
  }



  // save last state of the morse signal for debouncing
  lastKeyerState = morseKeyer;
}

I forgot to describe the schematics. I don't have any schematics atm, will post that later if requested.

Morse input is either via a push button on digital input 7, or audio input on analog input 0. This is just an electret microphone and a one-transitor preamplifier I strung together.

The output is a LED and also audible via a piezo transducer, both on digital output 13.

The piezo transducer needs a signal of some frequency as it doesn't make sounds itself. I just made a oscillator out from an old 74LS13 I had lying around. It's a dual 4-input NAND (Schmitt trigger), controlled by the Arduino. It's the IC and stuff seen in the middle of the breadboard.

Also there is a serial interface to a computer (well, two computers, one for each), showing decoded and encoded Morse, and also for controlling some settings like speed and audio threshold.

Very nice and interesting! So you're not doing the keying but the computers are doing them. I suppose you still can, but will you?

My approach is a half-duplex way so you either encode or decode. I've got more stand-alone interface than yours, where one arduino with my Phi-1 shield will be sufficient to encode or decode. I'll make a video of it and post it soon!

So you're not doing the keying but the computers are doing them. I suppose you still can, but will you?

Nah, thats what computers are for :stuck_out_tongue:
Actually, its the Arduino's that does the keying (encoding), the computer just sends commands and text via serial.

And yes, I can still key in Morse manually.

Morse input is either via a push button on digital input 7, or audio input on analog input 0

And yes my interface is more temporary breadboardish :slight_smile: I just got a little into the coding of the stuff.

And a slight update to include punctuation in the Morse table (and thus a doubling of the table size), but not non-english characters atm.

Except it's without the $ sign (it would require another doubling of the table since it is the only character that was 7 morse signals long - seems they could'nt use any of the other 60-ish vacant positions for that one. Not that its impossible I just didn't do that for now. Also it's not difficult to include - just double the table in the same manner as I just did. (Plus of course the table should be put in PROGMEN, but that's for another time).

There are some other things not really supported, like the close-parenthesis that can also be an open-parenthesis (how they transmit math I'm not sure of) - then again wikipedia also lists the open-parenthesis just above it in the dichotomic three. So that's where I put it.

Instead of posting two new posts with the entire thing (+ Im sure I will update it still further, and then I might do that anyway), I just posts the changes for now.

Change the old lines containing this:

const int morseTreetop = [glow]31[/glow];   // character position of the binary morse tree top.

into this:

// character index position of the binary morse tree top.
//const int morseTreetop = 31;   // This is for ITU international Morse Code (no punctuation)
const int morseTreetop = [glow]63[/glow];   // This is for ITU with puncutation, but without non-english extensions

And this old line just below it:

// Morse code binary tree table (or, dichotomic search table)
char morseTable[] = "5H4S?V3I?F?U??2E?L?R???A?P?W?J1 6B?D?X?N?C?K?Y?T7Z?G?Q?M8??O9?0";

into this:

// Morse code binary tree table (or, dichotomic search table)
// This is the table for ITU with punctuation (but without non-english characters - for now)
char morseTable[] = "*5*H*4*S***V*3*I***F***U?*_**2*E***L\"**R*+.****A***P@**W***J'1* *6-B*=*D*/"
                    "*X***N***C;*!K*()Y***T*7*Z**,G***Q***M:8*****O*9***0*";
// ITU - International Morse code table only
//char morseTable[] = "5H4S?V3I?F?U??2E?L?R???A?P?W?J1 6B?D?X?N?C?K?Y?T7Z?G?Q?M8??O9?0";

In addition a little bug fix at the almost very bottom of the old source code:

Change this:

    // Write out the character if pause is longer than 2/3 dash time (2 dots) and a character received
    if ((millis()-spaceTime >= (dotTime*2)) && (morseTableJumper < [glow]16[/glow]))

into this:

    // Write out the character if pause is longer than 2/3 dash time (2 dots) and a character received
    if ((millis()-spaceTime >= (dotTime*2)) && (morseTableJumper < [glow]((morseTreetop+1)/2)[/glow]))

Ok I hope this was not too confusing.

EDIT: fixed the morseTable string to avoid /* in the string from Coding Badly's nice solution (thanks!).

interesting project.
Check this also, it uses AM modulation with Arduino..

http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1170901754

raron,

I finally made my Morse decoder to work with manual input and packed both encoder and decoder together with a menu. I'll be adding speed options to the menu so it'll be more complete. I'll post it maybe next week.

I have finally made it into more reusable classes for the Arduino.
Project homepage: Google Code Archive - Long-term storage for Google Code Project Hosting.

It went better than I hoped making classes out of it :slight_smile:
(Not that I think very many will have use for this, I did it mostly as a programming exercise, plus I got a little caught up in the thing)
Use at your own risk though. I make no claim as to the accuracy or usability for anything.

martin_bg:
Interesting, thanks for the link! (Kind of have to like the frequency he got too - 1337 kHz :P)

liudr:
Looking forward to it!
PS! Of course you also have the option to use those classes above I made (but it would take the fun out of it?)

Very nice , well done . I will bookmark this to build when time permits
I did not see a schematic so if there is one I would appreciate it . I didn'tfollow all the links yet.

I could read it at 13 surprisingly easy but 300 wpm is a bit better than humans can do .
Cheers

Thanks!

Here is a schematics (made in Fritzing - first test). At first I used a buzzer like on the schematics, but when I made two, I discovered I only had one of those, but I had some piezo transduzers. Which dont "buzz" by themself but need an alternating signal, which is the IC and stuff in the middle of my breadboard. So in the video I use those. Not included in this schematics, but it's controlled by the Arduino all the same.

The electret microphone preamplifier circuit is a slight modification of what one normally finds. If your electret mic differs, it might not work as good. It is not made for "normal" (dynamic?) microphones! (A resistor and capacitor extra and you can have that too, preferably something more elaborate). I was experimenting a bit with some one-transistor electret preamplifier circuits for another project, when I found it fitted nicely for the morse decoder. I needed something small in component count, but preferably more sensitive than this one. Anyway, the idea is to get an audio signal centered around 2.5V into the analog input.

Of course, if you have an audio morse signal, it would be better to modify the analog input circuit to just plug in a jack from a line-out or something. This was just for testing, and it is sensitive to other nearby sounds also.

The schematics is for the code posted in the first two-three posts, but also works as-is for the endecoder class example. But then only for the button. To use audio as input, you must change the initialization of the decoder class:

int morseInPin = 0;
morseDecoder morseInput(morseInPin, MORSE_AUDIO, MORSE_ACTIVE_HIGH);

(The "MORSE_ACTIVE_HIGH" argument is only used for a digital signal, but must be included anyway for now. It is used to indicate an active-low or active high signal, and uses the internal pull-up resistor if it is acitive-low. When using audio (analog) input it doesn't matter what value it is, as long as it is a boolean (true or false))

I want to start off by saying I created an account here just to reply to this post. I have been following Arduino for the last year solid, Ive been hording article after article of the wonderful things people have been doing with their Arduino's. This Christmas I went out and splurged I broke down and bought one, should be in tomorrow and I can't wait!

As I have been hording the first thing I noticed is the arduino to arduino wireless communication in the beginning was highly dependent on Xbee, which I'm sure is a great product but also propriety. I couldn't find any other good long range wireless arduino to arduino communication hardware or protocol and so I have been following the posts on Morse.

Morse code is great, its universal and audio based, there are so many software titles for the PC to interface with this. It can be connected to a cellphone on a balloon, ham radio ( in any band) , FRS, or even a telephone and you can have any number of arduinos passing information to you or each other in any kind of network topography from a million miles away.

I am very happy to see such an open source "protocol" ( used lightly, but solid communication ) appear to be able to pass serial data so easily. Thank you Raron and all that contributed to this project, you have answered every question Ive had on how to connect every project I have thought up in my head without wires on an unlimited range (with a ham license or telephone).

Matt

I had kind of a brainflash/fart today but decided to share in case someone finds it useful.
An arduino encoded radio tracking system.
One end is a reciever with this morsecode-decoder and the other is a transmitter with an arduino sending the same code over and over again.
The transmitter could then be identified with the morse-decoder by the code it transmits.
Uses would be wildlifetracking, studies of streams och seacurrents.

Just wanted to throw this out there.

I had kind of a brainflash/fart today

Those can sometimes be difficult to distinguish :stuck_out_tongue:

Anyway, I haven't really given this that much thought, and I would assume there are more efficient ways of electronic communication. But maybe for some areas it is sufficient (and the speed can be increased if needed, at least to some degree, but some error-checking would preferably have to be implemented on the received data). And than there are the things Matt Adams point out.

@Matt Adams:
Thanks for pointing all that out, and also welcome to this forum :slight_smile: Not that I'm "qualified" to say something like that (I'm just a random user like most here), but anyway cool that this thread got you started!