Finally got around to working on the hardware and software for the new SNA. I decided to build the hardware in stages and test each as I went along. After getting the ESP32 module and display working, I started on the AD8307 power meter circuit. One of the differences between my version and the UR5FFR circuit, is that I use a separate AD8307 instead of switching between inputs. This made the board layout a lot easier, and I already had several AD8307s on hand.
I partially populated the input portion of the circuit board, and started working on the software. I want to be able to use this for broadband measurements of low power signals in dBm., and with a 40 dB. tap to measure levels up to around 100 Watts. There will be four ranges +15dBm with direct input, and 1, 10, 100 Watts using an external 40 dB tap. Range selection is made by moving the Joystick in a up or down direction. I decided I wanted to measure Peak value and the Average power during a user selectable time window. I currently have time windows of 0.1, 1, and 2.5 seconds. The time window is selected by moving the Joystick to the right or left. The use of the Joystick for the input device has made the changing of parameters much easier than it had been when I was using a rotary encoder and switches. With the Joystick, I can easily control two different parameters without having to create a sub-menu. I still have the ability to look for either a Short or Long press on the Joystick pushbutton . A short press will mostly be used to toggle a function or operation on or off. In all cases I have decided that a Long press will be used to get out of whatever modality or setting screen you are in and return to the main or upper menu.
The power meter screen will use my standard simulated analog meter with the scale changing with the range selection. The Peak reading during the time window will be displayed, along with the Average power over the entire time period. I also display the current range and time window.
The software for this is a basic While loop. It first uses the millis() function to get the start time then checks current time before each time through the loop. It exits the loop when the selected window time is reached. During each pass through the loop it checks for a higher peak reading and updates a sum and counter that are used to compute the average, I also store the last reading. I compare the current reading with the previous to determine if I should redraw the needle and print current value. This helps a lot with eliminating a flicker on the meter.
UPDATE 2/17/19
tft.setTextColor( BLACK, WHITE);
while ((millis() - startTime) < pmWindowTimes[pmWindow]) { // sample for window
adc = analogRead(pmADCpin); // get reading
average = average + adc; // add readings to compute average
if (adc > peak) peak = adc; // check if a new peak reading
count++; // increment count for computing average
pmDB = ADC2dBm(adc); // calculate dB value times 10
if ((abs(lastAdc - adc)) > 4) { // limit change size to reduce jitter
// erase old needle and draw in new position
tft.fillRect(DB_MTR_X + 2, DB_MTR_Y + 30, DB_MTR_W - 4, DB_MTR_H - 40, WHITE);
if (pmRange == 0) { // dBm direct input
plotNeedle(DB_MTR_X, DB_MTR_Y, DB_MTR_W, DB_MTR_H, adc, 3300, BLACK);
lastAdc = adc; // save last reading for comparrison
}
else { // Watts using 40 dB tap
pmWatt = dBm2Watt( pmDB); // convert dbm reading to watts
pmWatt = int( pmWatt * (1000 / pmRangeVal[pmRange])); // prescale
if (pmWatt > 1000)pmWatt = 1050; // keep needle on meter face
plotNeedle(DB_MTR_X, DB_MTR_Y, DB_MTR_W, DB_MTR_H, pmWatt , 1080, BLACK);
lastWatt = pmWatt; // save last reading for comparrison
}
tft.setCursor(120, 150);
printPMValues( pmDB);
}
}
average = average / count; // compute average and print
pmAverage = ADC2dBm(average); // convert adc readings and store in global
pmPeak = ADC2dBm(peak);
Made some changes to the loop that updates the display to reduce some of the jitter I was seeing. Also took care of some minor issues, such as keeping the needle display on the meter if value goes over range. After a lot of playing around, I was able to come up with a single function to print the power value correctly scaled for whatever range is selected.
void printPMValues( float value) {
switch (pmRange) {
case 0: // value directly in dBm.
tft.print ( value / 10, 1);
tft.println (" dBm. ");
break;
case 1: // value in mWatt
value = dBm2Watt( value ); // convert from dBm to watts
tft.print (value * 1000, 0); // scale up for mw display
tft.println (" mWatt ");
break;
case 2: // value in Watts 2 decimal places
value = dBm2Watt( value ); // convert from dBm to watts
tft.print (value , 2);
tft.println (" Watt ");
break;
case 3: // value in Watts only one decimal place
value = dBm2Watt( value ); // convert from dBm to watts
tft.print ( value , 1);
tft.println (" Watt ");
break;
}
}
This function is used for the value displayed on the meter, and the Peak and Average values.
I did a manual calibration using a signal source and a fixed attenuator to determine the ADC counts per dB. Later I will add a calibration function and have the value stored in eeprom.
Next I have decided to reuse some of these functions and add a modulation monitor. This will display the power output on a simulated oscilloscope screen. I think I will have both a Rectified and a RF envelope display. This should help in setting Mic. gain levels to prevent distortion.