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,
|
region_mask: 0x1F,
|
||||||
wavelength: 2,
|
wavelength: 2,
|
||||||
brightness: SCAN_BRIGHTNESS,
|
brightness: SCAN_BRIGHTNESS,
|
||||||
hold_time: 10
|
hold_time: 10,
|
||||||
|
control: 0x01
|
||||||
}).then(function () {
|
}).then(function () {
|
||||||
console.log('[SCAN] 扫描指令发送成功,等待ADC回传...')
|
console.log('[SCAN] 扫描指令发送成功,等待ADC回传...')
|
||||||
}).catch(function (err) {
|
}).catch(function (err) {
|
||||||
|
|||||||
@@ -90,7 +90,8 @@ Page({
|
|||||||
wavelength: 2,
|
wavelength: 2,
|
||||||
brightness: 200,
|
brightness: 200,
|
||||||
duration_ms: self.data.fixedDurationMs,
|
duration_ms: self.data.fixedDurationMs,
|
||||||
mode: self.data.selectedMode
|
mode: self.data.selectedMode,
|
||||||
|
control: 0x02
|
||||||
}).then(function () {
|
}).then(function () {
|
||||||
return ble.startTreatment(mask)
|
return ble.startTreatment(mask)
|
||||||
}).then(function () {
|
}).then(function () {
|
||||||
|
|||||||
@@ -181,12 +181,13 @@ function startTreatment(regionMask) {
|
|||||||
|
|
||||||
function stopTreatment() {
|
function stopTreatment() {
|
||||||
if (protocol.PROTOCOL_MODE === 'vendor_33') {
|
if (protocol.PROTOCOL_MODE === 'vendor_33') {
|
||||||
return writeRawCommand(protocol.buildVendorCommand33({
|
return writeRawCommand(protocol.buildVendorCommand({
|
||||||
region_mask: 0,
|
region_mask: 0,
|
||||||
wavelength: protocol.WAVELENGTH.R,
|
wavelength: protocol.WAVELENGTH.R,
|
||||||
brightness: 0,
|
brightness: 0,
|
||||||
current_gain: 0,
|
current_gain: 0,
|
||||||
hold_time: 0
|
hold_time: 0,
|
||||||
|
control: protocol.CONTROL.IDLE
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
return writeCommandWithRetry(protocol.CMD.STOP, [])
|
return writeCommandWithRetry(protocol.CMD.STOP, [])
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ module.exports = {
|
|||||||
WAVELENGTH: protocol.WAVELENGTH,
|
WAVELENGTH: protocol.WAVELENGTH,
|
||||||
TREAT_MODE: protocol.TREAT_MODE,
|
TREAT_MODE: protocol.TREAT_MODE,
|
||||||
REGION: protocol.REGION,
|
REGION: protocol.REGION,
|
||||||
|
CONTROL: protocol.CONTROL,
|
||||||
DEVICE_ERR: protocol.DEVICE_ERR,
|
DEVICE_ERR: protocol.DEVICE_ERR,
|
||||||
|
|
||||||
// Connection
|
// Connection
|
||||||
@@ -41,6 +42,7 @@ module.exports = {
|
|||||||
PROTOCOL_MODE: protocol.PROTOCOL_MODE,
|
PROTOCOL_MODE: protocol.PROTOCOL_MODE,
|
||||||
buildFrame: protocol.buildFrame,
|
buildFrame: protocol.buildFrame,
|
||||||
parseFrame: protocol.parseFrame,
|
parseFrame: protocol.parseFrame,
|
||||||
|
buildVendorCommand: protocol.buildVendorCommand,
|
||||||
buildVendorCommand33: protocol.buildVendorCommand33,
|
buildVendorCommand33: protocol.buildVendorCommand33,
|
||||||
parseVendorStatus: protocol.parseVendorStatus,
|
parseVendorStatus: protocol.parseVendorStatus,
|
||||||
parseStatusReport: protocol.parseStatusReport,
|
parseStatusReport: protocol.parseStatusReport,
|
||||||
|
|||||||
@@ -155,9 +155,19 @@ function bytesToHex(bytes) {
|
|||||||
return hex.toUpperCase()
|
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 || {}
|
options = options || {}
|
||||||
var regionMask = options.region_mask || REGION.FULL_FACE
|
var regionMask = options.region_mask || REGION.FULL_FACE
|
||||||
var wavelength = options.wavelength || WAVELENGTH.R
|
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 gain = clampUInt16(options.current_gain === undefined ? brightness : options.current_gain)
|
||||||
var durationMs = options.duration_ms || 600000
|
var durationMs = options.duration_ms || 600000
|
||||||
var holdSeconds = clampUInt16(options.hold_time === undefined ? Math.round(durationMs / 1000) : options.hold_time)
|
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 = [
|
var regions = [
|
||||||
REGION.LEFT_CHEEK,
|
REGION.LEFT_CHEEK,
|
||||||
REGION.RIGHT_CHEEK,
|
REGION.RIGHT_CHEEK,
|
||||||
@@ -190,10 +201,15 @@ function buildVendorCommand33(options) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bytes.push((holdSeconds >> 8) & 0xFF, holdSeconds & 0xFF)
|
bytes.push((holdSeconds >> 8) & 0xFF, holdSeconds & 0xFF)
|
||||||
|
bytes.push(control & 0xFF)
|
||||||
bytes.push(xorChecksum(bytes))
|
bytes.push(xorChecksum(bytes))
|
||||||
return bytes
|
return bytes
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function buildVendorCommand33(options) {
|
||||||
|
return buildVendorCommand(options)
|
||||||
|
}
|
||||||
|
|
||||||
function parseVendorStatus(buffer) {
|
function parseVendorStatus(buffer) {
|
||||||
var bytes = bufferToBytes(buffer)
|
var bytes = bufferToBytes(buffer)
|
||||||
if (bytes.length === 1) {
|
if (bytes.length === 1) {
|
||||||
@@ -233,6 +249,26 @@ function parseVendorStatus(buffer) {
|
|||||||
raw_hex: bytesToHex(bytes)
|
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) {
|
if (bytes.length === 33) {
|
||||||
var ios = []
|
var ios = []
|
||||||
for (var i = 0; i < 5; i++) {
|
for (var i = 0; i < 5; i++) {
|
||||||
@@ -248,6 +284,7 @@ function parseVendorStatus(buffer) {
|
|||||||
is_heartbeat: false,
|
is_heartbeat: false,
|
||||||
ios: ios,
|
ios: ios,
|
||||||
hold_time: (bytes[30] << 8) | bytes[31],
|
hold_time: (bytes[30] << 8) | bytes[31],
|
||||||
|
control: -1,
|
||||||
checksum: bytes[32],
|
checksum: bytes[32],
|
||||||
raw_hex: bytesToHex(bytes)
|
raw_hex: bytesToHex(bytes)
|
||||||
}
|
}
|
||||||
@@ -364,6 +401,7 @@ module.exports = {
|
|||||||
TREAT_MODE: TREAT_MODE,
|
TREAT_MODE: TREAT_MODE,
|
||||||
REGION: REGION,
|
REGION: REGION,
|
||||||
REGION_NAMES: REGION_NAMES,
|
REGION_NAMES: REGION_NAMES,
|
||||||
|
CONTROL: CONTROL,
|
||||||
DEVICE_ERR: DEVICE_ERR,
|
DEVICE_ERR: DEVICE_ERR,
|
||||||
|
|
||||||
bufferToBytes: bufferToBytes,
|
bufferToBytes: bufferToBytes,
|
||||||
@@ -373,6 +411,7 @@ module.exports = {
|
|||||||
bytesToUint32: bytesToUint32,
|
bytesToUint32: bytesToUint32,
|
||||||
hexToBytes: hexToBytes,
|
hexToBytes: hexToBytes,
|
||||||
bytesToHex: bytesToHex,
|
bytesToHex: bytesToHex,
|
||||||
|
buildVendorCommand: buildVendorCommand,
|
||||||
buildVendorCommand33: buildVendorCommand33,
|
buildVendorCommand33: buildVendorCommand33,
|
||||||
|
|
||||||
buildFrame: buildFrame,
|
buildFrame: buildFrame,
|
||||||
|
|||||||
在新工单中引用
屏蔽一个用户