/*************************************************************** * * G0MGX Power Meter * * Arduino Sketch started 1st November 2012 as version 1.0 * * Version 1.1 3rd November :- * Added ISR for meter hold * Changed to simpler linear calculation for power * * Version 1.2 9th November :- * Removed ISR for meter hold - it was a stupid idea * Changed one of the switches to be a dbM/dbW toggle * ****************************************************************/ // include the library code #include // this library is needed for the LCD #include // and we need this for the base 10 log function // initialize the LCD with the Arduino numbers of the interface pins LiquidCrystal lcd(7, 6, 5, 4, 3, 2); // declare global variables int Calibrate_Pin = 8; // use pin 8 to take us into calibrate mode int Sample_Hold = 9; // use pin 9 to set the sample and hold chips hold thier current value int Forward_AtoD = A0; // A0 is the forward a to d converter int Reflected_AtoD = A1; // A1 is the reflected a to d converter int MeterPin = 10; // Pin 10 for the PWM output int db_toggle = 47; // Pin 49 is the digital input for the dbM/dbW toggle switch int Sample_Time = 49; // Pin 47 is the digital input for Short or Long time between samples int Forward_Value_Read = 0; // declare variables to store the values int Reflected_Value_Read = 0; // read from the A to D converters int AtoD_Samples = 0x400; // seems like a nice round software kind of number but it is 1024 in decimal long Forward_Accumulated_Value = 0; // used to hold the total of all AtoD samples for the Forward channel long Reflected_Accumulated_Value = 0; // used to hold the total of all AtoD samples for the Reflected channel float Forward_Watts = 0.0; // these are used to store the calcualted Watts and dBm float Reflected_Watts = 0.0; float Forward_dBm = 0.0; float Reflected_dBm = 0.0; float Calculated_SWR = 0.0; // these are used for SWR calculation float Prev_over_Pfwd = 0.0; float Root_Prev_over_Pfwd = 0.0; int PWM_Value_of_SWR = 0; // this is the value used to drive the PWM output for the analogue SWR meter // ****************************************************** // These are the calibration determined values for the // forward and reverse equations in the spreadsheet // ****************************************************** float Forward_Alpha = 0.0948; float Forward_Beta = -37.658; float Reflected_Alpha = 0.0931; float Reflected_Beta = -35.413; // ****************************************************** // ****************************************************** void setup() { // this code runs once at startup and should be used to initiailise stuff // set the analoge inputs to reference the on board 2.56V reference (the default is 5V) // this ensures that the output of the AD8307 uses the largest range possible in the AtoD inputs // and gives us the best resolution for this application possible analogReference(INTERNAL2V56); // set up the LCD's number of columns and rows: lcd.begin(20, 4); // initialise IO pins pinMode(Calibrate_Pin, INPUT); // Calibrate pin is an input digitalWrite(Calibrate_Pin, HIGH ); // internal pull up on Calibrate pinMode(Sample_Hold, OUTPUT); // The sample and hold pin is an output digitalWrite(Sample_Hold, HIGH); // internal pull up to set sample as default pinMode(Forward_AtoD, INPUT); // The pins A0 and A1 are the forward and pinMode(Reflected_AtoD, INPUT); // reflected inputs respectively pinMode(db_toggle, INPUT); // The db toggle pin is input digitalWrite(db_toggle, HIGH); // internal pull up to set high as default (switch pulls to ground) pinMode(Sample_Time, INPUT); // The Meter_Hold pin is input digitalWrite(Sample_Time, HIGH); // internal pull up to set high as default (switch pulls to ground) // display welcome message once init_message(); // bad magic.... // initialise the PWM output to 0 init_PWM(); // do we need to enter calibrate mode if (digitalRead(Calibrate_Pin)==HIGH) // is the Calibrate pin high? (my switch is push to break so normally it would be grounded through the switch) { Calibrate_Mode(); // if it has then call the calibrate routine which should never return } else if (digitalRead(Calibrate_Pin)==LOW) // otherwise { // not calibrate mode so we can initialise the screen lcd.setCursor(0, 0); lcd.print(" G0MGX Power Meter"); // display the banner text on the LCD } } void loop() { // the code in here runs repeatedly once the stuff in startup is done. // so we start by reading the ADC values Read_ADC_Values(); // convert the read ADC numbers into power Calculate_Power(); // Now calculate the SWR from the power readings Calculate_SWR(); // Push the meter as far as necessary Set_SWR_Meter(); // check the switch settings and update display lcd.setCursor(16,3); if (digitalRead(db_toggle) == LOW) { lcd.print("dBm"); } else { lcd.print("dBw"); } lcd.setCursor(12,3); if (digitalRead(Sample_Time) == LOW) { lcd.print("Sht"); } else { lcd.print("Lng"); } // So now we display results on the screen lcd.setCursor(0, 1); lcd.print("FWD: "); if (Forward_Watts < 0.01) // dont bother outputing anything if the values are really low. { lcd.print("- "); } else { if (digitalRead(db_toggle) == LOW) { lcd.print(round(Forward_dBm)); // I've chosen a display in dbM lcd.print("dBm "); } else { lcd.print(round(Forward_dBm - 30)); // I've chosen a display in dbW so it's 30 less than the dbM value lcd.print("dBw "); } lcd.setCursor(12,1); lcd.print("("); lcd.print(round(Forward_Watts)); lcd.print(") "); } lcd.setCursor(0, 2); lcd.print("REF: "); if (Reflected_Watts < 0.01) // dont bother outputing anything if the values are really low. { lcd.print("- "); } else { if (digitalRead(db_toggle) == LOW) { lcd.print(round(Reflected_dBm)); // I've chosen a display in dbM lcd.print("dBm "); } else { lcd.print(round(Reflected_dBm - 30)); // I've chosen a display in dbW so it's 30 less than the dbM value lcd.print("dBw "); } lcd.setCursor(12,2); lcd.print("("); lcd.print(round(Reflected_Watts)); lcd.print(") "); } lcd.setCursor(0,3); lcd.print("SWR: "); if (round(Reflected_Watts) <= 0) // dont bother outputing anything if the values are really low. { lcd.print("- "); } else { if (Calculated_SWR > 20.0) { lcd.print(">20 "); } else { lcd.print(Calculated_SWR); } } } // routine to display the initial power on message void init_message() { lcd.setCursor(0,0); lcd.print(" G0MGX Power Meter"); lcd.setCursor(0,1); lcd.print("Arduino version 1.20"); lcd.setCursor(0,2); lcd.print(" Bad Magic..."); // always in the junk I write somewhere.... delay(2000); // hold the boat for 2 seconds (2000 ms) lcd.clear(); } // routine to read the two ADC channels void Read_ADC_Values() { // initialise the accumulators for this set of samples Forward_Accumulated_Value = 0; Reflected_Accumulated_Value = 0; // We are going to take 'AtoD_Samples' number of samples for (int for_counter = 0; for_counter < AtoD_Samples; for_counter++) { // start the for loop // so lets set the sample and hold chips into hold mode // by writing the digital output LOW digitalWrite(Sample_Hold, LOW); // the sample and hold should now be holding the value // now read the forward power channel Forward_Value_Read = analogRead(Forward_AtoD); // and then the reflected power channel Reflected_Value_Read = analogRead(Reflected_AtoD); digitalWrite(Sample_Hold, HIGH); // Release the sample and hold devices back to sample // so now we have the A to D average values, lets add them into the accumulated values Forward_Accumulated_Value = Forward_Accumulated_Value + Forward_Value_Read; Reflected_Accumulated_Value = Reflected_Accumulated_Value + Reflected_Value_Read; // now see if we are sampling at maximum bananas or slower if (digitalRead(Sample_Time)==HIGH) // is switch set? { delay(2); // delay between samples by 2ms (remember we take 'AtoD_Samples' samples anyhow!) } } // for loop end // now we are out of the for loop and have completed sampling // we can now average our accumulated result and put the results in the appropriate variables Forward_Value_Read = Forward_Accumulated_Value / AtoD_Samples; Reflected_Value_Read = Reflected_Accumulated_Value / AtoD_Samples; } // routine to convert the values read from the ADC into dbM and Watts void Calculate_Power() { // We use the constants defined before to solve the equation // dbM = Alpha ADC + Beta (where ADC = ADC value and Alpha and Beta are constants) // We originally determine the values of Alpha and Beta from the spreadsheet // First lets do the Forward Power Channel Forward_dBm = ( Forward_Value_Read * Forward_Alpha ) + Forward_Beta; Forward_Watts = (pow(10,(Forward_dBm/10)) / 1000); // Now let's do it again but for the reflected power values Reflected_dBm = ( Reflected_Value_Read * Reflected_Alpha ) + Reflected_Beta; Reflected_Watts = (pow(10,(Reflected_dBm/10)) / 1000); } // routine for calibration - tie pin 8 to ground void Calibrate_Mode() { // so we are going into calibrate mode, here we want to // read the two Analogue inputs and display the AtoD values // on the LCD. We also want to set the PWM output for the // SWR meter to be full scale to set the meter calibrate pot // start by telling the world that we are in calibrate mode lcd.setCursor(0,0); lcd.print(" G0MGX Power Meter"); // the first thing anyone needs to do is put your own call here! lcd.setCursor(0,1); // then anything you wish to complain about becomes your problem lcd.print(" Calibrate Mode"); delay(2000); // delay for 2 seconds (2000 ms) lcd.clear(); // now lets set the PWM output to drive the meter full scale for calibration // the output range is from 0 to 255 or FF hex analogWrite(MeterPin, 0xFF); // now lets display some useful text on the screen lcd.setCursor(0,0); lcd.print(" Forward = "); lcd.setCursor(0,1); lcd.print("Reflected = "); while (1){ // here we start to loop forever // call the routine to read the ADC values Read_ADC_Values(); // and tell the world the results lcd.setCursor(12,0); lcd.print(Forward_Value_Read); lcd.print(" "); lcd.setCursor(12,1); lcd.print(Reflected_Value_Read); lcd.print(" "); } // end of my infinate while loop so the whole process repeats } // routine to calculate SWR from the Power values void Calculate_SWR() { if (Reflected_Watts < 0.01) // if the reflected power is small dont bother calculating the SWR { Prev_over_Pfwd = 0.0; Calculated_SWR = 1.0; } else { Prev_over_Pfwd = Reflected_Watts / Forward_Watts; Root_Prev_over_Pfwd = sqrt(Prev_over_Pfwd); Calculated_SWR = ((1 + Root_Prev_over_Pfwd) / ( 1 - Root_Prev_over_Pfwd)); } } // routine to set the PWM output to 0 void init_PWM() { // simply initialise the PWM output to be 0 meter deflection analogWrite(MeterPin, 0); // tricky, hugh? } // routine to set the power meter reflection from the calculated SWR void Set_SWR_Meter() { // I kind of stumbled on the clculation used here - // you may wish to change it but anything over 13 SWR will be full scale and its logarithmic // so its probably close to whats needed // as my meter is labeled "none" "some" and "lots" it doesnt matter so much // I only ever intended this to be a tuning aid - the accurate SWR is on the LCD // if we are not displaying any reflected power numbers, then dont bother with the SWR indication if (round(Reflected_Watts) <= 0) // low power reflected and we are not holding the meter high { PWM_Value_of_SWR = 0; } else { PWM_Value_of_SWR = ((10 * log10(Calculated_SWR)) / 13) * 255; // the 255 ensures its in the PWM output range of 0 .. 255 or FF hex } // so just write out the value we've calculated to the meter analogWrite(MeterPin, PWM_Value_of_SWR); }