feat: BLE 로그에 파싱된 값 추가 (TX 명령어 + RX 응답 의미)

TX: "maa 6ch measure", "msn battery query", "mpa piezo power ON" 등
RX: "rsn battery=3850mV (100%)", "reb 100samples", "raa all-ch complete",
    "rpa piezo ack", "rag gain calibration (24 values)" 등

펌웨어팀에 보낼 때 raw hex 없이도 통신 흐름을 이해할 수 있음

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-23 15:40:23 +09:00
parent 796bce19a2
commit df42a47d7d
@@ -270,9 +270,21 @@ class BleManager private constructor(private val context: Context) {
loge { "sendRaw: bluetoothGatt is null!" } loge { "sendRaw: bluetoothGatt is null!" }
return return
} }
val cmdPreview = if (data.size >= 4) String(data, 0, 4, Charsets.US_ASCII).trimEnd('?') else "?" val cmdPreview = if (data.size >= 4) String(data, 0, 3, Charsets.US_ASCII) else "?"
val cmdDetail = when (cmdPreview) {
"maa" -> "6ch measure"
"mpa" -> "piezo power ON"
"mps" -> "piezo stop"
"mec" -> "single burst"
"msn" -> "battery query"
"mta" -> if (data.size > 4 && data[4].toInt() == 1) "NIRS power ON" else "NIRS power OFF"
"mqq" -> "NIRS activate"
"mag" -> "NIRS gain query"
"mcj" -> "NIRS MCJ query"
else -> ""
}
logd { "sendRaw: $cmdPreview (${data.size} bytes)" } logd { "sendRaw: $cmdPreview (${data.size} bytes)" }
debugLogger.tx("$cmdPreview (${data.size}B)", data.size) debugLogger.tx("$cmdPreview $cmdDetail", data.size)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
val result = gatt.writeCharacteristic(characteristic, data, BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE) val result = gatt.writeCharacteristic(characteristic, data, BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE)
logd { "sendRaw result: $result" } logd { "sendRaw result: $result" }
@@ -724,29 +736,58 @@ class BleManager private constructor(private val context: Context) {
private fun processReceivedData(data: ByteArray) { private fun processReceivedData(data: ByteArray) {
lastRxTimestamp = System.currentTimeMillis() lastRxTimestamp = System.currentTimeMillis()
val prefix = String(data, 0, minOf(4, data.size), Charsets.US_ASCII) val prefix = String(data, 0, minOf(4, data.size), Charsets.US_ASCII)
debugLogger.rx(prefix.trimEnd(':', '!'), data.size)
logd { "Received: $prefix (${data.size} bytes)" } logd { "Received: $prefix (${data.size} bytes)" }
when (prefix) { when (prefix) {
"rta:", "rta!" -> onNirsPowerOnReceived?.invoke() "rta:", "rta!" -> {
"rsh:", "rsh!", "rqq:", "rqq!" -> onNirsSensorActivated?.invoke() debugLogger.rx("rta", data.size, "piezo power ON")
"rag:", "rag!" -> onNirsMagReceived?.invoke(data) onNirsPowerOnReceived?.invoke()
"rcj:", "rcj!" -> onNirsMcjReceived?.invoke(data) }
"rpa:" -> onPiezoDataReceived?.invoke(data) "rsh:", "rsh!", "rqq:", "rqq!" -> {
// Piezo echo responses → route to packet collector debugLogger.rx(prefix.take(3), data.size, "sensor activated")
"rer:" -> { onNirsSensorActivated?.invoke()
// VesiScan-Basic preliminary header — just acknowledge, reb: follows }
logd { "rer: preliminary header (${data.size} bytes)" } "rag:", "rag!" -> {
debugLogger.rx("rag", data.size, "gain calibration (${(data.size - 6) / 2} values)")
onNirsMagReceived?.invoke(data)
}
"rcj:", "rcj!" -> {
debugLogger.rx("rcj", data.size, "MCJ packet")
onNirsMcjReceived?.invoke(data)
}
"rpa:" -> {
debugLogger.rx("rpa", data.size, "piezo ack")
onPiezoDataReceived?.invoke(data)
}
"rer:" -> {
debugLogger.rx("rer", data.size, "preliminary header")
}
"reb:" -> {
val samples = (data.size - 8) / 2
debugLogger.rx("reb", data.size, "${samples}samples")
piezoCollector.addPacket(data)
}
"red:" -> {
debugLogger.rx("red", data.size, "continuation")
piezoCollector.addPacket(data)
}
"ree:", "raa:" -> {
val tag = prefix.take(3)
debugLogger.rx(tag, data.size, if (tag == "raa") "all-ch complete" else "single-ch end")
piezoCollector.addPacket(data)
} }
"reb:" -> piezoCollector.addPacket(data)
"red:" -> piezoCollector.addPacket(data)
"ree:", "raa:" -> piezoCollector.addPacket(data)
"rsn:" -> { "rsn:" -> {
if (data.size >= 6) { if (data.size >= 6) {
val millivolts = ((data[4].toInt() and 0xFF) shl 8) or (data[5].toInt() and 0xFF) val millivolts = ((data[4].toInt() and 0xFF) shl 8) or (data[5].toInt() and 0xFF)
batteryLevel.value = ((millivolts - 3700) * 100 / 500).coerceIn(0, 100) val pct = ((millivolts - 3700) * 100 / 500).coerceIn(0, 100)
batteryLevel.value = pct
debugLogger.rx("rsn", data.size, "battery=${millivolts}mV (${pct}%)")
} }
} }
else -> logw { "Unknown prefix: '$prefix' hex=${data.take(8).joinToString(" ") { "%02X".format(it) }}" } else -> {
val hex = data.take(8).joinToString(" ") { "%02X".format(it) }
debugLogger.rx("???", data.size, "unknown prefix='$prefix' hex=$hex")
logw { "Unknown prefix: '$prefix' hex=$hex" }
}
} }
} }
} }