- Phone / tabletpaired by QR · HTTP + SSE
- FanPilotservice · web panel · safety rules
- Gigabyte.Engine.EnvironmentControl.dllGCC client SDK, loaded by reflection
- EasyTuneEngineService.NET Remoting · ipc://GigabyteEngine · SYSTEM, admins only
- ITE IT8689E Super I/OSmartGuardian · the curve runs in hardware
The problem
On Gigabyte boards, the only way to change the fan curves from Windows is the Gigabyte Control Center: a desktop app, on the PC itself. FanPilot set out to do the same from a phone on the same Wi-Fi, without replacing the vendor’s service and without shipping any of its files.
Finding the way in
The analysis started from the shipped artifacts: metadata and IL of the .NET assemblies, reflection-only API dumps
and targeted decompilation, then confirmed at runtime with small probes (tools/Probe*.cs). The reference machine was
a B760M GAMING X AX with an i5-13400.
The architecture turned out to be a client and a service talking over .NET Remoting:
GCC.exeis only the UI. It loadsGigabyte.Engine.EnvironmentControl.dll, a client SDK of*Moduleclasses.- Those modules connect to
ipc://GigabyteEngine/EnvironmentControl/<Object>, published by EasyTuneEngineService, a Windows service running as SYSTEM. - The service does the real work, down to
Gigabyte.EnvironmentControl.LowPinCount.dll, which talks to the board’s ITE IT8689E Super I/O chip over the LPC ports.
Two details mattered right away:
- The IPC port’s ACL admits Administrators only: a non-elevated process gets “access denied”. FanPilot asks for elevation for that reason.
- The first call on a new connection fails with “the pipe is being closed”; a retry works. FanPilot retries once.
Reading the sensors
HardwareMonitorControlModule.GetCurrentMonitoredData returns every reading as {Type, Title, DeviceUUID, Value, Unit}: on the test board, 23 items, from CPU and VRM temperatures to fan RPM, voltages, clocks and CPU power. The GPU
isn’t there, so FanPilot reads NVIDIA cards through the driver’s own nvml.dll.
Writing a curve
The Control Center picks a “generation” of fan control from the board’s group number. On this board it is the third,
and the module that works is SmartGuardianFanControlCoordinateSystemModule: Get and Set a curve per header,
choose which sensor drives it, read temperatures and RPM side by side.
A curve is a SmartFanDataPointSet: a mode and a list of points, X in °C and Y as PWM from 0 to 255. Inside the
service, Set goes through a converter that turns it into the chip’s native SmartGuardian parameters. That has
a big consequence: the curve runs in hardware, and keeps running with FanPilot, and even Windows, closed.
The point that had to be 255
The first write test “didn’t work”: every fan went to full speed. The converter, read from
SmartGuardianFanConfigConverter, explained why. It maps the points like this:
| Point | Becomes |
|---|---|
P[0].X |
the temperature at which the fan turns off |
P[1] |
start temperature and start PWM |
P[2..n-2] |
“extra vectors”: start, PWM and slope of each segment |
P[n-1] |
the full-speed temperature, and its Y must be 255 |
With fewer than three points, or a last point below 255, the conversion aborts silently and the service writes an all-zero configuration, which the chip treats as maximum speed. FanPilot’s editor therefore always ends a curve at 100%, and its validation refuses anything the chip would misread.
The fix was confirmed on the real board: System 1 set to 40% up to 70 °C went from 2352 to 1573 RPM in 8 seconds, and back up when the original curve was restored.
Built without the vendor’s files
FanPilot doesn’t redistribute or compile against any Gigabyte file. A thin layer, GigabyteApi, resolves the
Control Center’s types and members by reflection from the copy already installed on the PC, checks the whole contract
at startup, and caches the handles. If a Control Center update changes something, FanPilot says exactly what is
missing in the panel and carries on through LibreHardwareMonitor. One build serves every board, and the CI can build
it.
Every other board
Boards without the Control Center go through LibreHardwareMonitor and the PawnIO driver, which FanPilot offers to install with winget. There the chip only takes a duty cycle and keeps no curve, so FanPilot runs the curves itself, once a second. Each curve has a response (Fast, Balanced or Smooth): the temperature is smoothed, the fan only slows down after the PC cools a few degrees, and the speed changes gradually. Closing FanPilot, a sensor without a reading or repeated failures hand every fan back to the BIOS.
On Linux (beta), FanPilot reaches the board through the kernel’s hwmon drivers (nct6775, it87,
asus-ec-sensors…) and the GPU through NVML or amdgpu, as a systemd service.
Both paths sit behind one interface, IBoard, so the rest of FanPilot doesn’t care which one it is talking to.
The app around it
- A service, started with Windows before logon, restarted by Windows if it dies, plus a tray icon and a terminal interface.
- A web panel served by FanPilot itself over HTTP, with live readings pushed by Server-Sent Events.
- QR pairing: a single-use 144-bit code shown on the PC, then a session cookie of which only a hash is stored. The server answers only requests addressed to the PC (against DNS rebinding) and checks origin and a custom header on every write (against CSRF).
- Safety rules in one place: at least 20% on every curve, 100% at the last point by 90 °C, pumps kept at 100% or a chosen minimum, a 15-second watchdog that re-applies your curve if something overwrites it.
How it’s tested
- 308 C# test cases (xUnit), with no hardware and no Administrator, against a fake board with scenarios: the demo board, a worst case with eight fans and long names, an unsupported board, a service that is down, a slow one.
- Unit tests for the panel’s JavaScript (Node’s test runner): curve math, i18n, sensors.
- 70 end-to-end tests in 15 Playwright spec files, run in Edge: the curve editor by mouse, keyboard and touch, pairing, alerts, history, panel mode, the MQTT integration, reconnection after a restart, accessibility with axe-core, and screenshots at 320, 390 and 1024 px in both themes.
--hardware-teston the real board: fixed speed, a watched curve, Turbo, the GPU fan and a restart, never below 40%, refusing the CPU fan and pumps, stopping if the CPU or GPU passes 80 °C, and putting everything back.- CI on every push (GitHub Actions): build, all of the above as Administrator, and a smoke test of the release zip.

Sources: docs/REVERSE_ENGINEERING.md and the design specs in the repository. REVERSE_ENGINEERING.md