diff --git a/evershelf-scale-gateway/app/build.gradle.kts b/evershelf-scale-gateway/app/build.gradle.kts index 792eb77..b940870 100644 --- a/evershelf-scale-gateway/app/build.gradle.kts +++ b/evershelf-scale-gateway/app/build.gradle.kts @@ -11,8 +11,8 @@ android { applicationId = "it.dadaloop.evershelf.scalegate" minSdk = 24 targetSdk = 34 - versionCode = 4 - versionName = "1.5.0" + versionCode = 5 + versionName = "1.6.0" } buildFeatures { diff --git a/evershelf-scale-gateway/app/src/main/kotlin/it/dadaloop/evershelf/scalegate/BleScaleManager.kt b/evershelf-scale-gateway/app/src/main/kotlin/it/dadaloop/evershelf/scalegate/BleScaleManager.kt index abd37f2..af246f3 100644 --- a/evershelf-scale-gateway/app/src/main/kotlin/it/dadaloop/evershelf/scalegate/BleScaleManager.kt +++ b/evershelf-scale-gateway/app/src/main/kotlin/it/dadaloop/evershelf/scalegate/BleScaleManager.kt @@ -423,7 +423,12 @@ class BleScaleManager( if (reading != null && reading.weightKg > 0f) { mainHandler.post { listener.onWeightReceived(reading) } } else { - mainHandler.post { listener.onDebugEvent("⚠️ Peso non decodificato") } + val rawDump = data.mapIndexed { i, b -> + val v = b.toInt() and 0xFF + val h = "%02X".format(v) + "[$i]=$v(0x$h)" + }.joinToString(" ") + mainHandler.post { listener.onDebugEvent("⚠️ Peso non decodificato\n RAW: $rawDump") } } } } diff --git a/evershelf-scale-gateway/app/src/main/kotlin/it/dadaloop/evershelf/scalegate/GatewayWebSocketServer.kt b/evershelf-scale-gateway/app/src/main/kotlin/it/dadaloop/evershelf/scalegate/GatewayWebSocketServer.kt index 2085764..ac147b5 100644 --- a/evershelf-scale-gateway/app/src/main/kotlin/it/dadaloop/evershelf/scalegate/GatewayWebSocketServer.kt +++ b/evershelf-scale-gateway/app/src/main/kotlin/it/dadaloop/evershelf/scalegate/GatewayWebSocketServer.kt @@ -136,12 +136,18 @@ class GatewayWebSocketServer( } private fun buildWeightJson(weightKg: Float, stable: Boolean): String { - // Round to 2 decimal places - val rounded = (weightKg * 100).toLong() / 100.0 val obj = JSONObject() obj.put("type", "weight") - obj.put("value", rounded) - obj.put("unit", "kg") + // Food scale (<15kg): send grams for better precision + if (weightKg < 15f) { + val grams = Math.round(weightKg * 1000f) + obj.put("value", grams) + obj.put("unit", "g") + } else { + val rounded = (weightKg * 100).toLong() / 100.0 + obj.put("value", rounded) + obj.put("unit", "kg") + } obj.put("stable", stable) obj.put("timestamp", System.currentTimeMillis()) return obj.toString() diff --git a/evershelf-scale-gateway/app/src/main/kotlin/it/dadaloop/evershelf/scalegate/MainActivity.kt b/evershelf-scale-gateway/app/src/main/kotlin/it/dadaloop/evershelf/scalegate/MainActivity.kt index 65bc1a5..69e2244 100644 --- a/evershelf-scale-gateway/app/src/main/kotlin/it/dadaloop/evershelf/scalegate/MainActivity.kt +++ b/evershelf-scale-gateway/app/src/main/kotlin/it/dadaloop/evershelf/scalegate/MainActivity.kt @@ -222,8 +222,14 @@ class MainActivity : AppCompatActivity(), BleScaleListener, ServerEventListener } override fun onWeightReceived(reading: WeightReading) { - val kg = "%.2f".format(reading.weightKg) - binding.tvWeight.text = "$kg kg" + val isFood = reading.weightKg < 15f + if (isFood) { + val grams = (reading.weightKg * 1000).toInt() + binding.tvWeight.text = "$grams g" + } else { + val kg = "%.2f".format(reading.weightKg) + binding.tvWeight.text = "$kg kg" + } val extras = buildString { reading.fatPct?.let { append("Grasso: ${"%.1f".format(it)}% ") } diff --git a/evershelf-scale-gateway/app/src/main/kotlin/it/dadaloop/evershelf/scalegate/ScaleProtocol.kt b/evershelf-scale-gateway/app/src/main/kotlin/it/dadaloop/evershelf/scalegate/ScaleProtocol.kt index b04811a..b11a2c2 100644 --- a/evershelf-scale-gateway/app/src/main/kotlin/it/dadaloop/evershelf/scalegate/ScaleProtocol.kt +++ b/evershelf-scale-gateway/app/src/main/kotlin/it/dadaloop/evershelf/scalegate/ScaleProtocol.kt @@ -267,7 +267,7 @@ object ScaleProtocol { return null } - // --- Safe generic fallback --- + // --- Safe generic fallback (body + food scales) --- fun parseGenericSafe(data: ByteArray, debug: ((String) -> Unit)? = null): WeightReading? { if (data.size < 4) { @@ -275,30 +275,51 @@ object ScaleProtocol { return null } - data class Candidate(val pos: Int, val div: Float, val be: Boolean, val label: String) + data class Candidate( + val pos: Int, val div: Float, val be: Boolean, + val minKg: Float, val maxKg: Float, val label: String, + ) val candidates = listOf( - Candidate(1, 100f, true, "pos1 BE/100"), - Candidate(1, 100f, false, "pos1 LE/100"), - Candidate(3, 100f, true, "pos3 BE/100"), - Candidate(3, 100f, false, "pos3 LE/100"), - Candidate(2, 100f, true, "pos2 BE/100"), - Candidate(2, 100f, false, "pos2 LE/100"), - Candidate(1, 10f, true, "pos1 BE/10"), - Candidate(1, 10f, false, "pos1 LE/10"), - Candidate(3, 10f, true, "pos3 BE/10"), - Candidate(3, 10f, false, "pos3 LE/10"), - Candidate(1, 20f, true, "pos1 BE/20"), - Candidate(1, 20f, false, "pos1 LE/20"), + // Food scale: raw value in grams (div=1 -> kg=raw/1000 via pos) + Candidate(1, 1000f, false, 0.001f, 15f, "pos1 LE/g"), + Candidate(1, 1000f, true, 0.001f, 15f, "pos1 BE/g"), + Candidate(2, 1000f, false, 0.001f, 15f, "pos2 LE/g"), + Candidate(2, 1000f, true, 0.001f, 15f, "pos2 BE/g"), + Candidate(3, 1000f, false, 0.001f, 15f, "pos3 LE/g"), + Candidate(3, 1000f, true, 0.001f, 15f, "pos3 BE/g"), + // Food scale: raw in 0.1g (div=10000) + Candidate(1, 10000f, false, 0.001f, 15f, "pos1 LE/0.1g"), + Candidate(1, 10000f, true, 0.001f, 15f, "pos1 BE/0.1g"), + Candidate(2, 10000f, false, 0.001f, 15f, "pos2 LE/0.1g"), + Candidate(2, 10000f, true, 0.001f, 15f, "pos2 BE/0.1g"), + // Food scale: raw in 0.5g (div=2000) + Candidate(1, 2000f, false, 0.001f, 15f, "pos1 LE/0.5g"), + Candidate(1, 2000f, true, 0.001f, 15f, "pos1 BE/0.5g"), + // Body scale: standard divisors + Candidate(1, 100f, true, 2f, 250f, "pos1 BE/100"), + Candidate(1, 100f, false, 2f, 250f, "pos1 LE/100"), + Candidate(3, 100f, true, 2f, 250f, "pos3 BE/100"), + Candidate(3, 100f, false, 2f, 250f, "pos3 LE/100"), + Candidate(2, 100f, true, 2f, 250f, "pos2 BE/100"), + Candidate(2, 100f, false, 2f, 250f, "pos2 LE/100"), + Candidate(1, 10f, true, 2f, 250f, "pos1 BE/10"), + Candidate(1, 10f, false, 2f, 250f, "pos1 LE/10"), + Candidate(3, 10f, true, 2f, 250f, "pos3 BE/10"), + Candidate(3, 10f, false, 2f, 250f, "pos3 LE/10"), + Candidate(1, 20f, true, 2f, 250f, "pos1 BE/20"), + Candidate(1, 20f, false, 2f, 250f, "pos1 LE/20"), ) for (c in candidates) { if (c.pos + 1 >= data.size) continue val raw = if (c.be) u16be(data, c.pos) else u16le(data, c.pos) + if (raw == 0) continue val w = raw / c.div - if (w in 2f..250f) { - val wStr = "%.2f".format(w) - debug?.invoke("generic [" + c.label + "]: raw=$raw -> ${wStr}kg (UNSTABLE)") + if (w in c.minKg..c.maxKg) { + val grams = (w * 1000).toInt() + val wStr = "%.3f".format(w) + debug?.invoke("generic [" + c.label + "]: raw=$raw -> ${wStr}kg (${grams}g) (UNSTABLE)") return WeightReading(w, stable = false) } }