Must say I have not yet done the real test in my 4m PA but I do not yet see why this should not work
My 144PA 500W also uses an Arduino controller with a Nano and an OLED monochrome display. I initially programmed it myself, but later replaced the software with AI-generated code that runs in graphic mode, making it more efficient and compact.
![]() |
PA controller: Power, bar, drive power, sequencer, drive level protection, supply V/A, temperature NTC, fan PWM control. The peak/hold feature took me some time to figure out—it seemed not to work, but eventually I realized the wrong variable was being displayed. No matter what I tried, it didn’t help. Then the AI pointed out, “You displayed the wrong variable.” Turns out the AI did that, but blamed me!! took me a lot of time...
scary human behavior!
AI generated a new PA controller software very quick in a few hours I had a working system but it needed tuning character size and more.
//24-5-2026
//in één avond gemaakt met AI copilot en ook nog even tot hier gekeken!
//board: ESP32 Dev Espressif Systems 2.0.18 ! NIET UPDATEN! moest DOWNGRADEN NAAR ESP32 CORE 2.0.14\
//ESP‑X32 toolchain. heeft een fout Omdat de ESP‑X32 toolchain nog geen LEDC‑PWM ondersteunt.
//genereerd 2 com ports de enhanced kiezen voor download
// testopstelling vergeten de 3V2V te isoleren LD3983 3V2 regulator SOT23-5 super klein defect ESPwerkte nog wel met externe 3V2
// Chip ESP32 mini ID test:
//ESP32 Chip model = ESP32-D0WDQ6 Rev 1
//This chip has 2 cores
//Chip ID: 3897936
#include <Adafruit_GFX.h>
#include <Adafruit_ST7789.h>
#include <SPI.h>
// -------------------- TFT PINS --------------------
#define TFT_CS 5 // was 5 display werkt maar steeds niet
#define TFT_DC 2
#define TFT_RST 4
Adafruit_ST7789 tft = Adafruit_ST7789(TFT_CS, TFT_DC, TFT_RST);
// -------------------- IO PINS --------------------
#define PIN_NTC 13 //ADC 36 pin mist op deze ESP
#define PTT_IN 14
#define RELAY_TX 26
#define PA_ENABLE 27
#define PIN_FWD 32 //ADC
#define PIN_REF 33 //ADC
#define PIN_I_SENSE 34 //ADC input only
#define PIN_V_SENSE 35 //ADC input only
// FAN PWM
#define PIN_FAN_PWM 25 //PWM output
#define FAN_CHANNEL 0
#define FAN_FREQ 25000
#define FAN_RES 8
// -------------------- KALIBRATIE --------------------
float K_FWD = 200.0;
float K_REF = 20.0;
float K_I = 5.0;
float K_V = 20.0;
float SWR_LIMIT = 3.0;
float TEMP_LIMIT = 70.0;
float I_LIMIT = 30.0;
float P_MAX = 500.0;
//float Pf = 0; //globaal gemaakt ivm fout in peak maar de fout was in de display update de verkeerde power waarde
float peakPower = 0;
unsigned long peakHoldTime = 0;
unsigned long peakHoldDuration = 1000; // 500 ms hangtijd helaas werkt het niet
unsigned long lastDecayTime = 0;
bool fault = false;
String faultMsg = "";
// -------------------- HULPFUNCTIES --------------------
float readADCVoltage(int pin) {
int raw = analogRead(pin);
return (raw / 4095.0) * 3.3;
}
float readForwardPowerW() {
return readADCVoltage(PIN_FWD) * K_FWD;
}
float readReflectedPowerW() {
return readADCVoltage(PIN_REF) * K_REF;
}
float calcSWR(float Pf, float Pr) {
if (Pf <= 0.1) return 1.0;
float rho = sqrt(Pr / Pf);
if (rho >= 1.0) rho = 0.999;
return (1 + rho) / (1 - rho);
}
float readCurrentA() {
return readADCVoltage(PIN_I_SENSE) * K_I;
}
float readVoltageV() {
return readADCVoltage(PIN_V_SENSE) * K_V;
}
// ⭐ NTC 50k/50k Beta-formule
float readTempC() {
float V = readADCVoltage(PIN_NTC);
float Rntc = (V * 50000.0) / (3.3 - V);
const float R0 = 50000.0;
const float T0 = 298.15;
const float B = 3950.0;
float invT = (1.0 / T0) + (1.0 / B) * log(Rntc / R0);
float T = 1.0 / invT;
// return T - 273.15;
return 20,12; //voor test zonder NTC
}
void setFanByTemp(float temp) {
int duty = 0;
if (temp < 35) duty = 0;
else if (temp < 45) duty = 80;
else if (temp < 55) duty = 150;
else duty = 255;
ledcWrite(FAN_CHANNEL, duty);
}
void setFanFull() {
ledcWrite(FAN_CHANNEL, 255);
}
// -------------------- DISPLAY --------------------
void drawStaticLayout() {
tft.fillScreen(ST77XX_BLACK);
tft.setTextColor(ST77XX_GREEN);
tft.setTextSize(2);
tft.setCursor(10, 10);
tft.println("PA CONTROLL v1.0");
tft.setTextColor(ST77XX_WHITE);
tft.setTextSize(1);
}
void drawBigPower(float P) {
tft.setTextColor(ST77XX_WHITE, ST77XX_BLACK);
tft.setTextSize(4); // GROTE LETTERS
tft.setCursor(20, 55); // MOOIE POSITIE BOVEN DE BAR
tft.printf("%4.0f W", P);
tft.setTextSize(1); // TERUG NAAR NORMAAL
}
void drawPowerBar(float P) {
// oud int x = 80, y = 100, w = 140, h = 10;
int x = 20; // meer naar links
int y = 90; // lager op het scherm
int w = 200; // veel breder
int h = 20; // hogere bar
tft.drawRect(x, y, w, h, ST77XX_WHITE);
float ratio = P / P_MAX;
if (ratio > 1) ratio = 1;
if (ratio < 0) ratio = 0;
int fill = w * ratio;
uint16_t color = ST77XX_GREEN;
if (P >= 400 && P <= 500)
color = ST77XX_YELLOW;
if (P > 500)
color = ST77XX_RED;
tft.fillRect(x + 1, y + 1, w - 2, h - 2, ST77XX_BLACK);
if (fill > 0) tft.fillRect(x + 1, y + 1, fill - 2, h - 2, color);
}
void updateDisplay(float Pf, float Pr, float swr,
float V, float I, float temp,
bool tx, bool fault, String faultMsg) {
tft.setTextColor(ST77XX_WHITE, ST77XX_BLACK);
drawBigPower(Pf);
// Peak power rechtsboven naast de grote readout
tft.setTextSize(2);
tft.setTextColor(ST77XX_YELLOW, ST77XX_BLACK);
tft.setCursor(150, 75);
//tft.printf("%4.0fW", peakPower);
tft.setTextSize(1);
tft.setTextColor(ST77XX_WHITE, ST77XX_BLACK);
drawPowerBar(Pf);
tft.setTextSize(2); // GROTER FONT
tft.setCursor(0, 130);
tft.printf("%4.1fW %4.2f", Pr, swr);
tft.setCursor(0, 150);
tft.printf("%4.1fA %4.0fV %3.0fC", I, V, temp);
tft.setCursor(10, 185);
if (fault) tft.print("FULL");
else if (temp < 35) tft.print("OFF ");
else if (temp < 45) tft.print("LOW ");
else if (temp < 55) tft.print("MID ");
else tft.print("HIGH");
tft.setCursor(10, 205);
if (fault) {
tft.setTextColor(ST77XX_RED, ST77XX_BLACK);
tft.print(faultMsg);
tft.setTextColor(ST77XX_WHITE, ST77XX_BLACK);
} else tft.print("OK");
tft.setCursor(60, 205);
if (tx) {
tft.setTextColor(ST77XX_YELLOW, ST77XX_RED);
tft.print("TX");
} else {
tft.setTextColor(ST77XX_CYAN, ST77XX_BLACK);
tft.print("RX");
}
tft.setTextColor(ST77XX_WHITE, ST77XX_BLACK);
}
// -------------------- SEQUENCER --------------------
void goTX() {
digitalWrite(RELAY_TX, HIGH);
delay(20);
digitalWrite(PA_ENABLE, HIGH);
}
void goRX() {
digitalWrite(PA_ENABLE, LOW);
delay(20);
digitalWrite(RELAY_TX, LOW);
}
// -------------------- SETUP --------------------
void setup() {
Serial.begin(115200);
tft.init(240, 240);
tft.setRotation(0);
drawStaticLayout();
pinMode(PTT_IN, INPUT_PULLUP);
pinMode(RELAY_TX, OUTPUT);
pinMode(PA_ENABLE, OUTPUT);
digitalWrite(RELAY_TX, LOW);
digitalWrite(PA_ENABLE, LOW);
ledcSetup(FAN_CHANNEL, FAN_FREQ, FAN_RES);
ledcAttachPin(PIN_FAN_PWM, FAN_CHANNEL);
ledcWrite(FAN_CHANNEL, 0);
}
// -------------------- LOOP --------------------
void loop() {
bool txState = (digitalRead(PTT_IN) == LOW); // LOW = zenden
static bool inTX = false;
// --- EERST TX-STATUS UPDATEN ---
if (txState && !fault) {
if (!inTX) { goTX(); inTX = true; }
} else {
if (inTX) { goRX(); inTX = false; }
}
if (fault && inTX) {
goRX();
inTX = false;
}
// --- DAN PAS METEN --
float Pf = readForwardPowerW();
// --- PEAK HOLD LOGICA ---
// Nieuwe hogere piek?
if (Pf > peakPower) {
// debug
//Serial.print("Pf = ");
//Serial.print(Pf);
//Serial.print(" peakPower = ");
//Serial.println(peakPower);
// eind debug
peakPower = Pf;
peakHoldTime = millis(); // hangtijd opnieuw starten
}
else {
// Geen nieuwe piek → alleen dan naar hangtijd/decay kijken
if (millis() - peakHoldTime > peakHoldDuration) {
if (millis() - lastDecayTime > 50) { // elke 50 ms één stap
peakPower *= 0.98; // decay
if (peakPower < 0) peakPower = 0;
lastDecayTime = millis();
}
}
}
// --- REST VAN DE METINGEN ---
float Pr = readReflectedPowerW();
float swr = calcSWR(Pf, Pr);
float I = readCurrentA();
float V = readVoltageV();
float temp = readTempC();
fault = false;
faultMsg = "";
if (swr > SWR_LIMIT) { fault = true; faultMsg = "HIGH SWR"; }
if (temp > TEMP_LIMIT) { fault = true; faultMsg = "OVERTEMP"; }
if (I > I_LIMIT) { fault = true; faultMsg = "OVER CUR"; }
if (fault) setFanFull();
else setFanByTemp(temp);
/// peakPower ipv Pf lang gezocht naar fout in piekhold logica
updateDisplay(peakPower, Pr, swr, V, I, temp, txState, fault, faultMsg);
}


Comments