Project: A Simple Morse Code Beacon Transmitter
Project: A Simple Morse Code Beacon Transmitter
The idea for this project arose from a request from a friend to teach him the rudiments of programming an "Arduino" microcontroller. After providing hm with the basics of programmming, I thought a "Morse Code" program would be a nice idea - and should be fairly simple.
Rather than flashing a light, I thought it would be instructive to make a very simple beacon transmitter using a valve circuit.
The result is a very simple project that can be put together by almost anybody in an afternoon.
WW2 Beacon Transmitters
World War 2 Beacon transmitters used to be small single-valve transmitters operating on a set frequency. You could pick them up at surplus stores, and the ones I saw had a single triode valve operating at around 40MHz in a simple oscillator circuit.
I had intended to use a 3A5 valve in a Colpitts circuit, but the valve refused to amplify, let alone oscillate, so I gave up in some disgust. (Let it be said there was nothing wrong with the 3A5). If I had used the 3A5, it would have been too big anyway (sour grapes), so I scoured the local valve store for a suitable subminiature valve. It was essential that the transmitter be not much bigger than the Arduino board, and should (preferably) operate from 5 Volts HT.
The Final Design
The dusty shelves of my local parts store revealed a small quantity of DL70 and EC90 valves. The DL70 turned out to be a transmitting tetrode with a maximum anode voltage of 150 Volts and a filament voltage of 1.2 volts, making it suitable for use with a rechargeable nickel cadmium battery for the filament. I still prefer NiCADs over NiMH, because they seem to have a lower self-discharge. Be that as it may, the following circuit (A Pierce Oscillator) was assembled on Veroboard in about 30 minutes. Power was applied - gradually increasing the HT votage from zero. At about 5 volts
something started to happen and at 6 volts HT there was a strong steady carrier at the crystal frequency of 11.17 MHz. Since the supply to the Arduino was provided by a 9 volt battery, I decided to settle on that as the HT voltage. As you can see from the photo, the "transmitter" is about the same size as the Arduino - slightly narrower and a bit taller. I wouldn't be tempted to run the transmitter at a higher voltage - for one thing it would probably be illegal, and for another, the Pierce oscillator was always regarded as a "crystal cracker". With 9 volts there should be plenty of power for you to be able to receive on your chosen frequency. Note that for higher frequencies, you may have to reduce the value of the grid resistor R1.
Parts List
V1 DL70 (Other valves to try are the EF732, or larger B7G pentodes/tetrodes.)
R1 470k 1/4 Watt
R2 12k 1/4 Watt screen resistor
R3 5k 1/4 Watt anode resistor
X1 Junk Box crystal - I used 11.170 MHz
C1,C2 0.01uF 600 volts working (For appearance - those little transistor types look a bit daft)
Veroboard - 10 strips wide by 40 cm.
Construction
Cut the board using a hacksaw and smooth the edges with a file. Round the corners of the board so nobody can get hurt.
Cut away the unwanted leads on the valve - there are three(pins 1,3 and 6). Fan out the remaining leads in the order Screen grid, Anode. Control grid, filament and filament/g3. With the valve half way down the veroboard, solder the leads to every other strip. The strip with filament/g3 will be the ground. The strip furthest from the ground will be the "HT" supply, and the strip immediately adjacent will be the screen grid strip.
Next connect a 0.01 uF cap across HT and Ground, followed by a cap from Screen grid to ground. Now connect a 12k resistor from HT to screen, to complete the screen grid circuit.
Connect a 5k resistor from HT to anode. Connect the crystal from the anode to control grid strip.
Connect a 470k resistor from control grid to ground.
Clean the solder joints with solvent, otherwise it all corrodes and looks horrible in a few months time.
Finally, connect the power supply leads.
In order to limit the range, I attached a short antenna to the crystal's metal case.
Keying the Transmitter
The transmitter can be on/off keyed with a morse key in the "HT" lead. Unfortunately, it doesn't work reliably at 5.0 volts - so connecting pin 13 of the Arduino board isn't really an option (But you can try it, all the same.)
The two options that were considered were a MOSFET in the ground circuit, and Frequency shift keying using a varicap diode in the anode circuit to pull the crystal. In the end frequency shift keying was chosen, as shown in the circuit diagram.
Using the Arduino pin 13 results in a shift of about 600 Hz.
The Morse Code Sender
There are quite a number of code examples for automatic morse code senders on the internet and elsewhere. Generally , they strive for efficiency or if they are written for the Arduino, they follow a 'C' style of coding rather than a "C++" design - but, let it be said, may be more efficient in terms of speed than the design presented here. (At least they ought to be.)
This design had a number of different objectives.
1.It had to be short and very easy to follow,
2.It had to demonstrate the power of object-oriented programming,
3.It had to be an example of how to make an Arduino library for version 1.0
4.It had to have an example of pointers and dynamic memory allocation, and
5.It had to work at slow Morse speeds - 12 words/minute, say.
The Design
In this design, there are two C++ classes. There is a "Morse" class and a "MorseChar" class. To use the library, the user must first of all instantiate a Morse class. A Message is sent to the Morse object. Each character in a morse message is compared to the character in a MorseChar object. If it matches, the MorseChar object sends the character to the output pin. If no match is found, the Morse object tries to match the next character, until there are no more characters left in the message.
There are 41 "MorseChar" objects. These are instantiated (come into being) as soon as the Morse object is created. You can have an array of objects, in the same way you can have an array of characters or numbers.
Creating the Library
There have been some chages between this version and earlier versions of the Arduino programming environment. My first step was to take an existing library and copy it, renaming it to "Morse". The next step was to rename the files to Morse.cpp and Morse.h.
In the Morse.h file, delete everything except:
#ifndef Morse_h (Change whatever was there_h to this and in the line that follows)
#define Morse_h
#include <inttypes.h>
#endif
In the Morse.cpp file, delete everything except:
#include <inttypes.h
#include
#include
#include "Arduino.h"
Now make two new files, MorseChar.h and MorseChar.cpp, with the same contents, respectively.
Finally, alter the keywordss.txt file to:
######### #############################
# Syntax Coloring Map For Morse
######### #############################
######### #############################
# Datatypes (KEYWORD1)
######### #############################
Morse KEYWORD1
######### #############################
# Methods and Functions (KEYWORD2)
######### #############################
sendMessage KEYWORD2
setMessage KEYWORD2
########## ############################
# Constants (LITERAL1)
########## ############################
You don't have to have syntax 'coloring' for MorseChar.h, because nobody is going to see it.
Now complete the project by filling it in as follows:- (Some of the lines may have wrapped)
Morse.h
#ifndef Morse_h
#define Morse_h
#include
#include "MorseChar.h"
class Morse
{
public:
void setMessage(const char* _icray);
void sendMessage();
Morse();
virtual ~Morse();
private:
MorseChar MCray[45];
char *icray;
};
#endif
Morse.cpp
#include "MorseChar.h"
#include "Morse.h"
#include
#include
#include
#include "Arduino.h"
//
// Construction/Destruction
//
Morse::Morse()
{
MCray[0].init('A',".-");
MCray[1].init('B',"-...");
MCray[2].init('C',"-.-.");
MCray[3].init('D',"-..");
MCray[4].init('E',".");
MCray[5].init('F',"..-.");
MCray[6].init('G',"--.");
MCray[7].init('H',"....");
MCray[8].init('I',"..");
MCray[9].init('J',".---");
MCray[10].init('K',"-.-");
MCray[11].init('L',".-..");
MCray[12].init('M',"--");
MCray[13].init('N',"-.");
MCray[14].init('O',"---");
MCray[15].init('P',".--.");
MCray[16].init('Q',"--.-");
MCray[17].init('R',".-.");
MCray[18].init('S',"...");
MCray[19].init('T',"-");
MCray[20].init('U',"..-");
MCray[21].init('V',"...-");
MCray[22].init('W',".--");
MCray[23].init('X',"-..-");
MCray[24].init('Y',"-.--");
MCray[25].init('Z',"--..");
MCray[26].init('1',".---");
MCray[27].init('2',"..---");
MCray[28].init('3',"...--");
MCray[29].init('4',"....-");
MCray[30].init('5',".....");
MCray[31].init('6',"-....");
MCray[32].init('7',"--...");
MCray[33].init('8',"---..");
MCray[34].init('9',"----.");
MCray[35].init('0',"-----");
MCray[36].init('/',"-..-.");
MCray[37].init('*',".-.-.");
MCray[38].init('.',".-.-.-");
MCray[39].init('-',"-....-");
MCray[40].init(',',"--..--");
MCray[41].init('?',"..--..");
MCray[42].init(' '," ");
MCray[43].init(0," ");
}
Morse::~Morse()
{
}
void Morse::sendMessage()
{
for(unsigned i=0; i < strlen(icray)+1;i++)
{
for( int j = 0; j < 44; ++j)
{
if( toupper(*(icray+i)) == MCray[j].getChar())
{
MCray[j].send();
break;
}
}
}
}
void Morse::setMessage(const char *_icray)
{
icray=_icray;
sendMessage();
}
MorseChar.h
#ifndef MorseChar_h
#define MorseChar_h
#include
class MorseChar
{
public:
void init(char ichar,const char* _icray);
char getChar();
void send();
MorseChar();
virtual ~MorseChar();
private:
char *icray;
char ichar;
};
#endif
MorseChar.cpp
#include "Morse.h"
#include "MorseChar.h"
#include
#include
#include
#include "Arduino.h"
#define wpm 8
#define __pin 13
#define dot_len 1200/wpm
#define dot digitalWrite(__pin,HIGH); delay(dot_len);digitalWrite(__pin,LOW); delay(dot_len)
#define dash digitalWrite(__pin,HIGH); delay(3*dot_len);digitalWrite(__pin,LOW); delay(dot_len)
#define space digitalWrite(__pin,LOW);delay(2*dot_len)
//
// Construction/Destruction
//
MorseChar::MorseChar()
{
}
MorseChar::~MorseChar()
{
}
void MorseChar::send()
{
char* p=icray;
while(*p)
{
if(*p == '.')
{
dot;
}
else if(*p == '-')
{
dash;
}
else
{
space;
}
p++;
}
space;
}
char MorseChar::getChar()
{
return ichar;
}
void MorseChar::init(char ichar,const char *_icray)
{
icray=_icray;
this->ichar = ichar;
}
Finally, do the sketch:-
Beacon.ino
#include
/* Beacon*/
Morse MorseMessage;
void setup()
{
// initialize the digital pin as an output.
// Pin 13 has an LED connected on most Arduino boards:
pinMode(13, OUTPUT);
}
void loop()
{
MorseMessage.setMessage("VVV Frequency 11.17 MHZ Hello World");
delay(1000);
}
You will see the code all prettily coloured up.
Follow the Code
This started out as a fairly complicated program. All it consists of now seems to be a handful of almost trivial functions.
Morse.h and MorseChar.h
These two files contain the class definitions for the Morse and MorseChar classes. Note the first two lines and the last line of the file. If you think about it, this is just a nice way of avoiding inadvertent redefinition of the classes.
Note the array of MorseChar objects defined in the Morse.h class.
Morse.cpp
This has three functions. The first is the constructor, which initiallises all the MCray objects with their Character value and Morse equivalent. The getMessage function and the sendMessage fuction (which, I think I should have declared as "Private".)
Morse::setMessage(const char *_icray)
This is the only call that should be accessible to the user. The const keyword tells the compiler that what follows can't be modified.
Morse::sendMessage()
The function sendMessage goes through the message one character at a time. It compares the uppercase equivalent of the character in the message to a MorseChar and then if a match is found gets the MorseChar to send itself.
MorseChar.cpp
The init function stores the alphabetic character and its morse equivalent in member variables. The send function uses the dot, dash and space macros to set the state of the require pin high or low for a specified time. BEWARE - the dot macros etc. have to have curly brackets round them. One of the dangers of macros is that you think of them as functions. They're not.
Fire up the Beacon
Here is the complete Morse Code Beacon (The croc clip on the crystal can is the antenna). After about 5 minutes, it becomes very annoying.
Uses
You could use it as a proper beacon. This might be the first stage in a more powerful radio transmitter. You could fit a GPS to the Arduino and transmit your position in Morse code. I could be used as a morse code trainer, I suppose.
Conclusion
This is a really simple beginners project suitable for anyone to build in an evening. If you can't find a DL70, then you might find a FET, such as a 2N3819 or BF999 (Surface Mount) might work just as well. (Something I haven't tried.)
You might be surprised that some valves operate with such a low HT voltage, but a range of valves was designed to operate with 12 volts HT for car radio applications. Somewhere, I have a circuit for a simple regenerative reciever using a 6J7, also with 9 volts HT, and for a more challenging cicuit there's the superhet designed by Joe Sousa at radiomuseum.org.
(Some of the code got mangled by the html editor. The include files in angle brackes got interpreted as HTML sigh :( - I will try to sort it out - BR)
To thank the Author because you find the post helpful or well done.
Russian Rod Pentode
The 1j29b (or 1j29b-v or 1j29b-r) is about €2 each or less including postage from Russia or Ukraine on eBay. NOS fro 1985 to 1991
With 1/2 filament it's about 350mW and all filament it's nearly 1W RF. Works to over 150MHz at 60V + HT
1.2V parallel or 2.4V series filament.
It can be run off a HT between 36V and 120V. If you connect g2 and anode it will work at 18V to 24V. I have used the Rod Pentodes at 9V to 22V HT. But you will find lower gain, lower frequency limit and more sensitivity to parts. As I minimum I'd recommend 3 x PP3 batteries rather than one.
45V to 90V is good range.
Rod pentodes are Wire ended and best mounted flat.
To thank the Author because you find the post helpful or well done.
Errata
Dear Colleagues,
A number of gremlins got into my article (well, I admit, I put them there) and I wasn't able to make corrections. (The HTML editor tried to compose the C++ code.)
You can download the correct code here.
The original code (not presented here) used dynamic memory allocation. The present code presented here does not.
Instead of "I could be used as a morse..." read "it could be used as a morse"
apologies for this.
Bryce
To thank the Author because you find the post helpful or well done.