An ESP32-based power controller (with 2 relays) for a BC-250 motherboard running and controlling a standard ATX power supply. It lets a saved BLE or Bluetooth gamepad, a physical button, or a local web page start the system. It also coordinates the ATX PS_ON signal with the motherboard power-button input and monitors the PC's real power state for safer startup and shutdown handling.
Warning
This project switches PSU and motherboard control signals. Incorrect wiring can damage the ESP32, motherboard, or PSU. Disconnect mains power before changing wiring, verify every connection with a multimeter, and never feed 5 V or 12 V into an ESP32 GPIO. ATX supplies can retain hazardous energy after being unplugged.
- Provides short-press physical-button power toggling and a long-press emergency power cut.
- Controls an ATX PSU and BC-250 motherboard on/off.
- Powers on the BC-250 when a registered gamepad is detected over BLE or Bluetooth Classic.
- Provides web-UI power control, live state, gamepad management, and a rolling event log.
- Supports optional password protection for the WebUI and HTTP API.
- Connects to a configured 2.4 GHz Wi-Fi network and falls back to its own setup access point if the connection fails.
- Saves router Wi-Fi settings from the web portal and applies them after a guarded controller restart.
- Discovers nearby gamepads from the built-in web-UI.
- Supports manual MAC-address registration when a gamepad is not discoverable.
- Supports persistent nicknames for registered controllers.
- Supports web-UI Android & iOS shortcut icons.
- Keeps the fallback AP active after a failed or dropped router connection; another router attempt is made only when settings are saved again or the ESP32 boots.
- Supports Bluetooth MAC address spoofing for Bluetooth Classic gamepads that are already paired with the BC250.
The ESP32 remains powered from the ATX supply's always-on +5VSB rail. While the PC is off, it periodically scans for Bluetooth advertisements and inquiries from registered controllers.
When a known controller is detected, or power-on is requested from the button or web page, the controller:
- Activates the PSU relay, connecting ATX
PS_ONto ground. - Waits 1 second for the PSU rails to settle.
- Activates the motherboard power-button relay for 500 ms.
- Waits up to 15 seconds for the PC monitor input to go high.
- Keeps
PS_ONasserted while the PC is running. - Releases both relays if startup is not confirmed.
For a normal shutdown, including a short case-button press while the PC is on, it pulses the motherboard power-button input and leaves the PSU enabled while the operating system shuts down. Once the monitor input remains low and shutdown is confirmed, it releases PS_ON. Controller-triggered wake is ignored for 60 seconds after shutdown to avoid accidental wake loops.
Holding the case button for at least 3 seconds bypasses the normal shutdown sequence and immediately releases both relays to cut ATX power. This can cause data loss and should only be used when the PC cannot shut down normally.
- An original ESP32 with Wi-Fi, BLE, and Bluetooth Classic support.
- A two-channel, ESP32-compatible relay board; the sketch is arranged for an active-high ESP32 Relay X2-style board.
- BC-250 motherboard and its power-button header/pads.
- Standard ATX PSU with accessible
+5VSB,PS_ON, and ground connections. - Momentary normally-open push button (PC Case button).
- LED with a suitable current-limiting resistor, unless the button assembly already includes one.
- Hook-up wire, insulated terminals/connectors, and suitable enclosure.
- Bluetooth controller whose address can be observed through BLE or Bluetooth Classic discovery (can also use BluetoothViewer).
ESP32-C3 boards do not support Bluetooth Classic. Other ESP32 variants may also require changes to the Bluetooth APIs or pin assignments used by this sketch.
| ESP32 pin | Sketch name | Connects to | |
|---|---|---|---|
GPIO 17 |
RELAY_PWR_PIN |
BC25_PW |
Relay contact across the BC-250 power-button pins |
GPIO 16 |
RELAY_PSU_PIN |
PS_ON |
Relay contact between ATX green PS_ON and PSU ground |
GPIO 19 |
BUTTON_PIN |
Case_Button |
Physical button to ground |
GPIO 23 |
LED_PIN |
— | Status/button LED through a suitable resistor |
GPIO 4 |
PC_MONITOR_PIN |
TPMS1_pin_9 |
3.3 V PC-on indication |
GND |
— | — | Button, LED, monitor-signal, and relay-board logic ground |
The labels “left relay” and “right relay” in the sketch refer to the author's relay board layout. Trust the GPIO numbers and verify your own board's relay mapping rather than relying only on physical position.
The ESP32 and relay logic must remain powered when the main ATX rails are off:
| ATX connection | Controller connection |
|---|---|
Purple +5VSB |
Relay-board/ESP32 VCC or 5V supply input appropriate for the board |
| Black ground | Relay-board/ESP32 GND |
Check the exact relay board documentation before applying power. A terminal labelled 5V may be an output on some integrated ESP32 relay boards rather than the intended supply input.
Use the normally-open contacts of the relay controlled by GPIO 16:
- Relay
COMto the ATX greenPS_ONwire. - Relay
NOto an ATX black ground wire (GND). - Leave
NCunused.
Use the normally-open contacts of the relay controlled by GPIO 17:
- Relay
COMto one BC-250 power-button contact. - Relay
NOto the BC-250 TPMS1 pin 17 (GND). - Leave
NCunused.
- Connect a momentary button between
GPIO 19andGND. - If you have an external LED, connect it to
GPIO 23using the correct polarity and a current-limiting resistor.
- Connect the BC250 3.3V “PC on” signal to
GPIO 4.
Open BC250_ESP32_ATX_PSU.ino and review the settings near the top before compiling.
These values are the first-boot defaults. Saved values from the web portal take priority:
const char* DEFAULT_WIFI_SSID = "YOUR_2_4_GHZ_WIFI";
const char* DEFAULT_WIFI_PASSWORD = "YOUR_WIFI_PASSWORD";
const char* WEB_HOSTNAME = "bc250-controller";
const char* WIFI_AP_PASSWORD = "CHOOSE_AN_8_PLUS_CHARACTER_PASSWORD";- The ESP32 Wi-Fi radio uses 2.4 GHz; a 5 GHz-only network will not work.
- Set
DEFAULT_WIFI_SSIDto an empty string to start with the local setup AP. Router credentials can then be entered in the portal. - Set
WIFI_AP_PASSWORDto an empty string for an open AP, or use at least eight characters for a protected AP. - Router SSID and password changes made in the portal are stored in the ESP32
Preferencesnamespacewifi_cfgand survive restarts and power loss. - The portal never returns the saved password. A blank password field preserves it; select Open network to erase it deliberately.
- The fallback AP name and password remain compile-time settings.
Change the example fallback-AP password before flashing, or replace it with another password of at least eight characters. The portal also supports an optional WebUI password under ESP32 Settings. When enabled, the control panel and API require sign-in; passwords must be 8–64 characters and active sessions expire after eight hours of inactivity.
The server still uses plain HTTP rather than HTTPS, so credentials and commands are not encrypted in transit. Use it only on a trusted local network, choose strong AP and WebUI passwords, and do not expose port 80 to the internet.
Change the five pin constants if your hardware is wired differently. The supplied configuration expects active-high relay inputs:
const int RELAY_ON = HIGH;
const int RELAY_OFF = LOW;Some relay modules are active-low. Confirm the idle state before connecting the relay contacts to the PC or PSU, then reverse these constants if required. Both relays must be released at boot.
The main adjustable values are:
| Setting | Default | Purpose |
|---|---|---|
WAKE_COOLDOWN_MS |
15 s | Suppresses repeated wake requests after detection |
SHUTDOWN_COOLDOWN_MS |
60 s | Blocks controller wake immediately after shutdown |
WEB_SCAN_DURATION_MS |
15 s | Length of a controller scan started in the portal |
POWER_CUT_HOLD_MS |
3 s | Physical-button hold required for an immediate ATX power cut |
PSU_SETTLE_BEFORE_PWR_SW_MS |
1 s | Delay between PS_ON and motherboard button pulse |
POWER_BUTTON_PRESS_MS |
500 ms | Motherboard button pulse length |
STARTUP_CONFIRM_TIMEOUT_MS |
15 s | Maximum time to detect a successful startup |
If the ESP32 board has no onboard USB-to-serial interface, an Arduino Nano can be used as the programming adapter. See Using an Arduino Nano as a USB-to-TTL adapter to program the ESP32 for the tested wiring, Arduino IDE procedure, bootloader-button sequence, and 3.3 V/5 V serial-level warning.
The WebUI is stored separately in LittleFS, so both the sketch and the BC250_ESP32_ATX_PSU/data directory must be uploaded.
Before proceeding, install the arduino-littlefs-upload plugin for Arduino IDE 2:
- Download the latest
.vsixrelease. - Copy it to
~/.arduinoIDE/plugins/on macOS/Linux or%USERPROFILE%\.arduinoIDE\plugins\on Windows. - Restart Arduino IDE.
Then flash the project:
- Open
BC250_ESP32_ATX_PSU/BC250_ESP32_ATX_PSU.inoin Arduino IDE - Select
board>Select other board and port>ESP32 Dev Moduleand clickyesin the notification to install - Select
Tools>Partition Scheme>No OTA (2MB APP/2MB SPIFFS)(required due to the program size and WebUI filesystem) - Install an ESP32 LittleFS data-upload tool that supports Arduino IDE 2
- Click the
verifybutton (check icon) to test-compile the code - Go to
Tools>Portand see what ports are shown up - Connect the ESP32 to your PC (i.e. ESP32 to the Arduino and then through USB)
- Go to
Tools>Portand select the new port that appeared - Click
uploadand wait for the compile to finish - When you see
Connectingin the log, on ESP32 hold theIOObutton, press theENbutton once and then release theIOObutton - Hit CTRL + SHIFT + P and find the
Upload LittleFS to Pico/ESP8266/ESP32 - When you see
Connectingin the log, on ESP32 hold theIOObutton, press theENbutton once and then release theIOObutton - The firmware and WebUI are now installed
Re-upload the LittleFS data whenever a file under BC250_ESP32_ATX_PSU/data changes.
- Power the ESP32 from a current-limited USB supply and test the button, LED, monitor input, and relay outputs without the PSU/motherboard control contacts attached.
- Confirm relay polarity: both relays must be off immediately after reset when the PC monitor input is low.
- Power the controller from ATX
+5VSBand ground, still with the control contacts disconnected. - Watch Serial Monitor at
115200baud for the assigned LAN address. - If router Wi-Fi fails after about 20 seconds, join the access point named by
WEB_HOSTNAME(default:bc250-controller) and openhttp://192.168.4.1/. - Enter the 2.4 GHz router SSID and password under Wi-Fi & device, then select Save Wi-Fi.
- While the PC state is off, select Restart controller to apply the saved settings. Reconnect through the new LAN address shown in Serial Monitor.
- Under ESP32 Settings, enable a WebUI password and sign in again when prompted.
- Register a controller using the portal.
- Switch off and disconnect mains power, then wire the two relay contact pairs and PC monitor input.
- Recheck continuity, isolation, GPIO voltage, and relay idle states before reconnecting mains.
- Test web or physical-button power-on first, then test controller wake.
- Start a normal OS shutdown and confirm that
PS_ONremains active until the monitor signal goes low.
For the best compatibility with Bluetooth Classic controllers, pair each controller with the BC250 first and confirm that it works normally in the BC250 operating system. A paired Classic controller may stop advertising itself as discoverable and instead try to reconnect directly to the Bluetooth adapter address it remembers.
After pairing, find the Bluetooth adapter MAC address on the BC250:
bluetoothctl listUse the MAC address reported for the BC250 Bluetooth adapter or Bluetooth network connection.
In the ESP32 web interface, enter this address under Bluetooth config > Bluetooth spoof address, select Save, and restart the ESP32 while the BC250 is off. This is the BC250 adapter address, not the controller address. Each controller must still be added separately to the ESP32's Registered controllers list.
When the BC250 is off, the ESP32 temporarily uses the saved adapter address for Bluetooth Classic. This lets it detect a registered controller attempting to reconnect to the BC250 even when that controller is not discoverable through a normal inquiry. Once the BC250 turns on, the ESP32 stops answering under the spoofed address so the real BC250 Bluetooth adapter can take over. Leaving Bluetooth spoof address empty disables this behavior.
When connected to router Wi-Fi, open http://bc250-controller.local/ or use the assigned local IP address instead.
If router Wi-Fi is unavailable, join the fallback AP and open http://bc250-controller.local/ or http://192.168.4.1/.
The portal provides:
- Current PC state and internal power-operation state.
- A round power button that changes between power-on and shutdown according to the current PC state.
- The five saved-controller slots with removal controls.
- Optional persistent nicknames for saved controllers.
- A 15-second BLE/Bluetooth Classic scan for new devices.
- Persistent toggles for BLE, pairing-mode Bluetooth Classic, and spoof-address reconnect detection; all three default to enabled.
- Manual controller registration in
aa:bb:cc:dd:ee:ffformat. - A dedicated Bluetooth config section containing the detection-method switches and persistent Bluetooth adapter spoof-address configuration.
- Persistent 2.4 GHz router SSID/password configuration.
- Optional WebUI/API password protection with sign-in and sign-out controls.
- A guarded ESP32 restart button, enabled only while the PC is off and the power state machine is idle.
- The latest 40 in-memory log lines.
The page refreshes approximately every 1.5 seconds. Logs are held only in RAM and are cleared when the ESP32 restarts.
- Put the controller into its Bluetooth pairing/discoverable mode and keep it close to the ESP32.
- Open the web portal and select Scan under Discover controllers.
- Wait for the controller to appear in the discovered list.
- Select Pair next to the correct MAC address.
- Shut the PC down, wait for the 60-second shutdown cooldown, and activate the controller.
The discovered list shows each device's RSSI as a reference, but scan results are not filtered by signal strength. Verify the controller name and MAC address before pairing it.
Enter a known Bluetooth MAC address in the portal using colon-separated hexadecimal notation. You can assign an optional nickname while adding it and rename a saved controller later. Manual registration is useful when a controller does not advertise long enough to appear in a scan.
Controller addresses are stored in the ESP32 Preferences namespace xbox_cfg and survive power loss and firmware restarts. Adding a sixth unique controller replaces a slot using the sketch's rotating slot pointer. Controllers can be removed individually from the web page; there is currently no physical-button pairing or factory-reset gesture.
Some devices use private or rotating BLE addresses. Those devices may not wake reliably by a saved MAC address. Xbox controller behavior can also vary with controller model and firmware.
| Input | Result |
|---|---|
| Short press while PC is off | Starts the ATX power-on sequence |
| Short press while PC is on | Pulses the motherboard button for a normal shutdown |
| Hold for at least 3 seconds | Immediately releases both relays to cut ATX power |
The sketch performs two short LED blinks when accepting a short press or the long-press threshold. A forced power cut bypasses the operating system shutdown and may cause data loss. The LED toggles every 250 ms continuously; it is therefore best treated as an activity/heartbeat indicator rather than a direct PC-power indicator.
- PC-monitor changes must remain stable for 100 ms before they are accepted.
- Controller wake scanning runs only while the PC is off and at least one controller is saved.
- Normal controller wake is blocked for 60 seconds after the PC turns off.
- If PC-on confirmation is missing after 15 seconds, both relays are released.
The WebUI uses a small form-encoded HTTP API. It can also be called by trusted local automation. POST fields belong in an application/x-www-form-urlencoded request body, not the URL query string.
When WebUI password protection is enabled, first call /api/auth/login and retain the returned bc250_session cookie. All other API routes require that cookie. Sessions expire after eight hours of inactivity, and repeated failed sign-ins trigger an increasing delay.
| Method | Path | Purpose |
|---|---|---|
GET |
/api/status |
PC, Wi-Fi, scan, and power-state status |
GET |
/api/controllers |
Saved controller list |
GET |
/api/found |
Current discovery results |
GET |
/api/logs |
Rolling runtime log |
GET |
/api/wifi |
Saved network, Bluetooth, scan, and WebUI-password status (never returns saved passwords) |
POST |
/api/auth/login |
Sign in with password; this is the only API route available without a valid session when protection is enabled |
POST |
/api/auth/logout |
Invalidate the current session |
POST |
/api/auth/password |
Enable or change protection with currentPassword and newPassword, or disable it with currentPassword and remove=1 |
POST |
/api/power/on |
Request power-on |
POST |
/api/power/off |
Request normal shutdown |
POST |
/api/scan/start |
Start controller discovery |
POST |
/api/scan/options |
Set ble, inquiry, and paired to 0 or 1 |
POST |
/api/pair |
Save mac from the current discovery results |
POST |
/api/manual-add |
Save mac manually with optional name |
POST |
/api/nickname |
Set the optional name for zero-based slot |
POST |
/api/remove |
Remove a zero-based slot |
POST |
/api/wifi/save |
Save ssid, password, and clearPassword; a blank password preserves the current one unless clearPassword=1 |
POST |
/api/bluetooth/save |
Save the BC250 Bluetooth adapter spoof address as mac; a blank value disables spoofing after restart |
POST |
/api/restart |
Restart the ESP32; rejected while the PC is on or power control is busy |
The server rejects unknown host names, cross-origin POST requests, oversized requests, and malformed headers. These checks and optional authentication reduce accidental or cross-site access, but they do not provide TLS or replace network isolation. Do not expose port 80 to the internet.
- If the device responds with
WebUI filesystem is not availableorAsset not found, upload theBC250_ESP32_ATX_PSU/datadirectory to LittleFS using the same partition scheme used for the sketch. - Confirm the configured network is 2.4 GHz and the SSID/password are correct.
- After the 20-second connection timeout, join the fallback AP and browse to
192.168.4.1. - Ensure the client is on the same LAN and that client/AP isolation is disabled.
- If
http://bc250-controller.local/does not resolve, use the assigned local IP address.
- Confirm that the required detection method is enabled under Bluetooth config.
- Wait for the full 15-second scan because BLE and Classic inquiries share the radio.
- Read the controller MAC address elsewhere and add it manually.
- Wait at least 60 seconds after shutdown.
- Confirm the saved MAC matches the address the controller currently advertises.
- Add the BC250 Bluetooth adapter MAC address on the ESP32 WebUI to spoof its Bluetooth while BC250 is off.
