大家好,这篇文章我们继续分享家里网络设备的万兆升级和静音改造经验,希望对有类似需求的朋友有所帮助。
写在前面
在上一篇《家用网络升级实践:低成本实现局部万兆(一)》中,我们留下了一些待解决的问题。现在让我们一起来完成这些未完成的部分。
回顾一下上篇文章提到的内容:
不过这台设备本身运行的时候,就会有一些风扇声音,启动的时候比较大声,运行的时候相对安静,但是还有一些声音。作为计划不再按需启动使用,而是和其他设备一样长时间开机使用的设备,我计划对它进行一些改造,在改造结束后,再进行测试,这样对我而言更有意义。
关于这台设备的详细介绍,我在之前写过一篇文章《省心和颜值兼顾的 HomeLab 设备:HPE MicroServer Gen10 Plus v2(一)》,感兴趣的朋友可以去看看,这里就不重复介绍了。
其实把服务器搬回家里使用,既能提供安静可靠的实验环境,又能实现高性价比,这并不是我一个人的想法。网上有很多志同道合的玩家,都在分享各种实用的解决方案。
先回顾一下,2021年我写过一篇《廉价的家用工作站方案:前篇》,主要介绍了基于笔记本的方案。文章里我详细对比了实验开发环境中,在相同配置 (核心、内存、磁盘)下服务器的使用成本。这些“家用服务器”到现在还在我家里稳定运行,发挥着它们的价值。算下来,从 2015 年开始,这个方案已经使用了 10 年之久。
不过除了这种性价比方案外,传统服务器作为专业计算设备还是有其独特优势的:可以插入大量ECC内存、提供更稳定的虚拟化支持。特别是在需要持续运行服务或处理大规模数据时,这些优势就特别明显。(相信经常处理海量数据的朋友们都深有体会)
这篇文章能够成文,要特别感谢 zhaoyingpu 以及众多先行者的实践验证和探索。在接下来的内容中,我会详细介绍这篇文章凝聚了多少人的心血。
让我们开始吧。
定义问题
在明确改造方案之前,我们先来理清楚目前遇到的问题。
设备使用环境与噪音困扰
这台设备作为一台家用服务器使用,最让人头疼的就是噪音问题。即便我把服务器放在机柜里,确实能降低不少运行噪音,但那个服务器使用的工业风扇的“呼呼”声依然很明显。
如果你也用过 HPE 的 Gen8、Gen10 这类服务器,肯定知道它们对硬件特别“挑剔”。一旦安装了不在官方认证列表里的 PCIe 扩展卡,风扇就会立刻开启“狂暴模式”。问题是,我们实际需要用的扩展卡,不可能都在它的认证列表里。
另外,服务器自带的 iLO 芯片也是个“麻烦制造者”。用着用着它就会发热,然后又把风扇调到最大转速,制造吵闹的噪音。
考虑到这是要长期使用的设备,我不想和之前一样只能在需要的时候才开机。特别是完成万兆网络改造后,如果噪音问题得不到解决,那就太影响日常使用了。
值得一提的是,硬盘噪音倒不是问题。因为我用的都是SSD,既安静又不会产生太多热量。
噪音从何而来?
通过前面的分析,我们可以看到设备噪音主要是来自风扇。而风扇为什么会发出噪音,主要有两个原因:一是设备预设的温控策略不够优化,二是当某些部件温度升高时,采用了简单粗暴的降温方案。
这其实和产品的目标市场有关。在海外市场,很多用户会把设备放在办公室、机房,甚至是车库、阳台使用。这些场景下,用户对噪音的容忍度都比较高。从厂商角度看,优化降噪方案需要投入额外成本,但这些投入并不能提升计算、存储、网络性能。对主力用户群来说,他们更在意性能而不是噪音,所以厂商也就缺乏改进的动力。
另外,让设备在更低温的环境下运行,也能降低厂商的维护和售后成本。因为芯片和其他零部件出现故障,往往是因为过热、过冷或者环境太潮湿导致的。所以对厂商来说,让风扇保持高速运转是个省事又省钱的选择,反正用的是用户的电。
应对问题的挑战的策略
想解决设备噪音问题,最直接的方法无非是两个思路:一是更换静音风扇,二是想办法降低设备温度,避免风扇高速运转。听起来简单,但实际操作中还是有不少挑战需要克服。
让我们来看看具体有哪些难点:
首先,HPE服务器用的风扇接口与市面上常见的消费级静音风扇不兼容。这就限制了我们能用的风扇选择范围。
其次,就算找到合适的风扇可以更换,服务器默认的风扇控制策略是全速运转。即便装上了静音风扇,在这种策略下依然可能达不到理想的降噪效果,毕竟这是物理规律决定的。
再者,降噪改造必须在保证设备运行安全的前提下进行。我们需要确保更换风扇后,各个零部件不会因为散热不足而过热损坏。
最后还要考虑成本问题。这不仅包括初次改造的投入,还要考虑到后续的备件更换,以及其他设备改造的可能性。我们需要一个长期可持续的解决方案,而不是一次性的权宜之计。
既然已经理清了这些挑战,接下来就让我们一个个攻克它们。先从最基础的风扇选择开始说起吧。
解决方案
从问题到解决方案的转变,让我想起之前在知乎社区学到的一个观点:只要有问题,就一定能找到答案。
选择服务器风扇的替代
先说说我的改造经历。在 2021 年时,我在《近期家用设备(NUC、猫盘、路由器)散热升级记录》这篇文章中分享过给 NUC 8 迷你主机做散热改造的经验。
当时遇到的问题和现在很类似,这类小巧机箱的设计往往没有充分考虑散热需求,或者说它们的设计初衷可能就是低负载运行,某种程度上确实浪费了设备性能。我当时选择了经典的黑色“猫扇”来进行替换,效果相当不错:设备在高负载时只有轻微噪音,平时更是安静得几乎听不到声音。
这次的改造,我依然选择了猫扇,具体型号是经典配色的 NF-A8 。
选这款的原因也很简单:我这台服务器平时就是用来跑些虚拟化环境和一些突发性的轻量应用,再加上处理一些万兆网络中转任务。在这种低负载场景下,低转速的猫扇优势特别明显(当然,如果是高转速场景就另当别论了)。
不过在动手改造前,我们还是得先了解下新旧风扇的性能差异。
原装的风扇是台达电子生产的 PFB0812GHE(80x80x38mm 12V DC 10.2W,产品规格 datasheet),最高能转到 6100 转,噪音 55.5 分贝,气流量 86.4 CFM(约146.8 m³/h),风压 22 mmAq。
而我们选的NF-A8,虽然尺寸完全一致(免去了改装螺丝位的麻烦),但满载时只有 17.7 分贝,甚至能降到 13.8 分贝(1750转)。满载转速只有 2200 转,最低能到 450 转,气流量是 55.5 m³/h,风压 2.37 mm H₂O。
虽然不能简单地用线性计算来比较两个风扇的性能,但从这些数据也能看出一些门道。新风扇的最大转速只有原装的“六二折”左右,气流量是“四七折”,风压更是只有原来的十分之一。所以要提醒大家的是**:既然最大散热能力减弱了,用新风扇时就得特别注意控制整机的发热情况,不然真的会“压不住”。**
合理规划降低热量产生
前文提到,我们选择了性能较弱但更安静的风扇,虽然让耳朵舒服了,但机器的散热能力相应下降。因此,我们需要合理规划设备的使用方式。
在我的使用方案中,这台设备仅运行 ESXi 虚拟化服务,主要负责 IO、存储和网络类服务,尽可能减少 CPU 这个主要发热源的热量产生。
查阅惠普官方介绍,HPE ProLiant MicroServer Gen10 Plus v2 被定位为一款经济实惠、小巧的入门级服务器。
官方提供的 CPU 选项包括 Intel Xeon E-2314(2.80GHz)和 Intel Pentium Gold G6405(4.10GHz),TDP 分别为 65W 和 58W,核心配置为 4c4t 和 2c4t。我选择的 CC 150 CPU 实际功耗约 60W 出头,但拥有更多核心(8c16t)。由于其功耗与官方高配 E-2314 相近(甚至略低),理论上产生的热量应该不会更多,无需额外增加散热能力。
存储方面,我仅使用固态硬盘,并优先选择可搭配金属支架的 2.5 寸 SATA SSD。相比机械硬盘,SSD 本身发热就更少;而 SATA 接口相比 NVMe,由于速度较慢且散热面积更大,发热量也更低。配合散热支架使用后,风扇主要只需要负责 CPU、主板和网卡的散热工作。
实际效果如何?这是运行一天后的数据:
改造后的温度数据显示,规划内的设备温度都保持在较低水平。与其他用户相比,AHCI HD Max 温度低了约 10 度,BMC(iLO 位置)温度也降低了接近 10 度。如果想进一步降温,可以参考 HPE 海外社区用户的改装方案(有损改装)。
此前提到,iLO 控制芯片过热会导致风扇疯转以加强换气,产生噪音。如果再插入 PCIe 扩展卡,更会影响风道,让风扇徒劳运转,徒增声噪。
为解决这个问题,我们需要两个改进。首先是增加 iLO 的主动散热能力,比如加装散热器。参考了 Chiphell 社区用户 zhengwenxi1989 在 2022 年分享的《Gen10 Plus iLO 芯片低成本完美散热》方案。我暂时选择让气流带走散热器上的热量,而不是导向大散热器,后续可能会调整。
虽然原本打算采用同款方案,但考虑到物流时间,加上手头有一块理论散热性能更好的利民 SSD 散热器,就先用起来了。我用 3M 普通胶带替换了原有双面胶,后续可能还会更换,先观察一段时间效果。
除了通过更好的散热器增加被动散热面积外,我也借鉴原方案增加了主动散热。
这款风扇和原始方案中的略有不同,使用方式也有变化,下文详细展开。
接管设备风扇调度策略
接下来要解决的是一个比较有挑战的问题,如何接管惠普服务器的风扇控制。
这个问题的复杂之处在于,惠普服务器使用的是特殊的风扇控制方案,与普通消费级风扇有很大区别。如果我们想用噪音更小的风扇替换原装的,不仅要考虑接口兼容性的问题,还需要调整控制策略,避免低转速风扇一直以高速运转。
这个问题其实早有开源社区的先行者们在探索解决方案。2020年,GitHub 用户Max-Sum 分享了一个项目 Max-Sum/HP-fan-proxy。这个方案是在 Reddit 用户executivul 在2017年提出的方案(Arduino nano 实现代码)基础上优化而来,通过简化硬件设计,采用软件方案来解决问题。
这些前期工作的意义重大,不仅提供了代码参考,还给出了如何制作兼容惠普风扇接口(2x3 PHD2.0 connector)的细节,以及明确的接口定义说明。
在此特别感谢七年前那些先行者们。不管是进行静音改造的实践者,还是参与可行性讨论并提供建议的社区成员,比如这些讨论帖 7vxo5n/hp_dl380e_g8_arduino_fan_control_project/、72k3jf/faking_the_fan_signal_on_a_dl380_g7/,都为这个方案的最终成型贡献了力量。
九个月前,一个专门针对 Gen10 Plus 和猫扇的新方案出现了:zhaoyingpu/hpe-gen10-fan-proxy。这个项目参考了 2019 年的一篇技术笔记《Solving the DL180 G6 Fan Controller Problem》,并在 Max-Sum 方案的基础上做了改进。感谢这位 70 后 DIY 玩家的分享,让我在配件选购方面省去了很多摸索的时间,可以更专注于软件部分的优化。
由于我在硬件方面还算是个新手,我 fork 了 zhaoyingpu 的项目 soulteary/hpe-gen10-fan-proxy,增补了相关资料。后续的实验性改动我也会记录在这个仓库中(目前已经把之前的改动还原了,等稳定后再更新到仓库)。
核心代码逻辑如下:
#include <FastLED.h>
/* 定义引脚相关的宏 */
#define pinOfPin(P) (((P) >= 0 && (P) < 8) ? &PIND : (((P) > 7 && (P) < 14) ? &PINB : &PINC))
#define pinIndex(P) ((uint8_t)(P > 13 ? P - 14 : P & 7))
#define pinMask(P) ((uint8_t)(1 << pinIndex(P)))
#define isHigh(P) ((*(pinOfPin(P)) & pinMask(P)) > 0) /* 判断引脚是否为高电平 */
#define isLow(P) ((*(pinOfPin(P)) & pinMask(P)) == 0) /* 判断引脚是否为低电平 */
/* 引脚和常量定义 */
const int pwmInPin = A1; /* 输入PWM信号的引脚 */
const int pwmOutPin = 9; /* 输出PWM信号的引脚 */
const int pwm2OutPin = 10; /* 第二个输出PWM信号的引脚 */
const int rpmInPin = 4; /* RPM输入引脚 */
const int hpeTachPin = A2; /* HPE转速信号输出引脚 */
const int normalTachPin = 3; /* 正常转速信号输出引脚 */
const uint16_t pwmTop = 320; /* PWM的顶值 */
const int sample = 25000; /* 读取PWM的样本数量 */
const float pwmMap[4] = {0.1, 0.2, 0.5, 1.0}; /* PWM映射值 */
/* const float pwmMap[4] = {0, 1.0, 0, 1.0}; */
const int rpmUpper = 6000; /* 最大RPM */
const int rpmLower = 60; /* 最小RPM */
const float timer2Freq = 16000000 / 1024 / 2; /* Timer2频率 */
const int timer2RpmLower = 900; /* Timer2最低RPM */
const float pwm2OutDutty = 0.45; /* 第二个PWM输出的占空比 */
volatile uint32_t pulseCount = 0; /* 脉冲计数,使用volatile确保在中断中安全更新 */
/* 脉冲计数函数 */
void counter() { pulseCount++; }
/* 中断服务例程,处理RPM输入引脚的中断 */
ISR(PCINT2_vect) {
if (digitalRead(rpmInPin) == HIGH) /* 检查引脚状态 */
pulseCount++; /* 增加脉冲计数 */
}
void setup() {
/* 配置Timer 1以产生25 kHz的PWM信号 */
TCCR1A = 0; /* 重置配置 */
TCCR1B = 0; /* 重置配置 */
TCNT1 = 0; /* 重置计数器 */
TCCR1A = _BV(COM1A1) /* 非反向PWM信号 */ | _BV(COM1B1) /* 同上 */ | _BV(WGM11); /* 模式10:相位校正PWM,TOP = ICR1 */
TCCR1B = _BV(WGM13) /* 同上 */ | _BV(CS10); /* 预分频器 = 1 */
ICR1 = pwmTop; /* 设置TOP值为320 */
/* 配置Timer 2以产生7.8125 kHz的PWM信号,用于转速模拟 */
TCCR2A = 0;
TCCR2B = 0;
TCNT2 = 0;
TCCR2A = _BV(COM2B1) /* 非反向PWM信号 */ | _BV(WGM20); /* 模式5:相位校正PWM,TOP = OCR2A */
TCCR2B = _BV(WGM22) /* 同上 */ | _BV(CS20) | _BV(CS21) | _BV(CS22); /* 预分频器 = 1024 */
OCR2A = 255; /* 设置OCR2A为255 */
/* 设置引脚模式 */
pinMode(pwmInPin, INPUT); /* 输入PWM引脚 */
/* Tachometer output signal need pullup to 5v, ref https://noctua.at/pub/media/wysiwyg/Noctua_PWM_specifications_white_paper.pdf */
pinMode(rpmInPin, INPUT_PULLUP); /* RPM输入引脚,启用上拉电阻 */
pinMode(pwmOutPin, OUTPUT); /* 输出PWM引脚 */
pinMode(pwm2OutPin, OUTPUT); /* 第二个输出PWM引脚 */
pinMode(hpeTachPin, OUTPUT); /* HPE转速信号输出引脚 */
pinMode(normalTachPin, OUTPUT); /* 正常转速信号输出引脚 */
/* 设置初始状态 */
digitalWrite(hpeTachPin, LOW); /* HPE转速信号初始化为LOW */
analogWrite25k(pwm2OutPin, pwm2OutDutty * pwmTop); /* 输出第二个PWM信号 */
/*
* 启用中断
* attachInterrupt(digitalPinToInterrupt(rpmInPin), counter, RISING);
* Enable PCIE2 Bit3 = 1 (Port D)
*/
PCICR |= B00000100; /* 启用PCIE2中断 */
/* Select PCINT20 Bit4 = 1 (Pin D4) */
PCMSK2 |= _BV(rpmInPin); /* 选择要监测的引脚 */
/* 禁用Nano Mini板的RGB灯 */
CRGB leds[3] = {
{0, 0, 0},
{0, 0, 0},
{0, 0, 0},
};
FastLED.addLeds<WS2812, 2, GRB>(leds, 3); /* 添加LED */
FastLED.show(); /* 更新LED状态 */
Serial.begin(115200); /* 初始化串口通信 */
Serial.println("HP fan proxy"); /* 输出初始化信息 */
}
/* 读取HPE PWM信号 */
float readHpePWM(int pin, int sample) {
uint32_t total = 0; /* 总样本计数 */
uint32_t low = 0; /* 低电平计数 */
for (uint16_t i = 0; i < sample; i++) {
low += isLow(pin); /* 统计低电平持续时间 */
total++; /* 增加总样本计数 */
}
/* 返回反转的PWM值 */
return ((float)low / total);
}
/* 读取Intel PWM信号 */
float readIntelPWM(int pin, int sample) {
uint32_t total = 0; /* 总样本计数 */
uint32_t low = 0; /* 高电平计数 */
for (uint16_t i = 0; i < sample; i++) {
low += isHigh(pin); /* 统计高电平持续时间 */
total++; /* 增加总样本计数 */
}
/* 返回正常的PWM值 */
return ((float)low / total);
}
/* 在指定引脚输出PWM信号 */
void analogWrite25k(int pin, int value) {
switch (pin) {
case 9:
OCR1A = value; /* 设置引脚9的PWM值 */
break;
case 10:
OCR1B = value; /* 设置引脚10的PWM值 */
break;
default:
/* 不支持的引脚 */
break;
}
}
uint32_t pulseFanPrev = 0;
uint32_t clockPrev = 0;
/* 将输入值映射到指定范围 */
float map_to_float(float x, float in_min, float in_max, float out_min, float out_max) {
return ((x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min);
;
}
void loop() {
/* 处理第一次循环 */
if (pulseFanPrev == 0 && clockPrev == 0) {
pulseFanPrev = pulseCount; /* 记录脉冲计数 */
clockPrev = millis(); /* 记录时间 */
delay(1000); /* 等待1秒 */
return; /* 返回以开始下一次循环 */
}
/* 计算RPM */
uint32_t clockLoopBegin = millis(); /* 记录循环开始时间 */
uint32_t pulse = pulseCount - pulseFanPrev; /* 计算脉冲数 */
uint32_t clock = clockLoopBegin - clockPrev; /* 计算时间间隔 */
pulseFanPrev = pulseCount; /* 更新上一次脉冲计数 */
clockPrev = millis(); /* 更新上一次时间 */
/* 计算RPM(每分钟转速) */
uint32_t rpm = pulse * 1000 * 60 / 2 / clock; /* 根据脉冲和时间计算转速 */
digitalWrite(hpeTachPin, rpm > 0 ? LOW : HIGH); /* 根据转速状态输出信号 */
/* 读取反转PWM信号并输出映射值 */
float pwmOut;
float pwmIn = readHpePWM(pwmInPin, sample); /* 读取PWM输入信号 */
if (pwmIn >= pwmMap[1]) /* 根据输入PWM值选择输出 */
pwmOut = pwmMap[3];
else if (pwmIn <= pwmMap[0])
pwmOut = pwmMap[2];
else
pwmOut = map_to_float(pwmIn, pwmMap[0], pwmMap[1], pwmMap[2], pwmMap[3]); /* 映射输入PWM */
uint16_t out = uint16_t(pwmOut * pwmTop); /* 将输出PWM值转换为整型 */
analogWrite25k(pwmOutPin, out); /* 输出PWM信号 */
/* 打印调试信息 */
Serial.print("iLO: ");
Serial.print(pwmIn * 100);
Serial.print("% Out: ");
Serial.print(pwmOut * 100);
Serial.print("% RPM: ");
Serial.print(rpm);
Serial.print(" : ");
Serial.print(pulse);
Serial.print(" / ");
Serial.println(clock);
/* 固定1Hz的控制,并输出正常的转速信号 */
clock = millis() - clockLoopBegin; /* 计算循环持续时间 */
int rpmIn = pwmIn * rpmUpper; /* 将输入PWM转换为RPM值 */
if (rpmIn > timer2RpmLower) {
int freq = timer2Freq / (rpmIn * 2 / 60); /* 根据RPM计算频率 */
OCR2A = freq; /* 更新OCR2A值 */
analogWrite(normalTachPin, freq / 2); /* 输出正常转速信号 */
delay(1000 - clock); /* 等待剩余时间 */
} else if (rpmIn > 0) {
int count = max(rpmIn, rpmLower) * 2 / 60; /* 计算输出脉冲的数量 */
float half = 1000.0 / count / 2; /* 计算每个脉冲的持续时间 */
for (int i = 0; i < count * 2 - 1; ++i) {
digitalWrite(normalTachPin, (i & 1) ? LOW : HIGH); /* 交替输出高低电平 */
uint32_t now = millis() - clockLoopBegin; /* 记录当前时间 */
/* Serial.println(now); */
uint32_t next = clock + half * (i + 1); /* 计算下一个脉冲的时间 */
delay(next - now); /* 等待下一个脉冲 */
}
digitalWrite(normalTachPin, LOW); /* 结束脉冲输出 */
clock = millis() - clockLoopBegin; /* 更新循环时间 */
if (clock < 1000) {
delay(1000 - clock); /* 等待直到1秒结束 */
}
} else {
delay(1000 - clock); /* 如果RPM为0,等待剩余时间 */
}
}
代码具体的使用方法,我们先按下不表,等讨论完所有相关问题的解决方案后再详细展开。
确保备件来源可持续
在这个改造方案中,我们需要准备以下几类配件:
- HPE Gen10 系列替换用的大号风扇
- 适配 HPE 主板的风扇接口配件
- Arduino Nano Mini 开发板
- iLO 散热器
- iLO 区域芯片专用小风扇
- 各类连接线材(包括数据线、电源线)、接线端子、绝缘胶带等
在 zhaoyingpu/hpe-gen10-fan-proxy 项目主页上,作者提供了各个关键零件的网店地址。经过验证,这些配件都有稳定的供应渠道和替换方案,不用担心断货问题(不必担心损耗)。
上图展示了我这次改造所使用的具体配件型号。如果你能找到更经济的替代品,完全可以自行更换。考虑到制作过程中可能出现的失误和后期使用中的耗损,我准备了一些备用配件,总成本为 250.07 元。如果你有把握一次成功,实际成本可以控制在 175.9 元左右。
由于我没有专业的压线工具和配件座子的簧片端子,所以需要寻求硬件 DIY 达人的帮助。在北京同城找到了一家店铺,店主非常耐心地为我讲解制作流程,并帮我压制了六套线材(具体制作过程会在后文详述)。
这部分花费了 179 元。如果你对自己 DIY 整合有信心,只需要制作一套线材的话,并且不着急使用设备的话,成本可以降到 99 元内(同城高时效运费占据成本大头)。
值得一提的是,在完成这次改造后,我发现了一个更优的方案,可以将这部分成本降至 20 元,而且硬件可靠性更高。
在解决了上述四个主要技术难题后,我们就可以开始动手改造了。
动手实战
理论部分已经讲完,现在让我们开始动手实践环节。
软件部分:Arduino 控制程序的刷写
参考的原始项目和开发板的网店店铺对于开发板的软件使用说明都比较有限。不过经过一番研究,我找到了最简单的使用方法。(如果你想深入了解,我已经把相关资料整理在了 GitHub 仓库中:soulteary/hpe-gen10-fan-proxy/vendor/docs、soulteary/hpe-gen10-fan-proxy/vendor/misc)
首先,我们需要搭建Arduino的开发环境。请从 Arduino 官网下载 IDE(我使用的是2.3.4版本)。
为了让教程更容易理解,建议先把 IDE 的界面语言调整为中文。
连接 Arduino 开发板到电脑后,我们就可以测试是否能正常进行程序烧录了。如果你的系统无法识别开发板串口,可以在这里下载对应驱动:soulteary/hpe-gen10-fan-proxy/vendor/drivers。
对于 macOS 较新系统版本的用户,你需要在系统安全设置中允许“CH34x VCP Driver”,然后重新插入开发板,这样系统就能识别设备串口了。
无论是测试程序还是后续的风扇控制程序,我们都会用到LED功能,所以需要在IDE中安装 “FastLED” 和 “Atmega328 IO” 这两个插件。
接下来,为了验证开发板能够正常工作,我编写了一个有趣的程序,可以让开发板的LED灯随机闪烁并产生呼吸灯效果。如果你也想尝试,这段代码已经开源在 soulteary/hpe-gen10-fan-proxy/arduino-test.ino。
#include <FastLED.h>
#define uchar unsigned char
#define RGB_PIN 2 // RGB灯使用IO2进行控制
#define NUM_LEDS 3 // RGB灯的数量
CRGB leds[NUM_LEDS]; // CRGB是结构体类型
// 呼吸效果参数
const int BREATH_STEPS = 50; // 呼吸渐变步数
const int BREATH_DELAY = 20; // 每步延时(ms)
float breathBrightness = 0; // 当前亮度
float breathDelta = 1.0; // 亮度变化方向
// 定义颜色结构体
struct Color {
uchar r;
uchar g;
uchar b;
};
void RGB_Init(void) {
FastLED.addLeds<WS2812, RGB_PIN, GRB>(leds, NUM_LEDS);
}
// 带亮度控制的RGB灯控制函数
void RGB_Control(uchar cresset, uchar red, uchar green, uchar blue, float brightness) {
leds[cresset] = CRGB(
red * brightness,
green * brightness,
blue * brightness
);
FastLED.show();
}
// 生成随机颜色
Color getRandomColor() {
Color c;
// 随机选择一个主色调
switch(random(6)) {
case 0:
c = {255, 0, 0}; // 红
break;
case 1:
c = {0, 255, 0}; // 绿
break;
case 2:
c = {0, 0, 255}; // 蓝
break;
case 3:
c = {255, 255, 0}; // 黄
break;
case 4:
c = {255, 0, 255}; // 紫
break;
case 5:
c = {0, 255, 255}; // 青
break;
}
return c;
}
void setup() {
Serial.begin(115200);
RGB_Init();
randomSeed(analogRead(0)); // 初始化随机数生成器
// 设置输出引脚
for(int i = 3; i <= 13; i++) {
pinMode(i, OUTPUT);
digitalWrite(i, LOW);
}
}
void loop() {
// 为每个LED生成随机颜色
Color colors[NUM_LEDS];
for(int i = 0; i < NUM_LEDS; i++) {
colors[i] = getRandomColor();
}
// 呼吸效果循环
for(int step = 0; step < BREATH_STEPS; step++) {
// 更新呼吸亮度
breathBrightness += breathDelta * (1.0 / BREATH_STEPS);
// 在到达最大或最小亮度时改变方向
if(breathBrightness >= 1.0) {
breathBrightness = 1.0;
breathDelta = -1.0;
} else if(breathBrightness <= 0.0) {
breathBrightness = 0.0;
breathDelta = 1.0;
// 在完成一次呼吸周期后重新生成随机颜色
for(int i = 0; i < NUM_LEDS; i++) {
colors[i] = getRandomColor();
}
}
// 更新所有LED
for(int i = 0; i < NUM_LEDS; i++) {
RGB_Control(i,
colors[i].r,
colors[i].g,
colors[i].b,
breathBrightness
);
}
delay(BREATH_DELAY);
}
// 随机延时,增加闪烁的不规则性
delay(random(100, 500));
}
让我们开始上传程序:首先在IDE中选择正确的主板型号,然后将程序代码复制粘贴进去。
找到界面左上角的上传按钮(看起来像个“前进”箭头),点击它就会开始编译和上传程序到开发板。
等待几秒钟后,你就能看到开发板上的LED开始闪烁变幻,宛如 “闪亮的灯球”。
当我们确认开发板能正常工作、编译和运行测试程序后,就可以用相同的方式上传我们真正需要的风扇调度程序了。将上文“接管设备风扇调度策略”中的代码复制粘贴到 IDE 中,进行相同的编译、上传动作后,这样我们就完成了第一版惠普服务器风扇控制程序的部署。
硬件部分:最终部件制作
我们先来制作主板侧的风扇连接线。根据前文中开发者 Max-Sum 提供的风扇接脚定义(用于控制风扇调度)和开发板官方提供的接口定义,将定制好的接线插入 2x3 PHD2.0 连接器中:
开发板和连接器的对应关系如下:
- 1 号位:红色线,连接开发板 VIN
- 2 号位:预留空位
- 3 号位:蓝色线,连接开发板 A1
- 4 号位:黄色线,连接开发板 A2
- 5、6 号位:接地线,连接开发板 GND(其中 5 号位需要制作特殊的 “Loop” 线与 6 号位连通)
完成后和原厂版本对比,除了特殊的 “Loop” 线外,基本一致。
那么开发板如何与风扇连接呢?还记得我们购买的猫扇包装里就配备了需要的材料。
之前提到的猫扇配件中包含延长线、减速线和一转二线。由于我们需要连接两个风扇,可以直接使用一转二线。具体步骤是:
- 将一转二线风扇接口对侧的电源接口剪掉,剥出金属线待焊接
- 把延长线剪下来,取其外层热缩管,剪成合适长度套在新制作的线材上,起到保护作用
接线方案相对简单,不过因为用了成品线,颜色会和之前有些不同:
- 3PIN 线:蓝色接开发板 D1,黄色接 VIN
- 4PIN 线:蓝色接开发板 D2,黄色接 VIN,绿色接 D4
- 两根线的黑色都接开发板 GND
最终完成后的开发板造型别致,像是游戏里一个有着三条长腿的生物。
接下来的步骤是拆除原装风扇,安装上猫扇,并用我们事先焊接好的开发板将 HP 主板和两个风扇连接起来。
我选用的 iLO 降温风扇采用上透风下密封设计,底部是带线塑料结构,顶部是金属。为避免短路,我用绝缘胶带重新包裹了风扇,并用 3M 双面胶垫高固定。未来可能会进一步优化,提升底部芯片的散热效果,不过还是先使用一段时间再说。
与 Chiphell 原帖子中的方案不同,我没有选择朝向 CPU 散热器的风向设计。这是因为在 Gen10 Plus v2 机箱中,PCIe 扩展插槽的垂直挡板会阻挡气流。特别是在安装了双万兆网卡之后,合上机箱后能够保持的有效风道其实就只剩下从机箱前部到后部的这条直通路径了。
虽然散热器自带的硅胶垫具有一定粘性,但为了稳妥起见,我还是额外使用了 3M 胶带来固定。在进风口位置,我安装了一个主动散热风扇,将冷空气直接吹向大面积的散热器表面。散热器的末端正对着机箱背部,配合机箱风扇的气流方向,理论上能够有效地将热量导出机箱。不过,这个散热方案的实际效果,还需要通过更长期的使用来验证和优化。
到这里,我们的硬件静音改造就告一段落了,也解决了开篇提到的日常安静使用需求。
接下来,让我们回到万兆网络实践这个话题。
万兆网络实践
在上一篇《家用网络升级实践:低成本实现局部万兆(一)》的“Gen 10 Plus 启用万兆网络”部分,我们将万兆网卡安装到了 Gen10 Plus,系统直接识别了这张网卡(免驱动)。
在 ESXi 系统中,我们可以看到系统正确识别了网卡的速率。
在 ESXi 系统中,我们还可以设置硬件直通,分配给某个操作系统使用。
如果仅仅是给设备增加两个网口来实现万兆组网,未免太过“儿戏”了。毕竟还要考虑后续增加其他设备的问题,而且不是所有设备都支持光口的万兆连接。
还记得上一篇文章中提到的那款支持双万兆网口、4个2.5G网口的交换机吗?
上次购买之后,正好我又获得了一些购物补贴,就再入手了一台这个待机功耗仅 1 瓦的设备吧。
将新交换机放在设备旁边,看起来还真有点“父与子”的感觉。
我最终选择的网络拓扑方案是:从主交换机引出一根10G线连接到 Gen10 Plus,Gen10 Plus 的另一个 10G 光口连接到交换机,这台交换机的另一个光口则连接下一台交换机或其他光口设备。这样不仅满足了 10G 连接需求,还能顺便完成多个设备的 2.5G 网络接入,即便所有设备同时和 Gen10 Plus 进行数据交互,也不会出现数据阻塞。
在保证安静运行的前提下,这是目前发热最少、成本最低的方案。交换机无需风扇,也不会产生多余的热量(相比之下,多口交换机还是会有一定发热和噪音)。
当然,这个方案也存在一些不足。多个交换机就需要多个电源接口,而且因为是经济型交换机,电源适配器没有特别注重体积和美感设计,三个适配器就占用了整个插板的空间,造成了一定的插座资源(插口)浪费。
最后
好了,写到这里,我们的第二部分的改造就完成了。
我还有一些东西在陆续的物流过程中,关于文章中提到的改进,我想在后续的文章中继续补充和展开。包括并不仅限于更低成本、更简单的 DIY 配件验证,对于这台设备和网络中其他设备的可靠性保障、风扇代理开源程序的持续优化等等。
我们下篇文章再见!
–EOF
我们有一个小小的折腾群,里面聚集了一些喜欢折腾、彼此坦诚相待的小伙伴。
我们在里面会一起聊聊软硬件、HomeLab、编程上、生活里以及职场中的一些问题,偶尔也在群里不定期的分享一些技术资料。
关于交友的标准,请参考下面的文章:
致新朋友:为生活投票,不断寻找更好的朋友
当然,通过下面这篇文章添加好友时,请备注实名和公司或学校、注明来源和目的,珍惜彼此的时间 😄
关于折腾群入群的那些事
如果你觉得内容还算实用,欢迎点赞分享给你的朋友,在此谢过。
如果你想更快的看到后续内容的更新,请戳 “点赞”、“分享”、“收藏” ,这些免费的鼓励将会影响后续有关内容的更新速度。
本文使用「署名 4.0 国际 (CC BY 4.0)」许可协议,欢迎转载、或重新修改使用,但需要注明来源。 署名 4.0 国际 (CC BY 4.0)
本文作者: 苏洋
创建时间: 2025年01月05日
统计字数: 19422字
阅读时间: 39分钟阅读
本文链接: https://soulteary.com/2025/01/05/homelab-10-gigabit-network-practice-silent-transformation-of-compact-home-servers.html