Skip to main content

ESP32 Arduino perikelen PA-controller

 My pesent 144PA 500W  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.

The new  PA controller with ESP32 mini and ST7789 display shows power, bar, drive power, sequencer,SWR >3 and drive level protection, supply voltage/current, temperature via NTC, and fan PWM control. The peak/hold feature was tricky—it seemed broken until I realized the wrong variable was being displayed. No matter what I tried, it wouldn’t fix, and then the AI pointed out, “You displayed the wrong variable.” Turns out the AI caused it but blamed me! Quite the human-like behavior. Meanwhile, AI can whip up structured software in no time. PA0V needed a power control for a dual driver Matt65 small OLED display Nano, and it was done in a minute after describing the requirements.

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.

During testing, a lot of strange things happened. I shorted the 3V2, and the display went out, and I couldn’t get it to work again. The ESP Mini’s very small onboard regulator, the LD3985 to the right of the USB connector, was damaged. The USB 5V goes through a low-drop diode to the low-drop LD3985, rated for max 6V and 180mA. I tested with external 3V3, and the ESP was still okay. To test WiFi, the 3V2 needs to supply 180mA and higher pulses, though normally it takes about 40–50mA. I’ll replace the LD3985 so it can be repaired. Another  thing I learned there is a 2nd 3V2 regulator onboard that lets the programm run like blink led  once it is loaded and connected with USB power. But to program it power the CH9102 USB /serial and external devices the LD3985 3V2 output is needed
 For some reason, the ST7789 wouldn’t display anymore; it seemed like the display was damaged, but a newly received ST7789 also didn’t work. That turned out to be bad soldering. I struggled quite a bit to get it to display again. The driver was suspected but turned out to be fine.  sketch code is below

 
On the backside of the ST7789 a 6206 Vcc regulator, the top and middle pin are connected to Vcc. The regulator accepts 3.3V as well as 5V, so Vcc can be 5V or even higher without damaging the display. I discovered this after taking a deeper dive into the hardware when it broke down. In the end, the only damage was to the ESP32 the tiny low-dropout LD3985  regulator, which of course has to come from China since our electronics industry, like Philips, shut down 25 years ago. Sure, it’s better to ship it across the planet...


//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

Archive

Show more