# 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 ` 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 `, 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 32–39c — 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 32–39c 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. - The disk glob is `/dev/disk/by-path/*-sas-exp*-lun-0` — anything behind a SAS expander, i.e. the shelf. The host's own drives hang off `pci-…-scsi-*` and are not matched. A second enclosure would be lumped in with the first. ## 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).