Skip to content

Instantly share code, notes, and snippets.

@NT7S
Last active February 25, 2023 06:46
  • Star 8 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
Star You must be signed in to star a gist
Save NT7S/2b5555aa28622c1b3fcbc4d7c74ad926 to your computer and use it in GitHub Desktop.
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 AVR 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), 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, 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.
}
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);
}
@sv1onw
Copy link

sv1onw commented Mar 12, 2018

Hi Jason,
When I try to compile with Arduino IDE 1.8.2 I get issues with the libraries.
I have installed EtherkitJTEncode ver 1.1.2
EtherkitSi5351 ver 2.1.0
Time ver 1.4
Is that ok or do you recommend other versions.
Thanks in advance,
73 de Konstantinos, SV1ONW

@sv1onw
Copy link

sv1onw commented Mar 12, 2018

Hi Jason,
I downloaded new versions of JTEncode 1.1.4 and Si5351 2.1.1 . The issues remained.
I then changed to an older Si5351 that I had, ver 2.0.2 and most of the issues were gone. There must have been due to correction method change from one to two parameters I think.
The only remained issues are with JTEncode which they remain even with version 1.1.2.
I attach the error log file and wait for your kind feedback.

Arduino: 1.8.2 (Linux), Board: "Arduino/Genuino Uno"

JTEncode.cpp:58: error: prototype for 'void JTEncode::jt65_encode(char*, uint8_t*)' does not match any in class 'JTEncode'
void JTEncode::jt65_encode(char * message, uint8_t * symbols)
^
In file included from sketch/JTEncode.cpp:24:0:
JTEncode.h:210: error: candidate is: void JTEncode::jt65_encode(const char*, uint8_t*)
void jt65_encode(const char , uint8_t );
^
JTEncode.cpp:98: error: prototype for 'void JTEncode::jt9_encode(char
, uint8_t
)' does not match any in class 'JTEncode'
void JTEncode::jt9_encode(char * message, uint8_t * symbols)
^
In file included from sketch/JTEncode.cpp:24:0:
JTEncode.h:211: error: candidate is: void JTEncode::jt9_encode(const char*, uint8_t*)
void jt9_encode(const char , uint8_t );
^
JTEncode.cpp:143: error: prototype for 'void JTEncode::jt4_encode(char
, uint8_t
)' does not match any in class 'JTEncode'
void JTEncode::jt4_encode(char * message, uint8_t * symbols)
^
In file included from sketch/JTEncode.cpp:24:0:
JTEncode.h:212: error: candidate is: void JTEncode::jt4_encode(const char*, uint8_t*)
void jt4_encode(const char , uint8_t );
^
JTEncode.cpp:182: error: prototype for 'void JTEncode::wspr_encode(char
, char
, uint8_t, uint8_t*)' does not match any in class 'JTEncode'
void JTEncode::wspr_encode(char * call, char * loc, uint8_t dbm, uint8_t * symbols)
^
In file included from sketch/JTEncode.cpp:24:0:
JTEncode.h:213: error: candidate is: void JTEncode::wspr_encode(const char*, const char*, uint8_t, uint8_t*)
void wspr_encode(const char , const char , const uint8_t, uint8_t );
^
JTEncode.cpp:219: error: prototype for 'void JTEncode::fsq_encode(char
, char
, uint8_t
)' does not match any in class 'JTEncode'
void JTEncode::fsq_encode(char * from_call, char * message, uint8_t * symbols)
^
In file included from sketch/JTEncode.cpp:24:0:
JTEncode.h:214: error: candidate is: void JTEncode::fsq_encode(const char*, const char*, uint8_t*)
void fsq_encode(const char , const char , uint8_t );
^
JTEncode.cpp:306: error: prototype for 'void JTEncode::fsq_dir_encode(char
, char
, char, char
, uint8_t*)' does not match any in class 'JTEncode'
void JTEncode::fsq_dir_encode(char * from_call, char * to_call, char cmd, char * message, uint8_t * symbols)
^
In file included from sketch/JTEncode.cpp:24:0:
JTEncode.h:215: error: candidate is: void JTEncode::fsq_dir_encode(const char*, const char*, char, const char*, uint8_t*)
void fsq_dir_encode(const char , const char , const char, const char , uint8_t );
^
sketch/JTEncode.cpp: In member function 'void JTEncode::rs_encode(uint8_t
, uint8_t
)':
JTEncode.cpp:974: error: no matching function for call to 'JTEncode::encode_rs_int(void
&, unsigned int [12], unsigned int [51])'
encode_rs_int(rs_inst, dat1, b);
^
sketch/JTEncode.cpp:974:33: note: candidate is:
In file included from sketch/JTEncode.cpp:24:0:
/home/kostas/Arduino/libraries/Etherkit_JTEncode/src/JTEncode.h:236:8: note: void JTEncode::encode_rs_int(void
, data_t*, data_t*)
void encode_rs_int(void ,data_t , data_t );
^
/home/kostas/Arduino/libraries/Etherkit_JTEncode/src/JTEncode.h:236:8: note: no known conversion for argument 2 from 'unsigned int [12]' to 'data_t
{aka unsigned char
}'
exit status 1
prototype for 'void JTEncode::jt65_encode(char
, uint8_t*)' does not match any in class 'JTEncode'

73 de SV1ONW, Konstantinos

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment