feat: upgrade FFE1 command to 34 bytes with control byte

Per 协议简述(3): Byte33 is now control instruction (0=IDLE, 1=scan,
2=treat), Byte34 is XOR checksum. Backward compat with 33-byte
read-back preserved. All command sites updated with correct control:
- auto-scan: control=0x01 (scan mode)
- treatment-setup: control=0x02 (treat mode)
- stopTreatment: control=0x00 (IDLE)
这个提交包含在:
Guoguo
2026-06-12 09:00:01 -07:00
父节点 69621fd2b6
当前提交 36f12e098e
修改 5 个文件,包含 50 行新增6 行删除
+3 -2
查看文件
@@ -181,12 +181,13 @@ function startTreatment(regionMask) {
function stopTreatment() {
if (protocol.PROTOCOL_MODE === 'vendor_33') {
return writeRawCommand(protocol.buildVendorCommand33({
return writeRawCommand(protocol.buildVendorCommand({
region_mask: 0,
wavelength: protocol.WAVELENGTH.R,
brightness: 0,
current_gain: 0,
hold_time: 0
hold_time: 0,
control: protocol.CONTROL.IDLE
}))
}
return writeCommandWithRetry(protocol.CMD.STOP, [])
+2
查看文件
@@ -15,6 +15,7 @@ module.exports = {
WAVELENGTH: protocol.WAVELENGTH,
TREAT_MODE: protocol.TREAT_MODE,
REGION: protocol.REGION,
CONTROL: protocol.CONTROL,
DEVICE_ERR: protocol.DEVICE_ERR,
// Connection
@@ -41,6 +42,7 @@ module.exports = {
PROTOCOL_MODE: protocol.PROTOCOL_MODE,
buildFrame: protocol.buildFrame,
parseFrame: protocol.parseFrame,
buildVendorCommand: protocol.buildVendorCommand,
buildVendorCommand33: protocol.buildVendorCommand33,
parseVendorStatus: protocol.parseVendorStatus,
parseStatusReport: protocol.parseStatusReport,
+41 -2
查看文件
@@ -155,9 +155,19 @@ function bytesToHex(bytes) {
return hex.toUpperCase()
}
// --- vendor 33-byte command protocol ---
// --- vendor 34-byte command protocol ---
// Byte1-30: IO1~IO5 (6B each: R, IR, UV, Y, GainH, GainL)
// Byte31-32: hold time (seconds, big-endian)
// Byte33: control (0=IDLE, 1=scan, 2=treat)
// Byte34: XOR checksum of Byte1-33
function buildVendorCommand33(options) {
var CONTROL = {
IDLE: 0x00,
SCAN: 0x01,
TREAT: 0x02
}
function buildVendorCommand(options) {
options = options || {}
var regionMask = options.region_mask || REGION.FULL_FACE
var wavelength = options.wavelength || WAVELENGTH.R
@@ -165,6 +175,7 @@ function buildVendorCommand33(options) {
var gain = clampUInt16(options.current_gain === undefined ? brightness : options.current_gain)
var durationMs = options.duration_ms || 600000
var holdSeconds = clampUInt16(options.hold_time === undefined ? Math.round(durationMs / 1000) : options.hold_time)
var control = options.control !== undefined ? options.control : CONTROL.TREAT
var regions = [
REGION.LEFT_CHEEK,
REGION.RIGHT_CHEEK,
@@ -190,10 +201,15 @@ function buildVendorCommand33(options) {
}
bytes.push((holdSeconds >> 8) & 0xFF, holdSeconds & 0xFF)
bytes.push(control & 0xFF)
bytes.push(xorChecksum(bytes))
return bytes
}
function buildVendorCommand33(options) {
return buildVendorCommand(options)
}
function parseVendorStatus(buffer) {
var bytes = bufferToBytes(buffer)
if (bytes.length === 1) {
@@ -233,6 +249,26 @@ function parseVendorStatus(buffer) {
raw_hex: bytesToHex(bytes)
}
}
if (bytes.length === 34) {
var ios34 = []
for (var j = 0; j < 5; j++) {
var off34 = j * 6
ios34.push({
red: bytes[off34], infrared: bytes[off34 + 1],
uv: bytes[off34 + 2], warm_yellow: bytes[off34 + 3],
gain: (bytes[off34 + 4] << 8) | bytes[off34 + 5]
})
}
return {
type: 'params',
is_heartbeat: false,
ios: ios34,
hold_time: (bytes[30] << 8) | bytes[31],
control: bytes[32],
checksum: bytes[33],
raw_hex: bytesToHex(bytes)
}
}
if (bytes.length === 33) {
var ios = []
for (var i = 0; i < 5; i++) {
@@ -248,6 +284,7 @@ function parseVendorStatus(buffer) {
is_heartbeat: false,
ios: ios,
hold_time: (bytes[30] << 8) | bytes[31],
control: -1,
checksum: bytes[32],
raw_hex: bytesToHex(bytes)
}
@@ -364,6 +401,7 @@ module.exports = {
TREAT_MODE: TREAT_MODE,
REGION: REGION,
REGION_NAMES: REGION_NAMES,
CONTROL: CONTROL,
DEVICE_ERR: DEVICE_ERR,
bufferToBytes: bufferToBytes,
@@ -373,6 +411,7 @@ module.exports = {
bytesToUint32: bytesToUint32,
hexToBytes: hexToBytes,
bytesToHex: bytesToHex,
buildVendorCommand: buildVendorCommand,
buildVendorCommand33: buildVendorCommand33,
buildFrame: buildFrame,