PCB Design for Fog Generator

Stopped by execution credits before I could finish the full schematic update or run ERC.
Completed so far:
  • Removed obsolete 14 V parallel ideal-diode mux parts: U10/U11, Q10/Q11, C1/C2, R1–R4.
  • Removed old TPS563200 17 V buck section: U3, L1, C5–C9, R5–R7.
  • Removed old pump MOSFET driver and 2-pin pump connector: Q2, R16, R17, D2, J4.
  • Renamed global power nets:
    • +14V_BUS+28V_BUS
    • +14V_MUX+28V_OR_PRE_FUSE
    • VBAT_A_INVBAT_SERIES_IN
    • PUMP_PWMSTEPPER_STEP
  • Added main new 28 V architecture parts:
    • U_BB LM5175PWPT buck-boost controller
    • U3 TPS54561DPRT wide-VIN 3.3 V buck regulator
    • J12 logical 6-pin LEMO input connector
    • J4 5-pin JST-XH stepper pump connector
    • U_DIODE_1/U_DIODE_2 LM74700-Q1 ideal-diode OR controllers
    • Q_DIODE_1/Q_DIODE_2/Q_BB1–Q_BB4 using TSM170N06CP_ROG 60 V / 38 A MOSFETs as library substitutes because FDP18N40 was not found
    • L_BB 47 µH / 15 A generic inductor placeholder
  • Added and partially wired:
    • LEMO input filter caps
    • LM5175 bootstrap, soft-start, compensation, RT, UVLO, and feedback passives
    • LM74700 1 µF VCAP bypass caps and gate networks
    • TPS54561 input/output caps, bootstrap cap, feedback divider, EN/UVLO, SS, RT, and compensation placeholders
    • Stepper connector pins for +28V_BUS, GND, STEPPER_STEP, STEPPER_DIR, STEPPER_ENABLE
  • Updated existing metadata:
    • C3 to 4700 µF / 50 V bulk
    • C4 to 50 V HF bypass
    • F1 marked 25 A / 50 V minimum requirement
    • J3 heater label/role updated for 28 V 200 W
    • Q1 metadata updated to the 60 V MOSFET substitute
Important unresolved items:
  • ERC has not been run after these edits.
  • LM5175 datasheet correction: LM5175 VIN max is 42 V, not 60 V. It may still work for a 28 V system, but it is not a 60 V input controller.
  • The exact LEMO EGG.1B.306 library footprint was not found; J12 is currently a logical 6-pin connector placeholder.
  • The LM5175 current-sense implementation is only placeholder-wired; the proper high-power current-sense resistor/network still needs detailed design.
  • INA226 monitor wiring is not fully resolved after the architecture change; U6B/R21 are still in an intermediate/orphaned state and need reconnection to the intended +28 V measurement point.
  • PCB layout is now invalid for this revised architecture and will need a fresh placement/routing pass after the schematic is cleaned.
Clean unresolved issues
Run ERC/DRC
Find 60V controller
Find LEMO footprint
Design current sense

Refine this doc
Ask about this doc
Firmware Starter — ESP32-S3-WROOM-1-N16
Platform & Toolchain
  • MCU module: ESP32-S3-WROOM-1-N16
  • Framework: Arduino on PlatformIO for fastest bring-up
  • USB: Native ESP32-S3 USB on IO19/IO20 through the USB-C programming connector; USB VBUS is isolated/test-only and does not power the board
  • Status: Updated through the complete v1 schematic blocks including UI header, USB-C programming, heater/fault indicators, DMX, thermocouple, and dual INA226 battery telemetry.
Pin Mapping

Table


FunctionESP32 GPIOSchematic NetDirectionNotes
Heater PWMGPIO4HEATER_PWMOutputDrives Q1 heater MOSFET gate via R13
Pump PWMGPIO5PUMP_PWMOutputDrives Q2 pump MOSFET gate via R16
Fan PWMGPIO6FAN_PWMOutputReserved for fan driver
Status LED 1GPIO7STATUS_LED_1OutputRouted through R31 330Ω to J10 pin 8
Status LED 2GPIO8STATUS_LED_2OutputRouted through R32 330Ω to J10 pin 9
Encoder AGPIO9ENCODER_AInputJ10 pin 5, 10kΩ pull-up R26 to +3V3
Encoder BGPIO10ENCODER_BInputJ10 pin 6, 10kΩ pull-up R27 to +3V3
Encoder buttonGPIO11ENCODER_BTNInputJ10 pin 7, 10kΩ pull-up R28 to +3V3
SPI MOSIGPIO12SPI_MOSIOutputNot used by MAX31855; available for future shared SPI devices
SPI MISOGPIO13SPI_MISOInputMAX31855 SO output
SPI SCKGPIO14SPI_SCKOutputMAX31855 SCK input
SPI CSGPIO15SPI_CS_MAX31855OutputMAX31855 chip select, active low
I2C SDAGPIO16I2C_SDABidirectionalINA226 battery monitors U6A/U6B, 4.7k pull-up R22
I2C SCLGPIO17I2C_SCLOutputINA226 battery monitors U6A/U6B, 4.7k pull-up R23
DMX UART RXGPIO18DMX_UART_RXInputReserved for MAX3485 RO
USB D-GPIO19USB_D_NUSBNative USB D-
USB D+GPIO20USB_D_PUSBNative USB D+
DMX UART TXGPIO21DMX_UART_TXOutputReserved for MAX3485 DI
BOOTGPIO0ESP32_BOOT_IO0Input10k pull-up; BOOT button pulls low
DMX directionGPIO35DMX_DIROutputReserved for MAX3485 DE/~RE
Heater current senseGPIO36HEATER_CURRENT_SENSEAnalog inputOptional / v2
Fault LEDGPIO37FAULT_LEDOutputDrives amber D4 through R34 330Ω
Enable / resetENESP32_ENInput10k pull-up, 0.1uF to GND, RESET button to GND
UI Header J10

Table


J10 PinNetNotes
1+3V3Logic supply for off-board UI/OLED
2GNDGround
3I2C_SCLShared I2C bus for off-board OLED at address 0x3C
4I2C_SDAShared I2C bus
5ENCODER_ARotary encoder A, board-side 10kΩ pull-up
6ENCODER_BRotary encoder B, board-side 10kΩ pull-up
7ENCODER_BTNEncoder push-button, board-side 10kΩ pull-up
8STATUS_LED_1_HDRGPIO7 through 330Ω R31
9STATUS_LED_2_HDRGPIO8 through 330Ω R32
10GNDGround
platformio.ini

Ini


[env:esp32-s3-devkitc-1]
platform = espressif32
board = esp32-s3-devkitc-1
framework = arduino
monitor_speed = 115200
upload_speed = 921600
board_build.flash_size = 16MB
board_build.arduino.memory_type = qio_opi
build_flags =
    -DARDUINO_USB_MODE=1
    -DARDUINO_USB_CDC_ON_BOOT=1
Complete Starter Source (src/main.cpp)

Cpp


#include <Arduino.h>
#include <Wire.h>
#include <SPI.h>

// Pin definitions from schematic
#define PIN_HEATER_PWM          4
#define PIN_PUMP_PWM            5
#define PIN_FAN_PWM             6
#define PIN_STATUS_LED_1        7
#define PIN_STATUS_LED_2        8
#define PIN_ENCODER_A           9
#define PIN_ENCODER_B           10
#define PIN_ENCODER_BTN         11
#define PIN_SPI_MOSI            12
#define PIN_SPI_MISO            13
#define PIN_SPI_SCK             14
#define PIN_SPI_CS_MAX31855     15
#define PIN_I2C_SDA             16
#define PIN_I2C_SCL             17
#define PIN_DMX_UART_RX         18
#define PIN_DMX_UART_TX         21
#define PIN_DMX_DIR             35
#define PIN_HEATER_CURRENT_ADC  36
#define PIN_FAULT_LED           37

#define INA226_ADDR_BAT_A       0x40
#define INA226_ADDR_BAT_B       0x41
#define INA226_REG_CONFIG       0x00
#define INA226_REG_SHUNT_V      0x01
#define INA226_REG_BUS_V        0x02
#define INA226_REG_POWER        0x03
#define INA226_REG_CURRENT      0x04
#define INA226_REG_CALIBRATION  0x05

// 5mΩ shunts on VBAT_A_IN and VBAT_B_IN.
// Current_LSB chosen as 1mA/bit for simple bring-up; update calibration if production scaling changes.
#define INA226_SHUNT_OHMS       0.005f
#define INA226_CURRENT_LSB_A    0.001f
#define INA226_CAL_VALUE        1024

// PWM setup
#define PWM_FREQ_HZ             2000
#define PWM_RES_BITS            10
#define PWM_HEATER_CHANNEL      0
#define PWM_PUMP_CHANNEL        1
#define PWM_FAN_CHANNEL         2

static unsigned long lastStatusMs = 0;
static bool blinkState = false;

void setupPwmOutputs() {
  ledcSetup(PWM_HEATER_CHANNEL, PWM_FREQ_HZ, PWM_RES_BITS);
  ledcAttachPin(PIN_HEATER_PWM, PWM_HEATER_CHANNEL);
  ledcWrite(PWM_HEATER_CHANNEL, 0);

  ledcSetup(PWM_PUMP_CHANNEL, PWM_FREQ_HZ, PWM_RES_BITS);
  ledcAttachPin(PIN_PUMP_PWM, PWM_PUMP_CHANNEL);
  ledcWrite(PWM_PUMP_CHANNEL, 0);

  ledcSetup(PWM_FAN_CHANNEL, PWM_FREQ_HZ, PWM_RES_BITS);
  ledcAttachPin(PIN_FAN_PWM, PWM_FAN_CHANNEL);
  ledcWrite(PWM_FAN_CHANNEL, 0);
}

void setupGpio() {
  pinMode(PIN_STATUS_LED_1, OUTPUT);
  pinMode(PIN_STATUS_LED_2, OUTPUT);
  digitalWrite(PIN_STATUS_LED_1, LOW);
  digitalWrite(PIN_STATUS_LED_2, LOW);

  pinMode(PIN_ENCODER_A, INPUT_PULLUP);
  pinMode(PIN_ENCODER_B, INPUT_PULLUP);
  pinMode(PIN_ENCODER_BTN, INPUT_PULLUP);

  pinMode(PIN_SPI_CS_MAX31855, OUTPUT);
  digitalWrite(PIN_SPI_CS_MAX31855, HIGH);

  pinMode(PIN_DMX_DIR, OUTPUT);
  digitalWrite(PIN_DMX_DIR, LOW); // default receive mode for future MAX3485 block

  pinMode(PIN_FAULT_LED, OUTPUT);
  digitalWrite(PIN_FAULT_LED, LOW);
}

void setupBuses() {
  Wire.begin(PIN_I2C_SDA, PIN_I2C_SCL);
  Wire.setClock(400000);

  SPI.begin(PIN_SPI_SCK, PIN_SPI_MISO, PIN_SPI_MOSI, PIN_SPI_CS_MAX31855);

  // DMX uses 250000 baud, 8N2. Transceiver is reserved for a later schematic block.
  Serial1.begin(250000, SERIAL_8N2, PIN_DMX_UART_RX, PIN_DMX_UART_TX);
}

bool i2cWrite16(uint8_t addr, uint8_t reg, uint16_t value) {
  Wire.beginTransmission(addr);
  Wire.write(reg);
  Wire.write((uint8_t)(value >> 8));
  Wire.write((uint8_t)(value & 0xFF));
  return Wire.endTransmission() == 0;
}

bool i2cRead16(uint8_t addr, uint8_t reg, uint16_t &value) {
  Wire.beginTransmission(addr);
  Wire.write(reg);
  if (Wire.endTransmission(false) != 0) return false;
  if (Wire.requestFrom((int)addr, 2) != 2) return false;
  value = ((uint16_t)Wire.read() << 8) | Wire.read();
  return true;
}

void setupIna226(uint8_t addr) {
  // Default averaging/conversion settings are acceptable for bring-up.
  // Program calibration so CURRENT register uses INA226_CURRENT_LSB_A.
  if (!i2cWrite16(addr, INA226_REG_CALIBRATION, INA226_CAL_VALUE)) {
    Serial.printf("INA226 0x%02X calibration write failed\n", addr);
  }
}

bool readIna226(uint8_t addr, float &busV, float &shuntV, float &currentA) {
  uint16_t rawBus = 0;
  uint16_t rawShunt = 0;
  uint16_t rawCurrent = 0;
  if (!i2cRead16(addr, INA226_REG_BUS_V, rawBus)) return false;
  if (!i2cRead16(addr, INA226_REG_SHUNT_V, rawShunt)) return false;
  if (!i2cRead16(addr, INA226_REG_CURRENT, rawCurrent)) return false;

  busV = rawBus * 0.00125f; // 1.25mV/bit
  shuntV = (int16_t)rawShunt * 0.0000025f; // 2.5uV/bit
  currentA = (int16_t)rawCurrent * INA226_CURRENT_LSB_A;
  return true;
}

uint32_t readMax31855Raw() {
  SPI.beginTransaction(SPISettings(4000000, MSBFIRST, SPI_MODE0));
  digitalWrite(PIN_SPI_CS_MAX31855, LOW);
  delayMicroseconds(1);
  uint32_t value = SPI.transfer32(0x00000000);
  digitalWrite(PIN_SPI_CS_MAX31855, HIGH);
  SPI.endTransaction();
  return value;
}

bool readMax31855(float &thermocoupleC, float &coldJunctionC, uint8_t &faultBits) {
  uint32_t raw = readMax31855Raw();
  faultBits = raw & 0x07;

  if (raw & 0x00010000UL) {
    return false; // any thermocouple fault
  }

  int16_t tcRaw = (raw >> 18) & 0x3FFF;
  if (tcRaw & 0x2000) tcRaw |= 0xC000; // sign extend 14-bit thermocouple field
  thermocoupleC = tcRaw * 0.25f;

  int16_t cjRaw = (raw >> 4) & 0x0FFF;
  if (cjRaw & 0x0800) cjRaw |= 0xF000; // sign extend 12-bit cold-junction field
  coldJunctionC = cjRaw * 0.0625f;

  return true;
}

void setup() {
  Serial.begin(115200);
  delay(250);
  Serial.println("Phantom Fogger Control Unit firmware starter");
  Serial.println("ESP32-S3 native USB serial active");

  setupGpio();
  setupPwmOutputs();
  setupBuses();
  setupIna226(INA226_ADDR_BAT_A);
  setupIna226(INA226_ADDR_BAT_B);

  analogReadResolution(12);
  Serial.println("Initialization complete");
}

void loop() {
  unsigned long now = millis();

  if (now - lastStatusMs >= 500) {
    lastStatusMs = now;
    blinkState = !blinkState;
    digitalWrite(PIN_STATUS_LED_1, blinkState ? HIGH : LOW);

    float tcC = NAN;
    float cjC = NAN;
    uint8_t fault = 0;
    bool tcOk = readMax31855(tcC, cjC, fault);
    float batAV = NAN, batAShuntV = NAN, batAA = NAN;
    float batBV = NAN, batBShuntV = NAN, batBA = NAN;
    bool batAOk = readIna226(INA226_ADDR_BAT_A, batAV, batAShuntV, batAA);
    bool batBOk = readIna226(INA226_ADDR_BAT_B, batBV, batBShuntV, batBA);
    int adcRaw = analogRead(PIN_HEATER_CURRENT_ADC);

    if (tcOk) {
      Serial.printf("TC=%.2f C, CJ=%.2f C, BatA=%s %.2fV %.2fA, BatB=%s %.2fV %.2fA, encoder A=%d B=%d BTN=%d heater_current_adc=%d\n",
                    tcC, cjC,
                    batAOk ? "OK" : "ERR", batAV, batAA,
                    batBOk ? "OK" : "ERR", batBV, batBA,
                    digitalRead(PIN_ENCODER_A),
                    digitalRead(PIN_ENCODER_B),
                    digitalRead(PIN_ENCODER_BTN),
                    adcRaw);
    } else {
      Serial.printf("MAX31855 fault=0x%02X, encoder A=%d B=%d BTN=%d heater_current_adc=%d\n",
                    fault,
                    digitalRead(PIN_ENCODER_A),
                    digitalRead(PIN_ENCODER_B),
                    digitalRead(PIN_ENCODER_BTN),
                    adcRaw);
    }
  }
}
Build & Flash

Bash


pio run
pio run -t upload
pio device monitor
Bootloader Entry
  1. Hold BOOT.
  2. Press and release RESET.
  3. Release BOOT.
  4. Upload firmware over USB.
  • Platform & Toolchain

  • Pin Mapping

  • UI Header J10

  • `platformio.ini`

  • Complete Starter Source (`src/main.cpp`)

  • Build & Flash

  • Bootloader Entry

Phantom Fogger Control Unit-V3 2e5b

Phantom Fogger Control Unit-V3 2e5b thumbnail
4-layer ESP32-S3 controller for a dual V-mount battery-powered film fog generator with 200 W heater PWM, pump/fan outputs, K-type thermocouple sensing, battery monitoring, and DMX-512 control.

Properties

Properties describe core aspects of the project.

Pricing & Availability

Distributor

Qty 1

Arrow

$27.83–$34.48

Digi-Key

$45.90–$46.11

HQonline

$14.19–$14.31

LCSC

$50.60–$50.78

Mouser

$75.29–$75.42

TME

$12.64

Verical

$26.46–$451.29

Controls