Electronic Design and Family Site

Arduino Firmware for the LCD SWR & RF Power Meter

//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