Sunday 17 May 2015

Universal QRSS3 sketch

The Universal VFO shield can be used as a low power QRSS transmitter. I have published a low power QRPP PA for use with it for QRSS. But now here is the Arduino code for the transmitter. Transmit is enabled when the message is entered on the Arduino IDE Monitor, it then returns to receive.

Screen Shot 2015 05 17 at 21 55 03

The LCD displays RX & TX and the message letter-by-letter as it is sent, with the output frequency.

Photo 05 17 2015 21 54 44

Photo 05 17 2015 21 56 53

The transmit frequency is preset at 101401000Hz (10140.100kHz), but can be tuned with the rotary encoder in 10Hz steps.

Code

// Universal_QRSS_KB sends a QRSS3 message from the KB
// uses Universal VFO shield
// 30m only (at the moment), at 1014000000Hz
// IQ output for SDR GRG is at 10140kHz
// tunable in 10Hz steps, button does nothing

// ----- SHIELD 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 +5     (r)
// o GND    (bwn)

// I2C, Si5351 libraries
#include "Wire.h"
#include "si5351.h"
#include "LiquidCrystal_I2C.h"
#include "Rotary.h"

// rotary Encoder pins 2 & 3 (DT & CLK), TX & RX enable (LOW)
#define DT 2
#define CLK 3
#define TX 12
#define RX 13

// tuning freq STEPS, 1000cHz (10Hz)
#define STEPS 1000

// 3sec dot time
#define DOT 3000

// ASCII input
char msg[30];
bool newMsg = false;

// start freq
uint32_t qrg = 1014000000; // SDR 10140kHz
uint32_t freq = 1014000000; // cHz

// dds object
Si5351 dds;

// lcd object
LiquidCrystal_I2C lcd(0x27, 16, 2);

// rotary Encoder object
Rotary rot = Rotary(DT, CLK);

// morse code strings, _ = dot space, 0-9 numbers, 10-36 A..Z
// table from 0 - 36
char morse[][8] = {
  "-----_", // 0
  ".----_", // 1-9
  "..---_",
  "...--_",
  "....-_",
  "....._",
  "-...._",
  "--..._",
  "---.._",
  "----._",
  ".-_",   // A
  "-..._", // B
  "-.-._", // C
  "-.._",  // D
  "._",    // E
  "..-._", // F
  "--._",  // G
  "...._", // H
  ".._",   // I
  ".---_", // J
  "-.-_",  // K
  ".-.._", // L
  "--_",   // M
  "-._",   // N
  "---_",  // O
  ".--._", // P
  "--.-_", // Q
  ".-._",  // R
  "..._",  // S
  "-_",    // T
  "..-_",  // U
  "...-_", // V
  ".--_",  // W
  "-..-_", // X
  "-.--_", // Y
  "--.._", // Z
  "__",    // word space
};

void setup()
{
  Serial.begin(9600);

  // init dds si5351 module, "0" = default 25MHz XTAL
  dds.init(SI5351_CRYSTAL_LOAD_8PF, 0);

  // CLK2 output qrg x 4, for IQ
  dds.set_freq(qrg * 4, 0ULL, SI5351_CLK2); // CLK2/IQ SDR tuned to 10104.00kHz
  
 // CLK 0, 1 off, CLK2 on
  dds.output_enable(SI5351_CLK0, 0);
  dds.output_enable(SI5351_CLK1, 0);
  dds.output_enable(SI5351_CLK2, 1); // SDR on

  // init LCD & backlight on
  lcd.init();
  lcd.backlight();

  // encoder, button, RX, TX, band and XMIT pins
  pinMode(DT, INPUT_PULLUP);
  pinMode(CLK, INPUT_PULLUP);
  pinMode(RX, OUTPUT); // SDR RX enable
  pinMode(TX, OUTPUT);  // QRSS TX enable

  rx(); // receive
 
  freqOut(freq); // output freq
  dispMsg(0, 0, "QRSS            "); // display title
  dispFreq(5, 0, freq, 2); // display freq
}

void loop()
{
  tune();
  getMsg();
  qrssOut();
}
  
void tune()
{
  unsigned char dir;

  dir = rot.process(); // read encoder
  if (dir != DIR_NONE) // turned?
  {
    if (dir == DIR_CW) freq += STEPS; // increment freq +/- STEPS
    if (dir == DIR_CCW) freq -= STEPS;

    freqOut(freq); // output freq
    dispFreq(5, 0, freq, 2); // display freq
  }
}

// get input msg[] uc
void getMsg()
{
  static byte ndx = 0; // ndx into msg[]
  char in;

  while (Serial.available() > 0 && newMsg == false)
  {
    in = Serial.read();

    if (in != '\n')
    {
      if (in >= 97 & in <= 122) in = in - 32; // to uc
      msg[ndx] = in;
      ndx++;
    }
    else
    {
      msg[ndx] = '\0'; // terminate msg
      Serial.write(msg); // echo msg
      ndx = 0;
      newMsg = true;
    }
  }
}

// look up morse string, send char by char
void qrssOut()
{
  static byte ndx;
  byte n;
  char c;
  
  // step along msg chraracters
  ndx = 0;
  while (msg[ndx] != '\0' && newMsg == true)
  {
    tx(); // transmit
    dispChar(ndx, 1, msg[ndx]); // display char at col ndx   
    
    // convert to position in morse table
    // convert SPACE
    if (msg[ndx] == 32)
      c = msg[ndx] + 4;

    // convert ASCII
    else if (msg[ndx] >= 48 && msg[ndx] <= 57) // table 0-9
      c = msg[ndx] - 48;
    else if (msg[ndx] >= 65 && msg[ndx] <= 90) // table A-Z (uc)
      c = msg[ndx] - 55;
    else if (msg[ndx] >= 97 && msg[ndx] <= 122) // table a-z (lc)
      c = msg[ndx] - 87;


    // output morse, up to SPACE
    n = 0;
    while (morse[c][n] != '_')
    {
      if (morse[c][n] == '.')  dotOut(); // dot out
      else if (morse[c][n] == '-')  dashOut(); // dash out
      n++;
    }
    spaceOut(); // end of char
    ndx++;
  }
  
  // end of msg
  if(newMsg == true)
  {
    dispMsg(0, 1, "                "); // clear msg c, r
    Serial.println();
    rx(); // receive
  }
  newMsg = false;
}


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

// send a dot for DOT time
void dotOut()
{
  unsigned long t;
  dds.output_enable(SI5351_CLK0, 1);
  t = millis();
  while (millis() < t + DOT);
  dds.output_enable(SI5351_CLK0, 0);
  t = millis();
  while (millis() < t + DOT);
}

// send a dash for 3* DOT time
void dashOut()
{
  unsigned long t;
  dds.output_enable(SI5351_CLK0, 1);
  t = millis();
  while (millis() < t + DOT * 3);
  dds.output_enable(SI5351_CLK0, 0);
  t = millis();
  while (millis() < t + DOT);
}

// word space for 2 * DOT time (each character has its own one DOT space
void spaceOut()
{
  unsigned long t;
  t = millis();
  while (millis() < t + DOT * 2);
}

//switch to transmit
void tx()
{
  digitalWrite(RX, HIGH);
  digitalWrite(TX, LOW);
  dispMsg(14, 1, "TX");
}

// switch to receive
void rx()
{
  digitalWrite(TX, HIGH);
  digitalWrite(RX, LOW);
  dispMsg(14, 1, "RX");
}

// display character m at col c
void dispChar(uint8_t c, uint8_t r, char m)
{
  lcd.setCursor(c, r);
  lcd.print(m); 
}

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

// 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: