//libraries
#include <LiquidCrystal.h>
//20241115 - This program runs a simple Stockton-Bridge-Type SWR & Power meter.
// It employs 3 "modes": Power & SWR combined- numeric: Power- Numeric
// and Bar Graph; SWR- Numeric and bar graph.
// Those readings are across 2 ranges accomplished by reading directly
// on "low" range, and dividing by 2 (using a switched MOSFET) on "High"
// range. Computations are then made to the raw readings, and displayed
// on a LCD.
// Pin assignments
//Digital-
#define FWD_DIV 9 // FWD Reading Divider switchfwd_max
#define FWD_MULT 10 // FWD Reading multiplier switch
#define REV_DIV 11 // REV reading divider switch
#define REV_MULT 12 // REV reading Multiplier Switch
#define PIN_MODE 7 // Mode Switch Input
//Analog-
#define PIN_SWR_FWD A1 // Analog pin for SWR bridge forward
#define PIN_SWR_REV A0 // Analog pin for SWR bridge reverse
#define PIN_FW_REF A2 // Analog pin for switching reference
//Define variables
int n;
int z = 0;
float fwd_max = 0; //initialize all computation variables
float rev_max = 0;
float fwd_val = 0;
float temp = 0;
float pwr = 0;
float SWR = 0;
int modality = 0;
//Define the LCD-
//LiquidCrystal lcd(rs, enable, d4, d5, d6, d7) below
LiquidCrystal lcd(0, 1, 3, 4, 5, 6);
//**********************************************************************************
// Define the custom LCD blocks for use in the Bar Graph
//**********************************************************************************
uint8_t full[8] = {
0b11111,
0b11111,
0b11111,
0b11111,
0b11111,
0b11111,
0b11111,
0b11111,
}; //A full block
uint8_t single[8] = {
0b10000,
0b10000,
0b10000,
0b10000,
0b10000,
0b10000,
0b10000,
0b10000,
}; //1 column
uint8_t dual[8] = {
0b11000,
0b11000,
0b11000,
0b11000,
0b11000,
0b11000,
0b11000,
0b11000,
}; //2 columns
uint8_t triple[8] = {
0b11100,
0b11100,
0b11100,
0b11100,
0b11100,
0b11100,
0b11100,
0b11100,
}; //3 columns
uint8_t quad[8] = {
0b11110,
0b11110,
0b11110,
0b11110,
0b11110,
0b11110,
0b11110,
0b11110,
}; //4 columns
//**********************************************************************************
//**********************************************************************************
// This is a universal routine to read and average any analog input
//**********************************************************************************
int read_Analog_pin(int p)
{
// Take an averaged reading of Analog pin 'p'
int i, val=0, nbr_reads=20;
for (i=0; i<nbr_reads; i++)
{
val += analogRead(p);
}
return val/nbr_reads;
};
//**********************************************************************************
//This routine actually reads and stores all 3 analog inputs
//**********************************************************************************
void READ_SWR()
{
n = analogRead(PIN_SWR_FWD);
delay(100);
fwd_max = n;
fwd_val = n;
n = analogRead(PIN_SWR_REV);
delay(100);
rev_max = n;
};
//**********************************************************************************
// This routine reads the FWD ADC to set the "range" switches appropriately
//**********************************************************************************
void take_reading() {
unsigned int z=0; //declare a temporary variable
delay(100);
z = analogRead(PIN_FW_REF);
} //end reading
//**********************************************************************************
//**********************************************************************************
// Below are the display "Templates": Basic (both Pwr and SWR, numeric only),
// PWR: Power numeric and Bar Graph
// SWR: SWR both numeric and bar graph
//**********************************************************************************
//This is the basic, starting, display template. Both PWR and SWR are the same size
//**********************************************************************************
void display_basic()
{
lcd.clear();
lcd.setCursor(0,0);
lcd.print("PWR");
lcd.setCursor(5,0);
lcd.print(pwr);
lcd.print(" W ");
lcd.setCursor(0, 2); //the above writes the numeric PWR value to the lcd
//Test to see if there is ANY SWR info. If there isn't you get a NaN error
if (fwd_max ==0) //Test to tell if there's no input to the unit at all
{
lcd.print("Low SWR Signal"); //If there's no input, this avoids a "divide by zero" error
}
else
{
lcd.print("SWR");
lcd.setCursor(5, 2);
lcd.print(SWR, 2);
lcd.print(":1 "); //The above 4 lines simply print the SWR numeric
}
};
//***************************************************************************
//This display template only displays POWER: numeric on top and bar graph below
//***************************************************************************
void display_pwr() //
{
lcd.clear();
lcd.setCursor(0,0);
lcd.print("PWR ");
lcd.setCursor(5, 0);
lcd.print(pwr);
lcd.print(" W"); //the above writes the numeric PWR value to the lcd
//Now we make the bar graph
lcd.setCursor(0, 2); //write to the bottom row
temp = pwr;
if (temp >= 10) //This section writes a bar graph for higher power. See
// below for <10W
{
while (temp >= 10) { //this loop writes a solid block for every decade
//of power
temp = temp-10;
lcd.write(1); //(1)
}; //end while
// Now, with the decades written, the below determines the remaining number and
// assigns one of the custom blocks to it
if (temp <= 10 && temp >=7) {
lcd.write (5); //(5)
}
else if (temp <7 && temp >= 5) {
lcd.write(4); //(4)
}
else if (temp < 5 && temp >= 3) {
lcd.write(3); //(3)
}
else if (temp < 3 && temp >=0 ) {
lcd.write(2); //(2)
}
} //end if
//*********************************
else if (temp <10) //The below routine draws a bar graph for low power
{
while (temp >= 1) //this loop draws a block for each integer
{
temp = temp -1;
lcd.write(1);
}; //end while
//with the integers drawn, the below assigns a custom character to the remaining
// power info
if (temp <= 1 && temp >=.7) {
lcd.write (5); //(5)
}
else if (temp <.7 && temp >= .5) {
lcd.write(4); //(4)
}
else if (temp < .5 && temp >= .3) {
lcd.write(3); //(3)
}
else if (temp < .3 && temp >=0 ) {
lcd.write(2); //(2)
}
}; //end else if
}; //end display_pwr
//************************************************************************************************
void display_swr() //This template displays SWR only. Numeric on top, bar
// graph below
//************************************************************************************************
{
lcd.clear();
lcd.setCursor(0, 0);
if (fwd_max ==0) //Test to see if the SWR input exists. If not, you get an
// "divide by 0" error
{
lcd.print("Input Power Low");
lcd.setCursor(4,2);
lcd.print("For SWR");
} //end if
else
{
lcd.print("SWR");
lcd.setCursor(5,0);
lcd.print(SWR, 2);
lcd.print(":1 "); //The above writes the SWR value to the lcd
} //end else
lcd.setCursor(0,2);
temp = SWR;
if (temp >= 1)
{
temp = temp -1; //so I won't show block for 1:1
while (temp >= 1) {
temp = temp-1;
lcd.write(1); //1 block is simply too small for each "1" of SWR, thus 5
lcd.write(1);
lcd.write(1);
lcd.write(1);
lcd.write(1);
};
if (temp <= 1 && temp >=.7)
{
lcd.write (5);
}
else if (temp <.7 && temp >= .5)
{
lcd.write(4);
}
else if (temp < .5 && temp >= .3)
{
lcd.write(3);
}
else if (temp < .3 && temp >=0 )
{
lcd.write(2);
}
} //end if
}; //end display_swr
//**********************************************************************************
//**********************************************************************************
//I borrowed this from :
// https://forum.arduino.cc/t/question-about-changing-modes-with-a-button/425553
boolean buttonPressed()
{
static boolean lastButtonState;
boolean buttonState=digitalRead(PIN_MODE);
if (buttonState!=lastButtonState)
{
lastButtonState=buttonState;
if (buttonState==HIGH) return true;
}
return false;
}
//**********************************************************************************
void setup()
{
lcd.begin(16, 2); //declare the type of LCD
// Print an opening message to the LCD.
lcd.home();
lcd.print("W2EB SWR/PWR");
lcd.setCursor(6, 1);
lcd.print(" Final ");
pinMode(FWD_DIV, OUTPUT);
pinMode(REV_DIV, OUTPUT);
pinMode(FWD_MULT, OUTPUT);
pinMode(REV_MULT, OUTPUT);
pinMode(PIN_MODE, INPUT_PULLUP); //enable pullup resistor for the MODE switch
delay(2000);
lcd.clear();
digitalWrite(FWD_DIV, 1); // Turn the FWD Divider "ON"
digitalWrite(REV_DIV, 1); // Turn the REV divider "ON"
float pwr = 0;
float SWR = 0;
modality = 0;
READ_SWR(); //Take an initial ADC Reading
display_basic();
lcd.createChar(1, full);
lcd.createChar(2, single);
lcd.createChar(3, dual);
lcd.createChar(4, triple);
lcd.createChar(5, quad);
} //End Setup
//*****************************************************************************************
void loop() //Loop is the actual program that runs ("loops") repeatedly
{
//Pre-fill the FWD and REV values for use later when we display
//**********************************************************************************
READ_SWR();
//**********************************************************************************
//Take a reference reading, place it in "Z"
//**********************************************************************************
z = analogRead(PIN_FW_REF);
//**********************************************************************************
// Choose how to set the switches and determine the formula
//**********************************************************************************
if(z >= 789) //Over range (Z > 3.85V)
{
digitalWrite(FWD_DIV, 1); // Turn the FWD Divider "ON"
digitalWrite(REV_DIV, 1); // Turn the REV divider "ON"
lcd.clear();
lcd.setCursor(0,0);
lcd.print("Over Range"); //reading was too high, it's over range
loop(); //Don't bother to continue doing anything. Just keep "Over Range"
// displayed and read again.
}; //End "Over Range"
if(z >= 409 && z <= 788) //High Power Range (2V to 3.85V)
{
digitalWrite(FWD_DIV, 1); // Turn the FWD Divider "ON"
digitalWrite(REV_DIV, 1); // Turn the REV divider "ON"
READ_SWR();
SWR = (fwd_max + rev_max)/(fwd_max-rev_max); //the actual SWR calculation
if (SWR <1)
{
SWR = 1;
}
pwr = ((fwd_val*1.414*.00488*2)*(fwd_val*1.414*.00488*2));
// .00488 converts reading to voltage. The rest was found empirically from
// physical measurements. Multiplied by 2 because the input is divided by 2.
}; //End "compute_high"
if(z <= 408) //Low-Power Range (.0V to 2V)
{
digitalWrite(FWD_DIV, 0); // Turn the FWD Divider "OFF"
digitalWrite(REV_DIV, 0); // Turn the REV divider "OFF"
READ_SWR();
SWR = (fwd_max + rev_max)/(fwd_max-rev_max); //the actual SWR calculation
if (SWR <1)
{
SWR = 1;
}
pwr = ((fwd_val*1.414*.00488)*(fwd_val*1.414*.00488)); // *.00488 converts
//reading to voltage. The rest was found empirically from
//physical measurements
}; //end "compute_mid" IF
//**********************************************************************************
// Choose the display template
//**********************************************************************************
switch(modality){
case 0:
while(modality==0)
{
display_pwr();
if (buttonPressed()){
modality=1;
}
break;}
case 1:
while(modality==1)
{
display_basic();
if (buttonPressed()){
modality=2;
}
break;}
case 2:
while(modality==2)
{
display_swr();
if (buttonPressed()){
modality=0;
}
break;}
}
} //End Loop