feat: add decoded payloads
This commit is contained in:
102
app/src/main/java/com/example/sensortestingapp/DecoderIaq.kt
Normal file
102
app/src/main/java/com/example/sensortestingapp/DecoderIaq.kt
Normal file
@@ -0,0 +1,102 @@
|
||||
package com.example.sensortestingapp
|
||||
|
||||
import org.apache.commons.codec.DecoderException
|
||||
import org.apache.commons.codec.binary.Hex
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
object DecoderIaq {
|
||||
private const val INVALID_CO2 = 16383
|
||||
private const val INVALID_VOC = 2047
|
||||
private const val INVALID_HUMIDITY = 1023
|
||||
private const val INVALID_TEMPERATURE = 2047
|
||||
private const val INVALID_PRESSURE = 65535
|
||||
private const val INVALID_OCCUPANCY = 3
|
||||
private const val INVALID_PM25 = 1023
|
||||
private const val INVALID_PM10 = 1023
|
||||
fun parseMeasurement(input: String): Measurement {
|
||||
val measurement = Measurement()
|
||||
val inputBytes = hexStringToByteArray(input)
|
||||
val msgType = inputBytes[0].toInt() and 0xFF ushr 4
|
||||
measurement.msgType = msgType
|
||||
|
||||
val co2 = parseUnsignedInt(inputBytes, 0, 3) ushr 6 and INVALID_CO2
|
||||
measurement.co2 = if (co2 == INVALID_CO2) null else co2
|
||||
|
||||
val voc = parseUnsignedInt(inputBytes, 2, 4) ushr 3 and INVALID_VOC
|
||||
measurement.voc = if (co2 == INVALID_VOC) null else voc
|
||||
|
||||
val humidity =
|
||||
parseUnsignedInt(inputBytes, 3, 5) ushr 1 and INVALID_HUMIDITY
|
||||
measurement.humidity = if (humidity == INVALID_HUMIDITY) null else humidity / 10
|
||||
|
||||
val temperature =
|
||||
parseUnsignedInt(inputBytes, 4, 7) ushr 6 and INVALID_TEMPERATURE
|
||||
measurement.temperature =
|
||||
if (temperature == INVALID_TEMPERATURE) null else ((temperature / 10.0 - 40) * 10.0).roundToInt() / 10.0
|
||||
|
||||
val pressure =
|
||||
parseUnsignedInt(inputBytes, 6, 9) ushr 6 and INVALID_PRESSURE
|
||||
measurement.pressure =
|
||||
if (pressure == INVALID_PRESSURE) null else (30000 + 19000.0 * pressure / 13107).roundToInt()
|
||||
|
||||
val occupancy =
|
||||
parseUnsignedInt(inputBytes, 8, 9) ushr 4 and INVALID_OCCUPANCY
|
||||
measurement.occupancy = if (occupancy == INVALID_OCCUPANCY) null else occupancy
|
||||
|
||||
if (msgType == 0) {
|
||||
val pm25 =
|
||||
parseUnsignedInt(inputBytes, 8, 10) ushr 2 and INVALID_PM25
|
||||
measurement.pm25 = if (pm25 == INVALID_PM25) null else pm25
|
||||
|
||||
val pm10 = parseUnsignedInt(inputBytes, 9, 11) and INVALID_PM10
|
||||
measurement.pm10 = if (pm10 == INVALID_PM10) null else pm10
|
||||
}
|
||||
return measurement
|
||||
}
|
||||
|
||||
private fun parseUnsignedInt(bytes: ByteArray, startIncl: Int, endExcl: Int): Int {
|
||||
val section = bytes.copyOfRange(startIncl, endExcl)
|
||||
var unsignedInt = 0
|
||||
for (i in section.indices) {
|
||||
unsignedInt = unsignedInt shl 8
|
||||
unsignedInt = unsignedInt or (section[i].toInt() and 0xFF)
|
||||
}
|
||||
return unsignedInt
|
||||
}
|
||||
|
||||
private fun hexStringToByteArray(encoded: String): ByteArray {
|
||||
return try {
|
||||
Hex.decodeHex(encoded)
|
||||
} catch (e: DecoderException) {
|
||||
throw RuntimeException(e)
|
||||
}
|
||||
}
|
||||
|
||||
data class Measurement (
|
||||
var msgType: Number? = null,
|
||||
var co2: Number? = null,
|
||||
var voc: Number? = null,
|
||||
var humidity: Number? = null,
|
||||
var temperature: Number? = null,
|
||||
var pressure: Number? = null,
|
||||
var occupancy: Number? = null,
|
||||
var pm25: Number? = null,
|
||||
var pm10: Number? = null
|
||||
) {
|
||||
|
||||
|
||||
override fun toString(): String {
|
||||
return "M{" +
|
||||
"type=" + msgType +
|
||||
"co2=" + co2 +
|
||||
", voc=" + voc +
|
||||
", hum=" + humidity +
|
||||
", temp=" + temperature +
|
||||
", press=" + pressure +
|
||||
", occ=" + occupancy +
|
||||
", pm25=" + pm25 +
|
||||
", pm10=" + pm10 +
|
||||
'}'
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -128,7 +128,7 @@ class KirbyDevice(
|
||||
override var rssi: Int? = null
|
||||
|
||||
private fun addMeasurement(characteristic: BluetoothGattCharacteristic) {
|
||||
val hexPayload = characteristic.value.toHexString()
|
||||
val hexPayload = characteristic.value.toHexString().substring(2)
|
||||
val payload = Payload(hexPayload)
|
||||
val base64Payload = Base64.getEncoder().encodeToString(characteristic.value)
|
||||
Log.i("BleListener", "Char received: $payload")
|
||||
@@ -183,25 +183,10 @@ class KirbyDevice(
|
||||
get() = statuses.stream().map { it.name }.collect(Collectors.joining(", "))
|
||||
|
||||
override fun getMeasurements(): List<Measurement> {
|
||||
if (measurements.isEmpty()) {
|
||||
return emptyList()
|
||||
}
|
||||
val latest = measurements.last()
|
||||
val result = mutableListOf<Measurement>(object : Measurement {
|
||||
override fun getLabel(): String {
|
||||
return "Index"
|
||||
}
|
||||
val result = mutableListOf<Measurement>()
|
||||
|
||||
override fun getIcon(): Int {
|
||||
return R.drawable.baseline_numbers_24
|
||||
}
|
||||
|
||||
override fun getFormattedValue(): String {
|
||||
return measurements.size.toString()
|
||||
}
|
||||
})
|
||||
result.addAll(payloadToMeasurements(latest))
|
||||
return result
|
||||
measurements.forEach { m -> result.addAll(payloadToMeasurements(m))}
|
||||
return result.reversed()
|
||||
}
|
||||
|
||||
override fun getActions(): List<Action> {
|
||||
@@ -345,10 +330,9 @@ class KirbyDevice(
|
||||
data class Payload(
|
||||
val payload: String,
|
||||
val ts: String = LocalDateTime.now()
|
||||
.format(DateTimeFormatter.ofPattern("HH:mm:ss"))
|
||||
.format(DateTimeFormatter.ofPattern("dd.MM.yy HH:mm:ss"))
|
||||
)
|
||||
|
||||
|
||||
fun bytesToUInt16(arr: ByteArray, start: Int): Int {
|
||||
return ByteBuffer.wrap(arr, start, 2)
|
||||
.order(ByteOrder.LITTLE_ENDIAN).short.toInt() and 0xFFFF
|
||||
@@ -376,15 +360,15 @@ private fun payloadToMeasurements(payload: Payload): List<Measurement> {
|
||||
}
|
||||
|
||||
override fun getIcon(): Int? {
|
||||
return R.drawable.baseline_access_time_24
|
||||
return R.drawable.baseline_numbers_24
|
||||
}
|
||||
}, object : Measurement {
|
||||
override fun getLabel(): String {
|
||||
return "Payload"
|
||||
return ""
|
||||
}
|
||||
|
||||
override fun getFormattedValue(): String {
|
||||
return payload.payload
|
||||
return DecoderIaq.parseMeasurement(payload.payload).toString();
|
||||
}
|
||||
|
||||
override fun getIcon(): Int? {
|
||||
|
||||
Reference in New Issue
Block a user