海康威视R1打造完美的Ubuntu Server——氛围灯开关
前言
海康威视R1这台机器颜值、性价比都很高,功耗低、四盘位、N100、双2.5G网卡、板载EMMC、有两个NVME固态硬盘插槽,基本满足了我对NAS的需求。但机器自带的NAS系统不能当作普通Linux系统使用,所以机器到手第一时间就安装了Ubuntu Server 24.04系统(为了不破坏板载闪存上的原版系统,Ubuntu系统安装在NVME固态硬盘上,后续会庆幸这个英明的决定,哈哈)。 但海康威视R1这台机器当作一台7*24小时运行的Ubuntu服务器,存在几大硬伤:
一、机箱氛围灯无法关闭,虽然看着很酷炫
二、风扇无法控制转速,重负载时CPU温度会飙升到90度,平时无负载时风扇不减速或停止
三、前面板小屏幕无法关闭
经过一番折腾,以上问题全部解决,R1变身完美的Ubuntu Server!理论上这些经验也能用于飞牛Nas系统,但我没去尝试。
第一个问题,机箱氛围灯
原厂系统里可以手动关闭主板氛围灯,换成 Ubuntu 之后,这个开关功能直接消失了。
这个问题如果只是“看着不顺眼”,其实还不算大事;但 R1 很多人会放卧室、书房或者电视柜旁边,那条灯晚上一直亮着,是真的影响休息。我自己折腾这个问题的时候,前前后后试了 GPIO、ACPI、显示驱动、I2C,走了不少弯路,最后才从原厂 eMMC 镜像里把真正的控制方式抠出来。
这篇文章想做的不是简单甩一个命令,而是把整个过程完整记下来。因为很多时候,最终答案只需要一行命令,但真正值钱的是“为什么之前那些方法都不对”,以及“为什么最后这个方法可以确定就是对的”。
最终结果先放在前面:
- 在
Ubuntu Server 24.04下成功关闭海康威视 R1 的氛围灯 - 方案已经做成可持久化配置
- 重启系统后依然生效
如果你只想看结论,可以直接拉到本文后半部分的“最终修复方案”。
机器与问题现象
先说一下典型对照环境(文中 DMI 产品名为发布用占位示例,勿当成你机上的真实串):
- 机器:海康威视 R1 类四盘位 NAS,DMI
product_name示例写作HS-EXMP-R1(实机请用dmidecode -s system-product-name自行核对) - CPU:Intel N100
- 原厂系统:安装在板载 eMMC
- Ubuntu Server 24.04:安装在 NVMe
我最早注意到的现象,其实已经把问题范围缩得很小了:
- 在原厂系统里关闭氛围灯。
- 第一次进入 Ubuntu 时,灯还是灭的。
- 但只要在 Ubuntu 里再重启一次,氛围灯就又亮起来了。
这组现象非常有意思,因为它至少说明了四件事:
- 灯的状态不是单纯由“上电默认值”决定的。
- 原厂系统确实执行过某种“关灯动作”。
- Ubuntu 没有复现这个动作。
- 这件事大概率不是“随便拉一个 GPIO 电平”那么简单。
也正因为这个现象,我后面每排除掉一条路,都会更有把握一点。
一开始,我也以为是 GPIO
装完 Ubuntu 之后,最自然的第一反应当然是先看 Linux 有没有现成的 LED 接口:
ls /sys/class/leds
结果是空的。
那就只能往下钻,开始怀疑是不是某个 GPIO 在控制灯。这个思路并不离谱,因为很多小主机、NAS、软路由的前面板功能,本来就可能挂在 GPIO 或 Super I/O 上。
我当时重点排查了两类:
gpiochip0:Intel PCH 提供的 GPIOgpiochip1:IT8613 Super I/O 提供的 GPIO
后面还专门做了几轮扫描:
- 扫
gpiochip1多个线号 - 扫
gpiochip0的GPP_E范围
结果非常干脆:灯完全没有变化。
而且更尴尬的是,有些线号连正常驱动都做不到。也就是说,这条路不是“差一点就找到了”,而是从方向上就不对。
走到这里,我基本已经接受一个事实:
R1 这台机器的氛围灯,不是靠普通 GPIO 直接拉高拉低就能解决的。
中途还误判过 I2C 设备
既然 GPIO 没戏,就顺手开始看 I2C/SMBus:
sudo i2cdetect -y 2
在 i2c-2 上能看到几个设备地址,其中 0x44 一开始很容易让人多看两眼。原因也很简单:很多 LED 驱动芯片、控制芯片都挂在 I2C 上,看到一个“看起来像控制芯片”的地址,人很难不去试一下。
但这条线很快就被证伪了。
后面确认下来,i2c-2 上的 0x44 实际上是 Sensirion SHT4x 温湿度传感器,不是灯控芯片。这个结论非常重要,因为如果继续沿着 0x44 去改寄存器,不但解决不了问题,还有可能把温湿度传感器弄坏。
所以这一步的最终结论很明确:
i2c-2上的0x44不是氛围灯控制器,而是 SHT4x 温湿度传感器。
后来又怀疑 ACPI、BIOS、显示驱动
因为原厂系统能关灯,而 Ubuntu 做不到,所以我一度怀疑是不是厂商把开关逻辑藏到了 ACPI 里。
后面确实沿着这个方向做了不少事:
- 反编译 DSDT
- 找
_SB下和 RGB 相关的对象 - 找到
CSD0/CSD3 - 再用
acpi_call去实际调用
其中有一个方法看起来特别像“关 RGB”:
\_SB.CSD0 0x15
问题是,方法调用本身是成功的,灯却没灭。
这就意味着两种可能:
- 这个 ACPI 方法根本不是当前这块灯的实际开关
- 或者它只是改了某个 ACPI 侧状态,但没有真正打到负责灯控的 MCU
再往后,因为“原厂关灯后第一次进 Ubuntu 仍然是灭的,但 Ubuntu 再重启一次就亮了”这个现象太显眼,我又开始怀疑是不是前面板小屏、触摸链路、DSI 显示链路在暖重启时重新初始化,把灯恢复成默认开启状态。
所以我还试过:
- 修改 GRUB
- 加
i915.disable_display=1
但结果还是一样:没用。
这几条路并不是完全没有价值,它们至少帮我确认了两件事:
- 这个问题确实跟前面板整套控制链路有关
- 但真正的“关灯动作”并不在这些表面入口上
真正的突破口:别猜了,直接去看原厂系统
当 GPIO、ACPI、显示驱动这些方向都没有结果之后,思路反而一下子变简单了:
既然原厂系统能关灯,那最靠谱的办法不是继续猜,而是直接去原厂系统里找答案。
这一步做对以后,后面的事情就全顺了。
先把原厂 eMMC 挂起来
海康威视 R1 的 eMMC 分区其实很好认,挂载之后大致能对应出这些内容:
mmcblk0p3/mmcblk0p8:原厂系统分区mmcblk0p4/mmcblk0p5:histor.imgmmcblk0p7:小屏应用分区mmcblk0p6:配置分区
把这些分区挂出来之后,很快能发现两个关键点:
- 原厂前面板小屏应用是一个 Electron AppImage。
- 原厂真正的后端业务不在小屏分区里,而是在
histor.img。
这时候就知道该往哪下手了:前端看协议,后端找实现。
先从前端小屏应用里找线索
小屏分区里有这样一个文件:
HistorSub-Linux-1.1.15.AppImage
把它解包之后,前端代码里能看到一些非常直白的字段,比如:
deviceLightColordeviceLightMode
看到这里,其实已经能确认一件事:
这条氛围灯不是 Linux 标准
leds子系统在管,而是前面板/小屏这套软件栈的一部分。
继续往下看前端打包出来的 JS,很快又挖到了一个更关键的点:它并不是直接操作 GPIO,而是去访问一个本地 HTTPS 服务:
https://127.0.0.1:20443
而且代码里直接出现了这些动作:
get_atmo_lightset_atmo_lightset_atmo_colorget_light_themeset_light_theme
甚至还能看到灯效枚举:
LightEffectTurnOff = 999LightEffectMatchTheme = 1000LightEffectDynamicColor = 1001LightEffectBlueColor = 1002LightEffectWhiteColor = 1003LightEffectYellowColor = 1004
这一步非常有价值,因为它说明了两件事:
- 原厂前端确实在通过本地服务控制氛围灯
- “关灯”这个动作在原厂软件里是真实存在、可调用的
但 Ubuntu 下并没有这个 127.0.0.1:20443 服务,所以问题只剩最后一步:
这个服务到底是谁提供的?
最关键的后端,在 histor.img
把 histor.img 也挂出来之后,可以看到一批原厂二进制程序,比如:
emhistormod_intfproxyDaemonkey_event_handleserver_recv
其中最像“核心业务后端”的,就是:
/histor/bin/emhistor
继续在二进制字符串里搜,很快就发现它明确包含:
20443set_atmo_lightget_atmo_lightset_atmo_colorset_light_theme
到这里其实已经八九不离十了。
换句话说,原厂前端访问的那个本地控制面,背后基本就是 emhistor 这套后端在提供服务。
真正的答案,是 emhistor 自己说出来的
接下来我没有去整套复刻原厂系统,而是做了一个更小、更安全的实验:
- 临时把原厂
histor环境挂到 Ubuntu - 补上它需要的动态库
- 直接尝试启动
emhistor
虽然它启动后报了不少“原厂环境不完整”的错误,但日志里出现了一条真正决定胜负的内容:
i2cset -f -y 0 0x26 0x90 0x0
看到这一行的时候,事情其实就已经结束了。
因为这不再是推测,也不再是“我觉得可能是某个寄存器”,而是原厂后端自己把它执行的命令打出来了。某种意义上说,这等于它亲口告诉你:
对,关灯就是这么关的。
最终结论:真正可用的关灯命令
在海康威视 R1 上,Ubuntu 下可以直接关闭氛围灯的命令就是:
sudo i2cset -f -y 0 0x26 0x90 0x0
执行完之后,可以再读一下对应寄存器确认:
sudo i2cget -f -y 0 0x26 0x90
正常会读到:
0x00
这条命令已经在实机验证通过:
- 执行后氛围灯彻底熄灭
- 配成自动化之后,Ubuntu 重启后依然保持熄灭
为什么这次可以说是“彻底修好”
因为这次不是靠猜,而是把整条链路都闭环了:
- 从原厂 eMMC 镜像里找到了相关前后端程序。
- 从原厂小屏前端里找到了氛围灯接口名。
- 从原厂后端
emhistor运行日志里拿到了真实执行命令。 - 在 Ubuntu 下直接复现成功。
- 做成
systemd服务之后,重启验证也通过了。
这和之前那种“试试某个 GPIO”“看看某个 ACPI 方法有没有反应”的确定性,完全不是一个量级。
Ubuntu 下的最终修复方案
下面这套是我最终落地并验证通过的方案。
如果你只是想尽快把方案装上,不想手动一个文件一个文件地创建,那么可以直接使用我放在文章同目录下的一键安装脚本。
同目录里一共会有这几个文件:
hkvr1-ambient-lighthkvr1-ambient.defaulthkvr1-ambient-light-off.serviceinstall-hkvr1-ambient.sh
把这几个文件放到同一个目录后,先进入该目录,再执行安装脚本:
cd 你的文件所在目录
./install-hkvr1-ambient.sh
这个脚本会自动完成这些动作:
- 检查并安装
i2c-tools - 安装控制脚本到
/usr/local/sbin/hkvr1-ambient-light - 安装默认配置到
/etc/default/hkvr1-ambient - 安装
systemd单元到/etc/systemd/system/hkvr1-ambient-light-off.service - 加载
i2c-dev - 重载并启用开机自动关灯服务
如果你更喜欢完全手动安装,下面就是完整步骤。
1. 安装 i2c-tools
如果系统里没有 i2cset / i2cget:
sudo apt update
sudo apt install -y i2c-tools
2. 写入控制脚本
创建文件:
/usr/local/sbin/hkvr1-ambient-light
发布说明:内嵌脚本与本文同目录下的 hkvr1-ambient-light 保持一致维护;复制时以仓库内同名文件为准,避免网页排版截断。DMI 校验字符串在 /etc/default/hkvr1-ambient 里用 HKVR1_PRODUCT_PATTERN 配置,默认占位 HS-EXMP-R1,实机改成你的 product_name 即可。
内容如下:
#!/bin/sh
# 海康 R1 系氛围灯(DMI 匹配见 /etc/default/hkvr1-ambient 中 HKVR1_PRODUCT_PATTERN)。
# Reverse-engineered from the OEM eMMC image:
# i2cset -f -y 0 0x26 0x90 0x0 -> light off
set -eu
DEFAULTS=/etc/default/hkvr1-ambient
[ -r "$DEFAULTS" ] && . "$DEFAULTS"
: "${HKVR1_PRODUCT_PATTERN:=HS-EXMP-R1}"
: "${HKVR1_AMBIENT_BUS:=0}"
: "${HKVR1_AMBIENT_ADDR:=0x26}"
: "${HKVR1_AMBIENT_REG:=0x90}"
: "${HKVR1_AMBIENT_OFF:=0x0}"
: "${HKVR1_AMBIENT_ON:=0x9}"
usage() {
echo "Usage: $0 verify-dmi | get | off | on" >&2
echo " get - read current ambient-light register" >&2
echo " off - turn ambient light off" >&2
echo " on - restore OEM 'enabled' status value" >&2
exit 2
}
verify_dmi() {
pn=$(cat /sys/class/dmi/id/product_name 2>/dev/null || true)
case "$pn" in
"$HKVR1_PRODUCT_PATTERN"|"${HKVR1_PRODUCT_PATTERN}"*) return 0 ;;
esac
echo "hkvr1-ambient-light: unsupported product_name '$pn' (set HKVR1_PRODUCT_PATTERN in $DEFAULTS)" >&2
return 1
}
require_tools() {
command -v i2cget >/dev/null 2>&1 || {
echo "hkvr1-ambient-light: missing i2cget (install i2c-tools)." >&2
exit 1
}
command -v i2cset >/dev/null 2>&1 || {
echo "hkvr1-ambient-light: missing i2cset (install i2c-tools)." >&2
exit 1
}
}
cmd_get() {
verify_dmi || exit 1
require_tools
exec i2cget -f -y "$HKVR1_AMBIENT_BUS" "$HKVR1_AMBIENT_ADDR" "$HKVR1_AMBIENT_REG"
}
cmd_write() {
value=$1
verify_dmi || exit 1
require_tools
exec i2cset -f -y "$HKVR1_AMBIENT_BUS" "$HKVR1_AMBIENT_ADDR" "$HKVR1_AMBIENT_REG" "$value"
}
case "${1:-}" in
verify-dmi) verify_dmi ;;
get|status) cmd_get ;;
off|hold) cmd_write "$HKVR1_AMBIENT_OFF" ;;
on|release) cmd_write "$HKVR1_AMBIENT_ON" ;;
*) usage ;;
esac
给脚本加执行权限:
sudo chmod 755 /usr/local/sbin/hkvr1-ambient-light
3. 写入默认配置
创建文件:
/etc/default/hkvr1-ambient
内容如下:
# 海康 R1 系氛围灯(HKVR1_PRODUCT_PATTERN 须与本机 DMI product_name 一致;以下为占位示例)
HKVR1_PRODUCT_PATTERN=HS-EXMP-R1
# Hikvision R-class ambient light settings (I2C).
# Reverse-engineered from the OEM eMMC image.
#
# Known-good off command:
# sudo i2cset -f -y 0 0x26 0x90 0x0
#
# Manual checks:
# sudo hkvr1-ambient-light get
# sudo hkvr1-ambient-light off
# sudo hkvr1-ambient-light on
#
# Notes:
# - i2c-2 address 0x44 is the SHT4x temp/humidity sensor, not the LED controller.
# - The ambient light is controlled through i2c-0 address 0x26 register 0x90.
HKVR1_AMBIENT_BUS=0
HKVR1_AMBIENT_ADDR=0x26
HKVR1_AMBIENT_REG=0x90
HKVR1_AMBIENT_OFF=0x0
HKVR1_AMBIENT_ON=0x9
4. 配置开机自动关灯服务
创建文件:
/etc/systemd/system/hkvr1-ambient-light-off.service
内容如下:
[Unit]
Description=Hikvision R1 ambient light off
Documentation=file:/etc/default/hkvr1-ambient
After=systemd-modules-load.service systemd-udev-settle.service local-fs.target
Wants=systemd-udev-settle.service
[Service]
Type=oneshot
EnvironmentFile=-/etc/default/hkvr1-ambient
ExecStartPre=/sbin/modprobe i2c-dev
ExecStartPre=/usr/local/sbin/hkvr1-ambient-light verify-dmi
ExecStartPre=/bin/sh -c 'for _ in $(seq 1 20); do [ -c /dev/i2c-0 ] && exit 0; sleep 1; done; echo "hkvr1-ambient-light: /dev/i2c-0 not ready" >&2; exit 1'
ExecStart=/usr/local/sbin/hkvr1-ambient-light off
RemainAfterExit=yes
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target
启用服务:
sudo systemctl daemon-reload
sudo systemctl enable --now hkvr1-ambient-light-off.service
5. 验证
手动验证可以先这样做:
sudo hkvr1-ambient-light get
sudo hkvr1-ambient-light off
sudo hkvr1-ambient-light get
查看服务状态:
systemctl status hkvr1-ambient-light-off.service
最后重启一次系统:
sudo reboot
重新进入 Ubuntu 后,只需要确认两件事:
- 氛围灯依然是灭的
systemctl status hkvr1-ambient-light-off.service显示成功执行
关于“开灯”这件事
这次实机完整验证通过的是“关灯”方案,也就是:
sudo hkvr1-ambient-light off
脚本里我同时保留了:
sudo hkvr1-ambient-light on
它对应的是原厂逻辑里的启用状态值 0x9。如果你确实有重新打开氛围灯的需求,可以自己试一下;但这篇文章的核心目标只有一个:让 Ubuntu 下本来关不掉的氛围灯彻底闭嘴。这个目标已经完全达成。
这次排查里,最容易踩的几个坑
如果你也在折腾海康威视 R1,下面这几条基本可以帮你少走很多弯路。
1. 不要把时间继续砸在 GPIO 扫描上
至少对我这台 R1 来说,普通 GPIO 方向不是正确答案。它可能解释一些边缘现象,但不是最终控制入口。
2. 不要把 i2c-2 的 0x44 当作灯控芯片
它是 SHT4x 温湿度传感器,不是 LED 控制器。
3. ACPI 方法不等于真实灯控动作
就算在 DSDT 里看到了和 RGB 很像的对象,也不代表那个方法真能控制现在这块灯。
4. 真正靠谱的办法,是反向分析原厂系统
很多看起来像“驱动缺失”的问题,本质上并不是内核没有支持,而是原厂自己做了一套用户态控制链:
- 前端负责界面
- 本地服务负责设备控制
- 后端再去访问真正的硬件接口
这时候一直在 Linux 通用接口里盲猜,效率通常不高。直接去原厂系统里找真实实现,反而更快。
结语
这次海康威视 R1 的氛围灯问题,表面上只是一个很小的“体验问题”,但真正排下来,其实把这些层都过了一遍:
- GPIO
- I2C
- ACPI
- GRUB /
i915/ DSI - 原厂 Electron 小屏应用
- 原厂 eMMC 镜像
- 原厂后端业务二进制
最后真正把问题解决掉的,不是更复杂的猜测,而是回到一个特别朴素的思路:
原厂既然能做到,那就去看看它到底是怎么做到的。
最终答案也确实简单得近乎朴素:
sudo i2cset -f -y 0 0x26 0x90 0x0
但这条命令之所以值得写下来,不是因为它短,而是因为它背后已经被验证过:
- 来源明确
- 路径闭环
- 实机有效
- 可以持久化
- 重启后仍然生效
对于一台准备长期跑 Ubuntu Server 的海康威视 R1 来说,这件事补齐之后,使用体验就完整了很多。
如果你也正好在折腾这台机器,希望这篇文章能帮你少熬几晚,也少被那条灯照几晚。
发布与隐私:正文已避免写入可识别单机的信息(如真实 DMI 产品串、序列号、MAC、内网地址等);配套 hkvr1-ambient.default 中 HKVR1_PRODUCT_PATTERN 默认为占位 HS-EXMP-R1,部署到实机后请改为 dmidecode -s system-product-name 的输出后再启用服务。