Sunday, 17 May 2015

WSPR Beacon sketch

The WSPR Beacon uses the Universal_VFO and QRP PA or QRPP PA described before. The code will transmit a WSPR signal of 162 symbols on 4 FSK frequencies. Push the button to transmit! During transmission frequency shows the symbol number being sent (0-161).

WSPR centre frequencies are 7040 000 00, 10140 100 00 & 14090 000 00 cHZ. Turn the Rotary Encoder to chose one of five presets are given per band at 50Hz intervals.

Photo 05 17 2015 21 38 37

Photo 05 17 2015 21 38 52

// Universal_WSPR transmits WSPR
// use WSPR_Symbol_Generator sketch to make symbol tone table
// here it is hard coded for _M6KWH IO92 20 (100mW)
// then edit code to insert symbols and msg displays, line 73, 136 & 137
// step in 50Hz steps, giving 5 frequencies per band
// output on CLK0 VFO

// ----- CONNECTIONS
// DDS I2C SI5351
// SCL = A5
// SDA = A4
// I2C address 0x60
// ------
// display I2C LCD 16 * 2
// o A5 SCL (y)
// o A4 SDA (or)
// o +5     (r)
// o GND    (bwn)
// I2C address 0x27
// -----
// encoder KY-040
// o D2 DT  (y)
// o D3 CLK (g)
// o D4 SW  (or)
// o +5     (r)
// o GND    (bwn)
// -----

#include "Wire.h"
#include "si5351.h"
#include "LiquidCrystal_I2C.h"
#include "Rotary.h"
#include "TimerOne.h"

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

// rotary Encoder pins 2 & 3 (DT & CLK), button 4 (SW)
#define DT 2
#define CLK 3
#define SW 4

// transmit pin
#define TX 12

// dds object
Si5351 dds;

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

Rotary rot = Rotary(DT, CLK);

uint32_t freq;
uint8_t tone_ptr = 0; // Pointer to the current symbol
volatile uint8_t next_tone = 0; // Incremented by the ISR
uint8_t next_tone2 = 0; // Local store, to compare against.
uint8_t step_tone = 0; // Flag used to signify we need to move to the next symbol
uint8_t b; // band 0 - 26

// band frequencies (cHz)
uint32_t band[15] = 
{704000000, 704005000, 704010000, 704015000, 704020000,
1014010000, 1014015000, 1014020000, 1014025000, 1014030000,
1409700000, 1409705000, 1409710000, 1409715000, 1409720000
}; // 40, 30, 20m

// msg _M6KWH IO92 20 - use WSSPR Symbol Generator program to create
uint8_t msg[162] =
{ 3, 1, 2, 0, 0, 0, 2, 2, 1, 2, 0, 2, 3, 1, 3, 0, 2, 2, 1, 2, 0, 3, 0, 1, 3, 1, 1, 0, 2, 0, 0, 2,
  0, 0, 1, 0, 0, 1, 0, 1, 2, 0, 0, 0, 0, 2, 1, 2, 1, 3, 0, 0, 1, 3, 0, 1, 2, 0, 0, 1, 3, 2, 1, 0,
  2, 2, 0, 3, 3, 2, 3, 2, 3, 0, 3, 0, 3, 2, 0, 3, 2, 0, 1, 2, 1, 3, 0, 0, 0, 3, 1, 0, 1, 0, 1, 0,
  2, 2, 3, 0, 0, 0, 0, 0, 1, 0, 2, 1, 0, 2, 1, 3, 1, 2, 1, 1, 0, 0, 3, 3, 2, 1, 2, 0, 0, 1, 1, 1,
  2, 2, 2, 0, 0, 3, 0, 3, 2, 0, 3, 1, 2, 2, 2, 0, 2, 0, 2, 3, 1, 0, 1, 0, 1, 1, 2, 0, 0, 3, 1, 0,
  2, 0
};

// tone out at FREQ
uint32_t tones[4] = {0, 146, 293, 440};

void setup()
{
  // init LCD & backlight on
  lcd.init();
  lcd.backlight();
  
  dds.init(SI5351_CRYSTAL_LOAD_8PF, 0); // default 25MHz XTAL
  
   // all CLK disabled
  dds.output_enable(SI5351_CLK0, 0);
  dds.output_enable(SI5351_CLK1, 0);
  dds.output_enable(SI5351_CLK2, 0);
  
  // encoder, button, RX, TX, band and XMIT pins
  pinMode(DT, INPUT_PULLUP);
  pinMode(CLK, INPUT_PULLUP);
  pinMode(SW, INPUT_PULLUP);

  digitalWrite(TX, HIGH); // no TX

  b = 7; // band, middle 30m
  
  // init display
  dispMsg(0, 0, "WSPR            ");
  dispFreq(5, 0, band[b], 2); // display freq b
}

void loop()
{
  tune();
  tx(); // await button push
}

// tune
void tune()
{
  unsigned char dir; // tuning direction CW/CCW
  
  dir = rot.process(); // read encoder
  if(dir != DIR_NONE) // turned?
  {
    if(dir == DIR_CW && b > 0) b += 1; // increment band freq +/- 1
    if(dir == DIR_CCW && b < 15) b -= 1;
    
    dispFreq(5, 0, band[b], 2); // display freq
  } 
}

// transmit
void tx()
{
  if (digitalRead(SW) == LOW)
  {
    while (!digitalRead(SW)); // wait for release
    dispMsg(0, 0, " M6KWH IO92 20  ");
    dispMsg(0, 1, "Syb     100mW TX"); // show transmit
    
    txWspr(b); // transmit on freq b
    
    // restor display
    dispMsg(0, 0, "WSPR            ");
    dispFreq(5, 0, band[b], 2); // display freq
    dispMsg(0, 1, "                "); // clear row 1
  }
}

// transmit msg at freq fTable[b]
void txWspr(uint8_t b)
{
  // Start Transmitter
  digitalWrite(TX, LOW);

  // Start the timer interrupt, is called every 8192/12000 seconds.
  Timer1.initialize(682666UL);
  Timer1.attachInterrupt(wspr_isr);

  // Transmit!
  while (1)
  {

    // do the increment checking without the interrupts, in case it gets
    // modified while we are checking.
    noInterrupts();
    if (next_tone > next_tone2)
    {
      step_tone = 1;  // next_tone has incremented, raise flag
      next_tone2 = next_tone;
    }
    interrupts();

    if (step_tone)
    {
      // Got a call from the ISR to increment the tone
      step_tone = 0;

      // We enable the clock here to avoid having it be active before the first tone
      // is due to be transmitted.
      dds.output_enable(SI5351_CLK0, 1);

      // output freq
      freq = band[b] + tones[msg[tone_ptr]];
      freqOut(freq);
      dispNum(4, 1, tone_ptr); // display symbol number
      tone_ptr++; // next tone

      // If we're at the end of the symbol array, disable the timer and break.
      if (tone_ptr == 162)
      {
        Timer1.detachInterrupt();
        next_tone = 0;
        next_tone2 = 0;
        tone_ptr = 0;
        break;
      }
    }
  }

  // Disable clock, re-write display
  dds.output_enable(SI5351_CLK0, 0);
  digitalWrite(TX, HIGH);
}

// Timer ISR.
void wspr_isr(void)
{
  next_tone += 1;
}

// frequency (in cHz) for VFO, on CLK0
void freqOut(uint32_t f)
{
    dds.set_freq(f, 0ULL, SI5351_CLK0); // f cHz
}

// 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 freq in kHz,col c, row r, d decimal places
void dispFreq(uint8_t c, uint8_t r, uint32_t f, uint8_t d)
{
  lcd.setCursor(c, r); // clear last freq display
  lcd.print("           ");
  
  lcd.setCursor(c, r); // clear last freq display
  lcd.print((float)f / 100000, d); // convert to float for print function
  
  lcd.print("kHz");
}

No comments: