PowerEdge T440 において,メインファンの回転速度(風量)は iDRAC が様々な条件から算定して風量を決定します。
iDRAC ファームウェア 3.30.30.30まではファン動作条件の設定項目があったようですが,
それ以降のファームウェアでは削除されたようで,ユーザー側での設定は「増速」のみ可能となります。
さらに 7.00.00.00 以降にアップデートすると 4.40.40.00 以前に落とすことが出来なくなります。
(ブートローダが 4.40.10.00 から変更されていて, 7.00.00.00 を導入するとブートローダの書き換えすら拒否される仕様のようです)
ええ,手に入れたら最新にする癖が悪さをして,後戻りできなくなってしまいました。
・XEON GOLD 6138 2CPU
・RTX2070 SUPER
・HBA 9400-8i
・HDD 5基
特にRTX2070 Super と HBA 9400-8i はDELL純正カードではないことから,温度管理などが出来ずファン速度が超増速加算されます。
iDRACでモニタする限り,100%時に4680rpm。
低気密サーバー室から漏れてくる音量は50%が限界ですが,ファンは常に65%の3000回転で動作しています。
こんなに高速動作させなくとも余裕で冷却できているのですが・・・・・。
では,なぜか余っている,Arduino Nano でファンコントロールしてみます・・・か!
【動作条件】
・MBからのPWM信号(%)を読み取る。
・MB PWMから逆算した回転数をMBに返す。
・ファンに対して常に45%のPWM信号を与える。
・もし,MB PWMが75%を超えたら,ファンに与えるPWM信号はMB PWMと同じにする。
・Arduinoが故障するした場合(ファンにPWM信号を出さない)100%になるようにファン電源は直結。
*MBに返す回転数は少しでも回って入ればエラーにならないようです。
【結線図】
とりあえず,ブレッドボードに構築。
(そのうちユニバーサル基板で作成するか,基板オーダーしてやろう・・・だれか要ります?)
【arduino スケッチ】
シリアルモニタで各数値をモニタリングしつつ,規定の45%はシリアルモニタで%数値をメッセージ送信すると変更できます。
// ============================================================
// MB PWM Reader + Tach Generator (2 pulses per revolution)
// - Timer1 Input Capture (PWM read)
// - Timer2 CTC toggle (correct frequency)
// ============================================================
const int PIN_FAN_PWM_OUT = 5; // ICP1
const int PIN_MB_PWM_IN = 8; // ICP1
const int PIN_TACH_OUT = 3; // OC2B
volatile uint16_t rise_time = 0;
volatile uint16_t fall_time = 0;
volatile uint16_t last_rise = 0;
volatile uint16_t period_ticks = 0;
volatile bool edge_rising = true;
volatile bool cycle_ready = false;
volatile unsigned long last_edge_millis = 0;
volatile unsigned long ic_count = 0;
float base_pwm = 45.0; // 初期値 45%
float mb_pwm_duty = 0.0;
bool pwm_lost = false;
// -------------------- Input Capture ISR --------------------
ISR(TIMER1_CAPT_vect) {
ic_count++;
uint16_t now = ICR1;
last_edge_millis = millis();
if (edge_rising) {
rise_time = now;
if (last_rise != 0) {
if (now >= last_rise)
period_ticks = now - last_rise;
else
period_ticks = (uint16_t)(65536UL + now - last_rise);
}
last_rise = now;
TCCR1B &= ~(1 << ICES1); // 次は立下り
} else {
fall_time = now;
TCCR1B |= (1 << ICES1); // 次は立上り
cycle_ready = true;
}
edge_rising = !edge_rising;
}
//-----------------------------------------------------------
// -------------------- Duty calculation --------------------
void updateDuty() {
if (millis() - last_edge_millis > 5) {
mb_pwm_duty = 100.0;
pwm_lost = true;
return;
}
pwm_lost = false;
if (!cycle_ready) return;
uint16_t high_ticks;
if (fall_time >= rise_time)
high_ticks = fall_time - rise_time;
else
high_ticks = (uint16_t)(65536UL + fall_time - rise_time);
if (period_ticks > 0 && high_ticks <= period_ticks) {
float high_percent = (float)high_ticks / (float)period_ticks * 100.0;
mb_pwm_duty = 100.0 - high_percent; // Intel PWM は LOW が有効
}
cycle_ready = false;
}
//-----------------------------------------------------------
// -------------------- Timer2 CTC Tach ---------------------
void setupTachTimer() {
pinMode(PIN_TACH_OUT, OUTPUT);
// Timer2 を完全初期化
TCCR2A = 0;
TCCR2B = 0;
// CTC モード(WGM21=1, WGM22=0)
TCCR2A |= (1 << WGM21);
// OC2B toggle
TCCR2A |= (1 << COM2B0);
// Prescaler = 1024
TCCR2B = (1 << CS22) | (1 << CS21) | (1 << CS20);
}
//-----------------------------------------------------------
//-----------------------------------------------------------
// rpm → Timer2 OCR2A(2 pulses/rev)
void updateTach(float rpm) {
// rpm → Hz(2 pulses per rev)
float hz = (rpm / 60.0) * 2.0;
if (hz < 1.0) hz = 1.0;
int prescaler = 1024;
// CTC toggle の正しい式
int ocr = (F_CPU / (2 * (prescaler * hz))) - 1;
if (ocr < 0) ocr = 0;
if (ocr > 255) ocr = 255;
OCR2A = ocr;
}
//-----------------------------------------------------------
//-----------------------------------------------------------
void readSerialBasePWM() {
if (Serial.available() > 0) {
// 改行だけ来た場合は無視
char c = Serial.peek();
if (c == '\n' || c == '\r') {
Serial.read(); // 1文字捨てる
return;
}
int val = Serial.parseInt();
if (val >= 0 && val <= 100) {
base_pwm = val;
Serial.print("Base PWM updated to ");
Serial.print(base_pwm);
Serial.println("%");
}
}
}
//-----------------------------------------------------------
// -------------------- Setup -------------------------------
void setup() {
Serial.begin(115200);
pinMode(PIN_MB_PWM_IN, INPUT_PULLUP);
pinMode(PIN_FAN_PWM_OUT, OUTPUT); // 実ファン向け PWM 出力
TCCR1A = 0;
TCCR1B = (1 << ICES1) | (1 << CS10);
TIMSK1 = (1 << ICIE1);
last_edge_millis = millis();
setupTachTimer();
}
//-----------------------------------------------------------
// -------------------- Loop --------------------------------
void loop() {
//=========================================================
readSerialBasePWM();
//=========================================================
//=========================================================
// マザーボード出力のPWM DUTYを計測
updateDuty();
//=========================================================
//=========================================================
// MB_PWM DUTYから回転数を逆算してMBへ返す。
float rpm_for_mb = mb_pwm_duty * 46.8;
updateTach(rpm_for_mb);
//=========================================================
//=========================================================
// 実ファンの回転数制御
// PWM 計算
float fan_pwm;
if (mb_pwm_duty > 75.0) {
fan_pwm = mb_pwm_duty; // MB_PWM をそのまま使う
} else {
fan_pwm = base_pwm; // 初期値45% シリアルモニタの指示値で可変
}
// Intel PWM は LOW が有効なので反転
float intel_pwm = 100.0 - fan_pwm;
// Arduino の analogWrite は 0〜255
int pwm_out = (int)(intel_pwm * 255.0 / 100.0);
analogWrite(PIN_FAN_PWM_OUT, pwm_out);
//=========================================================
//=========================================================
static unsigned long last = 0;
if (millis() - last >= 2000) {
if (pwm_lost) {
Serial.println("IC_CNT=--- PERIOD=--- MB_PWM=100%");
} else {
Serial.print("IC_CNT=");
Serial.print(ic_count);
Serial.print(" PERIOD=");
Serial.print(period_ticks);
Serial.print(" MB_PWM=");
Serial.print(mb_pwm_duty);
Serial.println("%");
}
last = millis();
}
//=========================================================
}
よきかな~。
