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)
这个提交包含在:
@@ -67,7 +67,8 @@ Page({
|
||||
region_mask: 0x1F,
|
||||
wavelength: 2,
|
||||
brightness: SCAN_BRIGHTNESS,
|
||||
hold_time: 10
|
||||
hold_time: 10,
|
||||
control: 0x01
|
||||
}).then(function () {
|
||||
console.log('[SCAN] 扫描指令发送成功,等待ADC回传...')
|
||||
}).catch(function (err) {
|
||||
|
||||
@@ -90,7 +90,8 @@ Page({
|
||||
wavelength: 2,
|
||||
brightness: 200,
|
||||
duration_ms: self.data.fixedDurationMs,
|
||||
mode: self.data.selectedMode
|
||||
mode: self.data.selectedMode,
|
||||
control: 0x02
|
||||
}).then(function () {
|
||||
return ble.startTreatment(mask)
|
||||
}).then(function () {
|
||||
|
||||
@@ -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, [])
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
在新工单中引用
屏蔽一个用户