Wednesday, December 19, 2018

Minimal WSPR transmit with Arduino and Si5351

Intrigued by the ZachTek pocket WSPR transmitter Peter, VK3YE, has reviewed, I wondered if I could get a simple beacon going using just an Arduino, an Si5351 clock generator and (you'll be pleased to hear) a low pass filter.

Here's the rig. The low pass filter is from the VK3YE Beach 40 design which I like because it uses off the shelf inductors.


The waveform isn't perfect but it's certainly a long way from the square wave the comes out of the clock generator.


I've taken this sketch, ripped out the lovely GPS time synchronisation, and turned it into a 1 shot beacon. You power on, or press reset, it transmits one message and then turns off RF. Of course you need to push the button at the beginning of a two minute cycle.

Here's how it decodes locally on my receiver with no antenna connected.


You can see that big signal drift earlier where I powered up with the antenna disconnected to let it get up to temperature. In the actual slot it reports a drift of -1 and decodes nicely (well... twice).

Hooked it up to the antenna just now and the little Arduino/sSi5351 transmitter is being heard quite widely.




The software uses some very handy modules:

Both very easy to use. Here's my hack of the code, I'm afraid Blogger doesn't like parts of it like angle brackets so no doubt there will be some things to fix up.


/*
 * Minimal WSPR beacon using Si5351Arduino library
 *
 * Based on code:
 * Copyright (C) 2015 - 2016 Jason Milldrum 
 * 
 *
 * 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 .
 */
 // https://gist.github.com/NT7S/2b5555aa28622c1b3fcbc4d7c74ad926

#include "si5351.h"
#include "Wire.h"
#include <JTEncode.h>
#include <int.h>

#include "Wire.h"

#define TONE_SPACING            146           // ~1.46 Hz
#define WSPR_CTC                10672         // CTC value for WSPR
#define SYMBOL_COUNT            WSPR_SYMBOL_COUNT
#define CORRECTION              0             // Change this for your ref osc

#define TIME_HEADER             "T"           // Header tag for serial time sync message
#define TIME_REQUEST            7             // ASCII bell character requests a time sync message 

#define TX_LED_PIN              13
//#define SYNC_LED_PIN            13

Si5351 si5351;

JTEncode jtencode;
unsigned long freq = 7040500UL;                // Change this
char call[7] = "VK2TPM";                        // Change this
char loc[5] = "QF56";                           // Change this
uint8_t dbm = 10;
uint8_t tx_buffer[SYMBOL_COUNT];

// Global variables used in ISRs
volatile bool proceed = false;

// Timer interrupt vector.  This toggles the variable we use to gate
// each column of output to ensure accurate timing.  Called whenever
// Timer1 hits the count set below in setup().
ISR(TIMER1_COMPA_vect)
{
    proceed = true;
    // Serial.println("timer fired");
}

// Loop through the string, transmitting one character at a time.
void encode()
{
    uint8_t i;
  Serial.println("encode()");
    jtencode.wspr_encode(call, loc, dbm, tx_buffer);

    // Reset the tone to 0 and turn on the output
    si5351.set_clock_pwr(SI5351_CLK0, 1);
    digitalWrite(TX_LED_PIN, HIGH);

    // Now do the rest of the message
    for(i = 0; i < SYMBOL_COUNT; i++)
    {
      uint64_t frequency = (freq * 100) + (tx_buffer[i] * TONE_SPACING);
        si5351.set_freq(frequency, SI5351_CLK0);
        Serial.print("freq = ");
        Serial.println(tx_buffer[i]);
        proceed = false;
        while(!proceed);
    }
    Serial.println("message done");
    // Turn off the output
    si5351.set_clock_pwr(SI5351_CLK0, 0);
    digitalWrite(TX_LED_PIN, LOW);
}

void setup()
{
   Serial.begin(9600);
   Serial.println("setup");
  // Use the Arduino's on-board LED as a keying indicator.
  pinMode(TX_LED_PIN, OUTPUT);

  digitalWrite(TX_LED_PIN, LOW);
  Serial.begin(9600);

  // Set time sync provider
  //setSyncProvider(requestSync);  //set function to call when sync required

  // Initialize the Si5351
  // Change the 2nd parameter in init if using a ref osc other
  // than 25 MHz
  si5351.init(SI5351_CRYSTAL_LOAD_8PF, 0, CORRECTION);

  // Set CLK0 output
  si5351.set_freq(freq * 100, SI5351_CLK0);
  si5351.drive_strength(SI5351_CLK0, SI5351_DRIVE_8MA); // Set for max power
  si5351.set_clock_pwr(SI5351_CLK0, 0); // Disable the clock initially

  // Set up Timer1 for interrupts every symbol period.
  noInterrupts();          // Turn off interrupts.
  TCCR1A = 0;              // Set entire TCCR1A register to 0; disconnects
                           //   interrupt output pins, sets normal waveform
                           //   mode.  We're just using Timer1 as a counter.
  TCNT1  = 0;              // Initialize counter value to 0.
  TCCR1B = (1 << CS12) |   // Set CS12 and CS10 bit to set prescale
    (1 << CS10) |          //   to /1024
    (1 << WGM12);          //   turn on CTC
                           //   which gives, 64 us ticks
  TIMSK1 = (1 << OCIE1A);  // Enable timer compare interrupt.
  OCR1A = WSPR_CTC;       // Set up interrupt trigger count;
  interrupts();            // Re-enable interrupts.

  encode(); // transmit once and stop
}


void loop()
{
   // blink LED when we've finished the transmit
  digitalWrite(TX_LED_PIN, HIGH);
  delay(500);
  digitalWrite(TX_LED_PIN, LOW);
  delay(500);
}

13 comments:

Unknown said...

Peter,

Great fun isn't it! I made up a GPS Clock a while back, which is an Uno and Ublox6M GPS receiver. the code just reads NMEA headers and extracts time, lat/long, nbr satellites etc and displays these. The other day I discovered the ZachTech guy had opensourced his code, and that Jason NT7s had the JTEncode lib. So I'm busy trying to turn my GPS clock into a WSPR beacon. So far the basic control loop is working, getting GPS time and it kicks in on the even minutes. But the JTEncode lib is quite large and I cannot get it to all fit into the Uno's memory. I'll spend some more time cutting away everything and anything I can, to get it to fit. I'm also using Jason's si5351 lib. Well done on your WSPR experiment, the small size of your code shows just how easy it is with libraries.

Paul VK3HN.

M0BMN said...

Hi
Thanks for this code, I have taken it, added a bit and made a couple of dual mode (WSPR/QRSS CW DFCW) transmitters. I Added a RTC that triggers a transmission every 10 mins , put a single FET amp on the output of the si5351 and can get about 500mW (more if pushed) into the antenna via a QRPLabs LPF, getting many spots on the wspr network and the DFCW is making it to grabbers.
makes a great little unit, Thanks for the nice simple code that has made my job much easier.
all the best
Paul M0BMN

T_Rex said...

Nice work. I love the simplicity of it. I'm building one as soon as I get an SI5351 module from Aliexpress. Thanks for the posting.

BrianC said...

I was able to compile and get the code to run on my breadboard system, I can see a signal on the wsjt-x waterfall that looks like a wspr signal and can start the transmission on time, but it is not getting decoded..Breadboard is arduino nano and si5153 breakout board from adafruit.
I do not have a low pass filter in the circuit. How critical is the LPF and am I too close to the radio which is on the same bench?

Brian K9WIS

Peter Marks said...

Hello BrianC,

I don't know why it isn't being decoded for you. Even without the low pass filter it should decode. Perhaps the signal is too strong for your receiver, try moving further away.
Does it take the right amount of time? Perhaps your Arduino is running at a different clock speed?
73

glene77is said...

Excelent ! K4KKQ

hamradioal said...

HI,
Many thanks for posting this code. It looks to be very useful indeed and compiled fine on a UNO, maybe overkill for this job but I had one handy. I'll bodge up a test system tomorrow to play with.
Best 73s from Scotland.
Al. GM1SXX

Dave said...

Hello Paul My name is David G4RVH and have built up the SPRAT Autumn 2022 WSPR system with the 2 x line LED and all working and displaying correctly without the Si5351 as its on order from Adfruit. I have downloaded
Jasons Calibration file if needed. I want to add a Transmit LED externally on my box.. Could you please add the necessary code into the already working file for me please because been going round in circles and I don't want to screw up the already working Sketch Code. Much appreciate your help if you can help me out. I do actually have 2 x other WSPR systems but the one I have used for the last 4 years constantly uses a TEENSY 3.2 board and are no longer available, some old ones can be bought on EBAY for £90 which is rediculous Hence me build up this one of yours to transmit on 40 metres or any band when required. I thank you for this design as its fired up right away in the same box as I had my first WSPR system in with a AD9850 DDS chip in but times have moved on so went for your design. Kind Regards David G4RVH

Peter Marks said...

Hello David, adding a transmit LED output is very simple. On the Arduino reference, https://docs.arduino.cc/language-reference/ call pinMode() in setup to make the pin you're connecting to OUTPUT and then when transmitting use digitalWrite() to either make the pin HIGH or LOW depending on how you wire the pin to your LED. Don't forget the current limiting resistor. 200ohm is a good start. - Peter (AKA Paul)

Dave said...

Hello Peter, so D5,6,7 are spare plus Analog pins are spare. So if i want to add a Transmit LED so that it flashes while the Sketch is transmitting how and where do I write the Code as personally have not a clue. Sorry but no excuse for my age but 78 years old and struggle with Code, I can massage someone elses a little and that is all. Hope you can help me. 73s David G4RVH

Peter Marks said...

Dave, I haven't looked at this code in many years but I see that it already flashes an LED connected to pin 13. Is that not working for you?

Dave said...

Thanks for you reply again Peter. No that LED is on the Nano board inside the box and D13 is connected to the LCD Display, hence require an external LED. Sorry to be a pain. 73s David G4RVH

Peter Marks said...

Great! So just change the line #define TX_LED_PIN 13 to use the D5,6, or 7 that you wire your external LED to.