fix(mapping): correct region↔IO mapping per on-device measurement

Measured on real hardware: IO1=right IO2=left IO3=top IO4=middle IO5=bottom
(0x01=right 0x02=left 0x04=top 0x08=middle 0x10=bottom). The earlier verbal
'left=IO1/upper=IO2...' spec was wrong; this reverts to the original Phase-1
mapping, which the measurement confirms.

- protocol.js REGION constants + getRegionName + REGION_NAMES
- CRITICAL: buildVendorCommand ioMasks was built from REGION.* constants whose
  values just flipped, scrambling IO slot order; pin to literal [0x01..0x10]
- diagnosis.js REGION_DEFS key↔mask; pd_region_map values unchanged (PD is
  keyed by orientation, which is stable) — right=PD5 left=PD1 top=PD2
  middle=PD3,6,7 bottom=PD4
- common.js region labels back to right/left/top/middle/bottom (上/中/下)
- treatment-setup / manual-treatment / treating / scan-report / ble-debug
  face-map bindings and REGION_MAP realigned
- verified: mock pipeline lands left→IO2, middle→IO4 correctly
这个提交包含在:
Guoguo
2026-07-28 07:22:36 -07:00
父节点 74b0c5fa86
当前提交 21dccd2274
修改 11 个文件,包含 75 行新增73 行删除
+8 -8
查看文件
@@ -18,14 +18,14 @@ module.exports = {
saveFailed: '保存失败', saveFailed: '保存失败',
networkError: '网络异常,请重试', networkError: '网络异常,请重试',
deviceNotConnected: '设备未连接', deviceNotConnected: '设备未连接',
// 区域 = IO 纵列布局:left=IO1(0x01) top=IO2(0x02中上) middle=IO3(0x04) // 区域 = IO↔物理位置(实测):right=IO1(0x01) left=IO2(0x02) top=IO3(0x04)
// bottom=IO4(0x08中下) right=IO5(0x10)。key 名沿用,语义以掩码为准。 // middle=IO4(0x08) bottom=IO5(0x10)。语义以掩码为准。
regions: { regions: {
right: '右区',
left: '左区', left: '左区',
top: '上区', top: '上区',
middle: '中区', middle: '中区',
bottom: '下区', bottom: '下区'
right: '右区'
}, },
wavelengths: { wavelengths: {
red: '红光', red: '红光',
@@ -53,11 +53,11 @@ module.exports = {
networkError: 'Network error, please retry', networkError: 'Network error, please retry',
deviceNotConnected: 'Device not connected', deviceNotConnected: 'Device not connected',
regions: { regions: {
right: 'Right',
left: 'Left', left: 'Left',
top: 'Upper Center', top: 'Top',
middle: 'Center', middle: 'Center',
bottom: 'Lower Center', bottom: 'Bottom'
right: 'Right'
}, },
wavelengths: { wavelengths: {
red: 'Red', red: 'Red',
+1 -1
查看文件
@@ -10,7 +10,7 @@ var WAVES = [
{ key: 'uv', code: 3, label: '紫外' }, { key: 'uv', code: 3, label: '紫外' },
{ key: 'yellow', code: 4, label: '暖黄' } { key: 'yellow', code: 4, label: '暖黄' }
] ]
var REGION_LABELS = { 1: '区/IO1', 2: '中上/IO2', 4: '区/IO3', 8: '中/IO4', 16: '区/IO5' } var REGION_LABELS = { 1: '区/IO1', 2: '左区/IO2', 4: '区/IO3', 8: '中/IO4', 16: '区/IO5' }
var RUN_STATES = { 0: 'IDLE', 1: '检测', 2: '治疗', 255: '故障(0xFF)' } var RUN_STATES = { 0: 'IDLE', 1: '检测', 2: '治疗', 255: '故障(0xFF)' }
function hex(bytes) { function hex(bytes) {
+5 -5
查看文件
@@ -37,11 +37,11 @@
<view class="card"> <view class="card">
<view class="card-title">点分区 → 该区 2 秒治疗(对物理位置)</view> <view class="card-title">点分区 → 该区 2 秒治疗(对物理位置)</view>
<view class="face-map"> <view class="face-map">
<view class="face-region forehead" bindtap="onTapRegion" data-mask="2">上\nIO2</view> <view class="face-region forehead" bindtap="onTapRegion" data-mask="4">上\nIO3</view>
<view class="face-region left-cheek" bindtap="onTapRegion" data-mask="1">左\nIO1</view> <view class="face-region left-cheek" bindtap="onTapRegion" data-mask="2">左\nIO2</view>
<view class="face-region right-cheek" bindtap="onTapRegion" data-mask="16">右\nIO5</view> <view class="face-region right-cheek" bindtap="onTapRegion" data-mask="1">右\nIO1</view>
<view class="face-region nose" bindtap="onTapRegion" data-mask="4">中\nIO3</view> <view class="face-region nose" bindtap="onTapRegion" data-mask="8">中\nIO4</view>
<view class="face-region chin" bindtap="onTapRegion" data-mask="8">下\nIO4</view> <view class="face-region chin" bindtap="onTapRegion" data-mask="16">下\nIO5</view>
</view> </view>
</view> </view>
@@ -22,13 +22,13 @@ Page({
], ],
selectedFunctionId: 'rejuvenate', selectedFunctionId: 'rejuvenate',
selectedWavelength: 2, selectedWavelength: 2,
// 掩码 = IO 纵列布局(与 treatment-setup 一致):=0x01 中上=0x02 =0x04 中=0x08 =0x10 // 掩码 = IO↔物理位置(实测):=0x01 =0x02 =0x04 中=0x08 =0x10
regions: [ regions: [
{ key: 'left', mask: 0x01, checked: true }, { key: 'right', mask: 0x01, checked: true },
{ key: 'top', mask: 0x02, checked: true }, { key: 'left', mask: 0x02, checked: true },
{ key: 'middle', mask: 0x04, checked: true }, { key: 'top', mask: 0x04, checked: true },
{ key: 'bottom', mask: 0x08, checked: true }, { key: 'middle', mask: 0x08, checked: true },
{ key: 'right', mask: 0x10, checked: true } { key: 'bottom', mask: 0x10, checked: true }
], ],
// safety: a single session must NOT exceed 10 minutes (hardware caps at 10). // safety: a single session must NOT exceed 10 minutes (hardware caps at 10).
durations: [5, 10], durations: [5, 10],
@@ -26,11 +26,11 @@
<view class="page-title section-title">{{i18n.manual.regionTitle}}</view> <view class="page-title section-title">{{i18n.manual.regionTitle}}</view>
<!-- 脸型图布局(与「使用设置」一致);位置↔idx:0=左 1=中上 2=中 3=中下 4=右 --> <!-- 脸型图布局(与「使用设置」一致);位置↔idx:0=左 1=中上 2=中 3=中下 4=右 -->
<view class="face-map"> <view class="face-map">
<view class="face-region forehead {{regions[1].checked ? 'active' : ''}}" bindtap="onToggleRegion" data-idx="1">{{i18n.common.regions.top}}</view> <view class="face-region forehead {{regions[2].checked ? 'active' : ''}}" bindtap="onToggleRegion" data-idx="2">{{i18n.common.regions.top}}</view>
<view class="face-region left-cheek {{regions[0].checked ? 'active' : ''}}" bindtap="onToggleRegion" data-idx="0">{{i18n.common.regions.left}}</view> <view class="face-region left-cheek {{regions[1].checked ? 'active' : ''}}" bindtap="onToggleRegion" data-idx="1">{{i18n.common.regions.left}}</view>
<view class="face-region right-cheek {{regions[4].checked ? 'active' : ''}}" bindtap="onToggleRegion" data-idx="4">{{i18n.common.regions.right}}</view> <view class="face-region right-cheek {{regions[0].checked ? 'active' : ''}}" bindtap="onToggleRegion" data-idx="0">{{i18n.common.regions.right}}</view>
<view class="face-region nose {{regions[2].checked ? 'active' : ''}}" bindtap="onToggleRegion" data-idx="2">{{i18n.common.regions.middle}}</view> <view class="face-region nose {{regions[3].checked ? 'active' : ''}}" bindtap="onToggleRegion" data-idx="3">{{i18n.common.regions.middle}}</view>
<view class="face-region chin {{regions[3].checked ? 'active' : ''}}" bindtap="onToggleRegion" data-idx="3">{{i18n.common.regions.bottom}}</view> <view class="face-region chin {{regions[4].checked ? 'active' : ''}}" bindtap="onToggleRegion" data-idx="4">{{i18n.common.regions.bottom}}</view>
</view> </view>
<view class="page-title section-title">{{i18n.manual.durationTitle}}</view> <view class="page-title section-title">{{i18n.manual.durationTitle}}</view>
+14 -14
查看文件
@@ -35,26 +35,26 @@
<view class="card-title">{{i18n.report.faceTitle}}</view> <view class="card-title">{{i18n.report.faceTitle}}</view>
<!-- 脸型图(与「使用设置」同款布局);位置↔faceZones 下标:1=中上 0=左 4=右 2=中 3=中下 --> <!-- 脸型图(与「使用设置」同款布局);位置↔faceZones 下标:1=中上 0=左 4=右 2=中 3=中下 -->
<view class="face-map"> <view class="face-map">
<view class="face-region forehead {{faceZones[1].colorClass}}" bindtap="onTapZone" data-wave="{{faceZones[1].waveKey}}"> <view class="face-region forehead {{faceZones[2].colorClass}}" bindtap="onTapZone" data-wave="{{faceZones[2].waveKey}}">
<view class="fz-name">{{faceZones[1].name}}</view>
<view class="fz-mode" wx:if="{{faceZones[1].hasProblem}}">{{faceZones[1].modeLabel}}</view>
</view>
<view class="face-region left-cheek {{faceZones[0].colorClass}}" bindtap="onTapZone" data-wave="{{faceZones[0].waveKey}}">
<view class="fz-name">{{faceZones[0].name}}</view>
<view class="fz-mode" wx:if="{{faceZones[0].hasProblem}}">{{faceZones[0].modeLabel}}</view>
</view>
<view class="face-region right-cheek {{faceZones[4].colorClass}}" bindtap="onTapZone" data-wave="{{faceZones[4].waveKey}}">
<view class="fz-name">{{faceZones[4].name}}</view>
<view class="fz-mode" wx:if="{{faceZones[4].hasProblem}}">{{faceZones[4].modeLabel}}</view>
</view>
<view class="face-region nose {{faceZones[2].colorClass}}" bindtap="onTapZone" data-wave="{{faceZones[2].waveKey}}">
<view class="fz-name">{{faceZones[2].name}}</view> <view class="fz-name">{{faceZones[2].name}}</view>
<view class="fz-mode" wx:if="{{faceZones[2].hasProblem}}">{{faceZones[2].modeLabel}}</view> <view class="fz-mode" wx:if="{{faceZones[2].hasProblem}}">{{faceZones[2].modeLabel}}</view>
</view> </view>
<view class="face-region chin {{faceZones[3].colorClass}}" bindtap="onTapZone" data-wave="{{faceZones[3].waveKey}}"> <view class="face-region left-cheek {{faceZones[1].colorClass}}" bindtap="onTapZone" data-wave="{{faceZones[1].waveKey}}">
<view class="fz-name">{{faceZones[1].name}}</view>
<view class="fz-mode" wx:if="{{faceZones[1].hasProblem}}">{{faceZones[1].modeLabel}}</view>
</view>
<view class="face-region right-cheek {{faceZones[0].colorClass}}" bindtap="onTapZone" data-wave="{{faceZones[0].waveKey}}">
<view class="fz-name">{{faceZones[0].name}}</view>
<view class="fz-mode" wx:if="{{faceZones[0].hasProblem}}">{{faceZones[0].modeLabel}}</view>
</view>
<view class="face-region nose {{faceZones[3].colorClass}}" bindtap="onTapZone" data-wave="{{faceZones[3].waveKey}}">
<view class="fz-name">{{faceZones[3].name}}</view> <view class="fz-name">{{faceZones[3].name}}</view>
<view class="fz-mode" wx:if="{{faceZones[3].hasProblem}}">{{faceZones[3].modeLabel}}</view> <view class="fz-mode" wx:if="{{faceZones[3].hasProblem}}">{{faceZones[3].modeLabel}}</view>
</view> </view>
<view class="face-region chin {{faceZones[4].colorClass}}" bindtap="onTapZone" data-wave="{{faceZones[4].waveKey}}">
<view class="fz-name">{{faceZones[4].name}}</view>
<view class="fz-mode" wx:if="{{faceZones[4].hasProblem}}">{{faceZones[4].modeLabel}}</view>
</view>
</view> </view>
<!-- 底部图例:各光简要功效(果果指定保留) --> <!-- 底部图例:各光简要功效(果果指定保留) -->
<view class="face-legend"> <view class="face-legend">
+6 -6
查看文件
@@ -23,13 +23,13 @@ Page({
}, },
_getRegionText: function (regionMask) { _getRegionText: function (regionMask) {
// 掩码 = IO 纵列布局0x01 0x02中上 0x04 0x08中 0x10 // 掩码 = IO↔物理位置(实测)0x01 0x02 0x04 0x08中 0x10
var REGION_MAP = { var REGION_MAP = {
1: 'common.regions.left', 1: 'common.regions.right',
2: 'common.regions.top', 2: 'common.regions.left',
4: 'common.regions.middle', 4: 'common.regions.top',
8: 'common.regions.bottom', 8: 'common.regions.middle',
16: 'common.regions.right' 16: 'common.regions.bottom'
} }
if (regionMask === 0x1F) return i18n.t('treating.allRegions') if (regionMask === 0x1F) return i18n.t('treating.allRegions')
var names = [] var names = []
@@ -34,8 +34,8 @@ Page({
var app = getApp() var app = getApp()
this.setData({ statusBarHeight: app.globalData.statusBarHeight }) this.setData({ statusBarHeight: app.globalData.statusBarHeight })
i18n.bind(this) i18n.bind(this)
// 顺序对应 data.regions 掩码 0x01..0x10IO1..IO5 = 左/上/中/中下/右 // 顺序对应 data.regions 掩码 0x01..0x10 = IO1..IO5 = 右/左/上/中/下(实测
var regionKeys = ['left', 'top', 'middle', 'bottom', 'right'] var regionKeys = ['right', 'left', 'top', 'middle', 'bottom']
var regions = this.data.regions var regions = this.data.regions
regions.forEach(function (r, i) { regions.forEach(function (r, i) {
r.name = i18n.t('common.regions.' + regionKeys[i]) r.name = i18n.t('common.regions.' + regionKeys[i])
@@ -27,11 +27,11 @@
<!-- 位置↔idx 对应新纵列布局:idx0=左(0x01) idx1=中上(0x02) idx2=中(0x04) idx3=中下(0x08) idx4=右(0x10) --> <!-- 位置↔idx 对应新纵列布局:idx0=左(0x01) idx1=中上(0x02) idx2=中(0x04) idx3=中下(0x08) idx4=右(0x10) -->
<view class="face-map"> <view class="face-map">
<view class="face-region forehead {{regions[1].checked ? 'active' : ''}} {{selectedMode === 0 ? 'disabled' : ''}}" bindtap="onToggleRegion" data-idx="1">{{i18n.common.regions.top}}</view> <view class="face-region forehead {{regions[2].checked ? 'active' : ''}} {{selectedMode === 0 ? 'disabled' : ''}}" bindtap="onToggleRegion" data-idx="2">{{i18n.common.regions.top}}</view>
<view class="face-region left-cheek {{regions[0].checked ? 'active' : ''}} {{selectedMode === 0 ? 'disabled' : ''}}" bindtap="onToggleRegion" data-idx="0">{{i18n.common.regions.left}}</view> <view class="face-region left-cheek {{regions[1].checked ? 'active' : ''}} {{selectedMode === 0 ? 'disabled' : ''}}" bindtap="onToggleRegion" data-idx="1">{{i18n.common.regions.left}}</view>
<view class="face-region right-cheek {{regions[4].checked ? 'active' : ''}} {{selectedMode === 0 ? 'disabled' : ''}}" bindtap="onToggleRegion" data-idx="4">{{i18n.common.regions.right}}</view> <view class="face-region right-cheek {{regions[0].checked ? 'active' : ''}} {{selectedMode === 0 ? 'disabled' : ''}}" bindtap="onToggleRegion" data-idx="0">{{i18n.common.regions.right}}</view>
<view class="face-region nose {{regions[2].checked ? 'active' : ''}} {{selectedMode === 0 ? 'disabled' : ''}}" bindtap="onToggleRegion" data-idx="2">{{i18n.common.regions.middle}}</view> <view class="face-region nose {{regions[3].checked ? 'active' : ''}} {{selectedMode === 0 ? 'disabled' : ''}}" bindtap="onToggleRegion" data-idx="3">{{i18n.common.regions.middle}}</view>
<view class="face-region chin {{regions[3].checked ? 'active' : ''}} {{selectedMode === 0 ? 'disabled' : ''}}" bindtap="onToggleRegion" data-idx="3">{{i18n.common.regions.bottom}}</view> <view class="face-region chin {{regions[4].checked ? 'active' : ''}} {{selectedMode === 0 ? 'disabled' : ''}}" bindtap="onToggleRegion" data-idx="4">{{i18n.common.regions.bottom}}</view>
</view> </view>
<button class="btn-primary" bindtap="onStart" disabled="{{submitting}}" loading="{{submitting}}"> <button class="btn-primary" bindtap="onStart" disabled="{{submitting}}" loading="{{submitting}}">
+13 -11
查看文件
@@ -57,18 +57,18 @@ var TREAT_MODE = {
SMART: 1 SMART: 1
} }
// 区域 = IO 纵列布局(果果 2026-07 定案):=IO1(0x01),中上=IO2(0x02),中=IO3(0x04) // 区域 = IO↔物理位置(果果 2026-07-28 真机实测):=IO1(0x01)=IO2(0x02)=IO3(0x04)
// 中=IO4(0x08),右=IO5(0x10)。旧命名(左脸颊/额头/鼻部…)来自过时文档,与实物不符,已废弃 // 中=IO4(0x08)=IO5(0x10)。此前口头给的"纵列左IO1/中上IO2…"实测不符,以本表为准
var REGION = { var REGION = {
LEFT: 0x01, RIGHT: 0x01,
TOP: 0x02, LEFT: 0x02,
MIDDLE: 0x04, TOP: 0x04,
BOTTOM: 0x08, MIDDLE: 0x08,
RIGHT: 0x10, BOTTOM: 0x10,
FULL_FACE: 0x1F FULL_FACE: 0x1F
} }
var REGION_NAMES = ['left', 'top', 'middle', 'bottom', 'right'] var REGION_NAMES = ['right', 'left', 'top', 'middle', 'bottom']
// FFE4 Byte17 高三位 = 扫描时光色指示(协议简述-添加扫描时光色.docx 2026-07): // FFE4 Byte17 高三位 = 扫描时光色指示(协议简述-添加扫描时光色.docx 2026-07):
// 001=红光 010=红外 011=紫外(蓝) 100=暖黄。注意与下发命令的 WAVELENGTH 编码 // 001=红光 010=红外 011=紫外(蓝) 100=暖黄。注意与下发命令的 WAVELENGTH 编码
@@ -196,7 +196,9 @@ function buildVendorCommand(options) {
b = clampByte(b) b = clampByte(b)
return isTreat && b > MAX_TREAT_BRIGHTNESS ? MAX_TREAT_BRIGHTNESS : b return isTreat && b > MAX_TREAT_BRIGHTNESS ? MAX_TREAT_BRIGHTNESS : b
} }
var ioMasks = [REGION.LEFT, REGION.TOP, REGION.MIDDLE, REGION.BOTTOM, REGION.RIGHT] // IO1..IO5 // IO1..IO5 物理槽位 = bit 升序(Byte n*6 对应 mask 1<<n)。直接用字面量,不依赖
// REGION 常量名——区域↔mask 语义调整时这里必须始终是 0x01,0x02,0x04,0x08,0x10。
var ioMasks = [0x01, 0x02, 0x04, 0x08, 0x10] // IO1=右 IO2=左 IO3=上 IO4=中 IO5=下
var bytes = [] var bytes = []
var i, j var i, j
@@ -368,8 +370,8 @@ function parseException(payload) {
function getRegionName(mask) { function getRegionName(mask) {
var names = [] var names = []
var bits = [ var bits = [
[0x01, '区'], [0x02, '中上区'], [0x04, '区'], [0x01, '区'], [0x02, '区'], [0x04, '区'],
[0x08, '中区'], [0x10, '区'] [0x08, '中区'], [0x10, '区']
] ]
for (var i = 0; i < bits.length; i++) { for (var i = 0; i < bits.length; i++) {
if (mask & bits[i][0]) names.push(bits[i][1]) if (mask & bits[i][0]) names.push(bits[i][1])
+10 -10
查看文件
@@ -9,7 +9,7 @@
// PD ADC 值越大 = 反射光越弱 = 皮肤吸收越多。故 NR = zone/avg 1(吸收多→NR 高)。 // PD ADC 值越大 = 反射光越弱 = 皮肤吸收越多。故 NR = zone/avg 1(吸收多→NR 高)。
// config.polarity = 'direct' 可切回"值大=光强"的旧假设(备用开关,标定时救急)。 // config.polarity = 'direct' 可切回"值大=光强"的旧假设(备用开关,标定时救急)。
// //
// PD↔区域映射已由固件确认(2026-07-28,果果转达):左=PD1 上=PD2 中=PD3/6/7 下=PD4 右=PD5。 // PD↔区域映射果果转达):左=PD1 上=PD2 中=PD3/6/7 下=PD4 右=PD5(方位与实测 IO 表一致)
// 仍待定:3 路 PD 区的聚合——文档 7.1 自相矛盾(几何平均 vs 取最小),按更细的几何平均+MAD剔异常实现。 // 仍待定:3 路 PD 区的聚合——文档 7.1 自相矛盾(几何平均 vs 取最小),按更细的几何平均+MAD剔异常实现。
// 所有阈值/亮度/映射均可被服务端 diagnosis_config 覆盖,标定后免发版调参。 // 所有阈值/亮度/映射均可被服务端 diagnosis_config 覆盖,标定后免发版调参。
@@ -20,13 +20,13 @@ var WAVE_PROBLEM = { red: 'aging', uv: 'acne', yellow: 'pigment', ir: 'deep' }
// 波长 key -> 下发命令编码。 // 波长 key -> 下发命令编码。
var WAVE_KEY_CODE = { red: 2, uv: 3, yellow: 4, ir: 1 } var WAVE_KEY_CODE = { red: 2, uv: 3, yellow: 4, ir: 1 }
// 5 区 = IO 纵列布局(果果定案):=IO1 中上=IO2 中=IO3=IO4 右=IO5 // 5 区 = IO↔物理位置(果果 2026-07-28 实测):=IO1(0x01) 左=IO2(0x02) 上=IO3(0x04) 中=IO4(0x08) 下=IO5(0x10)
var REGION_DEFS = [ var REGION_DEFS = [
{ key: 'left', mask: 0x01 }, { key: 'right', mask: 0x01 },
{ key: 'top', mask: 0x02 }, { key: 'left', mask: 0x02 },
{ key: 'middle', mask: 0x04 }, { key: 'top', mask: 0x04 },
{ key: 'bottom', mask: 0x08 }, { key: 'middle', mask: 0x08 },
{ key: 'right', mask: 0x10 } { key: 'bottom', mask: 0x10 }
] ]
var DEFAULT_CONFIG = { var DEFAULT_CONFIG = {
@@ -35,8 +35,8 @@ var DEFAULT_CONFIG = {
th: 0.10, // 判定阈值(文档建议初始值,需临床标定) th: 0.10, // 判定阈值(文档建议初始值,需临床标定)
severity: { low: 0.05, mid: 0.15, high: 0.30 }, // 轻/中/重分级,仅报告展示 severity: { low: 0.05, mid: 0.15, high: 0.30 }, // 轻/中/重分级,仅报告展示
brightness: 204, // 治疗强度 80%(果果定案),仅用于推荐方案下发 brightness: 204, // 治疗强度 80%(果果定案),仅用于推荐方案下发
// PD↔区域映射(固件 2026-07-28 确认):左=PD1 上=PD2 中=PD3/6/7 下=PD4 右=PD5(下标 0 基)。 // PD↔区域映射(果果 2026-07-28 确认):左=PD1 上=PD2 中=PD3/6/7 下=PD4 右=PD5(下标 0 基)。
pd_region_map: { left: [0], top: [1], middle: [2, 5, 6], bottom: [3], right: [4] }, pd_region_map: { right: [4], left: [0], top: [1], middle: [2, 5, 6], bottom: [3] },
problem_wavelength: PROBLEM_WAVELENGTH problem_wavelength: PROBLEM_WAVELENGTH
} }
@@ -217,7 +217,7 @@ function mockReport() {
scanned_at: 0, scanned_at: 0,
region_mask: 0x1F, region_mask: 0x1F,
waves: { waves: {
// 下标: 0=PD1 1=中上PD2 2=PD3 3=中下PD4 4=PD5 5=PD6 6=PD7 // 下标: 0=PD1 1=PD2 2=PD3 3=PD4 4=PD5 5=PD6 6=PD7
red: [1350, 920, 900, 910, 905, 890, 940], red: [1350, 920, 900, 910, 905, 890, 940],
uv: [2050, 2020, 2600, 2010, 2080, 2560, 2620], uv: [2050, 2020, 2600, 2010, 2080, 2560, 2620],
yellow: [1500, 1520, 1490, 1505, 1495, 1530, 1510], yellow: [1500, 1520, 1490, 1505, 1495, 1530, 1510],