Monday 28 November 2016

BASIC Tech Group - MyNews 6 - WSPR reception using DCRX

Wonderful day today for WSPR reception on 40m, mainly from PA & DL.

Screen Shot 2016 11 28 at 14 59 00

This is again using the set up of Arduino UNO, Concept DDS, and DCRX receiver. With a squatty small vertical indoor aerial.

SDRTX abandoned - now to be a QRP TXRX RIG

I have re-thought the SDRTX and decided to try for a more ambitious full QRP rig, a single board (shield) with an SDR RX and SDR TX on it, output around 10mW. This will be topped off with a three band BPF shield to work on 40, 30 & 20m.



Screen Shot 2016 11 28 at 15 10 42 Components researched and found for this are the FST3253 for the Mixers, and the SSM2135 dual op-amp for the audio side. This device is a new find for me and is a kind of super op-amp with very high output drive capability needed for the TX driver (4 channels into 50R loads).

Schematic later and PCB layout when ready. WIll probably breadboard the TX first as not yet got one running...

Decided also to move across to SMA connectors, these are much lighter but can handle up to 20W! and plentifull low cost cables are on eBay (SMA-SMA, SMA-BNC, SMA-PL59...).

Saturday 12 November 2016

BASIC Tech Group - MyNews 5 - Arduino Sketches & libraries

New initiative

At the Banbury Amateur Radio Society we are starting a group to play with technology for Amateur Radio, we call it the BASIC Tech Group.

IMG 0459

The aim is to bring together people interested in building equipment and tools for Amateur Radio, and cooperating to get them done. I will post things of possible interest here.

First here are some interesting Arduino Sketches:

Sketches

CAP-Basic - a capacitance measurement sketch

FREQ-Basic - a frequency measurement sketch, needs an external divide by 10, CMOS 4017

I2CSCAN - a popular public program to scan the I2C bus (Arduino UNO pins A4 SDA and A5 SCL) and report any I2C addresses found

RFMETER - using an external circuit of a 20dB attenuator feeding an AD8307 IC, input on pin A0, with Arduino AREF connected to 3.3V

RTCSET-Basic - this allows you to set the date/time on the DS3231 IC on our DDS Arduino Shield.

SDR_40M-Basic - generates 40m I/Q signals from our DDS Arduino Shield to drive a Tayloe style detector

VFO-Basic - a general purpose VFO with output from 10kHz to 250MHz based on our DDS Arduino Shield using the Si5351 synthesiser. The output gives about 1Vrms into 100R load

VFO-Basic-4 - a version of the above that uses a 4 line 20x4 display, the top line showing date & time from the DDS Shield RTC.

libraries

You will need some Arduino BASIC-libraries to run these sketches, a whole bunch of libraries I have collected is here. Take care using the new LiquidCrystal_I2C library, HobbyComponents have posted a changed: the "init" routine, it is now called "begin".

The hardware projects have been posted before on this blog.

Tuesday 8 November 2016

BASIC Tech Group - MyNews 4 - SDR TX

SDR TX - ALL STOP!!! The PCB for this is wrong. I made a stupid number of mistakes when building the TDA7266D device library for Eagle and got the pin numbering wrong!! Project abandoned, see News 6 for replacement QRP RIG

The long awaited design for the SDTTX_BASIC simple SDR 40m transmitter has got to the PCB order stage. I am still not sure of the design, having seen others using a DRV135 (balanced line driver) audio stage...

This follows a careful check of the schematic and of the board layout. See below.

Screen Shot 2016 11 08 at 15 15 56

Screen Shot 2016 11 08 at 15 16 05

Software

Two pieces of software are used for the SDRTX. One running on the Arduino which is the same as that used for the SDR RX. This generates the IQ modulation signals at 4 x freq and drives the IQ modulator FST3253 chip.

The FST3253 chip is also fed with the four audio phases 0, 90, 180 & 270 degrees.

The audio is generated by the HDSDR.exe program running on my Macbook under the Wine emulator, and output via a Startech DAC. Anyone wanting to do this MUST download and install the file "ExtIO_SRlite.dll" too (see HDSDR page, download under "Softrock Lite v0.12"), put it in the HDSDR folder to enable the HDSDR program to enter Transmit mode (SPACE bar, use RETURN key to send morse code in CW mode).

Code

// My_SDR_40M
// for 40m, with TX/RX control and bandplan display
// Si5351 I2C bus
// SDA = A4
// SCL = A5
// LCD I2C bus
// SDA = A4
// SCL = A5
// ADDR 0x27 or 3F
// rotary encoder pins
// DT = 2
// CLK = 3
// SW = 4

// I2C, Si5351, LCD and rotary Encoder libraries
#include "Wire.h"
#include "si5351.h"
#include "LiquidCrystal_I2C.h"
#include "Rotary.h"

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

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

// Rx & Tx signals
#define RX 13
#define TX 12
#define KEY 8

// number of band plans
#define PLANS 12

// dds object
Si5351 dds;

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

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

// define plan structure
typedef struct {
  uint32_t lower;
  uint32_t upper;
  char alloc[30];
} plan;

// band plan array contents cHz/cHz/Text
plan bp[PLANS] = {
  {700000000, 700100000, "CW QRSS 7000.7  "},
  {700100000, 703990000, "CW QRP 7030     "},
  {703990000, 704690000, "NB WSPR 7040    "},
  {704600000, 704990000, "NB Auto         "},
  {704990000, 705290000, "ALL Auto        "},
  {705290000, 705990000, "ALL Digital     "},
  {705990000, 706990000, "ALL             "},
  {706990000, 707990000, "ALL HELL 7077   "},
  {707990000, 709990000, "ALL SSB QRP 7090"},
  {709990000, 712990000, "ALL EMGCY 7110  "},
  {712990000, 717490000, "ALL SSB CON 7165"},
  {717490000, 720010000, "ALL DX INTNL    "},
};

uint32_t freq = 700000000; // cHz, start frequency
uint32_t step = 10000; // cHz, init 100Hz step

void setup() {
  // init LCD & backlight on
  lcd.init();
  lcd.backlight();

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

  // set 8mA output drive (max possible)
  dds.drive_strength(SI5351_CLK2, SI5351_DRIVE_8MA);

  // can insert Si5351 calibration here if required

  // enable SDR output CLK2, disable CLK0 & 1
  dds.output_enable(SI5351_CLK0, 0);
  dds.output_enable(SI5351_CLK1, 0);
  dds.output_enable(SI5351_CLK2, 1);

  // encoder, button, RX, TX, band and KEY pins
  pinMode(DT, INPUT_PULLUP);
  pinMode(CLK, INPUT_PULLUP);
  pinMode(SW, INPUT_PULLUP);

  pinMode(RX, OUTPUT);
  pinMode(TX, OUTPUT);
  pinMode(KEY, INPUT_PULLUP);

  xmit(digitalRead(KEY)); // set RX|TX, KEY = LOW is TX

  freqOut(freq); // cHz, output freq

  dispFreq(4, 0, freq, 1); // display freq kHz col 4 row 0
  dispMsg(0, 1, scanPlan()); // display band plan col 0 row 1
}

void loop() {
  // tune?
  if (tune()) {
    freqOut(freq); // output freq
    dispFreq(4, 0, freq, 1); // update freq display
    dispMsg(0, 1, scanPlan()); // update band plan display
  }

  // step?
  if (button()) {
    dispStep(step, 14, 0);
  }
  xmit(digitalRead(KEY)); // RX|TX
}

// tune?
bool tune() {
  unsigned char dir; // tuning direction CW/CCW

  dir = rot.process(); // read encoder
  if (dir != DIR_NONE) { // turned?
    if (dir == DIR_CW && (freq < bp[PLANS - 1].upper - step)) freq += step;
    if (dir == DIR_CCW && (freq >= bp[0].lower + step)) freq -= step;
    return true;
  }
  return false;
}

// change step?
bool button() {
  if (digitalRead(SW) == LOW) { // button pressed?
    while (!digitalRead(SW)); // wait for release
    if (step == 1000000) step = 10000; // reset
    else step = step * 10; // or increment by x10
    return true;
  }
  return false;
}

// search for band info
char *scanPlan() {
  for (int i = 0; i < 15; i++) {
    if (freq >= bp[i].lower && freq < bp[i].upper) // find plan
      return bp[i].alloc; // return when found
  }
}

// Output Freq for SDR, on CLK2, f cHz
void freqOut(uint32_t f) {
  dds.set_freq(f * 4ULL, 0ULL, SI5351_CLK2);
}

// Tx/Rx KEY HIGH = RX, LOW = TX
void xmit(bool x)
{
  if (x == LOW) // TX
  {
    dispMsg(0, 0, "TX ");
    digitalWrite(RX, HIGH); // Rx off
    digitalWrite(TX, LOW); // Tx on
  }
  else
  {
    dispMsg(0, 0, "SDR");
    digitalWrite(RX, LOW); // Rx on
    digitalWrite(TX, HIGH); // Tx off
  }
}

// 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 at col c, row r, f cHz, d decimal places
void dispFreq(uint8_t c, uint8_t r, uint32_t f, uint8_t d) {
  lcd.setCursor(c, r);
  lcd.print((float)f / 100000, d); // convert to float & kHz
  lcd.print("kHz ");
}

// display step
void dispStep(uint32_t s, byte c, byte r)
{
  switch (s) // display step
  {
    case 10000:
      dispMsg(c, r, "  ");
      break;
    case 100000:
      dispMsg(c, r, " +");
      break;
    case 1000000:
      dispMsg(c, r, "++");
      break;
  }
}


The code include RX/TX switching, and so you must ground the Arduino UNO pin 8 for PTT, this pulls D12 low (and D12 high to disable the SDR RX) and enables the FST3253 TX Mixer.

Monday 7 November 2016

BASIC Tech Group - MyNews 3 - RF Meter

Here's the RFMETER. Input impedance is 50R, input attenuator of 20dB, max input power 2W. Two line display of dBm, Watts & Volts

Screen Shot 2016 11 07 at 17 53 10

Input is to Arduino Uno pin A0, AREF analog reference is connected to 3.3V.

Code

// RF-Meter,
// input 50R/2W via a pi 20dB attenuator 82R - 240R - 68R
// displays dBm, Watts, Volts. Autoscaling

#include 
#include 

#define LCDADDR 0x3F
#define LCDCOLS 16
#define LCDROWS 2

// Analog input pin
#define DCIN A0
#define AREF 3.3

// intercept (dBm), slope (mw/dB), input impedance, attenuator (dB).
#define INTERCEPT 84.0
#define SLOPE 25.0
#define IMP 50
#define ATTN -20

LiquidCrystal_I2C lcd(LCDADDR, LCDCOLS, LCDROWS);

void setup() {
  lcd.begin();
  lcd.backlight();

  // 3.3V connected to AREF
  analogReference(EXTERNAL);

  lcd.clear();
  lcd.setCursor(4, 0);
  lcd.print("RF METER");
}

void loop() {
  float mV, dBm, mW, V;
  
  // calculations for MV input, dBM, mW, and Volts
  mV = 1000.0 * (float)analogRead(DCIN) * (AREF / 1023);
  dBm = (mV / SLOPE) - INTERCEPT;
  dBm -= ATTN;
  mW = pow(10, (dBm / 10));
  V = sqrt((mW / 1000) * IMP);

  // dBm
  lcd.setCursor(0, 1);
  lcd.print(dBm, 0);
  lcd.print("dBm ");

  // Watts
  if (mW < 1.0) {
    lcd.print(mW * 1000, 0);
    lcd.print("uW ");
  }
  else if (mW < 1000.0) {
    lcd.print(mW, 0);
    lcd.print("mW ");
  }
  else {
    lcd.print(mW / 1000, 1);
    lcd.print("W ");
  }

  // Volts
  if (V < 1.0) {
    lcd.print(V * 1000.0, 0);
    lcd.print("mV  ");
  }
  else {
    lcd.print(V, 1);
    lcd.print("V  ");
  }

  delay(500);
}

float dbmMw(float dbm)
{
  return pow(10, (dbm / 10));
}