Arduino + Wire = Morse Code AM Radio Beacon

Based on some work found on this board at http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1169088394 and
http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1166896036,
I'm pleased to present a simple beacon, broadcasting Morse code over the radio airwaves. It works by using an assembly increment on PORTB in the fast loop in the broadcast function. The least significant bit toggles once per loop, the second toggles every two loops, the third every four, and so on.

Just run the code and plug a length of wire into Digital Pin 8 and tune your AM dial to around 1337 kHz. You can also get 1337/2 kHz on Pin 9 and 1337/4 kHz on Pin 10, etc. but this will probably be off your dial. The LED on Pin 13 flashes the message too, if you like to look at the pretty lights.

This is patched together from code from a lot of sources, so if you care about me infringing on your copyright, sorry. Feel free to infringe on this code however you feel necessary, but it expects to be cuddled after you've had your way with it.

long millisAtStart=0;
long millisAtEnd=0;

const long period_broadcast=8; //period of shortest broadcast (256 port changes)

#define LENGTH_DIT 64
//number of period_broadcasts in one 'dit', 
//all other lengths are scaled from this

const int length_dit=LENGTH_DIT;//number of periods for dit
const int pause_dit=LENGTH_DIT;//pause after dit
const int length_dah=3*LENGTH_DIT;//number of persots for dah
const int pause_dah=LENGTH_DIT;//pause after dah
const int length_pause=7*LENGTH_DIT;//pause between words

void dit(void); 
void dah(void); 
void pause(void);
void broadcast(int N_cycles);
void dontbroadcast(int N_cycles);

// ### INC ### Increment Register (reg = reg + 1)
#define ASM_INC(reg) asm volatile ("inc %0" : "=r" (reg) : "0" (reg)) 

void setup()  
{

  Serial.begin(9600);
  DDRB=0xFF;  //Port B all outputs
  //Do one dit to determine approximate frequency
  millisAtStart=millis();
  dit();
  millisAtEnd=millis();
  Serial.print(millisAtEnd-millisAtStart);  
  Serial.print(" ");
  Serial.print((length_dit+pause_dit)*period_broadcast*256/(millisAtEnd-millisAtStart)/2);
  Serial.print("kHz ");
  Serial.println(); 
}

void loop()  
{
  dah();
  dit();
  dah();
  dah();
  pause();
  dah();
  dit();
  dah();
  dah();
  pause();
  dah();
  dah();
  dit();
  dit();
  pause();
  pause();
}

void dit(void)
{
  for(int i=0;i<length_dit;i++)
  {
    broadcast(period_broadcast);
  }
  for(int i=0;i<pause_dit;i++)
  {
    dontbroadcast(period_broadcast);
  }
}


void dah(void)
{
  for(int i=0;i<length_dah;i++)
  {
    broadcast(period_broadcast);
  }
  for(int i=0;i<pause_dah;i++)
  {
    dontbroadcast(period_broadcast);
  }
}

void pause(void)
{
  for(int i=0;i<length_pause;i++)
  {
    dontbroadcast(period_broadcast);
  }
}

void broadcast(int N_cycles)
{
  unsigned int portvalue;
  for (int i=0;i<N_cycles;i++)
  {
    portvalue=0;
    do
    {
      PORTB=portvalue;
      ASM_INC(portvalue);
    }
    while(portvalue<255);
  }
}

void dontbroadcast(int N_cycles)
{
  unsigned int portvalue;
  PORTB=0x00;
  for (int i=0;i<N_cycles;i++)
  {
    portvalue=0;
    do
    {
      ASM_INC(portvalue);
      //add some assembly No OPerations to keep timing the same
      asm volatile ("NOP");  
    }
    while(portvalue<255);
  }
}

Neat :smiley:
Glad I could help.

Since this project doesn't really fully utilize the Arduino's ATMEGA's multiple input/output ports, I've migrated this project to the cheaper and much smaller ATTINY device. I don't know how this forum views non-Arduino projects (although it was a perfect prototype platform), so I won't post the code here unless someone asks for it.

why would you loop the characters yyz over and over?

btw cool idea... with some bit calculations, you could store characters so as to make it more adaptable... i did so for my (semi-functional) morse translator... i give it morse, via key, it sends me text via serial.... i say semi-working because it was way to strict for my terrible morse skills.

why would you loop the characters yyz over and over?

Obviously not a Rush fan, eh?

hey

it's Canadian for late arrival/ lost luggage!

D

hint 1 and if you still don't get it...hint 2 :slight_smile:

Can this broadcast at any frequency? If so how do i change the frequency?

He he, now that is fun. I just tried it out and it worked great!

What cool things can be accomplished by the Arduino and some clever code.

I add some lines to your code and now can send any message you want, just type some text an send it through serial COM and the Arduino will broadcast to the air in morse code, download this program (http://www.instructables.com/files/orig/F8J/EV05/NULEXCFDUNP/F8JEV05NULEXCFDUNP.zip) install it, and plug an comun AM radio to your sound card MIC tuned at around 1337 and you will see the message you send.

Setup the program with the main filter to 1000Hz, and with the left click of the mouse put the red line where you see the peaks when you send the messages.

this will automaticaly translate the message.

for better info check this http://www.instructables.com/id/Build-a-computer-controlled-radio-transmitter/.

long millisAtStart=0;
long millisAtEnd=0;

/*a = '01';
 b = '1000';
 c = '1010';
 d = '100';
 e = '0';
 f = '0010';
 g = '110';
 h = '0000';
 i = '00';
 j = '0111';
 k = '101';
 l = '0100';
 m = '11';
 n = '10';
 o = '111';
 p = '0110';
 q = '1101';
 r = '010';
 s = '000';
 t = '1';
 u = '001';
 v = '0001';
 w = '011';
 x = '1001';
 y = '1011';
 z = '1100';*/

int letters[26][4] = {
  {
    0,1          }
  ,
  {
    1,0,0,0          }
  ,
  {
    1,0,1,0          }
  ,
  {
    1,0,0          }
  ,
  {
    0          }
  ,
  {
    0,0,1,0          }
  ,
  {
    1,1,0          }
  ,
  {
    0,0,0,0          }
  ,
  {
    0,0          }
  ,
  {
    0,1,1,1          }
  ,
  {
    1,0,1          }
  ,
  {
    0,1,0,0          }
  ,
  {
    1,1          }
  ,
  {
    1,0          }
  ,
  {
    1,1,1          }
  ,
  {
    0,1,1,0          }
  ,
  {
    1,1,0,1        }
  ,
  {
    0,1,0         }
  ,
  {
    0,0,0         }
  ,
  {
    1          }
  ,
  {
    0,0,1          }
  ,
  {
    0,0,0,1          }
  ,
  {
    0,1,1          }
  ,
  {
    1,0,0,1          }
  ,
  {
    1,0,1,1          }
  ,
  {
    1,1,0,0          }
  ,
};                     

const long period_broadcast=8; //period of shortest broadcast (256 port changes)

#define LENGTH_DIT 64
//number of period_broadcasts in one 'dit',
//all other lengths are scaled from this

const int length_dit=LENGTH_DIT;//number of periods for dit
const int pause_dit=LENGTH_DIT;//pause after dit
const int length_dah=3*LENGTH_DIT;//number of persots for dah
const int pause_dah=LENGTH_DIT;//pause after dah
const int length_pause=7*LENGTH_DIT;//pause between words

void dit(void);
void dah(void);
void pause(void);
void broadcast(int N_cycles);
void dontbroadcast(int N_cycles);

// ### INC ### Increment Register (reg = reg + 1)
#define ASM_INC(reg) asm volatile ("inc %0" : "=r" (reg) : "0" (reg))

void setup()
{

  Serial.begin(9600);
  DDRB=0xFF;  //Port B all outputs
  //Do one dit to determine approximate frequency
  millisAtStart=millis();
  dit();
  millisAtEnd=millis();
  Serial.print(millisAtEnd-millisAtStart);
  Serial.print(" ");
  Serial.print((length_dit+pause_dit)*period_broadcast*256/(millisAtEnd-millisAtStart)/2);
  Serial.print("kHz ");
  Serial.println();
}

void loop()
{
  char buffer;
  int L=0;
  int P=0;  

  if (Serial.available())
  {
    buffer=Serial.read();
    switch (buffer) {
    case 'a': 
      L=0;
      P=2;
      break;
    case 'b': 
      L=1;
      P=4;
      break;
    case 'c': 
      L=2;
      P=4;
      break;
    case 'd': 
      L=3;
      P=3;
      break;
    case 'e': 
      L=4;
      P=1;
      break;
    case 'f': 
      L=5;
      P=4;
      break;
    case 'g': 
      L=6;
      P=3;
      break;
    case 'h': 
      L=7;
      P=4;
      break;
    case 'i': 
      L=8;
      P=2;
      break;
    case 'j': 
      L=9;
      P=4;
      break;
    case 'k': 
      L=10;
      P=3;
      break;
    case 'l': 
      L=11;
      P=4;
      break;
    case 'm': 
      L=12;
      P=2;
      break;
    case 'n': 
      L=13;
      P=2;
      break;
    case 'o': 
      L=14;
      P=3;
      break;
    case 'p': 
      L=15;
      P=4;
      break;
    case 'q': 
      L=16;
      P=4;
      break;
    case 'r': 
      L=17;
      P=3;
      break;
    case 's': 
      L=18;
      P=3;
      break;
    case 't': 
      L=19;
      P=1;
      break;
    case 'u': 
      L=20;
      P=3;
      break;
    case 'v': 
      L=21;
      P=4;
      break;
    case 'w': 
      L=22;
      P=3;
      break;
    case 'x': 
      L=23;
      P=4;
      break;
    case 'y': 
      L=24;
      P=4;
      break;
    case 'z': 
      L=25;
      P=4;
      break;
    }
    for (byte i=0; i<P; i++) {
      if (letters[L][i] == 0){
        dit();
      }
      else {
        dah();
      }
    }
    pause();
  }
  /*dah();
   dit();
   dah();
   dah();
   pause(); //Y
   dah();
   dit();
   dah();
   dah();
   pause();//Y
   dah();
   dah();
   dit();
   dit();
   pause();//Z
   pause();*/
}

void dit(void)
{
  for(int i=0;i<length_dit;i++)
  {
    broadcast(period_broadcast);
  }
  for(int i=0;i<pause_dit;i++)
  {
    dontbroadcast(period_broadcast);
  }
}


void dah(void)
{
  for(int i=0;i<length_dah;i++)
  {
    broadcast(period_broadcast);
  }
  for(int i=0;i<pause_dah;i++)
  {
    dontbroadcast(period_broadcast);
  }
}

void pause(void)
{
  for(int i=0;i<length_pause;i++)
  {
    dontbroadcast(period_broadcast);
  }
}

void broadcast(int N_cycles)
{
  unsigned int portvalue;
  for (int i=0;i<N_cycles;i++)
  {
    portvalue=0;
    do
    {
      PORTB=portvalue;
      ASM_INC(portvalue);
    }
    while(portvalue<255);
  }
}

void dontbroadcast(int N_cycles)
{
  unsigned int portvalue;
  PORTB=0x00;
  for (int i=0;i<N_cycles;i++)
  {
    portvalue=0;
    do
    {
      ASM_INC(portvalue);
      //add some assembly No OPerations to keep timing the same
      asm volatile ("NOP");
    }
    while(portvalue<255);
  }
}


I tried this project but the frequency seems to be more like 999 not 1337 for some reason???

Anyone else got same result as me??

Cheers

Bruton

i hear the signal @ 1323kHz

Ive had a quick fiddle with this one to try and get a cleaner output and with the scraps I could find lying around ive done this if it helps anyone.
This is the output from the Arduino.

This is my little circuit I build on breadboard.

And this is the output

Its far from perfect but once I can get a few more bits I will see if i can get it smoother and transmitting further afield.

What kind of range are you all getting with this?

I'm moving house and I can't find the box with the Arduino stuff :frowning:

BTW here's a varient on the general purpose morse generation code from my book. It takes the message to be transmitter from the Serial Monitor.

// Listing. Project 3.
int ledPin = 11;

char* letters[] = {
  ".-", "-...", "-.-.", "-..", ".", "..-.", "--.", "....", "..",    // A-I
  ".---", "-.-", ".-..", "--", "-.", "---", ".--.", "--.-", ".-.",  // J-R
  "...", "-", "..-", "...-", ".--", "-..-", "-.--", "--.."          // S-Z
};

char* numbers[] = {"-----", ".----", "..---", "...--", "....-", ".....", "-....", "--...", "---..", "----."};

int dotDelay = 200;

void setup()                 
{
  pinMode(ledPin, OUTPUT);
  Serial.begin(9600);
}

void loop()                    
{
  char ch;
  if (Serial.available())      // is there anything to be read from USB?
  {
    ch = Serial.read();        // read a single letter
    if (ch >= 'a' && ch <= 'z')
    {
      flashSequence(letters[ch - 'a']);
    }
    else if (ch >= 'A' && ch <= 'Z')
    {
      flashSequence(letters[ch - 'A']);
    }
    else if (ch >= '0' && ch <= '9')
    {
      flashSequence(numbers[ch - '0']);
    }
    else if (ch == ' ')
    {
     delay(dotDelay * 4);      // gap between words  
    }
  }
}

void flashSequence(char* sequence)
{
   int i = 0;
   while (sequence[i] != NULL)
   {
       flashDotOrDash(sequence[i]);
       i++;
   }
   delay(dotDelay * 3);    // gap between letters
}

void flashDotOrDash(char dotOrDash)
{
  digitalWrite(ledPin, HIGH);
  if (dotOrDash == '.')
  {
    delay(dotDelay);           
  }
  else // must be a -
  {
    delay(dotDelay * 3);           
  }
  digitalWrite(ledPin, LOW);    
  delay(dotDelay); // gap between flashes
}

with the right antennae you could transmit thousands of miles with less than 1Watt of power on this frequency but bare in mind that a quarter wave antennae should be 57 meters of wire connected to the center of your coax and 57 meters connected to the ground and strung out. You can reduce this length by using matching baluns or and A.T.U.