#include <Wire.h>
#include <LiquidCrystal.h>
#include <math.h>
LiquidCrystal lcd(12, 11, 10, 9, 8, 7);
const int analogPinF = A0; // Forward Power
const int analogPinR = A1; // Reflected Power
#define Buzzer 3 // output for Buzzer control (1 = ON)
#define buttonMenuPin 4 // Menu Button
#define buttonSelect 2 // Select Button
float referenceVoltage = 5.0;
float adcValueF = 0;
float voltageF = 0;
double powerdBmF = 0;
double powerdBmR = 0;
float adcValueR = 0;
float voltageR = 0;
double vswr = 1.0;
double refl_coeff = 0;
double pow_fwd = 0; // power forward (watts)
double pow_ref = 0; // power reflected (watts)
// Smoothing variables for slow decay (ADDED)
float smoothedPowerFwd = 0;
float smoothedPowerRef = 0;
// Bar Peak Hold Variables
double peakPowerFwd = 0;
double peakPowerRef = 0;
unsigned long peakHoldTimer = 0;
int peakHoldTime = 0;
int peakHoldTime_Fast = 1000; // 1 seconds
int peakHoldTime_Slow = 2000; // 2 seconds
const int PEAK_MOVE_INTERVAL = 500; // milliseconds between peak movements
const float smoothingFactor = 0.05; // Lower = slower decay (0. 05 = very slow 0.3 = Faster decay (more responsive))
// Progress Bar Variables
int previousValue = -1;
int currentValue = 0;
int peakValue = 0;
unsigned long peakTimer;
const int holdTime = 500; // Bar peak hold time
const int decayStep = 0.5; // Bar peak decay Step
unsigned long lastUpdateLCD = 0;
const unsigned long updateIntervalLCD = 2;
// Progress Bar Characters (6 custom characters)
byte barChars[6][8] = {
{0b00000, 0b00000, 0b00000, 0b00000, 0b10101, 0b00000, 0b00000, 0b00000},
{0b00100, 0b00000, 0b00100, 0b00000, 0b00100, 0b00000, 0b00100, 0b00000},
{0b00000, 0b01010, 0b11111, 0b11111, 0b01110, 0b00100, 0b00000, 0b00000}, // heart
{0b11110, 0b11110, 0b11110, 0b11110, 0b11110, 0b11110, 0b11110, 0b11110},
{0b01110, 0b01110, 0b01110, 0b01110, 0b01110, 0b01110, 0b01110, 0b01110},
{0b11111, 0b11111, 0b11111, 0b11111, 0b11111, 0b11111, 0b11111, 0b11111}
};
int menuIndex = -1;
bool fastMode = true;
bool forwardPower = true;
bool rmsValue = true;
int currentBand = 0; // 0-7 = Band1-band7
bool buttonPressed = false;
unsigned long lastButtonPressTime = 0;
const unsigned long menuTimeout = 3000; // 3 seconds
struct BandCalibration {
float slopeF;
float interceptF;
float slopeR;
float interceptR;
};
// Array for each of the 7 bands (Band1 to Band7)
BandCalibration bandCal[8] = {
{26.651, 52.942, 27.481, 56.212}, // Band 0 - Default Average Calibration
{26.651, 52.942, 27.481, 56.212}, // Band 1 Firsth pair is FWD, Second REF
{26.651, 52.942, 27.481, 56.212}, // Band 2
{26.651, 52.942, 27.481, 56.212}, // Band 3
{26.651, 52.942, 27.481, 56.212}, // Band 4
{26.651, 52.942, 27.481, 56.212}, // Band 5
{26.651, 52.942, 27.481, 56.212}, // Band 6
{26.651, 52.942, 27.481, 56.212} // Band 7
};
const char* bandNames[] = {
"Def", // index 0
"160m", // index 1
"80m", // index 2
"60-40m", // index 3
"30-20m", // index 4
"17-15m", // index 5
"12-10m", // index 6
"6m" // index 7
};
const unsigned long TX_BAR_TIMEOUT = 5000; // 5 seconds timeout of ProgressBar
////
int currentPower = 0; // Current power being displayed (forward or reflected)
int peakPower = 0; // Peak power being displayed
int targetPeak = 0;
bool showForwardPower = true; // Flag to indicate which power is being shown
float animatedPeakPosition = 0;
unsigned long lastPeakUpdate = 0;
void setup() {
lcd.begin(16, 2);
analogReference(DEFAULT);
pinMode(buttonMenuPin, INPUT_PULLUP);
pinMode(buttonSelect, INPUT_PULLUP);
digitalWrite(Buzzer, 0);
pinMode(Buzzer, OUTPUT);
peakTimer = millis();
// Create custom characters (only 6 instead of 8)
for (int i = 0; i < 6; i++) {
lcd.createChar(i, barChars[i]);
}
}
float readAveragedADC(int pin, int samples) {
float total = 0;
for (int i = 0; i < samples; i++) {
total += analogRead(pin);
}
return total / samples;
}
int readADC(int pin) {
return analogRead(pin);
}
void loop() {
readButtons();
if (menuIndex >= 0 && millis() - lastButtonPressTime > menuTimeout) {
menuIndex = -1; // Return to normal display
}
if (menuIndex == -1) {
if (forwardPower) UpdateTXScreen();
else UpdateTXScreenRef();
}
else if (millis() - lastUpdateLCD > updateIntervalLCD) {
displayMenu();
lastUpdateLCD = millis();
}
if (rmsValue) {
adcValueF = readADC(analogPinF);
adcValueR = readADC(analogPinR);
}
else {
adcValueF = readAveragedADC(analogPinF, 2);
adcValueR = readAveragedADC(analogPinR, 2);
}
voltageF = (adcValueF / 1023.0) * referenceVoltage;
powerdBmF = (bandCal[currentBand].slopeF * voltageF) - bandCal[currentBand].interceptF;
pow_fwd = pow(10, (powerdBmF - 30) / 10);
voltageR = (adcValueR / 1023.0) * referenceVoltage;
powerdBmR = (bandCal[currentBand].slopeR * voltageR) - bandCal[currentBand].interceptR;
pow_ref = pow(10, (powerdBmR - 30) / 10);
// Apply smoothing to power readings (ADDED)
smoothedPowerFwd = smoothingFactor * pow_fwd + (1 - smoothingFactor) * smoothedPowerFwd;
smoothedPowerRef = smoothingFactor * pow_ref + (1 - smoothingFactor) * smoothedPowerRef;
if (pow_fwd > 1 && pow_fwd > pow_ref) {
refl_coeff = abs(sqrt(pow_ref / pow_fwd));
vswr = (1 + refl_coeff) / (1 - refl_coeff);
}
else if (pow_fwd <= pow_ref && pow_fwd > 10) {
vswr = 99.9; // effectively infinite
}
else {
vswr = 1.00;
}
if (fastMode) {
peakHoldTime = peakHoldTime_Fast;
}
else {
peakHoldTime = peakHoldTime_Slow;
}
}
void UpdateTXScreen() {
if (millis() - lastUpdateLCD > updateIntervalLCD) {
showForwardPower = true;
// Peak Hold Logic
if (pow_fwd > peakPowerFwd) {
peakPowerFwd = pow_fwd;
peakHoldTimer = millis();
}
if (millis() - peakHoldTimer > peakHoldTime) {
peakPowerFwd = pow_fwd;
}
lcd.setCursor(0, 0);
lcd.print("Po:");
lcd.print((int)peakPowerFwd);
lcd.print("W ");
lcd.setCursor(9, 0);
lcd.print("SW:");
lcd.print(vswr, 2);
lcd.print(" ");
drawProgressBar();
lastUpdateLCD = millis();
}
}
void UpdateTXScreenRef() {
if (millis() - lastUpdateLCD > updateIntervalLCD) {
showForwardPower = false;
// Peak Hold Logic
if (pow_ref > peakPowerRef) {
peakPowerRef = pow_ref;
peakHoldTimer = millis();
}
if (millis() - peakHoldTimer > peakHoldTime) {
peakPowerRef = pow_ref;
}
lcd.setCursor(0, 0);
lcd.print("Pr:");
lcd.print((int)peakPowerRef);
lcd.print("W ");
lcd.setCursor(9, 0);
lcd.print("SW:");
lcd.print(vswr, 2);
lcd.print(" ");
drawProgressBar();
lastUpdateLCD = millis();
}
}
void drawProgressBar() {
static float lastValidPower = 0; // Stores the last non-zero power reading
float power = showForwardPower ? smoothedPowerFwd : smoothedPowerRef;
float peak = showForwardPower ? peakPowerFwd : peakPowerRef;
// If power suddenly drops to 0, use lastValidPower for slow decay
if (power > 0) {
lastValidPower = power; // Update last valid power if current power > 0
}
else if (lastValidPower > 0) {
// If power=0 but lastValidPower is still decaying, continue smoothing
power = lastValidPower;
lastValidPower *= 0.95; // Force slow decay (adjust multiplier for speed)
if (lastValidPower < 0.1) lastValidPower = 0; // Stop decay when close to 0
}
// Timeout check (unchanged)
if (millis() - peakHoldTimer > TX_BAR_TIMEOUT && power < 1) {
showMenuStatus();
return;
}
// Unified power scaling function (unchanged)
auto scalePower = [](float pwr) -> int {
if (pwr < 1) return 0;
else if (pwr > 1 && pwr < 100) return map(pwr, 0, 100, 0, 16);
else if (pwr > 100 && pwr < 500) return map(pwr, 0, 500, 0, 16);
else return map(pwr, 0, 1500, 0, 16);
};
currentPower = scalePower(power);
int targetPeak = scalePower(peak);
// Update animated peak position (unchanged)
if (millis() - lastPeakUpdate > PEAK_MOVE_INTERVAL) {
if (targetPeak > animatedPeakPosition) {
animatedPeakPosition = targetPeak;
} else if (animatedPeakPosition > currentPower) {
animatedPeakPosition -= 0.5;
} else {
animatedPeakPosition = currentPower;
}
lastPeakUpdate = millis();
}
// Draw the progress bar (unchanged)
lcd.setCursor(0, 1);
for (int i = 0; i < 16; i++) {
if (i < currentPower) {
lcd.write(uint8_t(5));
} else {
lcd.write(uint8_t(0));
}
}
// Draw peak indicator (unchanged)
if (animatedPeakPosition > currentPower && animatedPeakPosition <= 16) {
lcd.setCursor((int)animatedPeakPosition - 1, 1);
lcd.write(uint8_t(5));
}
}
void showMenuStatus() {
lcd.setCursor(0, 1);
lcd.print(forwardPower ? "FW" : "RE");
lcd.print(" ");
lcd.print(rmsValue ? "RM" : "AV");
lcd.print(" ");
lcd.print(fastMode ? "Fst" : "Slw");
lcd.print(" ");
lcd.print(bandNames[currentBand]);
lcd.print(" ");
}
void readButtons() {
buttonPressed = false;
// MENU button
if (digitalRead(buttonMenuPin) == LOW) {
delay(150);
menuIndex++;
if (menuIndex > 3) menuIndex = 0;
buttonPressed = true;
}
// Select button
if (digitalRead(buttonSelect) == LOW) {
delay(150);
switch (menuIndex) {
case 0: fastMode = !fastMode; break;
case 1: togglePowerDisplay(); break;
case 2: rmsValue = !rmsValue; break;
case 3: currentBand = (currentBand + 1) % 8; break;
}
buttonPressed = true;
}
if (buttonPressed) {
lastButtonPressTime = millis();
lcd.clear();
delay(150);
}
}
void displayMenu() {
lcd.setCursor(0, 0);
switch (menuIndex) {
case 0: lcd.print("Speed: "); lcd.print(fastMode ? "Fast" : "Slow"); break;
case 1: lcd.print("Display: "); lcd.print(forwardPower ? "FWD" : "REF"); break;
case 2: lcd.print("Mode: "); lcd.print(rmsValue ? "RMS" : "AVG"); break;
case 3: lcd.print("Band: "); lcd.print(bandNames[currentBand]); break;
}
lcd.setCursor(0, 1);
lcd.print("Use UP/DOWN keys");
}
void togglePowerDisplay() {
forwardPower = !forwardPower;
lcd.clear();
if (forwardPower) {
UpdateTXScreen();
} else {
UpdateTXScreenRef();
}
}
void beepBuzzer(int pin, int frequency, int beepDuration, int pauseDuration, int beepCount) {
for (int i = 0; i < beepCount; i++) {
tone(pin, frequency, beepDuration);
delay(beepDuration + pauseDuration);
noTone(pin);
}
lcd.clear();
}
FWD
REF
Slow
Fast