返回首页

海康威视 R1 打造完美的 Ubuntu Server——氛围灯开关

从 GPIO、I2C、ACPI 一路排查到原厂 eMMC 镜像逆向,最终用一行 i2cset 命令在 Ubuntu 下彻底关闭海康威视 R1 的氛围灯。完整记录排查过程与可持久化的最终方案。

海康威视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

我最早注意到的现象,其实已经把问题范围缩得很小了:

  1. 在原厂系统里关闭氛围灯。
  2. 第一次进入 Ubuntu 时,灯还是灭的。
  3. 但只要在 Ubuntu 里再重启一次,氛围灯就又亮起来了。

这组现象非常有意思,因为它至少说明了四件事:

  • 灯的状态不是单纯由“上电默认值”决定的。
  • 原厂系统确实执行过某种“关灯动作”。
  • Ubuntu 没有复现这个动作。
  • 这件事大概率不是“随便拉一个 GPIO 电平”那么简单。

也正因为这个现象,我后面每排除掉一条路,都会更有把握一点。

一开始,我也以为是 GPIO

装完 Ubuntu 之后,最自然的第一反应当然是先看 Linux 有没有现成的 LED 接口:

ls /sys/class/leds

结果是空的。

那就只能往下钻,开始怀疑是不是某个 GPIO 在控制灯。这个思路并不离谱,因为很多小主机、NAS、软路由的前面板功能,本来就可能挂在 GPIO 或 Super I/O 上。

我当时重点排查了两类:

  • gpiochip0:Intel PCH 提供的 GPIO
  • gpiochip1:IT8613 Super I/O 提供的 GPIO

后面还专门做了几轮扫描:

  • gpiochip1 多个线号
  • gpiochip0GPP_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 / mmcblk0p5histor.img
  • mmcblk0p7:小屏应用分区
  • mmcblk0p6:配置分区

把这些分区挂出来之后,很快能发现两个关键点:

  1. 原厂前面板小屏应用是一个 Electron AppImage。
  2. 原厂真正的后端业务不在小屏分区里,而是在 histor.img

这时候就知道该往哪下手了:前端看协议,后端找实现。

先从前端小屏应用里找线索

小屏分区里有这样一个文件:

  • HistorSub-Linux-1.1.15.AppImage

把它解包之后,前端代码里能看到一些非常直白的字段,比如:

  • deviceLightColor
  • deviceLightMode

看到这里,其实已经能确认一件事:

这条氛围灯不是 Linux 标准 leds 子系统在管,而是前面板/小屏这套软件栈的一部分。

继续往下看前端打包出来的 JS,很快又挖到了一个更关键的点:它并不是直接操作 GPIO,而是去访问一个本地 HTTPS 服务:

https://127.0.0.1:20443

而且代码里直接出现了这些动作:

  • get_atmo_light
  • set_atmo_light
  • set_atmo_color
  • get_light_theme
  • set_light_theme

甚至还能看到灯效枚举:

  • LightEffectTurnOff = 999
  • LightEffectMatchTheme = 1000
  • LightEffectDynamicColor = 1001
  • LightEffectBlueColor = 1002
  • LightEffectWhiteColor = 1003
  • LightEffectYellowColor = 1004

这一步非常有价值,因为它说明了两件事:

  • 原厂前端确实在通过本地服务控制氛围灯
  • “关灯”这个动作在原厂软件里是真实存在、可调用的

但 Ubuntu 下并没有这个 127.0.0.1:20443 服务,所以问题只剩最后一步:

这个服务到底是谁提供的?

最关键的后端,在 histor.img

histor.img 也挂出来之后,可以看到一批原厂二进制程序,比如:

  • emhistor
  • mod_intf
  • proxyDaemon
  • key_event_handle
  • server_recv

其中最像“核心业务后端”的,就是:

/histor/bin/emhistor

继续在二进制字符串里搜,很快就发现它明确包含:

  • 20443
  • set_atmo_light
  • get_atmo_light
  • set_atmo_color
  • set_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 重启后依然保持熄灭

为什么这次可以说是“彻底修好”

因为这次不是靠猜,而是把整条链路都闭环了:

  1. 从原厂 eMMC 镜像里找到了相关前后端程序。
  2. 从原厂小屏前端里找到了氛围灯接口名。
  3. 从原厂后端 emhistor 运行日志里拿到了真实执行命令。
  4. 在 Ubuntu 下直接复现成功。
  5. 做成 systemd 服务之后,重启验证也通过了。

这和之前那种“试试某个 GPIO”“看看某个 ACPI 方法有没有反应”的确定性,完全不是一个量级。

Ubuntu 下的最终修复方案

下面这套是我最终落地并验证通过的方案。

如果你只是想尽快把方案装上,不想手动一个文件一个文件地创建,那么可以直接使用我放在文章同目录下的一键安装脚本。

同目录里一共会有这几个文件:

  • hkvr1-ambient-light
  • hkvr1-ambient.default
  • hkvr1-ambient-light-off.service
  • install-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-20x44 当作灯控芯片

它是 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.defaultHKVR1_PRODUCT_PATTERN 默认为占位 HS-EXMP-R1,部署到实机后请改为 dmidecode -s system-product-name 的输出后再启用服务。