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;
  }
}

3 comments:

Unknown said...

hola mi nombre es Ariel EA3ATS de Barcelona EspaƱa y quisiera saber como subir al ide de arduino tu libreia "olde.h" por que lo he intentado varias veces y no lo se hacer por lo demas todo ok

Unknown said...

schematics please

vbf studios said...

VBF Studios offers professional video editing services in Noida, ensuring high-quality results for films, commercials, corporate videos, and digital content. Our expert editors use advanced tools to deliver seamless and creative edits that bring your vision to life. Contact VBF Studios for exceptional video editing solutions today