feat(led): drive VFD digits via vfdservice pipe (TITLE/CLOCK), keep display API
This commit is contained in:
@@ -172,6 +172,8 @@ CLAWD_LOG_FILE=1
|
|||||||
# CLAWD_ENABLE_BT=1
|
# CLAWD_ENABLE_BT=1
|
||||||
# OpenVFD sysfs 根路径(默认 /sys/class/leds/openvfd)
|
# OpenVFD sysfs 根路径(默认 /sys/class/leds/openvfd)
|
||||||
# CLAWD_OPENVFD_PATH=/sys/class/leds/openvfd
|
# CLAWD_OPENVFD_PATH=/sys/class/leds/openvfd
|
||||||
|
# 数码管 vfdservice 管道(默认 /tmp/openvfd_service)
|
||||||
|
# CLAWD_VFD_PIPE=/tmp/openvfd_service
|
||||||
# 多网口/特殊板型可固定 LAN 灯监控的以太网口(默认由 clawd 自动锁定首次 carrier 口)
|
# 多网口/特殊板型可固定 LAN 灯监控的以太网口(默认由 clawd 自动锁定首次 carrier 口)
|
||||||
# CLAWD_ETH_IFACE=end0
|
# CLAWD_ETH_IFACE=end0
|
||||||
EOF
|
EOF
|
||||||
|
|||||||
81
lib/led.js
81
lib/led.js
@@ -13,15 +13,71 @@ const { hasLanCableCarrier } = require('./network');
|
|||||||
* alarm → pwr(SETUP=灭 / APPS=亮)
|
* alarm → pwr(SETUP=灭 / APPS=亮)
|
||||||
* BT → 无 sysfs,仅日志
|
* BT → 无 sysfs,仅日志
|
||||||
*
|
*
|
||||||
* 数码管(AP/Conn/时间等):仍仅 debug 输出,不接 sysfs。
|
* 数码管:vfdservice 管道(660 字节,mode=4 TITLE / mode=1 CLOCK,与板端 Demo 一致)。
|
||||||
|
* CLAWD_VFD_PIPE 默认 /tmp/openvfd_service;管道不存在时仅 debug,不抛错。
|
||||||
*
|
*
|
||||||
* CLAWD_OPENVFD_PATH 默认 /sys/class/leds/openvfd
|
* CLAWD_OPENVFD_PATH 默认 /sys/class/leds/openvfd(图标灯)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const BLINK_INTERVAL_MS = 500;
|
const BLINK_INTERVAL_MS = 500;
|
||||||
const LAN_POLL_MS = 500;
|
const LAN_POLL_MS = 500;
|
||||||
|
|
||||||
const VFD_BASE = process.env.CLAWD_OPENVFD_PATH || '/sys/class/leds/openvfd';
|
const VFD_BASE = process.env.CLAWD_OPENVFD_PATH || '/sys/class/leds/openvfd';
|
||||||
|
const VFD_PIPE = process.env.CLAWD_VFD_PIPE || '/tmp/openvfd_service';
|
||||||
|
const VFD_BUF_SIZE = 660;
|
||||||
|
const VFD_MODE_CLOCK = 1;
|
||||||
|
const VFD_MODE_TITLE = 4;
|
||||||
|
const VFD_TITLE_OFF = 20;
|
||||||
|
|
||||||
|
function vfdPipePresent() {
|
||||||
|
try {
|
||||||
|
return fs.existsSync(VFD_PIPE);
|
||||||
|
} catch (_) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** TITLE:固定 4 字符,ASCII,供 vfdservice 解析为段码 */
|
||||||
|
function vfdTitleNormalize(raw) {
|
||||||
|
return String(raw || '')
|
||||||
|
.toUpperCase()
|
||||||
|
.replace(/[^A-Z0-9 \-]/g, ' ')
|
||||||
|
.slice(0, 4)
|
||||||
|
.padEnd(4, ' ');
|
||||||
|
}
|
||||||
|
|
||||||
|
function writeVfdPipeTitle(text4) {
|
||||||
|
if (!vfdPipePresent()) return;
|
||||||
|
const seg = vfdTitleNormalize(text4);
|
||||||
|
const data = Buffer.alloc(VFD_BUF_SIZE);
|
||||||
|
data.writeUInt16LE(VFD_MODE_TITLE, 0);
|
||||||
|
try {
|
||||||
|
data.write(`${seg}\0`, VFD_TITLE_OFF, 'ascii');
|
||||||
|
} catch (e) {
|
||||||
|
log.debug('display', `openvfd title encode: ${e.message}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const fd = fs.openSync(VFD_PIPE, 'w');
|
||||||
|
fs.writeSync(fd, data);
|
||||||
|
fs.closeSync(fd);
|
||||||
|
} catch (e) {
|
||||||
|
log.debug('display', `openvfd pipe title: ${e.message}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function writeVfdPipeClock() {
|
||||||
|
if (!vfdPipePresent()) return;
|
||||||
|
const data = Buffer.alloc(VFD_BUF_SIZE);
|
||||||
|
data.writeUInt16LE(VFD_MODE_CLOCK, 0);
|
||||||
|
try {
|
||||||
|
const fd = fs.openSync(VFD_PIPE, 'w');
|
||||||
|
fs.writeSync(fd, data);
|
||||||
|
fs.closeSync(fd);
|
||||||
|
} catch (e) {
|
||||||
|
log.debug('display', `openvfd pipe clock: ${e.message}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function vfdOn(icon) {
|
function vfdOn(icon) {
|
||||||
try {
|
try {
|
||||||
@@ -166,18 +222,18 @@ class Display {
|
|||||||
|
|
||||||
showAP() {
|
showAP() {
|
||||||
this._stopBlink();
|
this._stopBlink();
|
||||||
this._write('#m3AP ');
|
this._pipeTitle('AP ', '#m3AP ');
|
||||||
log.info('display', '显示屏 → AP');
|
log.info('display', '显示屏 → AP');
|
||||||
}
|
}
|
||||||
|
|
||||||
showConn() {
|
showConn() {
|
||||||
this._stopBlink();
|
this._stopBlink();
|
||||||
this._write('#m3Conn');
|
this._pipeTitle('Conn', '#m3Conn');
|
||||||
log.info('display', '显示屏 → Conn(闪烁)');
|
log.info('display', '显示屏 → Conn(闪烁)');
|
||||||
let visible = true;
|
let visible = true;
|
||||||
const blink = () => {
|
const blink = () => {
|
||||||
visible = !visible;
|
visible = !visible;
|
||||||
this._write(visible ? '#m3Conn' : '#c1');
|
this._pipeTitle(visible ? 'Conn' : ' ', visible ? '#m3Conn' : '#c1');
|
||||||
this._blinkTimer = setTimeout(blink, visible ? 1000 : 500);
|
this._blinkTimer = setTimeout(blink, visible ? 1000 : 500);
|
||||||
};
|
};
|
||||||
this._blinkTimer = setTimeout(blink, 1000);
|
this._blinkTimer = setTimeout(blink, 1000);
|
||||||
@@ -185,25 +241,26 @@ class Display {
|
|||||||
|
|
||||||
showErr0() {
|
showErr0() {
|
||||||
this._stopBlink();
|
this._stopBlink();
|
||||||
this._write('#m3Err0');
|
this._pipeTitle('Err0', '#m3Err0');
|
||||||
log.info('display', '显示屏 → Err0');
|
log.info('display', '显示屏 → Err0');
|
||||||
}
|
}
|
||||||
|
|
||||||
showTime() {
|
showTime() {
|
||||||
this._stopBlink();
|
this._stopBlink();
|
||||||
this._write('#s1');
|
log.debug('display', '[vfd] #s1');
|
||||||
|
writeVfdPipeClock();
|
||||||
log.info('display', '显示屏 → 时间');
|
log.info('display', '显示屏 → 时间');
|
||||||
}
|
}
|
||||||
|
|
||||||
showPin(pin) {
|
showPin(pin) {
|
||||||
this._stopBlink();
|
this._stopBlink();
|
||||||
const s = String(pin || '').padStart(4, '0').slice(-4);
|
const s = String(pin || '').padStart(4, '0').slice(-4);
|
||||||
this._write('#m2' + s);
|
this._pipeTitle(s, '#m2' + s);
|
||||||
log.info('display', `显示屏 → PIN: ${s}(闪烁)`);
|
log.info('display', `显示屏 → PIN: ${s}(闪烁)`);
|
||||||
let visible = true;
|
let visible = true;
|
||||||
const blink = () => {
|
const blink = () => {
|
||||||
visible = !visible;
|
visible = !visible;
|
||||||
this._write(visible ? '#m2' + s : '#c1');
|
this._pipeTitle(visible ? s : ' ', visible ? '#m2' + s : '#c1');
|
||||||
this._blinkTimer = setTimeout(blink, visible ? 1000 : 500);
|
this._blinkTimer = setTimeout(blink, visible ? 1000 : 500);
|
||||||
};
|
};
|
||||||
this._blinkTimer = setTimeout(blink, 1000);
|
this._blinkTimer = setTimeout(blink, 1000);
|
||||||
@@ -217,8 +274,10 @@ class Display {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_write(val) {
|
/** 保留原 #m3/#m2/#c1 的 debug 语义,并写 vfdservice TITLE */
|
||||||
log.debug('display', `[vfd] ${val}`);
|
_pipeTitle(fourCharText, debugLegacy) {
|
||||||
|
log.debug('display', `[vfd] ${debugLegacy}`);
|
||||||
|
writeVfdPipeTitle(fourCharText);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user