This design utilizes the Hosyond 3.5" ESP32 (ST7796U) to replace the SB-200's mechanical meter with a digital multi-function display. It measures High Voltage (0–3000V), Plate Current (0–600mA), and Grid Current (0–150mA) using isolated sensing circuits safe for the ESP32's 3.3V ADC.

 This design utilizes the Hosyond 3.5" ESP32 (ST7796U) to replace the SB-200's mechanical meter with a digital multi-function display.  It measures High Voltage (0–3000V), Plate Current (0–600mA), and Grid Current (0–150mA) using isolated sensing circuits safe for the ESP32's 3.3V ADC. 


⚠️ Critical Safety Warning

Lethal Voltage: The SB-200 contains 2,400V+.  The voltage divider must use high-voltage rated resistors (series string) to prevent arcing and resistor failure. 

Isolation: The ESP32 ground MUST be connected to the SB-200 chassis ground (Common Ground) for these resistive dividers to work.  If you cannot ensure a solid common ground, use Hall Effect sensors (e.g., ACS712/ACS758) or opto-isolators instead of direct resistive dividers.

RF Protection: All input lines to the ESP32 must have RF bypass capacitors and clamping diodes to prevent RF feedback from damaging the microcontroller. 

1. Hardware Interface Design

The Hosyond module uses the ST7796U driver.  We will use the TFT_eSPI library for the display and XPT2046_Touchscreen for input. 


Sensing Circuit Schematics

A. High Voltage Meter (0–3000V DC)

The ESP32 ADC maxes at ~3.3V. We need a divider ratio of roughly 1000:1. 


Input: HV Line (after glitch resistor). 

Divider:

R1 (HV): Three 4.7 MΩ, 1W resistors in series (Total 14.1 MΩ, Rating ~9kV).  Do not use a single resistor.

R2 (Low): 15 kΩ, 0.25W precision resistor.

Formula: $V_{out} = V_{in} \times \frac{15k}{14.1M + 15k}$. At 3000V, $V_{out} \approx 3.18V$.

Protection: Add a 3.3V Zener diode (cathode to signal, anode to GND) at the ESP32 input pin. 

B. Plate Current Meter (0–600mA)

The SB-200 measures plate current across a 1.0Ω internal shunt (R12/R14 network). 


Input: Across the existing 1.0Ω shunt resistor in the power supply return. 

Scaling: At 600mA, voltage drop is $0.6V$. This is safe for ESP32 directly, but we add gain for resolution. 

Op-Amp Buffer (Optional but Recommended): Use an LM358 (Single Supply) in non-inverting mode with Gain = 5.

$0.6V \times 5 = 3.0V$ (Full Scale).

Direct Connect (Simpler): Connect across the 1Ω shunt via a 1kΩ series resistor and 0.1µF cap to ground.

Max voltage ~0.6V. ESP32 reads this easily (approx. 400–500 ADC counts). 

C. Grid Current Meter (0–150mA)

The SB-200 measures grid current across a 1.5Ω shunt. 


Input: Across the 1.5Ω grid shunt resistor. 

Scaling: At 150mA, voltage drop is $0.225V$.

Connection: Direct to ADC pin with 1kΩ series resistor and 0.1µF cap. 

Wiring Diagram (ASCII)

       SB-200 AMPLIFIER CHASSIS (HIGH VOLTAGE ZONE)

       --------------------------------------------

       

       [HV LINE +2400V]

            |

      .-----+-----.

      | R1a 4.7M  | \

      '-----+-----'  |

            |        | R1b 4.7M (Series String for HV Safety)

      .-----+-----.  |

      | R1b 4.7M  | /

      '-----+-----'

            |

      .-----+-----.

      | R1c 4.7M  |

      '-----+-----'

            |

            +-----------------------+

            |                       |

          [R2 15k]               [Zener 3.3V]

            |                       |

           GND (Chassis)           GND

            |

            +-------> To ESP32 GPIO 34 (HV_SENSE)

            

       [PLATE SHUNT 1.0Ω] (In Power Supply Return)

            |

            +-------> To ESP32 GPIO 35 (PLATE_SENSE) via 1k Resistor + 0.1uF Cap to GND

            |

           GND


       [GRID SHUNT 1.5Ω] (In Grid Circuit)

            |

            +-------> To ESP32 GPIO 32 (GRID_SENSE) via 1k Resistor + 0.1uF Cap to GND

            |

           GND


       HOSYOND ESP32 MODULE

       --------------------

       GPIO 34 (VP) --> HV_SENSE

       GPIO 35 (VN) --> PLATE_SENSE

       GPIO 32      --> GRID_SENSE

       GND          --> SB-200 Chassis Ground (CRITICAL)

       5V           --> External 5V Supply (Do not power from SB-200 low-voltage rails if noisy)


2. Software Implementation

Required Libraries

Install these in Arduino IDE:


TFT_eSPI (by Bodmer) - Configure User_Setup_Select.h for ST7796 and ESP32.

XPT2046_Touchscreen (by Paul Stoffregen). 

User_Setup.h Configuration (for TFT_eSPI)

Edit the library folder TFT_eSPI/User_Setups/Setup25_ST7796.h (or create a new setup):


#define ST7796_DRIVER

#define TFT_WIDTH  320

#define TFT_HEIGHT 480

#define TFT_MISO 12

#define TFT_MOSI 13

#define TFT_SCLK 14

#define TFT_CS   15

#define TFT_DC   2

#define TFT_RST  4

#define TOUCH_CS 33

#define LOAD_GLCD

#define LOAD_FONT2

#define LOAD_FONT4

#define LOAD_FONT6

#define LOAD_FONT7

#define LOAD_FONT8

#define SMOOTH_FONT


Main Code (SB200_Meter.ino)

#include <TFT_eSPI.h>

#include <XPT2046_Touchscreen.h>

#include <SPI.h>


// --- Pin Definitions ---

#define HV_PIN 34

#define PLATE_PIN 35

#define GRID_PIN 32


// --- Calibration Constants (Adjust based on multimeter) ---

// ADC Range: 0-4095 (3.3V). 

// HV: 3000V = ~3.18V -> ADC ~3930. Factor = 3000/3930 ≈ 0.763

const float HV_FACTOR = 0.763; 

// Plate: 1.0 Ohm shunt. 0.6V (600mA) = ADC ~743. Factor = 600/743 ≈ 0.807 (mA per count)

const float PLATE_FACTOR = 0.807; 

// Grid: 1.5 Ohm shunt. 0.225V (150mA) = ADC ~278. Factor = 150/278 ≈ 0.539

const float GRID_FACTOR = 0.539;


// --- Objects ---

TFT_eSPI tft = TFT_eSPI();

TS_Point tp;


void setup() {

  Serial.begin(115200);

  tft.init();

  tft.setRotation(1); // Landscape

  tft.fillScreen(TFT_BLACK);

  

  // Draw UI Layout

  drawGauge("HV", 0, 0, 160, 240, TFT_RED, 3000);

  drawGauge("PLATE mA", 160, 0, 160, 240, TFT_GREEN, 600);

  drawGauge("GRID mA", 0, 240, 320, 240, TFT_BLUE, 150);

  

  pinMode(HV_PIN, INPUT);

  pinMode(PLATE_PIN, INPUT);

  pinMode(GRID_PIN, INPUT);

}


void loop() {

  // Read ADC (Average 10 samples for stability)

  int hv_raw = analogReadAvg(HV_PIN, 10);

  int plate_raw = analogReadAvg(PLATE_PIN, 10);

  int grid_raw = analogReadAvg(GRID_PIN, 10);


  // Convert to Engineering Units

  float hv_val = hv_raw * HV_FACTOR;

  float plate_val = plate_raw * PLATE_FACTOR;

  float grid_val = grid_raw * GRID_FACTOR;


  // Update Displays

  updateGauge(0, 0, 160, 240, hv_val, "kV", 100); // Scale HV to 30.00 kV display? No, keep Volts.

  updateGaugeValue(0, 0, 160, 240, (int)hv_val, "V", TFT_RED);

  

  updateGaugeValue(160, 0, 160, 240, (int)plate_val, "mA", TFT_GREEN);

  

  updateGaugeValue(0, 240, 320, 240, (int)grid_val, "mA", TFT_BLUE);


  delay(200); // 5Hz update rate

}


// --- Helper Functions ---


int analogReadAvg(int pin, int count) {

  long sum = 0;

  for (int i = 0; i < count; i++) {

    sum += analogRead(pin);

    delay(2);

  }

  return sum / count;

}


void drawGauge(String label, int x, int y, int w, int h, uint16_t color, int maxVal) {

  tft.fillRect(x, y, w, h, TFT_DARKGREY);

  tft.setTextColor(TFT_WHITE);

  tft.setTextSize(2);

  tft.setCursor(x + 10, y + 10);

  tft.print(label);

  

  // Draw Max Scale Line

  tft.drawLine(x + 10, y + h - 30, x + w - 10, y + h - 30, TFT_WHITE);

  tft.setTextSize(1);

  tft.setCursor(x + w - 40, y + h - 50);

  tft.print(maxVal);

}


void updateGaugeValue(int x, int y, int w, int h, int val, String unit, uint16_t color) {

  // Clear Value Area

  tft.fillRect(x + 10, y + 40, w - 20, h - 80, TFT_BLACK);

  

  tft.setTextColor(color);

  tft.setTextSize(4);

  tft.setCursor(x + 10, y + 60);

  tft.print(val);

  

  tft.setTextSize(2);

  tft.setCursor(x + 10 + tft.width(), y + 60); // Approximate unit pos

  tft.print(" " + unit);

  

  // Simple Bar Graph

  int barW = map(val, 0, (unit == "V" ? 3000 : (unit == "mA" && x==160 ? 600 : 150)), 0, w - 20);

  tft.fillRect(x + 10, y + h - 30, barW, 10, color);

  tft.fillRect(x + 10 + barW, y + h - 30, (w - 20) - barW, 10, TFT_DARKGREY);

}


3. Assembly & Calibration Steps

Enclosure: Mount the Hosyond display on the SB-200 rear apron or in a custom external box. Do not place the ESP32 board inside the HV RF cage without shielding.

Wiring:

Run shielded cables from the sensing points (HV divider, shunts) to the ESP32.

Connect the ESP32 GND to the SB-200 Chassis Ground at a single point (star ground) to avoid ground loops.

Calibration:

Power up the SB-200 in Standby (Filament only). HV should read 0V.

Switch to Operate (No Drive). Adjust HV_FACTOR in code until the display matches a reliable HV probe multimeter (Target: ~2200V).

Apply drive to reach 500mA plate current. Adjust PLATE_FACTOR to match the stock meter or a clamp meter (if DC capable).

Touch Functionality (Optional):

The code includes the touch library. You can expand the loop() to detect touches (ts.touched()) to toggle between "SWR" mode (if you add an SWR bridge sensor) or "Peak Hold" modes. 


High Voltage Resistor 4.7M 1W




ESP32 ST7796 Hosyond pinout configuration










Updated Guide: SB-200 Digital Meter with SWR & Peak Hold

This guide expands the previous ESP32 design to include SWR monitoring (via an external directional coupler) and PEP Peak Hold functionality.  The system now displays HV, Plate Current, Grid Current, Forward/Reflected Power, SWR, and Peak Power. 


1. Hardware Additions: SWR & Peak Sensing

To measure SWR and Peak Power, you cannot connect the RF directly to the ESP32. You must use a Directional Coupler (e.g., Monimatch) and RF Detectors. 


A. Directional Coupler (External or Internal)

Placement: Install a dual-directional coupler (e.g., Bruene or Monimatch type) between the SB-200 output and the antenna connector.

Outputs: Provides two DC voltages proportional to Forward Power (FWD) and Reflected Power (REF). 

Scaling: Ensure the coupler's output voltage does not exceed 3.0V at maximum amplifier power (600W).  Use a potentiometer or voltage divider if necessary.

B. Peak Hold Circuit (Hardware vs. Software)

Software Peak Hold (Recommended): The ESP32 samples the ADC rapidly (kHz range). You can implement a "decay" algorithm in code to simulate a peak-hold meter without extra components. This is more flexible for SSB/CW.

Hardware Peak Hold (Optional): If you want to smooth the RF envelope before the ADC:

Use a Schottky Diode (1N5711) -> 1nF Capacitor -> 100kΩ Resistor to Ground.

Connect the junction of the Cap/Resistor to the ESP32 ADC. 

C. Updated Wiring Diagram

       DIRECTIONAL COUPLER (Installed at Amp Output)

       ---------------------------------------------

       [RF OUT] ---> To Antenna

       [RF IN]  <--- From Amp Tank Circuit

       

       Coupler Outputs (DC Voltages proportional to Power):

       

       FWD Output o----[1kΩ]----+-----> ESP32 GPIO 33 (FWD_SENSE)

                                |

                              [0.1µF]

                                |

                               GND

       

       REF Output o----[1kΩ]----+-----> ESP32 GPIO 32 (REF_SENSE)

                                |

                              [0.1µF]

                                |

                               GND


       NOTE: Ensure FWD/REF voltages are < 3.3V at max power. 

       Use a 10k Potentiometer as a voltage divider if needed.


Updated ESP32 Pinout:


GPIO 34: HV Sense

GPIO 35: Plate Current Sense

GPIO 32: Grid Current Sense (Moved from 32 to GPIO 33 if using 32 for REF, or use GPIO 33 for FWD and GPIO 25 for REF).

Correction: Let's use GPIO 33 for FWD and GPIO 25 for REF to keep ADC1 channel consistency.

GPIO 33: Forward Power Sense

GPIO 25: Reflected Power Sense 

2. Software Implementation (SWR & Peak Hold)

The code now includes:


SWR Calculation: $SWR = \frac{1 + \sqrt{P_{ref}/P_{fwd}}}{1 - \sqrt{P_{ref}/P_{fwd}}}$

Peak Hold Algorithm: Captures the maximum value and decays it slowly over time. 

Touch Control: Touch the screen to reset the Peak Hold or toggle display modes.

Updated Code (SB200_Meter_Advanced.ino)

#include <TFT_eSPI.h>

#include <XPT2046_Touchscreen.h>

#include <SPI.h>

#include <math.h>


// --- Pin Definitions ---

#define HV_PIN 34

#define PLATE_PIN 35

#define GRID_PIN 32

#define FWD_PIN 33

#define REF_PIN 25

#define TOUCH_CS 33 // Note: Conflict with FWD_PIN if on same SPI. Use GPIO 21 for TOUCH_CS if needed.

// Fix: Reassign Touch CS to GPIO 21 in User_Setup.h or wire physically to 21.

// For this example, assume TOUCH_CS is on GPIO 21 (modify wiring/setup)

#define TOUCH_CS_PIN 21 


// --- Calibration Constants (ADJUST THESE) ---

const float HV_FACTOR = 0.763; 

const float PLATE_FACTOR = 0.807; 

const float GRID_FACTOR = 0.539;

const float PWR_FACTOR = 0.05; // Adjust based on coupler (Volts to Watts)


// --- Peak Hold Variables ---

float peakFwd = 0;

float peakTime = 0;

const float DECAY_TIME = 1500; // ms for peak to decay


// --- Objects ---

TFT_eSPI tft = TFT_eSPI();

XPT2046_Touchscreen ts(TOUCH_CS_PIN);


void setup() {

  Serial.begin(115200);

  tft.init();

  tft.setRotation(1); 

  tft.fillScreen(TFT_BLACK);

  ts.begin();

  

  // Draw Layout

  drawPanel("HV", 0, 0, 160, 120, TFT_RED);

  drawPanel("PLATE mA", 160, 0, 160, 120, TFT_GREEN);

  drawPanel("GRID mA", 0, 120, 160, 120, TFT_BLUE);

  drawPanel("SWR", 160, 120, 160, 120, TFT_YELLOW);

  drawPanel("PEP W", 0, 240, 320, 240, TFT_ORANGE); // Wide bar for PEP

  

  pinMode(HV_PIN, INPUT);

  pinMode(PLATE_PIN, INPUT);

  pinMode(GRID_PIN, INPUT);

  pinMode(FWD_PIN, INPUT);

  pinMode(REF_PIN, INPUT);

}


void loop() {

  // 1. Read Sensors

  float hv = analogReadAvg(HV_PIN, 5) * HV_FACTOR;

  float plate = analogReadAvg(PLATE_PIN, 5) * PLATE_FACTOR;

  float grid = analogReadAvg(GRID_PIN, 5) * GRID_FACTOR;

  

  int fwdRaw = analogReadAvg(FWD_PIN, 5);

  int refRaw = analogReadAvg(REF_PIN, 5);

  

  // Convert to Power (Simplified linear approx for demo, square law for diodes)

  float fwdVolts = (fwdRaw / 4095.0) * 3.3;

  float refVolts = (refRaw / 4095.0) * 3.3;

  

  // Diode correction (approx 0.3V drop for Schottky)

  if(fwdVolts < 0.3) fwdVolts = 0; else fwdVolts -= 0.3;

  if(refVolts < 0.3) refVolts = 0; else refVolts -= 0.3;

  

  float fwdPwr = sq(fwdVolts) * 100; // Calibration factor needed here

  float refPwr = sq(refVolts) * 100;


  // 2. Peak Hold Logic

  if (fwdPwr > peakFwd) {

    peakFwd = fwdPwr;

    peakTime = millis();

  } else if (millis() - peakTime > DECAY_TIME) {

    peakFwd *= 0.95; // Decay

    if(peakFwd < 1) peakFwd = 0;

  }


  // 3. SWR Calculation

  float swr = 1.0;

  if (fwdPwr > 5 && refPwr > 0) {

    float ratio = sqrt(refPwr / fwdPwr);

    if (ratio < 1.0) swr = (1.0 + ratio) / (1.0 - ratio);

    if (swr > 9.9) swr = 9.9;

  } else if (fwdPwr < 5) {

    swr = 0; // No power

  }


  // 4. Update Display

  updateValue(0, 0, 160, 120, (int)hv, "V", TFT_RED);

  updateValue(160, 0, 160, 120, (int)plate, "mA", TFT_GREEN);

  updateValue(0, 120, 160, 120, (int)grid, "mA", TFT_BLUE);

  

  // SWR Color Code

  uint16_t swrColor = TFT_GREEN;

  if(swr > 1.5) swrColor = TFT_YELLOW;

  if(swr > 2.5) swrColor = TFT_RED;

  updateValueFloat(160, 120, 160, 120, swr, "", swrColor);


  // PEP Display

  updateValueWide(0, 240, 320, 240, (int)peakFwd, "W (PEAK)", TFT_ORANGE);


  // 5. Touch Reset (Optional)

  if (ts.touched()) {

    peakFwd = 0; // Reset Peak on touch

  }


  delay(100);

}


// --- Helper Functions (Same as before + new ones) ---

int analogReadAvg(int pin, int count) {

  long sum = 0;

  for (int i = 0; i < count; i++) sum += analogRead(pin);

  return sum / count;

}


void drawPanel(String label, int x, int y, int w, int h, uint16_t color) {

  tft.fillRect(x, y, w, h, TFT_DARKGREY);

  tft.setTextColor(TFT_WHITE);

  tft.setTextSize(2);

  tft.setCursor(x + 5, y + 5);

  tft.print(label);

  tft.drawLine(x+5, y+h-25, x+w-5, y+h-25, TFT_WHITE);

}


void updateValue(int x, int y, int w, int h, int val, String unit, uint16_t color) {

  tft.fillRect(x + 5, y + 25, w - 10, h - 50, TFT_BLACK);

  tft.setTextColor(color);

  tft.setTextSize(4);

  tft.setCursor(x + 5, y + 40);

  tft.print(val);

  tft.setTextSize(2);

  tft.print(" " + unit);

}


void updateValueFloat(int x, int y, int w, int h, float val, String unit, uint16_t color) {

  tft.fillRect(x + 5, y + 25, w - 10, h - 50, TFT_BLACK);

  tft.setTextColor(color);

  tft.setTextSize(4);

  tft.setCursor(x + 5, y + 40);

  tft.print(val, 1);

  tft.setTextSize(2);

  tft.print(unit);

}


void updateValueWide(int x, int y, int w, int h, int val, String unit, uint16_t color) {

  tft.fillRect(x + 5, y + 25, w - 10, h - 50, TFT_BLACK);

  tft.setTextColor(color);

  tft.setTextSize(6);

  tft.setCursor(x + 10, y + 40);

  tft.print(val);

  tft.setTextSize(3);

  tft.print(" " + unit);

  

  // Bar Graph

  int maxP = 1000; // Scale to 1000W

  int barW = map(val, 0, maxP, 0, w - 20);

  tft.fillRect(x + 10, y + h - 20, barW, 10, color);

  tft.fillRect(x + 10 + barW, y + h - 20, (w - 20) - barW, 10, TFT_DARKGREY);

}


3. Calibration Steps

SWR Calibration:

Connect a dummy load (SWR=1.0). Transmit 100W. Adjust PWR_FACTOR until FWD reads 100W and SWR reads 1.0.

Introduce a known mismatch (if possible) or verify REF reads near 0.

Peak Hold Tuning:

Transmit SSB voice. Watch the PEP display. It should jump quickly and decay slowly (1.5s). Adjust DECAY_TIME in code to taste.

Touch Reset:

Tap the screen anytime to zero the Peak Hold reading. 

4. Safety & RF Proofing

Shielding: Place the ESP32 in a metal enclosure. Run shielded cables for FWD/REF inputs.

Clamping: Add 3.3V Zener diodes on FWD and REF lines to protect the ESP32 from high-voltage spikes if the coupler fails.

Grounding: Ensure the coupler ground is solidly connected to the SB-200 chassis. 

Comments