Friday 15 May 2015

Universal DFCW3 sketch

A more sophisicated way of sending QRSS, slow morse, is DFCW. This means Dual Frequency CW. This sends dashes at the tuned frequency, but dots at 5Hz above making visual decoding much easier. In addition it stop transmitting between dot and dashes for a short time, and in between characters.

Screen Shot 2015 05 15 at 15 16 41

Photo 05 17 2015 22 01 54

Photo 05 17 2015 22 03 40

The lower dash frequency can be tuned in 10Hz steps. TX shows when transmitting.

The Windows program "Argo", which I run on my iMac under "Wine" is suitable for reception. To remind you here's morse code!

Screen Shot 2015 05 15 at 15 31 37

And here's the SDR reception display, I am tuned to 10138.5kHz, so QRSS at around 10140kHz show as a 1500Hz signal in Argo.

Screen Shot 2015 05 15 at 15 27 41

Screen Shot 2015 05 15 at 15 32 28

This is pretty difficult to read, but with a little patience and practice it gets easier.

Code

 
// Universal_DFCW_KB sends a DFCW3 message from the KB
// DFCW spacing is 0/5Hz
// uses Universal VFO shield
// 30m only (at the moment), start freq 1014000000cHz
// 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), freq step 500cHz (5Hz)
#define STEPS 1000
#define FREQ 500

// 3sec dot time
#define DOT 3000

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

// start freqs
uint32_t qrg = 1014000000; // SDR 10140kHz
uint32_t dashFreq = 1014000000; // cHz;
uint32_t dotFreq = dashFreq + FREQ; // +FREQ

// 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);

  // CLK1 output qgr x 4, for IQ
  dds.set_freq(qrg * 4, 0ULL, SI5351_CLK2); // SDR tuned to QRG
    
  // VFO off, CLK2/IQ 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 (LOW)

  rx(); // receive
 
  freqOut(dashFreq); // init at dot freq
  dispMsg(0, 0, "DFCW            "); // display title
  dispFreq(5, 0, dashFreq, 2); // display freq
}

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

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

    dotFreq = dashFreq + FREQ; // +5Hz
    dispFreq(5, 0, dashFreq, 2); // update freq display
  }
}

// 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 dfcwOut()
{
  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 on lcd    
    
    // 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(1, 0, "                "); // clear msg
    dispFreq(0, 4, dashFreq, 2);
    Serial.println();
    rx(); // receive
  }
  newMsg = false;
}


// frequency (in cHz) for freq, 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;
  
  freqOut(dotFreq);
  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;
  
  freqOut(dashFreq);
  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 simple menu
void dispMenu()
{
  lcd.setCursor(0, 0);
  lcd.print("DFCW");
}

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

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