Arduino QRP Beacon
The below is a circuit diagram of the "Arduino Uno" QRP beacon. The first Arduino Uno program is for a RTTY beacon, while the second listed program is for sending “Morse Code” via the AD9850 DDS RF Board unit. While the RTTY program uses a data array for the beacon message, the morse code program uses the Arduino IDE monitor application of the Arduino IDE for linking up to the Arduino Uno. The monitor app is used to type a message for transmission via the morse code Arduino program. Please however note, that each program transmitter program is transmitting an RF carrier, the RTTY beacon example is on 40metre band, while the “CW” program is on the 20metre band. By altering the carrier frequency within the program, the transmission frequency can be changed, but the program will need to be re-compiled and uploaded into the Arduino Uno micro-controller. The “CW” program in order to make the “CW” process, the DDS board is powered down and powered up to produce the keyed carrier signal, the process part of the DDS 40bit serial code. Only the carrier frequency value in "Hz" is required for the Arduino Uno program.
To use the Arduino Uno program code, just "copy and paste" from this article into the Arduino IDE editor. Please if you are going to use the RF transmission, be sure that your Amateur Radio or Ham Radio licence is valid.
This Arduino program below is the RTTY Qrp beacon application. The AD9850 is serial programmed.
/* RTTY carrier modem generator by serial programming the AD9850
by Alastair GW0AJU
*/
int W_CLK=10; // Pin 10 - connect to AD9850 module word load clock pin (CLK)
int FQ_UD=11; // Pin 11 - connect to freq update pin (FQ)
int DATA=12; // Pin 12 - connect to serial data load pin (DATA)
int RESET=13; // Pin 13 - connect to reset pin (RST).
int RTTY_logic=9; // Pin 9 - TTL output of RTTY data signal logic
// mode number one, the "1 * start + 5 bit data + 2 * stop" = 8bit single byte
// 2 bits are used for the stop instead of the 1.5bits, the extra addition to 2bit for a stop bit will not matter,
// it will only extenduate the data sequence, but still be synchronised by the rtty decoder.
// bit zero is the start bit, next five are the RTTY data, followed by bits 6 and 7 for stop bit.
byte info[] = {0xD4,0xEA}; // letter shift followed by RY
unsigned long freq;
void pulseRESET()
{
digitalWrite(RESET, LOW);
delayMicroseconds(50);
digitalWrite(RESET, HIGH);
delayMicroseconds(50);
digitalWrite(RESET, LOW);
}
void pulseW_CLK()
{
digitalWrite(W_CLK, LOW);
delayMicroseconds(50);
digitalWrite(W_CLK, HIGH);
delayMicroseconds(50);
digitalWrite(W_CLK, LOW);
}
void pulseFQ_UD()
{
digitalWrite(FQ_UD, LOW);
delayMicroseconds(50);
digitalWrite(FQ_UD, HIGH);
delayMicroseconds(50);
digitalWrite(FQ_UD, LOW);
}
// frequency calc from AD9850 datsheet
// program data = (sys clock) * (frequency tuning word)/2^32
void sendFrequency(float frequency)
{
{
freq = frequency * 4294967295/125000000; // note 125 MHz clock on AD9850
//Serial.println(freq,HEX);
//Serial.println(freq,DEC);
for (int b=0; b<4; b++, freq>>=8)
{
tfr_byte(freq & 0xFF);
}
phase();
}
}
// transfer the phase info plus trigger AD9850 internal loading
void phase()
{
tfr_byte(0x00); // Final control byte, $00 for AD9850 chip
pulseFQ_UD();// Done! Should see output
}
// transfers a byte, a bit at a time, LSB first to the
// AD9850 via serial DATA line
void tfr_byte(byte data)
{
//Serial.println(data,HEX);
for (int i=0; i<8; i++, data>>=1)
{
digitalWrite(DATA, data & 0x01);
pulseW_CLK(); //after each bit sent, CLK is pulsed high
}
}
void collect_tx_message() // collect rtty data stream message
{
byte d;
digitalWrite(RTTY_logic, HIGH); // TTL RTTY output at pin 12
sendFrequency(14.061445e6); // mark freq = (space freq + 170Hz)
for (d=0 ; d<2 ; d++ ) // d = the number RTTY message + control characters in bytes
{
tx_data_message(info[d]);
//delay(30);
}
digitalWrite(RTTY_logic, HIGH); // TTL RTTY output at pin 12
sendFrequency(14.061445e6); // mark freq = (space freq + 170Hz)
}
void tx_data_message(byte mess) // decode data stream message and transmit rtty signal
{
byte p;
int sense;
for (p=0; p<8 ; p++, mess>>=1) // rtty data sent in pattern due to data sequence
{
if ((mess & 0x01) == 0x01) // detect for mark tone
{
sense = 1;
// Serial.print("1");
digitalWrite(RTTY_logic, HIGH); // TTL RTTY output at pin 12
sendFrequency(14.061445e6); // mark freq = (space freq + 170Hz)
delay(20); // delay period of data bit transmission
}
else if ((mess & 0x01) == 0x00) // detect for space tone
{
sense =0;
// Serial.print("0");
digitalWrite(RTTY_logic, LOW); // TTL RTTY output at pin 12
sendFrequency(14.061275e6); // space freq
delay(20); // delay period of data bit transmission
}
}
// delay period before retransmission of RTTY message
// TX tone is mark tone as last used for stop bit of 1.5bits
}
void setup()
{
// Serial.begin(9600);
// configure arduino data pins for output
pinMode(FQ_UD, OUTPUT);
pinMode(W_CLK, OUTPUT);
pinMode(DATA, OUTPUT);
pinMode(RESET, OUTPUT);
pinMode(RTTY_logic, OUTPUT);
Serial.begin(9600);
pulseRESET();
pulseW_CLK();
pulseFQ_UD(); // this pulse enables serial mode
sendFrequency(14.061445e6); // mark freq = (space freq + 170Hz)
}
void loop()
{
collect_tx_message();
//delay(500);
// Serial.println();
}
This Arduino program below is the "CW" morse code Qrp beacon application. The AD9850 is serial programmed.
/* This is a morse code RF transmission program. The Morse code logic is from the
"30 Arduino projects for the evil genius", while the RF modem part came from an
article found on the internet, but much was done extra to the program by myself
to achieve usefull success, otherwise the RF modem transmission program is from the RF RTTY modem.
The two programs have been linked together to produce a QRP RF modem Morse code transmitter.
The AD9850 has no extra amplification for the TX power, just the appropriate band plan RF filtering.
Application design by Alastair GW0AJU */
int ledPin = 13; // morse indicator LED visual signal
int W_CLK=8; // Pin 8 - connect to AD9850 module word load clock pin (CLK)
int FQ_UD=9; // Pin 9 - connect to freq update pin (FQ)
int DATA=10; // Pin 10 - connect to serial data load pin (DATA)
int RESET=11; // Pin 11 - connect to reset pin (RST).
int RTTY_logic=12; // Pin 12 - TTL output of RTTY data signal logic
char* letters[] = {".-","-...","-.-.","-..",".","..-.","--.","....","..",".---"
,"-.-",".-..","--","-.","---",".--.","--.-",".-.","...","-"
,"..-","...-",".--","-..-","-.--","--.."};
char* numbers[] = {"------",".----","..---","...--","....-",".....","-....",
"--...","---..","----."};
// setting up variables for program use
int dotDelay = 50; // I think is around 12 to 15 words per minute
byte i;
char ch;
unsigned long freq;
byte angle;
// sub-routines for DDS control use of logic pins
void pulseRESET()
{
digitalWrite(11, LOW);
delayMicroseconds(50);
digitalWrite(11, HIGH);
delayMicroseconds(50);
digitalWrite(11, LOW);
}
void pulseW_CLK()
{
digitalWrite(8, LOW);
delayMicroseconds(50);
digitalWrite(8, HIGH);
delayMicroseconds(50);
digitalWrite(8, LOW);
}
void pulseFQ_UD()
{
digitalWrite(9, LOW);
delayMicroseconds(50);
digitalWrite(9, HIGH);
delayMicroseconds(50);
digitalWrite(9, LOW);
}
// setting up the required variables for the AD9850 DDS device
void setup()
{
pinMode(ledPin, OUTPUT);
Serial.begin(9600);
// configure arduino data pins for output
pinMode(FQ_UD, OUTPUT);
pinMode(W_CLK, OUTPUT);
pinMode(DATA, OUTPUT);
pinMode(RESET, OUTPUT);
pinMode(RTTY_logic, OUTPUT);
pulseRESET();
pulseW_CLK();
pulseFQ_UD(); // this pulse enables serial mode
}
// decoding the message characters typed in on the arduino IDE terminal monitor
void flashSequence(char* sequence)
{
int i = 0;
while (sequence[i] != NULL )
{
flashDotOrDash(sequence[i]);
i++;
}
delay(dotDelay * 3 ); // gap between letters
}
// sending out for transmission the appropriate morse characters
void flashDotOrDash(char dotOrDash)
{
digitalWrite(ledPin, HIGH);
if (dotOrDash == '.')
{
sendFrequency(14.0506e6);
phase(0x00); //power up dds device
delay(dotDelay);
sendFrequency(14.0506e6);
phase(0x04); //power down dds device
}
else // must be a -
{
sendFrequency(14.0506e6);
phase(0x00); //power up dds device
delay(dotDelay * 3 );
sendFrequency(14.0506e6);
phase(0x04); //power down dds device
}
digitalWrite(ledPin, LOW);
delay(dotDelay); // gap between flashes
}
// frequency control of the AD9850 DDS device for transmitter
void sendFrequency(float frequency)
{
{
freq = frequency * 4294967295/125000000; // note 125 MHz clock on AD9850
// if using a system clock of 100MHz by using the AMQRP DDS60 daughter board, then use program below
//freq = frequency * 4294967295/100000000; // note 100 MHz clock on AD9850 AMQRP DDS60 daughter board
for (int b=0; b<4; b++, freq>>=8)
{
tfr_byte(freq & 0xFF); // to serial program sub-routine
}
}
}
// transfer the phase shift info plus trigger AD9850 internal loading
// the DDS device maybe "powered up" with phase(0x00), or otherwise "powered down" with phase(0x04)
void phase(byte angle)
{
tfr_byte(angle); // Final control byte for AD9850 phase shift keying
pulseFQ_UD();// Done! Should see output
}
//serial program sub-routine of DDS AD9850 device
//transfers a byte, a bit at a time, LSB first to the AD9850
//via serial DATA line "D7" to program in the carrier frequency
// note D0,D1 held +5V, while D2,D3,D4,D5,D6 held low GND zero Volts
void tfr_byte(byte data)
{
for (int i=0; i<8; i++, data>>=1)
{
digitalWrite(DATA, data & 0x01);
pulseW_CLK(); //after each bit sent, CLK is pulsed high
}
}
// Transmission porgram for morse code transmission via the terminal program
// used on the Arduino IDE, by typing in the TX message then
// clicking send button on IDE terminal monitor
void loop()
{
char ch;
int em;
if (Serial.available())
// is there anything to be read from USB
{
ch = Serial.read(); // read a simple letter from terminal monitor of typed in TX message
Serial.print(ch); // back to IDE terminal monitor of TX message progress
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
}
}
}