Files
md1200-fan-control/README.md
Zeb Hering 0fe728e28a Make the serial port and disk glob per host
The shelf moved from iz-pve1 to iz-pve0, which has two SAS HBAs and an
internal backplane that is itself behind an expander. The default
"*-sas-exp*" glob matched all 17 drives there, so the shelf's fans would
have been set from the server's own drive temperatures.

PORT and DISK_GLOB are now overridable from the unit. Documents that the
host holding the shelf also needs the mirror-image DISK_EXCLUDE_PATTERN
in fan_speed.service - it was counting 17 disks until that was set.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-30 16:36:15 -07:00

167 lines
7.0 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.
# md1200-fan-control
Temperature-aware fan control for a Dell PowerVault MD1200 disk shelf, driven over the
EMM's serial debug console.
Out of the box an MD1200 runs its fans at a fixed, very loud speed. The EMM firmware
accepts a `_shutup <percent>` command that overrides it — but it forgets the setting
after a few seconds, and it has no idea how hot your drives actually are. This repo
reads drive temperatures on the host and re-sends an appropriate fan speed on a
keepalive loop.
## Hardware you need
The MD1200 EMM exposes its debug console on the **top-left RJ45 port of the left EMM**
(the one labelled for service use, not the SAS ports). It is not Ethernet — it is RS-232
behind an RJ45 jack, so you need the Dell service cable:
**Dell Password Reset / Service Cable — part `MN657`, also sold as `CT109`.**
Same cable is listed for the MD1000 / MD1220 / MD3000 / MD3200 family, so search on any
of those. It terminates in a DB9 serial connector; if your host has no physical serial
port, add any USB-to-DB9 adapter and point the script at the resulting `/dev/ttyUSB*`.
On the machine this was written for, the cable lands on an onboard UART at `/dev/ttyS1`.
Serial settings: **38400 8-N-1**, no flow control.
> Note: Dell's own KB articles list 115200 for MD3xxx/ME3xxx *controllers*. That is a
> different port on different hardware. The MD1200 EMM debug console is 38400, which is
> what this repo uses and what was verified against the real shelf.
Once connected you get a prompt like:
```
BlueDress.106.000 >
```
## Debug commands
There is no `help` — the firmware answers `unknown_cmd` to `help`, `_help` and `?`. The
two commands this project relies on:
| Command | Effect |
|---|---|
| `_shutup <0-100>` | Set fan speed to that percentage. Forgotten if not re-sent. |
| `_temp_rd` | Print the enclosure temperature sensors. |
`_temp_rd` output looks like:
```
BP_1[2] = 30c <- drive backplane
BP_2[3] = 31c <- drive backplane
SIM0[0] = 36c <- enclosure management module
EXP0[4] = 59c <- SAS expander chip, runs hot by design
AVG = 39c
```
Commands are terminated with a bare `\r` (not `\n`).
## What this does
`das_fanctl.sh` runs as a systemd service and loops:
1. Read the temperature of every drive in the shelf with `smartctl`, take the hottest.
2. Interpolate a fan percentage between the low and high thresholds.
3. Send `_shutup <percent>`, and re-send it every 5s so the EMM doesn't revert.
4. Re-read temperatures every 30s.
**Drive temperature, not enclosure air.** The `BP_*` sensors read backplane intake air,
which sat at 30c while the drives themselves were 3239c — it lags what you actually
care about. Drive temps come from `smartctl -n standby`, so a sleeping drive is skipped
rather than spun up just to be measured. If no drive answers (all in standby, smartctl
missing), it falls back to the hottest `BP_*` sensor with its own threshold pair.
`EXP0` is deliberately ignored. The SAS expander idles around 59c by design; including
it in the curve pegs the fans permanently.
## Usage
```
das_fanctl.sh # the service loop (default)
das_fanctl.sh once # one read + set, prints what it did
das_fanctl.sh disks # per-drive temperatures and the hottest
das_fanctl.sh temps # raw _temp_rd output from the shelf
das_fanctl.sh selftest # asserts the parser, the curve, and that drives are readable
```
## Install
The script sources `functions.sh` by absolute path, so it expects to live in
`/root/fan_speed/`:
```sh
install -m 755 das_fanctl.sh functions.sh /root/fan_speed/
mkdir -p /root/fan_speed/log
install -m 644 fan_speed_das.service /etc/systemd/system/
systemctl daemon-reload
systemctl enable --now fan_speed_das.service
```
Check it: `das_fanctl.sh selftest`, then `tail -f /root/fan_speed/log/das_fan.log`.
```
2026-08-24 23:26:03 || DAS disk:39c | Fan Speed:10
```
## Tuning
All knobs are at the top of `das_fanctl.sh`:
| Variable | Default | Meaning |
|---|---|---|
| `PORT` | `/dev/ttyS1` | Serial device the service cable lands on |
| `CHECK_INTERVAL` | `30` | Seconds between temperature readings |
| `KEEPALIVE_INTERVAL` | `5` | How often to re-send `_shutup` |
| `DISK_LOW_TEMPERATURE_THRESHOLD` | `40` | At/below this, run at `LOW_FAN_SPEED` |
| `DISK_HIGH_TEMPERATURE_THRESHOLD` | `50` | At/above this, run at `HIGH_FAN_SPEED` |
| `LOW_FAN_SPEED` | `10` | Percent |
| `HIGH_FAN_SPEED` | `60` | Percent |
| `BP_*_THRESHOLD` | `30` / `45` | Fallback curve, backplane air sensors |
The defaults were sized against an idle spread of 3239c across nine mixed SATA/SAS
drives. Your airflow is not that airflow — watch a day of log before trusting them.
`KEEPALIVE_INTERVAL` is inherited from the 5s cadence the original setup used, not
measured. How fast the EMM actually reverts is unknown; if the fans audibly surge
between keepalives, lower it.
## Notes
- Only one process may talk to the serial port. The script takes an exclusive `flock` on
`/run/das_fanctl.lock` and exits rather than interleaving garbage with another writer.
Detach `attachDAS.sh`'s `screen` session before starting the service.
- **`DISK_GLOB` must name the shelf's HBA on any host with more than one.** The default
`*-sas-exp*-lun-0` means "behind a SAS expander", which is not the same as "in the
shelf": a server's own backplane is frequently behind an expander too. On `iz-pve0` the
internal drives sit on `pci-0000:02:00.0` and the shelf on `pci-0000:04:00.0`, so the
default glob matched all 17 drives and would have set the shelf's fans from the
server's internal drive temperatures. Set
`Environment=DISK_GLOB=/dev/disk/by-path/*04:00.0*-lun-0` in the unit.
- **The server's own fan controller needs the mirror-image setting.** Whichever host the
shelf is plugged into must set `DISK_EXCLUDE_PATTERN` to that same HBA in
`fan_speed.service`, or the shelf's drives drive the *server* fans as well. Moving the
shelf between hosts means moving both settings.
- `PORT` is per host too. Check which UART actually answers rather than assuming:
`grep -v uart:unknown /proc/tty/driver/serial` lists the real ones, then send
`_temp_rd` to each and watch for the `BlueDress` prompt.
## Files
| File | |
|---|---|
| `das_fanctl.sh` | The fan controller |
| `fan_speed_das.service` | systemd unit for it |
| `functions.sh` | Only `calculate_interpolated_fan_speed` is used here — the rest is iDRAC/IPMI |
| `attachDAS.sh` | Drop into an interactive `screen` session on the shelf console |
| `get_das_temp.sh` | Superseded by `das_fanctl.sh temps` — it never read the reply back |
| `log_rotate.txt` | logrotate snippet for the log directory |
## Credit
`functions.sh` comes from the
[Dell iDRAC fan controller](https://github.com/tigerblue77/Dell_iDRAC_fan_controller_Docker)
project; this repo only uses its `calculate_interpolated_fan_speed`. Prior art on the MD1200 console specifically:
[tonybaltovski/md1200-reduce-fans-systemd](https://github.com/tonybaltovski/md1200-reduce-fans-systemd)
and [iamjoshgilman/md1200-fan-controller](https://github.com/iamjoshgilman/md1200-fan-controller).