Skip to content

Instantly share code, notes, and snippets.

@NT7S
Last active July 5, 2021 19:10

Revisions

  1. NT7S revised this gist Jul 5, 2021. 1 changed file with 1 addition and 3 deletions.
    4 changes: 1 addition & 3 deletions Si5351_WSPR_2560.ino
    Original file line number Diff line number Diff line change
    @@ -1,4 +1,3 @@
    ```cpp
    // Si5351_WSPR
    //
    // Simple WSPR beacon for Arduino Uno, with the Etherkit Si5351A Breakout
    @@ -194,5 +193,4 @@ void PrintBuffer(uint8_t * buf)
    Serial.print(buf[i]);
    }
    Serial.println();
    }
    ```
    }
  2. NT7S revised this gist Jul 5, 2021. 1 changed file with 16 additions and 16 deletions.
    32 changes: 16 additions & 16 deletions Si5351_WSPR_2560.ino
    Original file line number Diff line number Diff line change
    @@ -1,7 +1,8 @@
    ```cpp
    // Si5351_WSPR
    //
    // Simple WSPR beacon for Arduino Uno, with the Etherkit Si5351A Breakout
    // Board, by Jason Milldrum NT7S.
    // Board by Jason Milldrum NT7S.
    //
    // Original code based on Feld Hell beacon for Arduino by Mark
    // Vandewettering K6HX, adapted for the Si5351A by Robert
    @@ -12,7 +13,8 @@

    // Hardware Requirements
    // ---------------------
    // This firmware must be run on an Arduino AVR microcontroller
    // This firmware should be able to run on most Arduino microcontrollers, since it solely relies
    // on the millis() function for timing.
    //
    // Required Libraries
    // ------------------
    @@ -48,27 +50,24 @@
    #include <int.h>
    #include <TimeLib.h>

    #define TONE_SPACING 146 // ~1.46 Hz
    constexpr uint8_t TONE_SPACING = 146; // ~1.46 Hz
    constexpr uint16_t WSPR_INTERVAL = 683;
    #define SYMBOL_COUNT WSPR_SYMBOL_COUNT
    #define CORRECTION 0 // Change this for your ref osc
    constexpr uint32_t TIMER_BASE_CLOCK = 4000000;
    constexpr uint16_t TIMER_PRESCALER_DIV = 1;
    constexpr uint16_t TIMER_FREQUENCY = 1000;
    constexpr uint8_t SYMBOL_COUNT = WSPR_SYMBOL_COUNT;
    constexpr uint32_t 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
    constexpr char TIME_HEADER = 'T'; // Header tag for Serial time sync message
    constexpr char TIME_REQUEST = 7; // ASCII bell character requests a time sync message

    #define TX_LED_PIN 25
    #define SYNC_LED_PIN 13
    constexpr uint8_t TX_LED_PIN = 25;
    constexpr uint8_t SYNC_LED_PIN = 13;


    // Global variables
    Si5351 si5351;
    JTEncode jtencode;
    unsigned long freq = 10140200UL; // Change this
    char call[13] = "NT7S"; // Change this
    char loc[7] = "CN85NM"; // Change this
    uint64_t freq = 10140200UL; // Change this
    char call[13] = "NT7S"; // Change this
    char loc[7] = "CN85NM"; // Change this
    uint8_t dbm = 30;
    uint8_t tx_buffer_1[SYMBOL_COUNT];
    uint8_t tx_buffer_2[SYMBOL_COUNT];
    @@ -195,4 +194,5 @@ void PrintBuffer(uint8_t * buf)
    Serial.print(buf[i]);
    }
    Serial.println();
    }
    }
    ```
  3. NT7S revised this gist Jul 5, 2021. 1 changed file with 2 additions and 2 deletions.
    4 changes: 2 additions & 2 deletions Si5351_WSPR_2560.ino
    Original file line number Diff line number Diff line change
    @@ -174,12 +174,12 @@ void loop()
    // 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(timeSet && timeStatus() == timeSet && minute() % 2 == 0 && second() == 0)
    if(timeSet && timeStatus() == timeSet && minute() % 10 == 0 && second() == 0)
    if(timeSet && timeStatus() == timeSet && (minute() % 10 == 0 || minute() % 10 == 4) && second() == 0)
    {
    encode(tx_buffer_1);
    delay(1000);
    }
    if(timeSet && timeStatus() == timeSet && minute() % 10 == 2 && second() == 0)
    if(timeSet && timeStatus() == timeSet && (minute() % 10 == 2 || minute() % 10 == 6) && second() == 0)
    {
    encode(tx_buffer_2);
    delay(1000);
  4. NT7S revised this gist Jul 5, 2021. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion Si5351_WSPR_2560.ino
    Original file line number Diff line number Diff line change
    @@ -82,7 +82,7 @@ void encode(uint8_t * buf)
    {
    uint8_t i;

    jtencode.wspr_encode(call, loc, dbm, tx_buffer_1);
    //jtencode.wspr_encode(call, loc, dbm, tx_buffer_1);

    timer_expire = millis() + WSPR_INTERVAL;

  5. NT7S created this gist Jul 5, 2021.
    198 changes: 198 additions & 0 deletions Si5351_WSPR_2560.ino
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,198 @@
    // 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>

    #define TONE_SPACING 146 // ~1.46 Hz
    constexpr uint16_t WSPR_INTERVAL = 683;
    #define SYMBOL_COUNT WSPR_SYMBOL_COUNT
    #define CORRECTION 0 // Change this for your ref osc
    constexpr uint32_t TIMER_BASE_CLOCK = 4000000;
    constexpr uint16_t TIMER_PRESCALER_DIV = 1;
    constexpr uint16_t TIMER_FREQUENCY = 1000;

    #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 25
    #define SYNC_LED_PIN 13


    // Global variables
    Si5351 si5351;
    JTEncode jtencode;
    unsigned long freq = 10140200UL; // Change this
    char call[13] = "NT7S"; // Change this
    char loc[7] = "CN85NM"; // Change this
    uint8_t dbm = 30;
    uint8_t tx_buffer_1[SYMBOL_COUNT];
    uint8_t tx_buffer_2[SYMBOL_COUNT];
    uint32_t timer_expire = UINT32_MAX;

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

    // Loop through the string, transmitting one character at a time.
    void encode(uint8_t * buf)
    {
    uint8_t i;

    jtencode.wspr_encode(call, loc, dbm, tx_buffer_1);

    timer_expire = millis() + WSPR_INTERVAL;

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

    // Now do the rest of the message
    for(i = 0; i < SYMBOL_COUNT; i++)
    {
    si5351.set_freq((freq * 100) + (buf[i] * TONE_SPACING), SI5351_CLK0);
    while(millis() < timer_expire);
    timer_expire = millis() + WSPR_INTERVAL;
    }

    // Turn off the output
    si5351.set_clock_pwr(SI5351_CLK0, 0);
    digitalWrite(TX_LED_PIN, HIGH);
    }

    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, HIGH);
    digitalWrite(SYNC_LED_PIN, LOW);
    Serial.begin(115200);
    while(!Serial);

    // 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_0PF, 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

    jtencode.wspr_encode(call, loc, dbm, tx_buffer_1);
    jtencode.wspr_encode("NT7S/X", "CN84AB", 23, tx_buffer_2);
    // PrintBuffer(tx_buffer_1);
    // PrintBuffer(tx_buffer_2);
    }

    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(timeSet && timeStatus() == timeSet && minute() % 2 == 0 && second() == 0)
    if(timeSet && timeStatus() == timeSet && minute() % 10 == 0 && second() == 0)
    {
    encode(tx_buffer_1);
    delay(1000);
    }
    if(timeSet && timeStatus() == timeSet && minute() % 10 == 2 && second() == 0)
    {
    encode(tx_buffer_2);
    delay(1000);
    }

    //delay(100);
    }

    void PrintBuffer(uint8_t * buf)
    {
    for(uint8_t i = 0; i < SYMBOL_COUNT; ++i)
    {
    Serial.print(buf[i]);
    }
    Serial.println();
    }