// PA144 controller met NANO 2025-9-24 Jaap van Oosten
// Bepaald de sequense van schakelen van antenne relais en de 50V voeding. Zodat de kontact overgang stroomvrij is.
// Geeft alarm op te hoog stuurniveau van de power FET en SWR, de verstarker gaat dan in doorlaat.
// De reset knop zet het alarm af nadat de oorzaak is verholpen.
// Stuurt leds voor TX PTT LNA en ANT alarm SWR en of overdrive.
// De koel ventilator heeft temperauur afhankelijke PWM toerental regeling.
// schakelt 12V op de coax kern naar mast preamp in RX toestand , is uitschakelbaar.
// OLED display 128x64 toont de waarden
// SH1106 OLED NTC temp PWM gestuurde ventilator centrifugaal 12V 0.6A
// Gemaakt met hulp van Copilot AI en WOKWI simulator.
//
#include <Adafruit_GFX.h>
//#include <Adafruit_SSD1306.h>
#include <Adafruit_SH110X.h>
#include <Wire.h>
#include <math.h>
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_RESET -1
Adafruit_SH1106G display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
// Pinnen
#define FAN_PIN 9
#define PTT_PIN 2
#define SWR_ALARM 7
#define SWR_ResetButton 8
#define NTC_PIN A6
#define FWD_PIN A0
#define REF_PIN A1
#define SHUNT_PIN A2
#define DRIVE_PIN A3
#define AntRelay 4 // IRF510 low= ON opto pull down low is gate high( max 5V zener) eerst
#define TxPWRPIN 6 // BTS low= ON opto pull down na 20mS
bool PTTstate = true; // PTT in
bool TxState = false; // PTT in
bool TxEnable = true; // enable Tx
bool alarmTriggered = false;
// debug timing
int timePTT;
int timeRelay;
int timeBTS;
int rawValue;
float swr = 1;
float powerThreshold = 5.0; // minimum vermogen voor DSWR test
float alarmswr = 1;
float tempC;
float DriveLevel;
//5-4-2022
unsigned int VoltDriveLevel =0; // Sensor PA ingang 50R 50k potmeter
int peakFwd = 0;
int peakRef = 0;
//float peakFwd = 0;
//float peakRef = 0;
// peek hold en decay power bar
const unsigned long peakHoldTime = 2000; // 3 seconden hangtijd
unsigned long peakTimestamp = 0;
bool swrTrip = false;
float readTempNTC() {
int adc = analogRead(NTC_PIN);
float voltage = adc * (5.0 / 1023.0);
tempC = (voltage - 0.5) * 10; // eenvoudige benadering
return tempC;
}
float readSWR() {
float fwd = analogRead(FWD_PIN);
float ref = analogRead(REF_PIN);
if (fwd < 10) return 1.0;
float ratio = ref / fwd;
return (1 + ratio) / (1 - ratio);
}
void setup() {
pinMode(FAN_PIN, OUTPUT);
// aanpassing voor gebruik van D9 voor PWM
// Reset Timer1 registers
TCCR1A = 0;
TCCR1B = 0;
// Stel Fast PWM mode in (Mode 14: WGM13:WGM10 = 1110)
TCCR1A |= (1 << COM1A1); // Non-inverting mode op D9
TCCR1A |= (1 << WGM11);
TCCR1B |= (1 << WGM13) | (1 << WGM12);
// Prescaler instellen op 8 → PWM-frequentie ≈ 2 kHz
TCCR1B |= (1 << CS11);
// TOP-waarde instellen voor 2 kHz bij 16 MHz klok
// Bereken TOP-waarde: ICR1 = 2,000,000 / 7,000 ≈ 286
ICR1 = 286; //7kHz
// Duty cycle instellen (bijv. 50%)
OCR1A = 143; //7kHz
pinMode(PTT_PIN, INPUT_PULLUP);
pinMode(SWR_ALARM, OUTPUT);
pinMode(SWR_ResetButton, INPUT_PULLUP);
pinMode(AntRelay,OUTPUT);
pinMode(TxPWRPIN,OUTPUT);
display.begin(0x3c, true);
display.clearDisplay();
display.setTextSize(1);
display.setTextColor(SH110X_WHITE);
display.setCursor(0, 0);
display.println("144MHz PA 24-9-2025");
display.display();
delay (3000);
Serial.begin(9600);
delay(1000);
}
void loop() {
// delay (2000); // voor debug loop vertraging
// test PTT ingang
PTTstate = digitalRead(PTT_PIN);
timePTT = millis() ;
if (PTTstate == 0 & TxEnable){ // enable TxTxEnable ) { // PTT gnd transmit
if (TxState == false) // start TX sequentie
{
digitalWrite(AntRelay, HIGH); //coaxrelay ON
timeRelay = millis() - timePTT ;
delay(10); // 2sec voorr test //18-6-2024 Sequenzer delay was 20mS swr pieken bij inschakellen TX geeft Alarm hielp niet
digitalWrite(TxPWRPIN, HIGH); //Power PA -ON
timeBTS = millis() -timePTT;
TxState = true;
}
if (TxEnable == false)
{
digitalWrite(TxPWRPIN, LOW); //PA uit
digitalWrite(AntRelay, LOW); //coaxrelay uit
}
LeesSwrVoltages();
}
// eind PTT=0 = uit
if (PTTstate == true) { // schakel terug naar ontvangst indien nog in TX mode
if (TxState == true)
{
digitalWrite(TxPWRPIN, LOW); //PWR PA off dwz optocoupler uit BTS gate hoog
delay(10); // 20 ms TX eerst uit
digitalWrite(AntRelay, LOW); // op ontvangst
TxState = false;
}
LeesSwrVoltages(); //
}
}
void LeesSwrVoltages(){
int fwdRaw = analogRead(FWD_PIN);
int refRaw = analogRead(REF_PIN);
int fwdLevel = map(fwdRaw, 0, 1023, 0, 100);
int refLevel = map(refRaw, 0, 1023, 0, 100);
float forward = (float)fwdRaw;
float reflected = (float)refRaw;
//VoltDriveLevel = analogRead(DRIVE_PIN); // sensor Voltage op 50R ingang dummy
float forwardVoltage = forward * ((800.0 / 1023) / 5.0);
float forwardPower = (forwardVoltage * forwardVoltage) / 50.0;
// nieuw voor piek hang
// Nieuwe piek?
if (fwdLevel > peakFwd) {
peakFwd = fwdLevel;
peakTimestamp = millis();
}
// Hangtijd verstreken?
if (millis() - peakTimestamp > peakHoldTime) {
if (peakFwd > fwdLevel) {
// peakFwd--; // Langzaam afnemen
peakFwd-=2; // Langzaam afnemen
} else {
peakFwd = fwdLevel; // Bijwerken naar actuele waarde
}
}
// reflected piek hang
if (refLevel > peakRef) {
peakRef = refLevel;
peakTimestamp = millis();
}
// Hangtijd verstreken?
if (millis() - peakTimestamp > peakHoldTime) {
if (peakRef > refLevel) {
// peakRef--; // Langzaam afnemen
peakRef-=2; // Langzaam afnemen
} else {
peakRef = refLevel; // Bijwerken naar actuele waarde
}
}
// einde piek hang
rawValue = analogRead(DRIVE_PIN);
DriveLevel = rawValue * (20.0 / 1023); // schaalfactor
if (DriveLevel>15) {
// VoltDriveLevel alarm 9-10-2024
digitalWrite(TxPWRPIN, LOW); //PA uit
alarmTriggered = true;
alarmswr = DriveLevel;
TxEnable = false;
}
if (forward > powerThreshold && forward > reflected) {
swr = (forward + reflected) / (forward - reflected);
} else {
swr = 0;
}
if (digitalRead(SWR_ResetButton) == LOW) {
alarmTriggered = false;
TxEnable = true;
/// SWR_ALARM =0:
}
if ((forward > powerThreshold) && (swr > 3.0)) {
alarmTriggered = true;
alarmswr = swr ;
TxEnable = false;
}
digitalWrite(SWR_ALARM, alarmTriggered ? HIGH : LOW);
if (swr > 10.0) swr = 10.0;
int swrPercent = (int)(log10(swr) * 100.0 / log10(10.0));
display.clearDisplay();
// formule: x = 30 + ((log10(val) - log10(min)) / (log10(max) - log10(min))) * 90;
// SWR schaalverdeling (1–10)
float swrMin = log10(1);
float swrMax = log10(10);
for (int swrVal = 1; swrVal <= 10; swrVal++) {
float logSWR = log10(swrVal);
int x = 30 + (int)((logSWR - swrMin) / (swrMax - swrMin) * 90);
display.drawLine(x, 20, x, 22, SH110X_WHITE); // SWR schaal
}
// Power schaalverdeling (1–800W)
float pwrMin = log10(1);
float pwrMax = log10(800);
for (int pwrVal = 1; pwrVal <= 800; pwrVal *= 2) {
float logPWR = log10(pwrVal);
int x = 30 + (int)((logPWR - pwrMin) / (pwrMax - pwrMin) * 90);
display.drawLine(x, 0, x, 2, SH110X_WHITE); // FWD schaal
//display.drawLine(x, 10, x, 12, SH110X_WHITE); // REF schaal
}
// FWD bar + peak
display.setCursor(0, 0);
display.print("FWD:");
//display.fillRect(30, 0, fwdLevel * 0.9, 8, SH110X_WHITE);
display.fillRect(30, 0, peakFwd * 0.9, 8, SH110X_WHITE);
int peakXfwd = 30 + (peakFwd * 0.9);
display.drawLine(peakXfwd, 0, peakXfwd, 7, SH110X_WHITE); // piek-lijn
// REF bar + peak
display.setCursor(0, 10);
display.print("REF:");
display.fillRect(30, 10, peakRef * 0.9, 8, SH110X_WHITE);
int peakXref = 30 + (peakRef * 0.9);
display.drawLine(peakXref, 10, peakXref, 17, SH110X_WHITE); // piek-lijn
// SWR bar
display.setCursor(0, 20);
display.print("SWR:");
display.fillRect(30, 20, swrPercent * 0.9, 8, SH110X_WHITE);
// SWR value
display.setCursor(70, 35);
display.print("SWR: ");
display.print(swr, 2);
// Power value
display.setCursor(0, 35);
display.print("PWR: ");
display.print(forwardPower, 0);
display.print("W");
//SupplyCurent
display.setCursor(0, 45);
rawValue = analogRead(SHUNT_PIN);
float SupplyCurrent = rawValue * (30.0 / 1023); // schaalfactor
display.print(SupplyCurrent, 1);
display.print(" A");
// Stuurnieau
display.setCursor(80, 45);
rawValue = analogRead(DRIVE_PIN);
DriveLevel = rawValue * (20.0 / 1023); // schaalfactor
display.print(DriveLevel, 1);
display.print(" W");
// Alarm status
display.setCursor(0, 55);
if (alarmTriggered) {
display.print("ALARM! ");
display.print(alarmswr, 2);
} else {
display.print("OK ");
}
float tempC = readTempNTC();
int pwm = map(tempC, 20, 50, 0, 255);
pwm = constrain(pwm, 0, 255);
analogWrite(FAN_PIN, pwm);
display.setCursor(75, 55);
display.print("T: ");
display.print(tempC);
//display.println(" C");
display.display();
}
// test timing
// Serial.print("timePTT ");
// Serial.print(timePTT );
// Serial.print(" timeRelay ");
// Serial.print(timeRelay);
// Serial.print(" timeBTS ");
// Serial.println(timeBTS);
// Serial.print("Forward Power: ");
// Serial.print(forwardPower, 2);
// Serial.print(" W | Reflected: ");
// Serial.print(reflected, 2);
// Serial.print(" | SWR: ");
// Serial.print(swr, 2);
// Serial.print(" | T: ");
// Serial.print(tempC, 2);
// Serial.print(" | PTT:");
// Serial.println(PTTstate);
// delay(10);
//} werd niet gezien !