Thursday 5 January 2017

BASIC Tech Group - MyNews 16 - WSPR code for 30m

Here's the code for 30m WSPR transmissions, it has my new call sign embedded. Reminder: the code runs on my Arduino shield using a Si5351 modules and a DS3231 RTC module for timing. The time has to be set to the nearest 1sec using special sketch.

IMG 5260

Code

// WSPR_30M-Basic
// Uses WsprMessage library of John Newcombe
// reads time from RTC, must be set accurately by spearate program
// Si5351 I2C bus
// SDA = A4
// SCL = A5
// LCD I2C bus (16x2)
// SDA = A4
// SCL = A5
// rotary encoder pins
// DT = 2
// CLK = 3
// SW = 4

#include "WsprMessage.h"
#include "si5351.h"
#include "LiquidCrystal_I2C.h"

// LCD 0x27 or 0x3F
#define LCDADDR 0x27
//#define LCDADDR 0x3F
#define LCDCOLS 16
#define LCDROWS 2

// RTC address
#define RTCADDR 0x68

// RTC time
byte sec, mns, hrs;

// repeat TX interval (sec)
uint8_t repeat;

// WSPR data
char callsign[] = " M0IFA";            // your callsign
char loc_short[] = "IO92";            // your short locator
int power = 20;                       // transmit power in dBm
char msgtxt[] = {" M0IFA IO92 20  "}; // msg displayed on lcd

// WSPR Frequency
uint64_t WSPRFreq = 1014020000;       // WSPR frequency (cHz)

// WSPR Delta Freq for symbols 0, 1, 2, 3. = 145.48 x 0, 1, 2 and 3 to nearest cHz
uint64_t delta[] = {0, 145, 291, 436};

// Data from WsprMessage
unsigned char *sym;                   // ptr to symbol vector from WsprMessage

// blank MSG_ZISE (= 162) char array for WSPR message, will be populated (0, 1, 2 or 3) in setup() by WsprMessage
char WSPR_Message[] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};

// create SI5351 object
Si5351 dds;

// create LCD object
LiquidCrystal_I2C lcd(LCDADDR, LCDCOLS, LCDROWS);

//=======SETUP========
void setup() {
  // init I2C
  Wire.begin();

  // init lcd
  lcd.begin();
  lcd.backlight();
  
  // init dds
  dds.init(SI5351_CRYSTAL_LOAD_8PF, 0, 0);

  // set 8mA output drive (max possible)
  dds.drive_strength(SI5351_CLK0, SI5351_DRIVE_8MA);
  
  // init transmit frequency, set freq 1st as turns outputs on
  dds.set_freq(WSPRFreq, SI5351_CLK0);
  
  // disable outputs (CLK0 enabled on TX)
  dds.output_enable(SI5351_CLK0, 0);
  dds.output_enable(SI5351_CLK1, 0);
  dds.output_enable(SI5351_CLK2, 0);

  // build WSPR message, get size and pointer to symbols
  WsprMessage WsprMessage(callsign, loc_short, power);
  sym = WsprMessage.symbols;            // get address ptr to symbols
  for(int i = 0; i < 162; i++) {        // copy sym array to WSPR_Message array
    WSPR_Message[i] = sym[i];           // both are char arrays, same as bytes
  }

  repeat = 2;                           // defalt repeat WSPR TX every 2 minutes

  // initial display
  dispMsg(0, 0, "WSPR            ");
  dispFreq(5, 0, WSPRFreq, 2); // display freq b
  dispNum(0, 1, repeat);
  dispMsg(1, 1, " min   ");
}

// ========MAIN LOOP=========
void loop() {
  // get time & display
  getRTC();
  dispTime(8, 1);

  // send WSPR?
  if (mns % repeat == 0 && sec == 0)
  {
    // display message
    dispMsg(0, 0, msgtxt);
    dispMsg(8, 1, "     TX ");              // show transmit
 
    sendWspr(682);                          // transmit on freq. 682ms per suymbol 162 * 682 = 110.48 sec total frame

    // restore display
    dispMsg(0, 0, "WSPR           ");
    dispFreq(5, 0, WSPRFreq, 2); // display freq
    dispMsg(1, 1, " min           ");
    dispNum(0, 1, repeat);
  }
}

//========WSPR send routine
void sendWspr(int SymbolLength) {
  // Send WSPR Message
  dds.output_enable(SI5351_CLK0, 1);                                      // enable CLK0 output
  for  (int s = 0; s < MSG_SIZE; s++) {                                   // For all symbols, MSG_SIZE in WsprMessage.h
    dds.set_freq(WSPRFreq + delta[WSPR_Message[s]], SI5351_CLK0);         // transmit frequency
    delay(SymbolLength);                                                  // symbol timing
  }
  dds.output_enable(SI5351_CLK0, 0);                                      // disable CLK0 output
}

// get time from RTC, convert bcd to decimal
void getRTC()
{
  // Reset the register pointer
  Wire.beginTransmission(RTCADDR);
  byte zero = 0x00;
  Wire.write(zero);
  Wire.endTransmission();

  // request 1st 3 bytes from the RTC address
  Wire.requestFrom(RTCADDR, 3);

  // get the s/m/h time data
  sec = bcdToDec(Wire.read());
  mns = bcdToDec(Wire.read());
  hrs = bcdToDec(Wire.read() & 0b111111); // mask 24 hour time bit
}

// Convert binary coded decimal to normal decimal numbers
byte bcdToDec(byte val)
{
  return ( (val / 16 * 10) + (val % 16) );
}

//=======Display routines
// display freq in kHz,col c, row r, d decimal places
void dispFreq(uint8_t c, uint8_t r, uint64_t f, uint8_t d)
{
  lcd.setCursor(c, r); // clear last freq display
  lcd.print((float)f / 100000, d); // convert to float for print function
  lcd.print("kHz  "); // + trailing spaces to clear previous display
}

// display char msg at col c, row r
void dispMsg(uint8_t c, uint8_t r, char *m)
{
  lcd.setCursor(c, r);
  lcd.print(m);
}

// display a number at col c, row r
void dispNum(uint8_t c, uint8_t r, uint16_t n)
{
  lcd.setCursor(c, r);
  lcd.print(n);
}

// display time at col, row
void dispTime(byte c, byte r) {
  lcd.setCursor(c, r);
  if (hrs < 10)
    lcd.print("0");
  lcd.print(hrs);
  lcd.print(":");
  if (mns < 10)
    lcd.print("0");
  lcd.print(mns);
  lcd.print(":");
  if (sec < 10)
    lcd.print("0");
  lcd.print(sec);
}

No comments: