Files
ESP32-Code-for-the-Half-Bri…/README.md
T
2026-10-10 01:35:14 +00:00

320 lines
24 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# ESP32 Complementary Half-Bridge Controller
**Hardware-timed complementary PWM, manual control, and automated frequency sweeps from a local web page.**
This project runs on a **classic ESP32-DevKitC** (ESP32-WROOM-class board) using the **Arduino-ESP32 3.x** core. It generates two complementary logic signals for a dual-channel gate driver, controls the gate driver's disable input, and serves a browser-based control panel over Wi-Fi. The ESP32 first tries a normal Wi-Fi router and, if it cannot join one at startup, falls back to its own Wi-Fi access point.
> [!CAUTION]
> **High-voltage hardware:** This firmware was developed for an IGBT half-bridge / Paul-trap driver. **The PWM outputs have no programmed deadtime.** Complementary ESP32 pins do **not** guarantee non-overlap at the IGBTs: propagation-delay mismatch, turn-off time, and IGBT tail current can cause shoot-through. Do not energize the HV DC bus until you have checked **both gate-emitter waveforms** and the power-stage switching at a safe test voltage. The gate-driver **DIS** input is *not* a means of isolating or discharging the HV bus. Provide an independent hardware interlock/emergency shutdown and appropriate discharge arrangements.
## 1. Features
- **Two synchronized, complementary PWM outputs** generated by one internal **LEDC hardware channel** (not two independently started timers).
- Adjustable frequency **100–200,000 Hz** and CH1 duty cycle **1–99%**; CH2 is the inverted output.
- Automatic search for the highest supported LEDC duty-resolution setting, from **14 bits down to 1 bit**. Failed trial settings are reported as normal progress instead of misleading driver-error spam.
- Gate-driver disable on **GPIO19**. Default state after boot: **disabled** until an explicit Enable command.
- Web-based **manual controls** and **frequency sweeps** (start, finish, step, on-time, and separate HV-off preparation time).
- Non-blocking sweep engine: timing runs on the ESP32, not in the browser; it continues if the browser disconnects.
- A large green/amber/red status panel showing the selected frequency, sweep step, and time remaining. The browser checks a small `/status` response only while an automatic transition is expected.
- **Router-first Wi-Fi** with **mDNS** (`http://hvboard.local/`) and **standalone AP fallback** (`http://192.168.1.1/`). The two modes are alternatives, **not** simultaneous AP+STA operation.
- USB Serial Monitor commands for manual operation and debugging.
## 2. Hardware and pinout
| ESP32 DevKitC pin | Purpose | Connect to |
|---|---|---|
| **GPIO23** | CH1: normal LEDC PWM | Upper IGBT gate-driver logic input (via the intended input circuitry) |
| **GPIO22** | CH2: inverted LEDC PWM | Lower IGBT gate-driver logic input (via the intended input circuitry) |
| **GPIO19** | Gate-driver disable; **HIGH = disabled, LOW = enabled** | UCC21550 `DIS` control input as wired in this design |
| **GND** | Logic reference | Gate-driver **input-side** ground, as required by the interface design |
| **USB** | Programming and serial debugging | Computer with a **data-capable** USB cable |
**Do not physically connect GPIO23 to GPIO22.** Both GPIOs are assigned *internally* to LEDC channel 0; GPIO22 is inverted by the ESP32 output logic. Keep the two gate-driver input connections separate.
The `DIS` polarity above is the convention used by **this sketch**; verify it against your actual UCC21550 wiring and datasheet. The sketch drives GPIO19 HIGH as early as `setup()` can control it, but **the pin may be floating during reset/boot before firmware starts**. Design a suitable *hardware-defined safe disable state* (for example, an appropriately chosen pull-up on the logic input) rather than relying on firmware alone.
This version is for a **classic ESP32**, not the previous ESP32-CAM pinout or a four-IGBT NPC stage. GPIO16/17 were used in earlier iterations; GPIO23/22/19 are the current pins. On some ESP32 modules GPIO16/17 are reserved for PSRAM, which is another reason **not** to assume earlier pinouts apply here.
## 3. Software requirements
### Recommended development environment
| Item | Use |
|---|---|
| IDE | **Arduino IDE 2.x** |
| Board package | **esp32 by Espressif Systems**, **3.x** series |
| Board selection | **Tools → Board → ESP32 Arduino → ESP32 Dev Module**, or the matching **ESP32 DevKitC** profile if listed |
| Upload port | The USB serial/COM port for your connected DevKitC |
| Serial Monitor baud | **115200** |
| Serial Monitor line ending | **Newline** or **Both NL & CR** |
Use the current **stable** 3.x release of Espressif's Arduino board package. The sketch was developed with the 3.x LEDC API; **Arduino-ESP32 2.x is not compatible without porting the LEDC calls**. As with any hardware-control firmware, record the precise working board-package version in your lab/repository notes after a successful upload; that exact patch version is not specified in this project.
### Install ESP32 board support
1. Install [Arduino IDE](https://www.arduino.cc/en/software).
2. Open **File → Preferences** (or Arduino IDE → Settings on macOS).
3. In **Additional Boards Manager URLs**, add the Espressif **stable** URL:
```text
https://espressif.github.io/arduino-esp32/package_esp32_index.json
```
4. Open **Boards Manager**, search for **esp32 by Espressif Systems**, and install a **3.x** version.
5. Select **ESP32 Dev Module** (or the exact matching classic DevKitC board), then select its USB serial port.
**Do not select an ESP32-S3, C3, ESP8266, or ESP32-CAM board profile** for a classic ESP32-DevKitC.
### Libraries: no separate installations required
The sketch includes:
```cpp
#include <Arduino.h>
#include <WiFi.h>
#include <ESPmDNS.h>
#include <WebServer.h>
#include "esp_log.h"
```
`Arduino`, `WiFi`, `ESPmDNS`, `WebServer`, and the underlying ESP-IDF logging headers are provided by the **Espressif Arduino-ESP32 board package**. **Do not install unrelated third-party libraries with the same names**; they may cause ambiguous includes or incompatibilities. No RMT, MCPWM, timer-interrupt, display, JSON, or external web-server library is needed. The HTML/CSS/JavaScript page is embedded directly in the sketch.
> **Common API trap:** Do **not** copy LEDC examples based on the old `ledcSetup()` / `ledcAttachPin()` APIs. This code uses `ledcAttachChannel()`, `ledcOutputInvert()`, `ledcWriteChannel()`, and `ledcChangeFrequency()` from the **3.x** API.
## 4. Find and open the sketch
The intended sketch filename is:
```text
ESP32_LEDC_2CH_200kHz_sweep_router_mdns_fallback.ino
```
In Arduino IDE, use **File → Open** to select this `.ino`. If Arduino asks to create a folder with the same basename, accept. Alternatively, arrange the repository as:
```text
ESP32_LEDC_2CH_200kHz_sweep_router_mdns_fallback/
├── ESP32_LEDC_2CH_200kHz_sweep_router_mdns_fallback.ino
└── README.md
```
If your repository uses a different sketch filename, use that filename consistently; the Arduino sketch folder should normally match its `.ino` basename.
## 5. Configure Wi-Fi **before** uploading
Near the top of the `.ino`:
```cpp
const char *ROUTER_SSID = "YOUR_ROUTER_WIFI_NAME";
const char *ROUTER_PASSWORD = "YOUR_ROUTER_WIFI_PASSWORD";
const char *MDNS_HOSTNAME = "hvboard";
const uint32_t ROUTER_CONNECT_TIMEOUT_MS = 12000;
const char *AP_SSID = "2-Channel HV board";
const char *AP_PASSWORD = "12345678";
```
**Option A — existing router:** Replace the two `YOUR_ROUTER_...` placeholders with the normal Wi-Fi credentials of a **2.4 GHz** network. You do **not** need router administrator access. On startup, the board attempts to join for up to **12 seconds**. If successful, open **http://hvboard.local/** from a computer/phone on the **same local network**. If `.local` does not work, type `wifi` in Serial Monitor and use the **Local IP** printed there.
**Option B — standalone:** Leave the router credential placeholders unchanged. The ESP32 creates its own network:
- Wi-Fi name: **`2-Channel HV board`**
- Wi-Fi password: **`12345678`** (change it before shared use)
- Control page: **http://192.168.1.1/**
A browser/device may warn that this Wi-Fi has **no internet**; choose to stay connected. Type the URL with **`http://`**, not `https://`.
**How fallback works:** If joining the router **fails during startup**, the sketch switches from `WIFI_STA` to `WIFI_AP`. It does **not** run a permanent AP alongside a working router connection. The fallback decision is made during boot: if the router later drops out while STA mode is active, the existing sketch does **not** automatically change to AP without restarting/resetting the board. `hvboard.local` is a **local-network** name, not a public internet URL. Guest/client isolation, multicast filtering, VPNs, and client mDNS support can prevent name resolution or local access.
> [!WARNING]
> **Security:** The webpage deliberately has **no second username/password prompt**. Anyone who can reach the ESP32 over the network can access Enable/Disable and sweep endpoints. **Do not put this HV controller on a shared/untrusted campus or public Wi-Fi network without adding authentication and network restrictions.** The default fallback AP password is only an example, not production-grade protection.
## 6. Upload and first safe test
1. **Disconnect the HV DC bus** and prevent the gate-driver/power stage from switching dangerous voltage. Start with the ESP32 and logic signals only.
2. Connect the DevKitC over a **data-capable USB cable**. Select the board and port in Arduino IDE.
3. Click **Verify** to compile, then **Upload**.
4. If upload hangs at `Connecting...`, try a different USB data cable/port; on some DevKitC boards, hold **BOOT** during the initial connection and release after the upload begins. Avoid anything connected to the USB UART pins (**GPIO1/TX, GPIO3/RX**) during flashing.
5. Open **Serial Monitor**, select **115200 baud** and **Newline**.
6. Confirm that LEDC initializes, Wi-Fi starts in **STA or fallback AP** mode, and the startup state reports **`Gate-driver output: DISABLED`**.
7. With the HV supply still disconnected, measure GPIO23, GPIO22, and GPIO19 with an oscilloscope. GPIO23/22 are complementary logic signals, and GPIO19 is HIGH while the driver is disabled. Check the actual gate-driver outputs **and gate-emitter voltages** separately before any powered switching test.
8. Connect to the selected network and open the control page. Try **Enable** and **Disable** at safe logic/low-voltage conditions before proceeding.
`AUTO_START_AFTER_BOOT = false` is intentional. The messages:
```text
Automatic startup is disabled.
Gate-driver output: DISABLED
```
mean GPIO19 is held **HIGH** after startup. They are **not errors**. Press **Enable** on the page or enter `enable` in Serial Monitor to begin the configured blanking interval and then set GPIO19 LOW.
## 7. Manual control
Default values:
| Setting | Default / permitted values |
|---|---|
| Frequency | **1,500 Hz** initially; **100–200,000 Hz** allowed |
| CH1 duty | **50%** initially; **1–99%** allowed |
| CH2 duty | Complementary to CH1 (nominally **100% − CH1 duty**) |
| Initial/manual blanking | **2,000 ms** initially |
| Automatic startup | **Off** |
| Gate-driver DIS | **GPIO19 HIGH = disabled**, LOW = enabled |
On the webpage, **Apply settings** sets manual frequency/duty/blanking. **Enable** waits the configured blanking time and then enables the gate driver; **Disable / Stop sweep** disables the driver immediately in the main loop. Changing manual settings while running disables the driver, updates LEDC, then resumes after blanking. Manual controls also take precedence over and cancel an active sweep.
**Three different timings:**
- `parameterBlankMs = 2000` — manual Enable/Apply blanking **and** initial blanking before the first sweep step.
- `STARTUP_BLANK_MS = 2000` — only relevant if `AUTO_START_AFTER_BOOT` is changed to `true` (not recommended for HV testing).
- `delay(500)` near `Serial.begin(115200)` — a short **serial/startup settling pause**, **not** the HV blanking interval. GPIO19 is already configured to HIGH before that pause.
**LEDC behavior:** PWM continues to run at GPIO23/22 even while GPIO19 holds the *gate driver* disabled. A red “driver disabled” status does **not** mean the ESP32 pins are static, nor does it mean the HV supply is discharged. The “actual LEDC frequency” is a **peripheral configuration/readback value, not an oscilloscope measurement**.
## 8. Web frequency sweep
The **Frequency sweep** section accepts:
| Field | Meaning |
|---|---|
| Start frequency (Hz) | First frequency |
| Finish frequency (Hz) | Final frequency |
| Frequency step (Hz) | Positive step size; direction is automatic (up or down) |
| HV ON at each frequency (seconds) | Time the driver is enabled at that frequency, **0.1–3,600 s** |
| HV OFF preparation interval (seconds) | Time between adjacent steps when **GPIO19 is HIGH**, **0.1–3,600 s** |
Frequency inputs must be integer Hz. Sweep start/finish must be **100–200,000 Hz**; step must be **1–200,000 Hz**. The finish frequency is visited even if the last step is shorter. The preparation interval is **separate** from `parameterBlankMs`.
**Example** — start `1000`, finish `3000`, step `1000`, ON `3 s`, prep `5 s`, initial blank `2 s`:
```text
Start sweep GPIO19 HIGH 2 s (initial blank)
1,000 Hz GPIO19 LOW 3 s (driver enabled)
Prepare / spray GPIO19 HIGH 5 s (driver disabled)
2,000 Hz GPIO19 LOW 3 s
Prepare / spray GPIO19 HIGH 5 s
3,000 Hz GPIO19 LOW 3 s
Sweep complete GPIO19 HIGH driver disabled
```
During each preparation interval, the code **disables the gate driver first**, configures the **next** LEDC frequency, and waits with the driver off. It does **not** apply the initial blanking interval again between steps. The duty cycle remains at the current manual setting throughout the sweep. The sweep runs with `millis()` scheduling rather than blocking `delay()` calls, so the web server and serial controls remain responsive.
The page shows a **large colour-coded driver status box**:
- **Green:** driver enabled; shows the active frequency and current sweep step.
- **Amber/orange:** initial blanking or preparation; shows the upcoming frequency/time remaining.
- **Red:** driver disabled, sweep finished, stopped, or error.
The page uses small asynchronous `/status` requests roughly every **750 ms** while blanking/sweeping and stops periodic polling once a stable manual state is reached. The **ESP32** owns the actual sweep timing; closing the browser **does not cancel the sweep**.
**Stop:** Press **Stop sweep / Disable driver** or **Disable / Stop sweep**, or send `sweepstop`/`disable` through Serial Monitor. A sweep completes with GPIO19 HIGH. No automatic repeat is implemented.
## 9. Serial commands
Open **Serial Monitor → 115200 baud → Newline** and enter one command per line:
| Command | What it does |
|---|---|
| `help` | Print available commands |
| `status` | Show driver, frequency, duty, resolution, and sweep phase |
| `wifi` | Show whether STA/router or AP fallback is active and how to access the page |
| `set 1500 50` | Set frequency to 1,500 Hz and CH1 duty to 50% |
| `f 3000` | Set frequency to 3,000 Hz |
| `d 45` | Set CH1 duty to 45% (CH2 complementary) |
| `45%` | Alternative CH1 duty command |
| `blank 2000` | Set manual/initial blanking to 2,000 ms |
| `enable` | Begin blanking, then enable the gate driver |
| `disable` | Disable gate driver and stop an active sweep |
| `sweepstop` | Stop the sweep and disable the driver |
**Starting a sweep is implemented on the webpage (`/sweep/start`), not as a Serial Monitor `sweepstart` command** in this version. Serial `status` and `sweepstop` work during a web-started sweep.
## 10. Troubleshooting: previously encountered errors
### A. `Compilation error: cannot convert 'ledc_clk_src_t' to 'ledc_clk_cfg_t'`
**Cause:** An earlier version tried to call:
```cpp
ledcSetClockSource(LEDC_APB_CLK);
```
On the affected Arduino-ESP32 core, that argument is not the type expected by `ledcSetClockSource()`.
**Fix:** **Do not re-add that call.** The current sketch uses the Arduino core's default LEDC clock and probes which frequency/resolution combinations are accepted. Removing the line was the compatibility fix; changing USB drivers will not resolve this compiler error.
### B. `LEDC setup error: LEDC could not represent this frequency ...` at 1,500 Hz
An earlier sketch treated a **zero immediately returned by `ledcReadFreq(CH1_PIN)`** as a failed setup even after `ledcAttachChannel()` had succeeded. The current sketch recognizes successful attachment and uses the **requested frequency as a fallback reporting value** if the initial readback is zero.
**If this error returns:** Use the latest sketch, select a **classic ESP32** board, check the Arduino-ESP32 core is **3.x**, and inspect which API call actually fails. **Do not assume 8-bit duty resolution is too high**: the code starts at **14 bits** and tries down to **1 bit**.
### C. Repeated logs such as `requested frequency 150000 and duty resolution 14 can not be achieved`
These were **expected trial failures**, not an overall failure. The firmware probes 14, 13, 12, ... bits and selects the **highest resolution supported** at the requested frequency. The current version suppresses the expected ESP-IDF `ledc` log messages *during each trial* and instead prints human-readable progress, ending with the **selected resolution** and configured frequency. This is normal: **higher frequencies leave fewer PWM duty steps**.
If every candidate fails, the sketch reports a genuine error. As an example, 200 kHz may require fewer resolution bits than 1.5 kHz. The reported exact resolution depends on the clock/core configuration.
### D. `ledcSetup` / `ledcAttachPin` not declared; old RMT/timer examples do not compile
This project uses **hardware LEDC**, *not* the old timer-ISR waveform generator, four independent RMT transmitters, or MCPWM. The Arduino-ESP32 **2.x → 3.x** migration changed peripheral APIs. Do not merge older snippets into this firmware without porting them. Use **one shared LEDC channel** and **per-pin inversion** for these two outputs.
### E. Upload fails: `Failed to connect to ESP32`, `Timed out waiting for packet header`, or no COM port
- Choose the correct **ESP32 Dev Module** and **Tools → Port**.
- Use a **USB data cable**; try another USB port (prefer direct computer connection over a hub).
- Install your board's **USB-to-UART driver** only if the interface isn't recognized (commonly CP210x or CH340, depending on board revision/clone).
- Close the Serial Monitor or other program holding the port while uploading.
- If needed, hold the **BOOT** button while upload begins; release it after the connection starts. Some boards may require EN/Reset.
- Check stable ESP32 power, remove connections from UART/boot-related pins, and **disconnect the HV system** for debugging.
### F. Serial Monitor is blank or commands are ignored
Use **115200 baud** and **Newline** (or Both NL & CR). The firmware collects input until `\n`; with **No line ending**, commands may appear to do nothing. Press **EN/Reset** once to see startup logs if Serial Monitor opened after boot. The early `delay(500)` is *not* related to the blanking time.
### G. `hvboard.local` does not open
- Check `wifi` in Serial Monitor: is the board actually in **STA** mode? If it fell back to AP, use **http://192.168.1.1/** after joining its Wi-Fi.
- In STA mode, phone/computer and ESP32 must be able to reach each other on the **same LAN**. Guest isolation and multicast blocking can prevent this.
- Make sure you used **`http://hvboard.local/`**, not `https://...`.
- Try the **Local IP** printed by `wifi`; it bypasses mDNS name lookup.
- mDNS support varies by device/OS; Windows configurations sometimes require additional discovery support (e.g., Bonjour), while macOS/iOS normally support mDNS.
- Ensure the existing router provides **2.4 GHz Wi-Fi** for a classic ESP32; use the correct SSID/password.
- The router/AP choice happens at **boot**. Reset the ESP32 if you want it to retry joining a now-available router.
### H. The webpage is slow or doesn't update after blanking
Use the **AJAX** version in this repository, not an older form/redirect sketch. The browser posts control commands without reloading the full page and calls the lightweight **`GET /status`** endpoint only while blanking/sweeping. If a page seems stale after installing new firmware, perform one **hard refresh** or close/reopen the tab. LEDs/Wi-Fi do not schedule sweep timing; that runs locally on the ESP32.
### I. Webpage shows `ENABLED`, but no HV waveform is visible
Verify **GPIO19** and the *actual* gate-driver output stages. `ENABLED` is the **firmware's GPIO19 command state**, not independent hardware feedback. LEDC logic signals can still toggle at GPIO23/22 even while DIS is HIGH. Verify driver input-side logic supply, grounding, isolated gate supplies, and the gate-emitter voltage with suitable instruments. **Do not infer the HV output from the webpage status alone.**
### J. PWM looks complementary on ESP32 pins but overlaps at IGBTs
A shared LEDC channel removes software phase drift, but this firmware contains **no intentional deadtime**. IGBT delays and turn-off tails can make both devices conductive simultaneously. **Stop HV testing** until appropriate non-overlap/deadtime and hardware protection have been verified. Raising frequency toward the **200 kHz software limit** does not establish that the UCC21550/IGBT power stage is safe at that switching rate.
## 11. Implementation notes and scope
- **LEDC is a hardware peripheral inside the ESP32**, not an external chip or additional library.
- **One LEDC channel** supplies both outputs; CH2 is inverted with `ledcOutputInvert(CH2_PIN, true)`.
- There is **no configurable deadtime**, **no shoot-through interlock**, **no independent HV-voltage/current feedback**, and **no hardware fault/brake input** implemented by this sketch.
- Frequency and duty are quantized to actual LEDC timer/duty settings; high-frequency duty percentages may not be represented exactly.
- The web server is basic HTTP on **port 80** (not HTTPS), with **no separate webpage login**.
- Sweep delays are software-timed using `millis()` and serviced from `loop()`; the PWM edges themselves come from LEDC hardware. UI updates and Wi-Fi activity do not require an interrupt per PWM edge.
- Router-first + AP fallback is retained intentionally. It does **not** keep two Wi-Fi networks operating at once.
- The firmware is provided as experimental control code, **not** a certified high-voltage safety controller.
## 12. Official references
- [Espressif — Arduino-ESP32 installation and Boards Manager](https://docs.espressif.com/projects/arduino-esp32/en/latest/installing.html)
- [Espressif — LEDC API (`ledcAttachChannel`, `ledcChangeFrequency`, inversion)](https://docs.espressif.com/projects/arduino-esp32/en/latest/api/ledc.html)
- [Espressif — Arduino-ESP32 migration from 2.x to 3.x](https://docs.espressif.com/projects/arduino-esp32/en/latest/migration_guides/2.x_to_3.0.html)
- [Espressif — DevKitC pinout and board guide](https://docs.espressif.com/projects/esp-dev-kits/en/latest/esp32/esp32-devkitc/user_guide.html)
- [Espressif — Arduino-ESP32 upload troubleshooting](https://docs.espressif.com/projects/arduino-esp32/en/latest/troubleshooting.html)
- [Espressif — ESPmDNS web-server example](https://github.com/espressif/arduino-esp32/blob/master/libraries/ESPmDNS/examples/mDNS_Web_Server/mDNS_Web_Server.ino)
---
**Before connecting HV:** Confirm correct module, pinout, polarity of DIS, logic power/ground, measured complementary waveforms, safe gate-driver operation, and a working independent emergency power-off path.