Create a gist now

Instantly share code, notes, and snippets.

What would you like to do?
10 Milliwatt Si5351A WSPR Beacon
import serial
import sys
import time
# Arduino serial dev paramaters
DEVICE = '/dev/ttyUSB0' # Change this as necessary
BAUD = 9600
# Open serial port
try:
ser = serial.Serial(port=DEVICE, baudrate=BAUD, timeout=1, writeTimeout=1)
except:
print('Cannot open serial port')
sys.exit(0)
#Wait for ASCII bell, then send the Unix time string
while True:
ser_in = ser.read()
if('\a' in ser_in):
ser.write('T' + str(int(time.time())))
// Si5351_WSPR
//
// Simple WSPR beacon for Arduino Uno, with the Etherkit Si5351A Breakout
// Board, by Jason Milldrum NT7S.
//
// Original code based on Feld Hell beacon for Arduino by Mark
// Vandewettering K6HX, adapted for the Si5351A by Robert
// Liesenfeld AK6L <ak6l@ak6l.org>. Timer setup
// code by Thomas Knutsen LA3PNA.
//
// Time code adapted from the TimeSerial.ino example from the Time library.
// Hardware Requirements
// ---------------------
// This firmware must be run on an Arduino Zero capable microcontroller
//
// Required Libraries
// ------------------
// Etherkit Si5351 (Library Manager)
// Etherkit JTEncode (Library Manager)
// Time (Library Manager)
// Wire (Arduino Standard Library)
//
// License
// -------
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject
// to the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
// ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
// CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
#include <si5351.h>
#include <JTEncode.h>
#include <int.h>
#include <TimeLib.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 12
#define SYNC_LED_PIN 13
// Global variables
Si5351 si5351;
JTEncode jtencode;
unsigned long freq = 10140200UL; // Change this
char call[7] = "N0CALL"; // Change this
char loc[5] = "AB12"; // 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;
}
// Loop through the string, transmitting one character at a time.
void encode()
{
uint8_t i;
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++)
{
si5351.set_freq((freq * 100) + (tx_buffer[i] * TONE_SPACING), 0, SI5351_CLK0);
proceed = false;
while(!proceed);
}
// Turn off the output
si5351.set_clock_pwr(SI5351_CLK0, 0);
digitalWrite(TX_LED_PIN, LOW);
}
void processSyncMessage()
{
unsigned long pctime;
const unsigned long DEFAULT_TIME = 1357041600; // Jan 1 2013
if(Serial.find(TIME_HEADER))
{
pctime = Serial.parseInt();
if( pctime >= DEFAULT_TIME)
{ // check the integer is a valid time (greater than Jan 1 2013)
setTime(pctime); // Sync Arduino clock to the time received on the serial port
}
}
}
time_t requestSync()
{
Serial.write(TIME_REQUEST);
return 0; // the time will be sent later in response to serial mesg
}
void setup()
{
// Use the Arduino's on-board LED as a keying indicator.
pinMode(TX_LED_PIN, OUTPUT);
pinMode(SYNC_LED_PIN, OUTPUT);
digitalWrite(TX_LED_PIN, LOW);
digitalWrite(SYNC_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);
// Set CLK0 output
si5351.set_correction(CORRECTION);
si5351.set_freq(freq * 100, 0, 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.
}
void loop()
{
if(Serial.available())
{
processSyncMessage();
}
if(timeStatus() == timeSet)
{
digitalWrite(SYNC_LED_PIN, HIGH); // LED on if synced
}
else
{
digitalWrite(SYNC_LED_PIN, LOW); // LED off if needs refresh
}
// Trigger every 10th minute
// WSPR should start on the 1st second of the minute, but there's a slight delay
// in this code because it is limited to 1 second resolution.
if(timeStatus() == timeSet && minute() % 10 == 0 && second() == 0)
{
encode();
delay(1000);
}
//delay(100);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment