Sunday 29 April 2018

Output transformer for a 20-30W PA

I have lashed out and bought on of those incredibly low cost "45W PA" kits!

Screen Shot 2018 04 30 at 00 15 18

These are rated at 45W output, but this is definitely pushing it as far as the safe operating area of the IRF510 output transistors. Anyway I don't want 45W, so I will redesign for 20-30W output at 12V supply.

CALCULATION

The output load for 20W for each transistor is

Vdd^2 / 2 * Pout, or 12^2/2*20 = 3.6R.

Next the output is the usual 50R, so we have to match 3.6 to 50R. The formula for the turns ratio is

Zout/Zin = (tout/tin)^2. or

tout = tin * sqrt(Zout / Zin), or 2 * sqrt(50 / 3.6) = 7.5

So the MOSFET side input turns are 2+2 turns, and the output turns are about 7 turns.

So I need a transformer with this turns ratiot on a suitable binocular ferrite core.

PLAN

My plan is to try to shoehorn this 20W PA and some LPFs (from QRP Labs for 20/30/40m) into a small box similar to those I have used before (see previous posts). It will be a squeeze as it will need also switching repays and a Arduino Nanocomputer for control and an OLED band display, I might even try to squeeze in a power meter.

Thursday 26 April 2018

Driving an OLED 1.3" display - Arduino functions

Over the last year I have developed a header file which provides a set of standard functions based on the u8g2 library for display on an OLED display (I use a 1.3" one with an SH1106 controller and I2C interface to A4-SDA & A5 SCL pins.)

Here is the Oled.h manual

And here is the code.

Put this header file in a folder called "Oled" in your Arduino's libraries folder and include it in your sketches.

Sunday 22 April 2018

Progress - QRP SDR Transceiver

I made a boo, boo and destroyed the Nanocomputer on the first DDS I built - wired up the Rotary Encoder wrong way round! Thus shorting out the +5V supply! But I have rebuilt it now. And this is what I have now, not finished, but the Transceiver is complete, and the DDS is working, but not yet the /4 74AC74 Johnson counter...

IMG 1845

The Transceiver

IMG 1844

The DDS, 74AC74 in place but not wired yet

IMG 1846

The display, and software which drives the DDS

The QRP TCVR is expected to have all of 50mW output! and will need a decent PA to use it on the 40, 30 & 20m bands.

Sunday 15 April 2018

Update of my sketches and libraries

Here they are for downloading

Update and tidy up of AD9851 WSPR code

My code for using an AD9851 synthesiser, with a DS3231 Real Time Clock, to send WSPR messages has always been a bit messy. Now I have tidied it up.

Note that my code sends out signals on Arduino pins A0 & A1 which are used to control the LPF band switching in a PA. You can see the codes sent over a 4 pin 3.5mm jack in the code. At the PA these switch the LPF relays to chose the correct band, as well as switching from RX/TX modes.

The code uses four libraries, my own AD9851.h and Oled.h, and public Rotary.h & WsprMessage.h.

My libraries are listed below. All my sketches and libraries can be downloaded from the blogpost above.

CODE

// WSPR with AD9851 synth
// V4.0 18-4-15 simplify code to remove wspr message copy
//   enter callsign, MH locator and Power in the code below

// HEADERS & LIBRARIES
#include "Oled.h"
#include "ADS9851.h"
#include "Rotary.h"
#include "WsprMessage.h"

// CONNECTIONS
#define RESET 8      // AD9851
#define DATA 9
#define FQ_UD 10
#define W_CLK 11
#define CLK 2        // ENC
#define DT 3
#define SW 4         // button


// PARAMETERS
// RTC I2C address
#define RTCADDR 0x68
// PA modes
#define RX 0
#define TX40 1
#define TX30 2
#define TX20 3

// xtal calibration, WSPR symbollength adj, orginal 682
#define CALIBRATE 180000000
#define SYMBOLLENGTH 684

// OBJECTS
// create AD9851 object
ADS9851 ads;

// Encoder object
Rotary enc = Rotary(DT, CLK);

// GLOBAL VARIABLES
// RTC Data, Decimal
byte sec, mns, hrs;
byte dow;
byte dy, mth, yr;

// repeat TX interval (sec)
uint8_t repeat;

// WSPR data
char callsign[10] = " M0IFA";         // your callsign 6 char (SP first if needed)
char loc_short[5] = "IO92";           // your short locator (read from EEPROM)
char power[5] = "30";                 // TX power in dBm, 30 = 1W

// band
byte band = 1; // band 1 = 40m

// table of band frequencies 0 RX, 1-3 TX40, 30, 20
double freqTable[] = {
  0, 7040100, 10140200, 14097100};
  
// WSPR cHz Freq for symbols 0, 1, 2, 3.
// = 145.48 x 0, 1, 2 and 3
double freqChz[] = {0.0, 146.0, 291.0, 437.0};

// WSPR Frequency & phase
double freqHz = freqTable[band];
volatile double freqStep = 10;         // (Hz) freqStep 10Hz
uint8_t phase = 0;                     // phase

// TX flag
bool tx;

// freq change flag
bool freqChange;

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

// SETUP
void setup() {
  Serial.begin(9600);
  
  // encoder
  pinMode(DT, INPUT_PULLUP);
  pinMode(CLK, INPUT_PULLUP);

  // button
  pinMode(SW, INPUT_PULLUP);

  // jack PA control outputs
  pinMode(A0, OUTPUT);
  pinMode(A1, OUTPUT);

  // setup interrupts from DT or CLK for tuning
  PCICR |= (1 << PCIE2);
  PCMSK2 |= (1 << PCINT18) | (1 << PCINT19);
  sei();

  // oled init, sets I2C addr to 0x3C
  oled.begin();

  // ads begin
  ads.begin(W_CLK, FQ_UD, DATA, RESET);

  // calibrate to xtal actual frequency
  ads.calibrate(CALIBRATE);

  // create WSPR message, define MSG_SIZE and get pointer to symbols
  WsprMessage WsprMessage(callsign, loc_short, atoi(power));
  sym = WsprMessage.symbols;            // get address ptr to symbols

  repeat = 4;                           // repeat every 4 min, must be even number
                                        // Should be random 4-20 or so
  
  tx = false;                           // display flag
  
  // PA init TX40
  setPA(band);  // PA S/W requires that band must be initialised by TX command,
                // followed by return to passive RX
  delay(1000);
  setPA(RX);
  
  freqChange = false;
}

void loop() {
  // get time & display
  readRTC();
  dispUpdate();

  if (button()) {                // button updates band choice
    if (band < 3) band += 1;
    else band = 1;
    if (band == 1) setPA(TX40); // 40m
    if (band == 2) setPA(TX30); // 30m
    if (band == 3) setPA(TX20); // 20m
    delay(500);
    setPA(RX);
    freqHz = freqTable[band];
    dispUpdate();
  }

  if (freqChange) {
    freqChange = false;
    dispUpdate();
  }

  // send WSPR?
  if (mns % repeat == 0 && sec == 0)
  {
    // send
    tx = true;                  // display msgTxt
    if (band == 1) setPA(TX40); // 40m
    if (band == 2) setPA(TX30); // 30m
    if (band == 3) setPA(TX20); // 20m
    dispUpdate();
    sendWspr(SYMBOLLENGTH);
    tx = false;              // display time
    setPA(RX);
  }
}

// BUTTON
bool button() {
  if (digitalRead(SW) == LOW) { // button pressed?
    while (!digitalRead(SW)); // wait for release
    return true;
  }
  else {
    return false;
  }
}

// ENCODER/INTERRUPT
// ISR - encoder interrupt service routine
ISR(PCINT2_vect) {
  unsigned char result;

  result = enc.process();
  if (result == DIR_CW ) {
    freqHz += freqStep;
    freqChange = true;
  }
  else if (result == DIR_CCW) {
    freqHz -= freqStep;
    freqChange = true;
  }
}


//send the WSPR msg
void sendWspr(int SymbolLength) {
  // Send WSPR Message
  for  (int s = 0; s < MSG_SIZE; s++) {                    // For all symbols, MSG_SIZE in WsprMessage.h
    ads.setFreq(freqHz, freqChz[sym[s]], phase);           // transmit frequency
    delay(SymbolLength);                                   // symbol timing
  }
  ads.down();                                              // disable output
}

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

// READ RTC
void readRTC() {
  // Reset the RTC register pointer
  Wire.beginTransmission(RTCADDR);
  Wire.write(0x00);
  Wire.endTransmission();

  // request 7 bytes from the RTC address
  Wire.requestFrom(RTCADDR, 7);

  // get the time date
  sec = bcdToDec(Wire.read()); // 0 - 59
  mns = bcdToDec(Wire.read()); // 0 - 59
  hrs = bcdToDec(Wire.read() & 0b111111); // mask 12/24 bit
  dow = bcdToDec(Wire.read()); // 0 = Sunday
  dy  = bcdToDec(Wire.read()); // 1 - 31
  mth = bcdToDec(Wire.read()); // 0 = jan
  yr  = bcdToDec(Wire.read()); // ..yy
}

// SET PA MODE
// modes 0, 1, 2, 3 output on A1, A0, active LOW
void setPA(int m) {
  // set mode
  switch (m) {
    case 0:
      digitalWrite(A1, HIGH);
      digitalWrite(A0, HIGH);    // RX
      break;
    case 1:
      digitalWrite(A1, HIGH);
      digitalWrite(A0, LOW);    // TX40
      break;
    case 2:
      digitalWrite(A1, LOW);
      digitalWrite(A0, HIGH);   // TX30
      break;
    case 3:
      digitalWrite(A1, LOW);
      digitalWrite(A0, LOW);    // TX20
      break;
  }

}


// picture loop, display init data
void dispUpdate() {
  oled.firstPage();
  do {
    // initial display
    dispMsg(50, 0, "WSPR");
    dispFreq(15, 15, freqHz, freqChz[0], 2); // display frequency
    if (tx == true) {
      dispMsg(10, 35, callsign);
      dispMsg(55, 35, loc_short);
      dispMsg(85, 35, power);
    }
    else {
      dispTime(40, 35, hrs, mns, sec);
    }
    dispMsg(20, 50, "Every");
    dispNum(70, 50, repeat, 0);
    dispMsg(85, 50, "min");
  } while ( oled.nextPage() );
}

LIBRARIES

Put AD9851.h & AD9851.cpp in a folder named AD9851, and put Oled.h in a folder named Oled both in your Arduino "libraries" folder

AD9851.h

// Arduino Library for AD9851 frequency synthesiser module, with 30MHz clock
// V1.2 4-9-17 Antony Watts, M0IFA, code update
// frequency in Hz and cHz
// functions
// void begin(int W_CLK, int FQ_UD, int DATA, int RESET); intialise pins and reset AD9850
// void setFreq(double Hz, double Chz, uint8_t p); set frequency(Hz) and centi-freq(Chz)
//   phase coding, 0-180 in 11.25deg steps 0x00, 0x01, 0x02, 0x04, 0x08, 0x10
// void calibrate(double calHz); change xtal frequency from standard 125MHz to new value
// void down(); power down on, x6 remains on b00000101

#ifndef ADS9851_H
#define ADS9851_H

#include "Arduino.h"

#define ADS_XTAL 180000000.0

class ADS9851 {

    public:
        ADS9851();

        void begin(int W_CLK, int FQ_UD, int DATA, int RESET);
        void setFreq(double Hz, double Chz, uint8_t phase);
        void calibrate(double calHz);
        void down();

    private:
		int _W_CLK;
		int _FQ_UD;
		int _DATA;
		int _RESET;

		double _calFreq;

        void update(uint32_t d, uint8_t p);
		void pulse(int _pin);

};

#endif


AD9851.cpp

// Arduino Library for AD9851 frequency synthesiser module, with 30MHz clock
// V1.2 4-9-17 Antony Watts, M0IFA, pulser code update
// frequency in Hz and cHz
// W_CLK, FQ_UD, DATA, RESET to (e.g.) D11, D10, D9 & D8
// functions
// void begin(int W_CLK, int FQ_UD, int DATA, int RESET); reset, serial, down
// void setFreq(double Hz, double Chz, uint8_t p); set f(Hz) and cHz(Chz),
//   p)hase in steps 0x00, 0x01, 0x02, 0x04, 0x08, 0x10 (11.5, 22.5, 45. 90, 180deg)
// void calibrate(double calHz); correct xtal frequency from standard 180MHz (30MHz x6)
// void down(); freq zero, power down (power up with setFreq())

#include "Arduino.h"
#include "ADS9851.h"

// constructor
ADS9851::ADS9851() {

}

// init calFreq, pins, reset & serial mode
void ADS9851::begin(int W_CLK, int FQ_UD, int DATA, int RESET) {
	_W_CLK = W_CLK;
	_FQ_UD = FQ_UD;
	_DATA = DATA;
	_RESET = RESET;

	_calFreq = ADS_XTAL;

	pinMode(_W_CLK, OUTPUT); // outputs default to LOW
	pinMode(_FQ_UD, OUTPUT);
	pinMode(_DATA, OUTPUT);
	pinMode(_RESET, OUTPUT);

	pulse(_RESET); // reset, parallel mode, ptr to W0

        pulse(_W_CLK); // switch to serial mode, xxxxx011 wired on d2-d0

        down(); // init/clear freq & phase registers, power down
}

// write 40 bits, 4x8 freq + 8 control & phase
void ADS9851::update(uint32_t fW, uint8_t cP) {
    for (int i=0; i <4 ; i++, fW >>= 8) {
       shiftOut(_DATA, _W_CLK, LSBFIRST, fW); // output 32 freq bits
    }
    shiftOut(_DATA, _W_CLK, LSBFIRST, cP); // output 8 control & phase bits

    pulse(_FQ_UD);
}

// calculate 32 freq bits, convert double to to uint32_t, set PD off & x6, update
void ADS9851::setFreq(double f, double cf, uint8_t p) {
     uint32_t delta;

	delta = (uint32_t)((f + cf/100.0) * 4294967296.0 / _calFreq);
	p = p << 3; // PD off = ppppp000
        bitSet(p, 0); // ppppp001 set x6 REFCLK on
	update(delta, p);
}

// clear freq & phase registers
void ADS9851::down() {
        update(0x00000000, 0x04); // freq zero, ppppp100 set PD on, x6 REFCLK off
}


// pulse a pin LOW-HIGH-LOW
void ADS9851::pulse(int _pin) {
        digitalWrite(_pin, LOW);
	digitalWrite(_pin, HIGH);
        digitalWrite(_pin, LOW);
}

// load a new value for _calFreq
void ADS9851::calibrate(double calXtal) {
	_calFreq = calXtal;
}



OLED<br />
// Oled.h
// V2.0 18-4-5 added UL numbers
// defines oled pins, creates "oled" object for 128x64 SH1106 display
// fonts github.com/olikraus/u8g2/wiki

// HEADERS & LIBRARIES
#include "U8g2lib.h"
#include "Wire.h"

// OBJECTS
// oled object, SH1106 controller, 128X64, HW I2C and normal orientation R0
U8G2_SH1106_128X64_NONAME_1_HW_I2C oled(U8G2_R0);

//FUNCTIONS

// BAR
// display bar at x, y, h)eight, l)ength (0-128 pixels)
void dispBar(u8g2_uint_t x, u8g2_uint_t y, byte h, byte l) {
  byte n;

  oled.drawFrame(x, y, 100, h+1);
  for ( n = 0; n < l; n++) {
    oled.drawLine(x + n, y, x  + n, y + h);
  }
}

// FREQ
// display freq at x, y, f (Hz) plus cf (cHz), d)ecimal places (max 3)
void dispFreq(u8g2_uint_t x, u8g2_uint_t y, double f, double cf, byte d) {
  oled.setFont(u8g2_font_10x20_tf); // font
  oled.setFontPosTop(); // origin top

  f = f / 1000.0;
  cf = cf / 100000.0;

  oled.setCursor(x, y);
  oled.print(f + cf, d);
  oled.print("kHz");
}

//MSG
// display small message at at x), y), *m)essage
void dispMsgS(u8g2_uint_t x, u8g2_uint_t y, char *m) {
  // sets font, cursor position and displays message
  oled.setFont(u8g2_font_5x8_tf); // font
  oled.setFontPosTop();
  oled.setCursor(x, y);
  oled.print(m);
}

// display message at at x), y), *m)essage
void dispMsg(u8g2_uint_t x, u8g2_uint_t y, char *m) {
  // sets font, cursor position and displays message
  oled.setFont(u8g2_font_7x13_tf); // font
  oled.setFontPosTop();
  oled.setCursor(x, y);
  oled.print(m);
}

// display large message at at x), y), *m)essage
void dispMsgL(u8g2_uint_t x, u8g2_uint_t y, char *m) {
  oled.setFont(u8g2_font_10x20_tf); // font
  oled.setFontPosTop();
  oled.setCursor(x, y);
  oled.print(m);
}

// display ultra large message at at x), y), *m)essage
void dispMsgUL(u8g2_uint_t x, u8g2_uint_t y, char *m) {
  oled.setFont(u8g2_font_logisoso30_tf); // font
  oled.setFontPosTop();
  oled.setCursor(x, y);
  oled.print(m);
}

// NUM
// display number at x), y), n)umber (double), d)ecimal places
void dispNum(u8g2_uint_t x, u8g2_uint_t y, double n, byte d) {
  oled.setFont(u8g_font_7x14); // fix font for now
  oled.setFontPosTop();
  oled.setCursor(x, y);
  oled.print(n, d);
}

// display number large at x), y), n)umber (double), d)ecimal places
void dispNumL(u8g2_uint_t x, u8g2_uint_t y, double n, byte d) {
  oled.setFont(u8g2_font_10x20_tf); // font
  oled.setFontPosTop();
  oled.setCursor(x, y);
  oled.print(n, d);
}

// display number ultra large at x), y), n)umber (double), d)ecimal places
void dispNumUL(u8g2_uint_t x, u8g2_uint_t y, double n, byte d) {
  oled.setFont(u8g2_font_logisoso24_tf); // font
  oled.setFontPosTop();
  oled.setCursor(x, y);
  oled.print(n, d);
}

// DATE
// display date
void dispDate(u8g2_uint_t x, u8g2_uint_t y, byte dw, byte da, byte mo, byte yr) {
  oled.setFont(u8g_font_7x14); // fix font for now
  oled.setFontPosTop();
  oled.setCursor(x, y);
  
  switch (dw) {
    case 1:
      oled.print("Mon");
      break;
    case 2:
      oled.print("Tue");
      break;
    case 3:
      oled.print("Wed");
      break;
    case 4:
      oled.print("Thu");
      break;
    case 5:
      oled.print("Fri");
      break;
    case 6:
      oled.print("Sat");
      break;
    case 7:
      oled.print("Sun");
      break;
  }

  oled.print(" ");
  oled.print(da);

  oled.print(" ");
  switch (mo)
  {
    case 1:
      oled.print("Jan");
      break;
    case 2:
      oled.print("Feb");
      break;
    case 3:
      oled.print("Mar");
      break;
    case 4:
      oled.print("Apr");
      break;
    case 5:
      oled.print("May");
      break;
    case 6:
      oled.print("Jun");
      break;
    case 7:
      oled.print("Jul");
      break;
    case 8:
      oled.print("Aug");
      break;
    case 9:
      oled.print("Sep");
      break;
    case 10:
      oled.print("Oct");
      break;
    case 11:
      oled.print("Nov");
      break;
    case 12:
      oled.print("Dec");
      break;
  }
  oled.print(" ");
  oled.print("20");
  oled.print(yr);
}

// TIME
// display time HH:MM:SS at x), y)
void dispTime(u8g2_uint_t x, u8g2_uint_t y, byte h, byte m, byte s) {
  oled.setFont(u8g_font_7x14); // fix font for now
  oled.setFontPosTop();
  oled.setCursor(x, y);
  if (h < 10)
    oled.print("0");
  oled.print(h);
  oled.print(":");
  if (m < 10)
    oled.print("0");
  oled.print(m);
  oled.print(":");
  if (s < 10)
    oled.print("0");
  oled.print(s);
}

// display time HH:MM:SS at x), y)
void dispTimeL(u8g2_uint_t x, u8g2_uint_t y, byte h, byte m, byte s) {
  oled.setFont(u8g2_font_10x20_tf); // font
  oled.setFontPosTop();
  oled.setCursor(x, y);
  if (h < 10)
    oled.print("0");
  oled.print(h);
  oled.print(":");
  if (m < 10)
    oled.print("0");
  oled.print(m);
  oled.print(":");
  if (s < 10)
    oled.print("0");
  oled.print(s);
}

// STEP
// display step at x) y) s)tep
void dispStep(u8g2_uint_t x, u8g2_uint_t y, unsigned int s) {
  oled.setFont(u8g_font_7x14); // fix font for now
  oled.setFontPosTop();
  
  oled.setCursor(x, y);
  switch (s) // display freqStep
  {
    case 10:
      oled.print(" 10Hz");
      break;
    case 100:
      oled.print("100Hz");
      break;
    case 1000:
      oled.print(" 1kHz");
      break;
    case 10000:
      oled.print(" 10kHz");
      break;
    case 100000:
      oled.print("100kHz");
      break;
    case 1000000:
      oled.print(" 1MHz");
      break;
  }
}
Note: Oled.h uses the public u8glib.

Thursday 5 April 2018

SWR digital meter 100mW - 10W

Finally I have made and got working my SWR meter. Wow, aren't there so many designs out there on the web! Parts of the existing offerings I liked were the use of a Bi-core ferrite transformer, I used a BN43-2402 with a turns ratio of 1:10. I have used a couple of the wonderful log detection AD8307 for the Forward and Reflected dBm detection. I worked out that 10W = 22V input, and with the detectors AD8307 maximum input of about 2.5V, I needed the 1:10 ratio.

The output of the AD8307s is fed to the analog A0 & A1 inputs of an Arduino Nano microcomputer. I used an OLED display I have worked with many times, and show just the Forward Power as a bar graph and the VSWR.

IMG 1815

The circuit board showing the Transformer, two AD8307s and the Nano

IMG 1816

The board and the OLED display

IMG 1823

My VFO which outputs 1-60MHz at -10 to +10dBm

The loop antenna I used for testing - the Wonder Wand - The tuning on this is VERY sharp to get a low VSWR, I tested it here on 40m.

IMG 1824

I have yet to calibrate the SWR meter, which can be done in software, but it is giving somewhat sensible readings.

Code - Updated V2

This uses a header file I wrote myself which defines a number of displays, for example the Bar graph and the Ultra-large numbers. This header (listed at the bottom below) calls up the open source U8G2 library.
// SWR_2 V2.0
// 18-4-12 Calibrate mWFwd with Power Meter, re-write bar length calculation.

//HEADERS & LIBRARIES
#include "Oled.h"

// CONNECTIONS
// Fwd & Ref analog in, transformer ratio
#define FWDIN A0
#define REFIN A1
#define TRANSRATIO 10

// PARAMETERS
// note AREF is nominally +3300mV. RF in 22V=10W, 10:1 transformer, AD8307 in 2.2V=20dB
#define AREF 3204
#define AMAX 1023

// intercept (dBm), slope (mW/dB), bridge impedance,
// bar display scale, transformer ratio (dB)
#define INTERCEPT_FWD -87.0
#define INTERCEPT_REF -88.0
#define SLOPE_FWD 25.0
#define SLOPE_REF 25.0
#define IMP 51.0
#define BARSCALE 100/20
#define TRATIO 20.0

// GLOBAL VARIABLES
// for display function
double bLFwd;
double vswr;

void setup() {
  analogReference(EXTERNAL);
  // oled init, sets I2C addr to 0x3C
  oled.begin();
}

void loop() {
  int inFwd, inRef;
  double mVFwd, mVRef;
  double VrmsFwd, VrmsRef, dBmFwd, dBmRef, mWFwd, mWRef, vRatio;

  // read inputs FWD & REF
  // is about 1.5-2.5V, calibrate with INTERCEPT value
  inFwd = analogRead(FWDIN);
  inRef = analogRead(REFIN);

  mVFwd = (double)(map(inFwd, 0, AMAX, 0, AREF));
  mVRef = (double)(map(inRef, 0, AMAX, 0, AREF));
  
  // calculations for display
  dBmFwd = (mVFwd / SLOPE_FWD) + INTERCEPT_FWD + TRATIO; // in doubles
  mWFwd = pow(10.0, (dBmFwd / 10.0)); // in double, out double, 0dBm = 1mW
  mVFwd = sqrt((mWFwd * 1000.0) * IMP); // in double, out double

  dBmRef = (mVRef / SLOPE_REF) + INTERCEPT_REF + TRATIO; // in doubles
  mWRef = pow(10.0, (dBmRef / 10.0)); // in double, out double, 0dBm = 1mW
  mVRef = sqrt((mWRef * 1000.0) * IMP); // in double, out double

  // calculatio for bar graph, forward power
  bLFwd = (dBmFwd * BARSCALE) - 100; // tbd 20-30-40dB, 0.1W-1W-10W

  // calculation SWR
  vRatio = mVRef / mVFwd;

  vswr = ((1 + vRatio) / (1 - vRatio));

  delay(200); // helps to slow display flicker

  dispUpdate();
}

// PICTURE LOOP
void dispUpdate() {
  oled.firstPage();
  do {
    dispMsg(50, 0, "SWR_2"); // prog name
    
    dispMsgS(0, 28, "PWR");  // bar title
    dispMsgS(20, 15, "100mW     1W      10W"); // bar scale

    if (bLFwd < 5 || bLFwd > 95) {
      dispBar(20, 28, 5, 0);        // blank bar
      dispMsgL(30, 38, "No Input"); // no input
    }

    else if (vswr > 10) {
      dispBar(20, 28, 5, 0);        // blank bar
      dispMsgL(30, 38, "SWR > 10"); // too big
    }

    else {
      dispBar(20, 28, 5, bLFwd);        // bar value
      dispNumUL(40, 38, abs(vswr), 2);  // always display as positive
    }
  } while (oled.nextPage());
}


Header

// Oled.h
// V2.0 18-4-5 added UL numbers
// defines oled pins, creates "oled" object for 128x64 SH1106 display
// fonts github.com/olikraus/u8g2/wiki

// HEADERS & LIBRARIES
#include "U8g2lib.h"
#include "Wire.h"

// OBJECTS
// oled object, SH1106 controller, 128X64, HW I2C and normal orientation R0
U8G2_SH1106_128X64_NONAME_1_HW_I2C oled(U8G2_R0);

//FUNCTIONS

// BAR
// display bar at x, y, h)eight, l)ength (0-128 pixels)
void dispBar(u8g2_uint_t x, u8g2_uint_t y, byte h, byte l) {
  byte n;

  oled.drawFrame(x, y, 100, h+1);
  for ( n = 0; n < l; n++) {
    oled.drawLine(x + n, y, x  + n, y + h);
  }
}

// FREQ
// display freq at x, y, f (Hz) plus cf (cHz), d)ecimal places (max 3)
void dispFreq(u8g2_uint_t x, u8g2_uint_t y, double f, double cf, byte d) {
  oled.setFont(u8g2_font_10x20_tf); // font
  oled.setFontPosTop(); // origin top

  f = f / 1000.0;
  cf = cf / 100000.0;

  oled.setCursor(x, y);
  oled.print(f + cf, d);
  oled.print("kHz");
}

//MSG
// display small message at at x), y), *m)essage
void dispMsgS(u8g2_uint_t x, u8g2_uint_t y, char *m) {
  // sets font, cursor position and displays message
  oled.setFont(u8g2_font_5x8_tf); // font
  oled.setFontPosTop();
  oled.setCursor(x, y);
  oled.print(m);
}

// display message at at x), y), *m)essage
void dispMsg(u8g2_uint_t x, u8g2_uint_t y, char *m) {
  // sets font, cursor position and displays message
  oled.setFont(u8g2_font_7x13_tf); // font
  oled.setFontPosTop();
  oled.setCursor(x, y);
  oled.print(m);
}

// display large message at at x), y), *m)essage
void dispMsgL(u8g2_uint_t x, u8g2_uint_t y, char *m) {
  oled.setFont(u8g2_font_10x20_tf); // font
  oled.setFontPosTop();
  oled.setCursor(x, y);
  oled.print(m);
}

// display ultra large message at at x), y), *m)essage
void dispMsgUL(u8g2_uint_t x, u8g2_uint_t y, char *m) {
  oled.setFont(u8g2_font_logisoso30_tf); // font
  oled.setFontPosTop();
  oled.setCursor(x, y);
  oled.print(m);
}

// NUM
// display number at x), y), n)umber (double), d)ecimal places
void dispNum(u8g2_uint_t x, u8g2_uint_t y, double n, byte d) {
  oled.setFont(u8g_font_7x14); // fix font for now
  oled.setFontPosTop();
  oled.setCursor(x, y);
  oled.print(n, d);
}

// display number large at x), y), n)umber (double), d)ecimal places
void dispNumL(u8g2_uint_t x, u8g2_uint_t y, double n, byte d) {
  oled.setFont(u8g2_font_10x20_tf); // font
  oled.setFontPosTop();
  oled.setCursor(x, y);
  oled.print(n, d);
}

// display number ultra large at x), y), n)umber (double), d)ecimal places
void dispNumUL(u8g2_uint_t x, u8g2_uint_t y, double n, byte d) {
  oled.setFont(u8g2_font_logisoso24_tf); // font
  oled.setFontPosTop();
  oled.setCursor(x, y);
  oled.print(n, d);
}

// DATE
// display date
void dispDate(u8g2_uint_t x, u8g2_uint_t y, byte dw, byte da, byte mo, byte yr) {
  oled.setFont(u8g_font_7x14); // fix font for now
  oled.setFontPosTop();
  oled.setCursor(x, y);
  
  switch (dw) {
    case 1:
      oled.print("Mon");
      break;
    case 2:
      oled.print("Tue");
      break;
    case 3:
      oled.print("Wed");
      break;
    case 4:
      oled.print("Thu");
      break;
    case 5:
      oled.print("Fri");
      break;
    case 6:
      oled.print("Sat");
      break;
    case 7:
      oled.print("Sun");
      break;
  }

  oled.print(" ");
  oled.print(da);

  oled.print(" ");
  switch (mo)
  {
    case 1:
      oled.print("Jan");
      break;
    case 2:
      oled.print("Feb");
      break;
    case 3:
      oled.print("Mar");
      break;
    case 4:
      oled.print("Apr");
      break;
    case 5:
      oled.print("May");
      break;
    case 6:
      oled.print("Jun");
      break;
    case 7:
      oled.print("Jul");
      break;
    case 8:
      oled.print("Aug");
      break;
    case 9:
      oled.print("Sep");
      break;
    case 10:
      oled.print("Oct");
      break;
    case 11:
      oled.print("Nov");
      break;
    case 12:
      oled.print("Dec");
      break;
  }
  oled.print(" ");
  oled.print("20");
  oled.print(yr);
}

// TIME
// display time HH:MM:SS at x), y)
void dispTime(u8g2_uint_t x, u8g2_uint_t y, byte h, byte m, byte s) {
  oled.setFont(u8g_font_7x14); // fix font for now
  oled.setFontPosTop();
  oled.setCursor(x, y);
  if (h < 10)
    oled.print("0");
  oled.print(h);
  oled.print(":");
  if (m < 10)
    oled.print("0");
  oled.print(m);
  oled.print(":");
  if (s < 10)
    oled.print("0");
  oled.print(s);
}

// display time HH:MM:SS at x), y)
void dispTimeL(u8g2_uint_t x, u8g2_uint_t y, byte h, byte m, byte s) {
  oled.setFont(u8g2_font_10x20_tf); // font
  oled.setFontPosTop();
  oled.setCursor(x, y);
  if (h < 10)
    oled.print("0");
  oled.print(h);
  oled.print(":");
  if (m < 10)
    oled.print("0");
  oled.print(m);
  oled.print(":");
  if (s < 10)
    oled.print("0");
  oled.print(s);
}

// STEP
// display step at x) y) s)tep
void dispStep(u8g2_uint_t x, u8g2_uint_t y, unsigned int s) {
  oled.setFont(u8g_font_7x14); // fix font for now
  oled.setFontPosTop();
  
  oled.setCursor(x, y);
  switch (s) // display freqStep
  {
    case 10:
      oled.print(" 10Hz");
      break;
    case 100:
      oled.print("100Hz");
      break;
    case 1000:
      oled.print(" 1kHz");
      break;
    case 10000:
      oled.print(" 10kHz");
      break;
    case 100000:
      oled.print("100kHz");
      break;
    case 1000000:
      oled.print(" 1MHz");
      break;
  }
}

Wednesday 4 April 2018

RF Bridge

I spotted on eBay this RF Bridge, with 50R resistors and a Balun output transformer.

IMG 1807

I have connected it to my VFO, which uses an AD9851 + MMIC amplifier (10mW output), and to my RF power meter built with an AD8307 log detector and able to display 1uW to 10W power! The result is that I can take a look at the resonant frequency of an antenna, the Wonder Wand tuneable loop.

IMG 1808

IMG 1810

This is the result over the 40m band:

Screen Shot 2018 04 04 at 16 08 31