GARAGE_komatech

GARAGE_komatech

Komatechの作業日誌

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();

  }

  //=========================================================

}

 

 

 

 

よきかな~。