#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <EEPROM.h>
#include <math.h>
//#include <OneWire.h>
//#include <DallasTemperature.h>
enum Status { NO = 0, YES = 1 };
byte zero[] = {
B00000,
B00000,
B00000,
B00000,
B00000,
B00000,
B00000,
B00000
};
byte one[] = {
B10000,
B10000,
B10000,
B10000,
B10000,
B10000,
B10000,
B10000
};
byte two[] = {
B11000,
B11000,
B11000,
B11000,
B11000,
B11000,
B11000,
B11000
};
byte three[] = {
B11100,
B11100,
B11100,
B11100,
B11100,
B11100,
B11100,
B11100
};
byte four[] = {
B11110,
B11110,
B11110,
B11110,
B11110,
B11110,
B11110,
B11110
};
byte five[] = {
B11111,
B11111,
B11111,
B11111,
B11111,
B11111,
B11111,
B11111
};
struct Calibration {
float voltmeter_full_scale;
float ampmeter_full_scale;
float power_drv_min;
byte power_fwd_min;
byte power_ref_min;
byte power_drv_max;
byte TXdelay;
byte RXdelay;
float temp_alarm_threshold;
byte swr_threshold;
float fan_temp_min;
float fan_temp_max;
byte fan_pwm_min;
byte fan_pwm_max;
float power_max;
float Bvalue;
float Rref;
float a_pwr;
float b_pwr;
float c_pwr;
};
Calibration calib;
const int calibEEPROMAddr = 0; // startadres in EEPROM
constexpr byte FAN_PWM = 9; // PWM-uitgang voor ventilator
// constexpr byte TEMP_SENSOR = 7; // DS18B20
constexpr byte SWR_ResetButton = 7;
constexpr byte PApwr = 6;
constexpr byte AntRelay = 4;
constexpr byte ALARM_PIN = 3;
constexpr byte PTT = 2;
constexpr byte FWD = A0;
constexpr byte REV = A1;
constexpr byte VOLT = A2;
constexpr byte DRV = A3;
constexpr byte AMP = A6;
constexpr byte NTC = A7;
const float a_drv = 0, b_drv = 0.227, c_drv = 2; //https://tools.softinery.com/CurveFitter/
struct Coupler {
float voltage = 0;
float power = 0;
Status error = NO;
};
Coupler fwd, ref, drv;
float swr = 0, reflection_coefficient = 0;
float PSUvoltage = 0, PSUcurrent = 0;
float tempC;
bool AlarmState = false, TxState = false, TxEnable = true;
bool PTTstate = false;
bool debugMode = false;
char buffer[8];
int pwmValue;
LiquidCrystal_I2C lcd(0x27, 20, 4);
void setup() {
pinMode(AntRelay, OUTPUT);
pinMode(PApwr, OUTPUT);
pinMode(ALARM_PIN, OUTPUT);
pinMode(PTT, INPUT_PULLUP);
pinMode(SWR_ResetButton, INPUT_PULLUP);
digitalWrite(PTT, HIGH);
digitalWrite(ALARM_PIN, LOW);
pinMode(FAN_PWM, OUTPUT);
// TCCR0B = TCCR0B & B11111000 | B00000010; // Prescaler 8 ? ~7.8 kHz
// Stel Timer1 in op Fast PWM met 20 kHz
TCCR1A = _BV(COM1A1) | _BV(WGM11);
TCCR1B = _BV(WGM13) | _BV(WGM12) | _BV(CS10); // Geen prescaler
ICR1 = 799; // 16 MHz / (799 + 1) = 20 kHz
Serial.begin(115200);
lcd.init();
lcd.backlight();
lcd.setCursor(0, 0);
lcd.print(F("PA M O N I T O R"));
lcd.setCursor(0, 1);
lcd.print(F("21-09-2025 by PA3EKM"));
delay(1000);
lcd.clear();
lcd.createChar(0, zero);
lcd.createChar(1, one);
lcd.createChar(2, two);
lcd.createChar(3, three);
lcd.createChar(4, four);
lcd.createChar(5, five);
EEPROM.get(calibEEPROMAddr, calib);
// Als EEPROM leeg is (bijv. eerste keer), zet standaardwaarden
if (isnan(calib.voltmeter_full_scale) || calib.voltmeter_full_scale == 0) {
calib.voltmeter_full_scale = 66.0;
calib.ampmeter_full_scale = 61.0;
calib.power_drv_min = 0.1;
calib.power_fwd_min = 1;
calib.power_ref_min = 1;
calib.power_drv_max = 8;
calib.TXdelay = 50;
calib.RXdelay = 50;
calib.temp_alarm_threshold = 50.0;
calib.swr_threshold = 3;
calib.fan_temp_min = 20;
calib.fan_temp_max = 50;
calib.fan_pwm_min = 50;
calib.fan_pwm_max = 255;
calib.power_max = 400;
calib.Bvalue = 3950;
calib.Rref = 10000;
calib.a_pwr = 0.0524;
calib.b_pwr = 10;
calib.c_pwr = 18;
EEPROM.put(calibEEPROMAddr, calib);
}
showCalibration() ;
}
void loop() {
PTTstate = digitalRead(PTT);
if (!PTTstate && TxEnable && !TxState) {
digitalWrite(AntRelay, HIGH);
delay(calib.TXdelay);
digitalWrite(PApwr, HIGH);
Serial.println(F("TX on"));
TxState = true;
}
if (PTTstate && TxState) {
digitalWrite(PApwr, LOW);
delay(calib.RXdelay);
digitalWrite(AntRelay, LOW);
Serial.println(F("TX off"));
TxState = false;
}
readCouplers();
if (AlarmState) alarm();
displayCouplers();
// measureTemp();
readTemperature(NTC);
if (AlarmState) alarm();
measureSupply();
displaySupply(); //and temp
controlFan();
if (debugMode) Debugging();
CLIinterface();
}
void CLIinterface() {
if (Serial.available()) {
String command = Serial.readStringUntil('\n');
command.trim();
if (command.equalsIgnoreCase("HELP")) {
showHelp();
}
else if (command.equalsIgnoreCase("DEBUG ON")) {
debugMode = true;
Serial.println(F("Debugmodus AAN"));
Serial.println("PSU Current,Temperatuur,FWD power,REF power,DRV power,FWD voltage,FWD error,SWR,Fan-pwm,PSU Voltage");
}
else if (command.equalsIgnoreCase("DEBUG OFF")) {
debugMode = false;
Serial.println(F("Debugmodus UIT"));
}
else if (command.equalsIgnoreCase("SHOWCAL")) {
showCalibration();
}
else if (command.equalsIgnoreCase("RESETCAL")) {
resetCalibrationEEPROM();
}
else if (command.startsWith("SET ")) {
setCalibration(command);
}
}
}
char* dtos(float val, int width, int prec) {
dtostrf(val, width, prec, buffer);
return buffer;
}
void measureSupply() {
PSUvoltage = analogRead(VOLT) * calib.voltmeter_full_scale / 1023;
PSUcurrent = analogRead(AMP) * calib.ampmeter_full_scale / 1023;
}
/*void measureTemp() { // DS18B20
sensors.requestTemperatures();
tempC = sensors.getTempCByIndex(0);
AlarmState = (tempC >= calib.temp_alarm_threshold) ;
}*/
float readTemperature(int analogPin) {
// [5V] --- [10k NTC] ---+--- [10k weerstand] --- [GND]
// |
// [A7 pin]
// 3950 is een typische B-waarde, maar check de datasheet van jouw NTC.
// 10000.0 is de weerstand bij 25°C.
int raw = analogRead(analogPin);
float resistance = (1023.0 / raw - 1.0) * calib.Rref;
tempC = 1.0 / (log(resistance / calib.Rref) / calib.Bvalue + 1.0 / 298.15) - 273.15;
AlarmState = (tempC >= calib.temp_alarm_threshold) ;
}
void controlFan() {
int pwmRaw = map(constrain(tempC, calib.fan_temp_min, calib.fan_temp_max),
calib.fan_temp_min, calib.fan_temp_max,
calib.fan_pwm_min, calib.fan_pwm_max);
OCR1A = pwmRaw; // Directe duty cycle op D9
}
void readCouplers() {
fwd.voltage = analogRead(FWD) * 5.0 / 1023 ;
ref.voltage = analogRead(REV) * 5.0 / 1023 ;
drv.voltage = analogRead(DRV) * 5.0 / 1023 ;
fwd.power = max(0.0, calib.a_pwr + calib.b_pwr * fwd.voltage + calib.c_pwr * pow(fwd.voltage, 2));
ref.power = max(0.0, calib.a_pwr + calib.b_pwr * ref.voltage + calib.c_pwr * pow(ref.voltage, 2));
drv.power = max(0.0, a_drv + b_drv * drv.voltage + c_drv * pow(drv.voltage, 2));
fwd.error = (fwd.power < calib.power_fwd_min) ? YES : NO;
ref.error = (ref.power < calib.power_ref_min) ? YES : NO;
drv.error = (drv.power < calib.power_drv_min || drv.power > calib.power_drv_max) ? YES : NO;
if (!drv.error && (ref.power <= fwd.power ) ) {
reflection_coefficient = sqrt(ref.power / fwd.power);
swr = (1 + reflection_coefficient) / (1 - reflection_coefficient);
}
else swr = 0;
AlarmState = (TxState && !fwd.error && (ref.power <= fwd.power ) && swr > calib.swr_threshold) ;
}
void displayCouplers() {
lcd.setCursor(0, 0);
lcd.print(F("FWD W REF W"));
lcd.setCursor(4, 0);
lcd.print((fwd.error == NO) ? dtos(fwd.power, 2, 0) : "<1");
lcd.setCursor(16, 0);
lcd.print((ref.error == NO) ? dtos(ref.power, 2, 0) : "<1");
lcd.setCursor(0, 1);
lcd.print(F("IN W SWR "));
lcd.setCursor(4, 1);
lcd.print((drv.error == NO) ? dtos(drv.power, 3, 1) : "<");
lcd.setCursor(15, 1);
lcd.print(dtos(swr, 2, 1));
lcd.setCursor(0, 3);
updateProgressBar(fwd.power, calib.power_max, 3);
}
void displaySupply() {
lcd.setCursor(0, 2);
lcd.print(F(" V"));
lcd.setCursor(0, 2);
lcd.print(dtos(PSUvoltage, 3, 1));
lcd.setCursor(8, 2);
lcd.print(F(" A"));
lcd.setCursor(8, 2);
lcd.print(dtos(PSUcurrent, 3, 1));
lcd.setCursor(15, 2);
lcd.print(F(" C"));
lcd.setCursor(15, 2);
lcd.print(dtos(tempC, 3, 1));
}
void updateProgressBar(float value, float maxValue, int line) {
int totalBlocks = 20;
float percentage = constrain(value / maxValue, 0.0, 1.0);
int fullChars = percentage * totalBlocks;
int partialChar = (percentage * totalBlocks - fullChars) * 5;
for (int i = 0; i < totalBlocks; i++) {
lcd.setCursor(i, line);
if (i < fullChars) {
lcd.write(byte(5)); // volledig blok
} else if (i == fullChars && partialChar > 0) {
lcd.write(byte(partialChar)); // gedeeltelijk blok
} else {
lcd.write(' '); // leeg
}
}
}
void alarm() {
if (tempC >= calib.temp_alarm_threshold) {
Serial.println("?? Temperatuur ALARM!");
digitalWrite(ALARM_PIN, HIGH);
}
if (swr > calib.swr_threshold) {
Serial.println("?? SWR ALARM!");
digitalWrite(ALARM_PIN, HIGH);
}
// Zet zender uit
if (TxState && AlarmState) {
digitalWrite(PApwr, LOW);
delay(calib.RXdelay);
digitalWrite(AntRelay, LOW);
Serial.println("TX uitgeschakeld");
TxState = false;
TxEnable = false; // Blokkeer nieuwe uitzendingen
}
if (tempC < calib.temp_alarm_threshold) {
Serial.println("Temperatuur weer normaal!");
digitalWrite(ALARM_PIN, LOW);
AlarmState = NO;
TxEnable = true;
}
if (digitalRead(SWR_ResetButton) == LOW) {
Serial.println(F("Alarm reset"));
digitalWrite(ALARM_PIN, LOW);
AlarmState = NO;
TxEnable = true;
}
}
void showCalibration() {
Serial.println(F("Calibratiewaarden:"));
Serial.print(F("voltmeter_full_scale = ")); Serial.println(calib.voltmeter_full_scale);
Serial.print(F("ampmeter_full_scale = ")); Serial.println(calib.ampmeter_full_scale);
Serial.print(F("power_drv_min = ")); Serial.println(calib.power_drv_min);
Serial.print(F("power_fwd_min = ")); Serial.println(calib.power_fwd_min);
Serial.print(F("power_ref_min = ")); Serial.println(calib.power_ref_min);
Serial.print(F("power_drv_max = ")); Serial.println(calib.power_drv_max);
Serial.print(F("TXdelay = ")); Serial.println(calib.TXdelay);
Serial.print(F("RXdelay = ")); Serial.println(calib.RXdelay);
Serial.print(F("temp_alarm_threshold = ")); Serial.println(calib.temp_alarm_threshold);
Serial.print(F("SWR_threshold = ")); Serial.println(calib.swr_threshold);
Serial.print(F("fan_temp_min = ")); Serial.println(calib.fan_temp_min);
Serial.print(F("fan_temp_max = ")); Serial.println(calib.fan_temp_max);
Serial.print(F("fan_pwm_min = ")); Serial.println(calib.fan_pwm_min);
Serial.print(F("fan_pwm_max = ")); Serial.println(calib.fan_pwm_max);
Serial.print(F("power_max = ")); Serial.println(calib.power_max);
Serial.print(F("Bvalue = ")); Serial.println(calib.Bvalue);
Serial.print(F("Rref = ")); Serial.println(calib.Rref);
Serial.print(F("a_pwr coefficient = ")); Serial.println(calib.a_pwr);
Serial.print(F("b_pwr coefficient = ")); Serial.println(calib.b_pwr);
Serial.print(F("c_pwr coefficient = ")); Serial.println(calib.c_pwr);
}
void setCalibration(String cmd) {
cmd.remove(0, 4); // haal "SET " weg
int sep = cmd.indexOf('=');
if (sep == -1) return;
String key = cmd.substring(0, sep);
String val = cmd.substring(sep + 1);
val.trim();
if (key == "voltmeter") calib.voltmeter_full_scale = val.toFloat();
else if (key == "ampmeter") calib.ampmeter_full_scale = val.toFloat();
else if (key == "drvmin") calib.power_drv_min = val.toFloat();
else if (key == "fwdmin") calib.power_fwd_min = val.toInt();
else if (key == "refmin") calib.power_ref_min = val.toInt();
else if (key == "drvmax") calib.power_drv_max = val.toInt();
else if (key == "txdelay") calib.TXdelay = val.toInt();
else if (key == "rxdelay") calib.RXdelay = val.toInt();
else if (key == "tempalarm") calib.temp_alarm_threshold = val.toFloat();
else if (key == "swralarm") calib.swr_threshold = val.toInt();
else if (key == "fan_temp_min") calib.fan_temp_min = val.toFloat();
else if (key == "fan_temp_max") calib.fan_temp_max = val.toFloat();
else if (key == "fan_pwm_min") calib.fan_pwm_min = val.toInt();
else if (key == "fan_pwm_max") calib.fan_pwm_max = val.toInt();
else if (key == "power_max") calib.power_max = val.toFloat();
else if (key == "Bvalue") calib.Bvalue = val.toFloat();
else if (key == "Rref") calib.Rref = val.toFloat();
else if (key == "a_pwr") calib.a_pwr = val.toFloat();
else if (key == "b_pwr") calib.b_pwr = val.toFloat();
else if (key == "c_pwr") calib.c_pwr = val.toFloat();
else {
Serial.println(F("Onbekende parameter"));
return;
}
EEPROM.put(calibEEPROMAddr, calib);
Serial.println(F("Calibratie bijgewerkt."));
showCalibration();
}
void showHelp() {
Serial.println(F("?? Beschikbare commando's:"));
Serial.println(F("HELP ? Toon dit overzicht"));
Serial.println(F("DEBUG ON ? Zet seriële logging aan"));
Serial.println(F("DEBUG OFF ? Zet seriële logging uit"));
Serial.println(F("SHOWCAL ? Toon huidige calibratiewaarden"));
Serial.println(F("RESETCAL ? Wis alle calibratiewaarden"));
Serial.println(F("SET voltmeter=X ? Stel voltmeter schaal (float)"));
Serial.println(F("SET ampmeter=X ? Stel ampmeter schaal (float)"));
Serial.println(F("SET drvmin=X ? Minimaal driver vermogen (float)"));
Serial.println(F("SET fwdmin=X ? Minimaal forward vermogen (int)"));
Serial.println(F("SET refmin=X ? Minimaal reflected vermogen (int)"));
Serial.println(F("SET drvmax=X ? Maximaal driver vermogen (int)"));
Serial.println(F("SET txdelay=X ? Delay bij zenden (ms, int)"));
Serial.println(F("SET rxdelay=X ? Delay bij ontvangst (ms, int)"));
Serial.println(F("SET tempalarm=X ? Temperatuurgrens alarm (float °C)"));
Serial.println(F("SET swralarm=X ? SWR grens alarm (float °C)"));
Serial.println(F("SET fan_temp_min=X? min temp fan on (float °C)"));
Serial.println(F("SET fan_temp_max=X? max temp fan on (float °C)"));
Serial.println(F("SET fan_pwm_min=X ? min pwm fan on (byte)"));
Serial.println(F("SET fan_pwm_max=X ? max pwm fan on (byte)"));
Serial.println(F("SET power_max=X ? max power (float)"));
Serial.println(F("SET Bvalue=X ? Steinhart B (float)"));
Serial.println(F("SET Rref=X ? R reference (float)"));
Serial.println(F("SET a_pwr=X ? a_pwr coefficient (float)"));
Serial.println(F("SET b_pwr=X ? b_pwr coefficient (float)"));
Serial.println(F("SET c_pwr=X ? c_pwr coefficient (float)"));
Serial.println(F(""));
Serial.println(F("Voorbeeld: SET tempalarm=65.5"));
}
void Debugging() {
Serial.print(PSUvoltage);Serial.print(",");
Serial.print(PSUcurrent);Serial.print(",");
Serial.print(tempC);Serial.print(",");
Serial.print(fwd.power); Serial.print(",");
Serial.print(ref.power); Serial.print(",");
Serial.print(drv.power); Serial.print(",");
Serial.print(fwd.voltage); Serial.print(",");
Serial.print(fwd.error); Serial.print(",");
Serial.print(swr); Serial.print(",");
Serial.print(pwmValue); Serial.print(",");
Serial.println();
}
void resetCalibrationEEPROM() {
int CALIBRATION_START_ADDR = 0;
for (int i = 0; i < sizeof(Calibration); i++) {
EEPROM.write(CALIBRATION_START_ADDR + i, 0);
}
}
drive
forward
reflected
FAN
Alarm
PTT
Vd on
Relays
reset alarm