ble: rid: 파서 fixed-width 12B 필드로 + mfv? fallback
명세서 (BLE Command Table) 재확인: rid: = [tag 4B] [HW 12B] [Serial 12B] [FW 12B] [crc 2B] = 42B 각 12B 필드는 ASCII 가변 + NULL/공백/제어문자 padding 기존 파서는 whitespace split 후 prefix 매칭. padding 이 NULL 이면 동작하지만 다른 바이트(예: 0xFF, 0x20 외 임의값)면 전체가 한 토큰으로 합쳐져 fw 추출 실패. 새 펌웨어에서 firmware 안 뜨던 증상의 원인. 수정: - field(offset) 헬퍼로 정확히 12B 씩 슬라이스 후 NULL/제어 제거 - 명세대로 4 / 16 / 28 offset 에서 HW / SN / FW 분리 추가 fallback: - sendFirmwareVersionQuery() 신규 — mfv? 단독 쿼리 - rfv: 응답 핸들러 추가 - service ready 4초 후 firmwareVersion 여전히 비어있으면 mfv? 자동 송신 (mid? 미지원 펌웨어 대응)
This commit is contained in:
@@ -536,6 +536,11 @@ class BleManager private constructor(private val context: Context) {
|
|||||||
sendRaw(CRC16.buildCommandASCII("mid", " "))
|
sendRaw(CRC16.buildCommandASCII("mid", " "))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** mfv? — FW Version only. mid? 응답 안 오는 펌웨어용 fallback. */
|
||||||
|
fun sendFirmwareVersionQuery() {
|
||||||
|
sendRaw(CRC16.buildCommandASCII("mfv", " "))
|
||||||
|
}
|
||||||
|
|
||||||
fun sendLedMode(state: Int) {
|
fun sendLedMode(state: Int) {
|
||||||
sendRaw(CRC16.buildCommandBE("mls", intArrayOf(state)))
|
sendRaw(CRC16.buildCommandBE("mls", intArrayOf(state)))
|
||||||
}
|
}
|
||||||
@@ -995,6 +1000,13 @@ class BleManager private constructor(private val context: Context) {
|
|||||||
startBatteryPolling()
|
startBatteryPolling()
|
||||||
startWatchdog()
|
startWatchdog()
|
||||||
sendDeviceInfoQuery()
|
sendDeviceInfoQuery()
|
||||||
|
// 4초 후에도 펌웨어 미수신이면 mfv? fallback (구 펌웨어는 mid? 미지원 가능성)
|
||||||
|
handler.postDelayed({
|
||||||
|
if (firmwareVersion.value.isEmpty() && isConnected.value) {
|
||||||
|
debugLogger.info("FW fallback: sending mfv?")
|
||||||
|
sendFirmwareVersionQuery()
|
||||||
|
}
|
||||||
|
}, 4_000)
|
||||||
logd { "Service ready! (CCCD written)" }
|
logd { "Service ready! (CCCD written)" }
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -1115,27 +1127,38 @@ class BleManager private constructor(private val context: Context) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
"rid:" -> {
|
"rid:" -> {
|
||||||
// BLE 응답 끝에 NULL byte padding이 붙는 경우가 있음 — PostgreSQL JSONB가
|
// 명세: [tag 4B] [HW 12B] [Serial 12B] [FW 12B] [crc 2B] = 42B.
|
||||||
// 0x00을 거부하므로 trim()으로 안 빠지는 NULL/제어문자를 명시적으로 제거.
|
// 각 12B 필드는 ASCII 가변 길이 + NULL/공백/제어문자 padding.
|
||||||
val info = if (data.size > 4)
|
// (구) whitespace split 방식은 padding 이 space 가 아닐 때 깨졌음.
|
||||||
String(data, 4, data.size - 4, Charsets.US_ASCII)
|
fun field(offset: Int): String {
|
||||||
.replace(Regex("[\\x00-\\x1F\\x7F]"), " ").trim()
|
val end = (offset + 12).coerceAtMost(data.size - 2) // CRC 2B 앞까지
|
||||||
else "?"
|
if (end <= offset) return ""
|
||||||
|
return String(data, offset, end - offset, Charsets.US_ASCII)
|
||||||
|
.replace(Regex("[\\x00-\\x1F\\x7F]"), "").trim()
|
||||||
|
}
|
||||||
|
val hw = field(4)
|
||||||
|
val sn = field(16)
|
||||||
|
val fw = field(28)
|
||||||
|
val info = "$hw $sn $fw".trim()
|
||||||
debugLogger.rx("rid", data.size, "device_info=$info")
|
debugLogger.rx("rid", data.size, "device_info=$info")
|
||||||
debugLogger.info("DEVICE_INFO $info")
|
debugLogger.info("DEVICE_INFO hw=$hw sn=$sn fw=$fw")
|
||||||
// 형식: "VBTHW0100 VBT26040001 VBTFW0111" (HW, Serial, FW)
|
|
||||||
val parts = info.split(Regex("\\s+")).filter { it.isNotBlank() }
|
|
||||||
val hw = parts.firstOrNull { it.startsWith("VBTHW") } ?: ""
|
|
||||||
val fw = parts.firstOrNull { it.startsWith("VBTFW") } ?: ""
|
|
||||||
val sn = parts.firstOrNull { it.startsWith("VBT") && !it.startsWith("VBTHW") && !it.startsWith("VBTFW") } ?: ""
|
|
||||||
handler.post {
|
handler.post {
|
||||||
if (hw.isNotEmpty()) hardwareVersion.value = hw
|
if (hw.isNotEmpty()) hardwareVersion.value = hw
|
||||||
if (fw.isNotEmpty()) firmwareVersion.value = fw
|
if (fw.isNotEmpty()) firmwareVersion.value = fw
|
||||||
if (sn.isNotEmpty()) serialNumber.value = sn
|
if (sn.isNotEmpty()) serialNumber.value = sn
|
||||||
}
|
}
|
||||||
// ble.log에 헤더 블록 형태로 (텍스트 에디터로 열었을 때 즉시 보임)
|
|
||||||
debugLogger.deviceInfo(fw, hw, sn)
|
debugLogger.deviceInfo(fw, hw, sn)
|
||||||
}
|
}
|
||||||
|
"rfv:" -> {
|
||||||
|
// 명세: [tag 4B] [FW ASCII N B] [crc 2B] — FW 만 별도 쿼리(mfv?) 응답.
|
||||||
|
// mid? 가 응답 안 하는 펌웨어 fallback 용.
|
||||||
|
if (data.size > 6) {
|
||||||
|
val fw = String(data, 4, data.size - 6, Charsets.US_ASCII)
|
||||||
|
.replace(Regex("[\\x00-\\x1F\\x7F]"), "").trim()
|
||||||
|
debugLogger.rx("rfv", data.size, "fw=$fw")
|
||||||
|
if (fw.isNotEmpty()) handler.post { firmwareVersion.value = fw }
|
||||||
|
}
|
||||||
|
}
|
||||||
"rls:" -> {
|
"rls:" -> {
|
||||||
val state = if (data.size >= 5) data[4].toInt() and 0xFF else -1
|
val state = if (data.size >= 5) data[4].toInt() and 0xFF else -1
|
||||||
debugLogger.rx("rls", data.size, "led_state=$state")
|
debugLogger.rx("rls", data.size, "led_state=$state")
|
||||||
|
|||||||
Reference in New Issue
Block a user