/*
** Digital PWR & SWR meter v1.0 by ON7IR **
** Published at on7ir.blogspot.com May 2021 **
** Copyright (C) 2021 Rudi Imbrechts (ON7IR) **
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
** This sketch makes use of the SWR bridge from a Daiwa CN-410M analog SWR meter
Forward and reflected values from this SWR bridge are the input for the Arduino.
Output is presented on a 2.8" touchscreen. Touch function is not used here but will be in future version.
More information on https://on7ir.blogspot.com/2021/05/digital-swr-and-power-meter-part-1.html
and https://on7ir.blogspot.com/2021/05/digital-swr-and-power-meter-part-2.html
** This sketch uses several libraries.
- Adafruit_GFX by Adafruit Industries
- ILI9341_Fast library and Numeric display - RREFont are (c) 2020 Pawel A. Hernik,
see https://github.com/cbm80amiga/ILI9341_Fast and https://github.com/cbm80amiga/RREFont
- multiMap library, see https://github.com/RobTillaart/MultiMap
** ILI9341 240x320 2.8" TFT pinout:
1 = +5V
2 = GND
3 = CS
4 = RST
5 = DC
6 = MOSI
7 = SCK
8 = Backlight
9 = MISO
10 = T_CLK
11 = T_CS
12 = T_DIN
13 = T_DO
14 = T_IRQ
** Warning ** TFT screen needs 5V power supply but the signal levels are 3.3V.
Use voltage converters between Arduino and TFT pins!
*/
#include <SPI.h>
#include <Adafruit_GFX.h>
#include <ILI9341_Fast.h>
#include "MultiMap.h"
#define TFT_CS 8
#define TFT_DC 10
#define TFT_RST 9
ILI9341 tft = ILI9341(TFT_DC, TFT_RST, TFT_CS); // initialise the TFT
//-----------------------------------------------------------------------------
// define RRE fonts
#include "RREFont.h"
#include "rre_6x8.h"
#include "rre_arialn_16.h"
#include "rre_bold13x20.h"
#include "rre_bold13x20v.h"
#include "rre_bold13x20no.h"
RREFont font; // needed for RREFont library initialization, define your fillRect
void customRect(int x, int y, int w, int h, int c) {
return tft.fillRect(x, y, w, h, c);
}
//-----------------------------------------------------------------------------
const int SCR_WD = 320; // screen width
const int SCR_HT = 240; // screen height
const int fwdInPin = A0; // input forward power from SWR bridge
const int refInPin = A1; // input reflected power from SWR bridge
unsigned int sensorFWD = 0; // raw forward value
unsigned int sensorREF = 0; // raw reflected value
float fwdInVoltage; // calculated forward voltage from SWR bridge
float refInVoltage; // calculated reflected voltage from SWR bridge
float outputValueFWD; // calculated forward power
float outputValueREF; // calculated reflected power
float cswr; // used to calculate swr
float swr; // contains SWR value
const int swrTreshold = 5; // SWR treshold value. Not interested in SWR higher than 5
int xbar = 110; // horizontal position of first graphic bar
int nextbar = 50; // horizontal position of next bar
int barHeight = 25; // height of the graphical bar
int i = 0;
int mode = 0, lastMode = -1;
// define color scheme
const unsigned short defaultColor = RGBto565(255, 255, 255); // rgb color areas (general) is white
const unsigned short labelColor = RGBto565(250, 250, 250); // rgb color label text
const unsigned short FwdColor = RGBto565(50, 250, 50); // green
const unsigned short RefColor = LGREY; // light grey
const unsigned short Swr1Color = RGBto565(0, 255, 140); // light blue when SWR is less than 2
const unsigned short Swr2Color = YELLOW; // yellow when SWR is between 2 and 2.5
const unsigned short Swr25Color = RGBto565(255, 100, 0); // orange when SWR is between 2.5 and 3
const unsigned short Swr3Color = RED; // red when SWR is 3 or higher
const unsigned short barBgColor = RGBto565(50, 50, 50); // grey background of the bars
const unsigned short on7irColor = RGBto565(50, 100, 50); // dark greenish
void setup() {
pinMode(TFT_CS, OUTPUT); // avoid chip select contention. This is...
digitalWrite(TFT_CS, HIGH); // ...needed later when touchscreen is activated
tft.init(); // initialize TFT screen
tft.setRotation(3); // rotate to landscape, pins at left side
font.init(customRect, SCR_WD, SCR_HT); // custom fillRect function and screen width and height values
}
void setBigNumFont() {
font.setFont(&rre_Bold13x20v);
font.setSpacing(1);
font.setScale(1, 2);
font.setDigitMinWd(16);
}
void setInfoFont() {
font.setFont(&rre_arialn_16);
}
void drawField(int x, int y, int w, int h, char *label, unsigned short col = defaultColor) {
tft.drawRect(x, y + 7, w, h - 7, col);
setInfoFont();
font.setScale(1);
font.setColor(labelColor, BLACK); // black is the background color of label text
int wl = font.strWidth(label);
font.printStr(x + (w - wl) / 2, y, label); // center the label text
}
void showVal(float v, int x, int y, int w, int p, unsigned short col) {
setBigNumFont();
font.setColor(col, BLACK); // black is the background color of the computed values
char txt[10];
dtostrf(v, w, p, txt);
font.printStr(x, y, txt);
}
void constData() {
drawField(0, 0, 95, 78, " FWD ", FwdColor); // print the frames and label text.
drawField(112, 0, 95, 78, " REF ", RefColor);
font.setScale(1);
font.setColor(labelColor, BLACK);
font.printStr(0, xbar - 18, "forward"); // print the text above each graphic bar
font.printStr(0, xbar + nextbar - 18, "reflected");
font.printStr(0, xbar + nextbar * 2 - 18, "SWR");
font.setFont(&rre_6x8);
font.setColor(on7irColor, BLACK);
font.printStr(140, 90, "SWR&POWER METER by ON7IR"); // :-)
font.setColor(WHITE, BLACK); // print SWR scale above graphic SWR bar in a white color
font.printStr(60, xbar + nextbar * 2 - 10, "1");
font.printStr(75, xbar + nextbar * 2 - 10, ".");
font.printStr(91, xbar + nextbar * 2 - 10, ":");
font.printStr(106, xbar + nextbar * 2 - 10, ".");
font.printStr(122, xbar + nextbar * 2 - 10, "2");
font.printStr(140, xbar + nextbar * 2 - 10, ".");
font.printStr(158, xbar + nextbar * 2 - 10, ":");
font.printStr(180, xbar + nextbar * 2 - 10, "3");
font.printStr(215, xbar + nextbar * 2 - 10, ".");
font.printStr(250, xbar + nextbar * 2 - 10, "4");
font.printStr(310, xbar + nextbar * 2 - 10, "5");
}
void varData() {
showVal(outputValueFWD, 4, 24, 4, 0, FwdColor); // show forward power in figures
if (outputValueREF >= 100) { // show reflected power in figures
showVal(outputValueREF, 117, 24, 4, 0, RefColor); // but don't show digits after decimal point when reflected power is above 100 watts
} else {
showVal(outputValueREF, 132, 24, 4, 1, RefColor);
}
if (outputValueFWD == 0) { // show SWR in figures
swr = 0; // SWR is zero if nothing is transmitted
}
if (swr > 2.9) { // use various colors for SWR values
showVal(swr, 254, 24, 3, 1, Swr3Color); // if SWR is dangerously high
drawField(225, 0, 95, 78, " SWR ", Swr3Color);
} else if (swr > 2.5) {
showVal(swr, 254, 24, 3, 1, Swr25Color); // if SWR is between 2.5 and 3
drawField(225, 0, 95, 78, " SWR ", Swr25Color);
} else if (swr > 2) {
showVal(swr, 254, 24, 3, 1, Swr2Color); // if SWR is between to and 2.5
drawField(225, 0, 95, 78, " SWR ", Swr2Color);
} else {
showVal(swr, 254, 24, 3, 1, Swr1Color); // if SWR is below 2
drawField(225, 0, 95, 78, " SWR ", Swr1Color);
}
}
void drawBarFWD(int level) {
level = map(level, 0, 120, 0, 100); // map to scale. Although the SWR bridge can handle up to 150W, for my purpose 120W full scale is sufficient
int i = level * SCR_WD / 100;
tft.fillRect(0, xbar, i, barHeight, FwdColor); // draw the bar with the forward power
tft.fillRect(i, xbar, SCR_WD - i, barHeight, barBgColor); // bar background
}
void drawBarREF(int level) {
level = map(level, 0, 120, 0, 100);
int i = level * SCR_WD / 100;
tft.fillRect(0, xbar + nextbar, i, barHeight, RefColor); // draw the bar with the reflected power
tft.fillRect(i, xbar + nextbar, SCR_WD - i, barHeight, barBgColor);
}
void drawBarSWR(float level) {
level = level * 10;
level = map(level, 0, 50, 0, 100); // map to scale
int i = (level * SCR_WD / 100);
if (swr < 2) { // change color of the bar according to SWR value
tft.fillRect(0, xbar + nextbar * 2, i, barHeight, Swr1Color);
} else {
if (swr < 2.5) {
tft.fillRect(0, xbar + nextbar * 2, i, barHeight, Swr2Color);
} else {
if (swr < 3) {
tft.fillRect(0, xbar + nextbar * 2, i, barHeight, Swr25Color);
} else {
tft.fillRect(0, xbar + nextbar * 2, i, barHeight, Swr3Color);
}
}
}
tft.fillRect(i, xbar + nextbar * 2, SCR_WD - i, barHeight, barBgColor);
}
// Converting forward and reflected power from the SWR bridge.
// inREF, outREF, inFWD and outFWD values are obtained from measurements done on _my_ SWR bridge and transceiver at the same time.
// They cannot be used with another SWR bridge, you have to measure them yourself and replace them in here.
void ref2watt() {
refInVoltage = sensorREF * (5.0 / 1023.0); // convert the reflected input value to milliVolts
float outREF[] = { 0, 0.2, 0.4, 0.6, 0.8, 1, 1.5, 2, 4, 13, 16, 20, 42, 92, 110 }; // measured reflected power in Watts
float inREF[] = { 18, 118, 185, 230, 270, 310, 400, 465, 773, 1488, 1664, 1926, 2700, 3900, 4300 }; // measured voltage in milliVolts
outputValueREF = multiMap(refInVoltage * 1000, inREF, outREF, 15); // map these 15 values
outputValueREF = constrain(outputValueREF, 0, outputValueFWD); // reflected power cannot be higher than forwarded power
}
void fwd2watt() {
fwdInVoltage = sensorFWD * (5.0 / 1023.0); // same comments as in the void ref2watt() except that these are the forward values
float outFWD[] = { 0, 1, 1.5376, 2.6896, 4, 5.0176, 6.1504, 7.3984, 8.2944, 9.2416, 15.6816, 24.6016, 33.64, 45.56, 53.29, 64, 72.25, 81, 92.16, 100, 120 };
float inFWD[] = { 350, 370, 424, 581, 695, 790, 877, 959, 996, 1107, 1332, 1697, 2039, 2398, 2601, 2838, 3055, 3287, 3502, 3800, 4000 };
outputValueFWD = multiMap(fwdInVoltage * 1000, inFWD, outFWD, 21); // map these 21 values
}
void calcSWR() {
cswr = sqrt((outputValueREF) / (outputValueFWD)); // use formulas to calculate SWR
swr = ((1 + cswr) / (1 - cswr));
swr = constrain(swr, 1, swrTreshold);
}
void loop() {
sensorFWD = analogRead(fwdInPin); // read analog value of forwarded signal
sensorFWD = constrain(sensorFWD, 70, 1023); // constrain to prevent a negative output value
sensorREF = analogRead(refInPin); // read analog value of reflected signal
sensorREF = constrain(sensorREF, 0, 1023);
fwd2watt(); // get forward power
ref2watt(); // get reflected power
calcSWR(); // get calculated SWR
drawBarFWD(outputValueFWD); // and draw them graphically
drawBarREF(outputValueREF);
drawBarSWR(swr);
if (mode != lastMode) { // screen refresh
lastMode = mode;
tft.fillScreen(BLACK);
constData(); // display constant screen data
}
varData(); // display variable screen data
}