From 857b937813f7b1e63528d1fa9387b07d881a07d4 Mon Sep 17 00:00:00 2001 From: Jcheng Lee Date: Mon, 5 Jun 2023 20:00:01 +0800 Subject: [PATCH 01/21] developing 20230605 --- .idea/.name | 1 + .idea/compiler.xml | 2 +- .idea/gradle.xml | 4 +--- .idea/kotlinc.xml | 6 ++++++ .idea/misc.xml | 3 +-- app/src/main/java/de/jnns/bmsmonitor/MainActivity.kt | 2 +- .../main/java/de/jnns/bmsmonitor/bluetooth/BleManager.kt | 3 +++ .../de/jnns/bmsmonitor/bluetooth/BmsGattClientCallback.kt | 8 ++++++++ 8 files changed, 22 insertions(+), 7 deletions(-) create mode 100644 .idea/.name create mode 100644 .idea/kotlinc.xml diff --git a/.idea/.name b/.idea/.name new file mode 100644 index 0000000..1d40cc9 --- /dev/null +++ b/.idea/.name @@ -0,0 +1 @@ +BMSMonitor \ No newline at end of file diff --git a/.idea/compiler.xml b/.idea/compiler.xml index 61a9130..fb7f4a8 100644 --- a/.idea/compiler.xml +++ b/.idea/compiler.xml @@ -1,6 +1,6 @@ - + \ No newline at end of file diff --git a/.idea/gradle.xml b/.idea/gradle.xml index 9d38be2..56d6814 100644 --- a/.idea/gradle.xml +++ b/.idea/gradle.xml @@ -4,7 +4,7 @@ diff --git a/.idea/kotlinc.xml b/.idea/kotlinc.xml new file mode 100644 index 0000000..7e340a7 --- /dev/null +++ b/.idea/kotlinc.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml index 19aa6a5..9b6d99b 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,6 +1,5 @@ - - + diff --git a/app/src/main/java/de/jnns/bmsmonitor/MainActivity.kt b/app/src/main/java/de/jnns/bmsmonitor/MainActivity.kt index f84ae65..873c37f 100644 --- a/app/src/main/java/de/jnns/bmsmonitor/MainActivity.kt +++ b/app/src/main/java/de/jnns/bmsmonitor/MainActivity.kt @@ -4,6 +4,7 @@ import android.Manifest import android.content.Intent import android.content.pm.PackageManager import android.os.Bundle +import android.util.Log import androidx.appcompat.app.AppCompatActivity import androidx.core.content.ContextCompat import androidx.navigation.findNavController @@ -22,7 +23,6 @@ class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - Realm.init(this) Realm.compactRealm(Realm.getDefaultConfiguration()!!) diff --git a/app/src/main/java/de/jnns/bmsmonitor/bluetooth/BleManager.kt b/app/src/main/java/de/jnns/bmsmonitor/bluetooth/BleManager.kt index 1539980..f0f00a1 100644 --- a/app/src/main/java/de/jnns/bmsmonitor/bluetooth/BleManager.kt +++ b/app/src/main/java/de/jnns/bmsmonitor/bluetooth/BleManager.kt @@ -1,6 +1,7 @@ package de.jnns.bmsmonitor.bluetooth import android.bluetooth.BluetoothDevice +import android.util.Log import kotlinx.coroutines.Runnable class BleManager { @@ -9,6 +10,8 @@ class BleManager { fun addDevice(device: BluetoothDevice) { if (!bleDevices.contains(device)) { + Log.d("BleManager", "Device Address:" + device.address) + Log.d("BleManager", "Device Name:" + device.name) bleDevices.add(device) for (func in onUpdateFunctions) diff --git a/app/src/main/java/de/jnns/bmsmonitor/bluetooth/BmsGattClientCallback.kt b/app/src/main/java/de/jnns/bmsmonitor/bluetooth/BmsGattClientCallback.kt index 7cf4aed..6169d04 100644 --- a/app/src/main/java/de/jnns/bmsmonitor/bluetooth/BmsGattClientCallback.kt +++ b/app/src/main/java/de/jnns/bmsmonitor/bluetooth/BmsGattClientCallback.kt @@ -55,9 +55,17 @@ class BmsGattClientCallback( super.onServicesDiscovered(gatt, status) if (status != BluetoothGatt.GATT_SUCCESS) { + Log.d("BluetoothGatt", "onServicesDiscovered failed") return } + var i: Int = 0 + + for(serv in gatt.services) { + i++ + Log.d("BluetoothGatt", "Services" + i.toString() + ":" + serv.uuid.toString()) + } + val uartService = gatt.getService(uartUuid) if (uartService != null) { From 30c2e975bd2a06961dae29d96ffc00bbc2a688c6 Mon Sep 17 00:00:00 2001 From: David Lee Date: Wed, 7 Jun 2023 10:24:27 +0800 Subject: [PATCH 02/21] Developing 20230607 --- .../bluetooth/BmsGattClientCallback.kt | 96 +++++++++++-------- .../bmsmonitor/bms/BmsGeneralInfoResponse.kt | 21 +++- .../de/jnns/bmsmonitor/data/BatteryData.kt | 7 +- .../de/jnns/bmsmonitor/services/BmsService.kt | 26 +++-- 4 files changed, 95 insertions(+), 55 deletions(-) diff --git a/app/src/main/java/de/jnns/bmsmonitor/bluetooth/BmsGattClientCallback.kt b/app/src/main/java/de/jnns/bmsmonitor/bluetooth/BmsGattClientCallback.kt index 6169d04..0bbce26 100644 --- a/app/src/main/java/de/jnns/bmsmonitor/bluetooth/BmsGattClientCallback.kt +++ b/app/src/main/java/de/jnns/bmsmonitor/bluetooth/BmsGattClientCallback.kt @@ -23,11 +23,14 @@ class BmsGattClientCallback( lateinit var readCharacteristic: BluetoothGattCharacteristic lateinit var writeCharacteristic: BluetoothGattCharacteristic - private val uartUuid = UUID.fromString("0000ff00-0000-1000-8000-00805f9b34fb") - private val rxUuid = UUID.fromString("0000ff01-0000-1000-8000-00805f9b34fb") - private val txUuid = UUID.fromString("0000ff02-0000-1000-8000-00805f9b34fb") - - private val bufferSize: Int = 80 +// private val uartUuid = UUID.fromString("0000ff00-0000-1000-8000-00805f9b34fb") +// private val rxUuid = UUID.fromString("0000ff01-0000-1000-8000-00805f9b34fb") +// private val txUuid = UUID.fromString("0000ff02-0000-1000-8000-00805f9b34fb") + private val uartUuid = UUID.fromString("0000A002-0000-1000-8000-00805f9b34fb") + private val rxUuid = UUID.fromString("0000C305-0000-1000-8000-00805f9b34fb") + private val txUuid = UUID.fromString("0000C302-0000-1000-8000-00805f9b34fb") + + private val bufferSize: Int = 256 private var uartBuffer = ByteArray(bufferSize) private var uartBufferPos: Int = 0 private var uartBytesLeft: Int = 0 @@ -63,14 +66,17 @@ class BmsGattClientCallback( for(serv in gatt.services) { i++ - Log.d("BluetoothGatt", "Services" + i.toString() + ":" + serv.uuid.toString()) + Log.d("BMS", "Services" + i.toString() + ":" + serv.uuid.toString()) } val uartService = gatt.getService(uartUuid) + Log.d("BMS", "Got uartService:" + uartService.uuid.toString()) if (uartService != null) { readCharacteristic = uartService.getCharacteristic(rxUuid) writeCharacteristic = uartService.getCharacteristic(txUuid) + Log.d("BMS", "Got readCharacteristic:" + readCharacteristic.uuid.toString()) + Log.d("BMS", "Got writeCharacteristic:" + writeCharacteristic.uuid.toString()) gatt.setCharacteristicNotification(writeCharacteristic, true) gatt.setCharacteristicNotification(readCharacteristic, true) @@ -84,36 +90,43 @@ class BmsGattClientCallback( super.onCharacteristicChanged(gatt, characteristic) // Log.d("BluetoothGatt", "BLE Data (" + characteristic.value.size + "): " + characteristic.value.toHexString()) + Log.d("BMS", "BLE Data (" + characteristic.value.size + "): " + characteristic.value.toHexString()) for (byte: Byte in characteristic.value) { - if (uartBufferPos >= bufferSize) { - isInFrame = false - uartBufferPos = 0 - uartBytesLeft = 0 - } - +// if (uartBufferPos >= bufferSize) { +// isInFrame = false +// uartBufferPos = 0 +// uartBytesLeft = 0 +// } +// +// uartBuffer[uartBufferPos] = byte +// +// if (isInFrame) { +// if (uartBufferPos == 3) { +// uartBytesLeft = byte.toInt() +// } +// +// if (byte.toUByte() == 0x77.toUByte() && uartBytesLeft < 1) { +// isInFrame = false +// onFrameComplete(uartBufferPos) +// uartBufferPos = 0 +// uartBytesLeft = 0 +// } else { +// +// uartBufferPos++ +// uartBytesLeft-- +// } +// } else if (byte.toUByte() == 0xDD.toUByte()) { +// isInFrame = true +// uartBufferPos++ +// } uartBuffer[uartBufferPos] = byte - - if (isInFrame) { - if (uartBufferPos == 3) { - uartBytesLeft = byte.toInt() - } - - if (byte.toUByte() == 0x77.toUByte() && uartBytesLeft < 1) { - isInFrame = false - onFrameComplete(uartBufferPos) - uartBufferPos = 0 - uartBytesLeft = 0 - } else { - - uartBufferPos++ - uartBytesLeft-- - } - } else if (byte.toUByte() == 0xDD.toUByte()) { - isInFrame = true - uartBufferPos++ + uartBufferPos++ + if (uartBufferPos >= bufferSize) { + break } } + onFrameComplete(uartBufferPos) } private fun onFrameComplete(size: Int) { @@ -123,15 +136,18 @@ class BmsGattClientCallback( val frameBytes = uartBuffer.slice(IntRange(0, size)).toByteArray() - // Log.d("BluetoothGatt", "FrameData (" + frameBytes.size + "): " + frameBytes.toHexString()) - - if (frameBytes[1] == 0x3.toByte()) { - val generalInfo = BmsGeneralInfoResponse(frameBytes) - onGeneralInfoCallback(generalInfo) - } else if (frameBytes[1] == 0x4.toByte()) { - val cellInfo = BmsCellInfoResponse(frameBytes) - onCellInfoCallback(cellInfo) - } + Log.d("BMS", "FrameData (" + frameBytes.size + "): " + frameBytes.toHexString()) + +// if (frameBytes[1] == 0x3.toByte()) { +// val generalInfo = BmsGeneralInfoResponse(frameBytes) +// onGeneralInfoCallback(generalInfo) +// } else if (frameBytes[1] == 0x4.toByte()) { +// val cellInfo = BmsCellInfoResponse(frameBytes) +// onCellInfoCallback(cellInfo) +// } +// + val generalInfo = BmsGeneralInfoResponse(frameBytes) + onGeneralInfoCallback(generalInfo) } private fun ByteArray.toHexString() = joinToString("") { "%02x".format(it) } diff --git a/app/src/main/java/de/jnns/bmsmonitor/bms/BmsGeneralInfoResponse.kt b/app/src/main/java/de/jnns/bmsmonitor/bms/BmsGeneralInfoResponse.kt index 5cbba17..4344892 100644 --- a/app/src/main/java/de/jnns/bmsmonitor/bms/BmsGeneralInfoResponse.kt +++ b/app/src/main/java/de/jnns/bmsmonitor/bms/BmsGeneralInfoResponse.kt @@ -17,19 +17,27 @@ class BmsGeneralInfoResponse(bytes: ByteArray) { var temperatureProbeValues: RealmList var cycles: Short = 0 + var temperature: Float = 0.0f + init { command = bytes[1].toInt() status = bytes[2].toInt() dataLength = bytes[3].toInt() - totalVoltage = bytesToShort(bytes[4], bytes[5]) / 100.0f + // Pack Voltage +// totalVoltage = bytesToShort(bytes[4], bytes[5]) / 100.0f + totalVoltage = bytesToShort(bytes[8], bytes[9]) / 100.0f // discharging - totalCurrent = bytesToShort(bytes[6], bytes[7]) / 100.0f +// totalCurrent = bytesToShort(bytes[6], bytes[7]) / 100.0f + totalCurrent = bytesToShort(bytes[10], bytes[11]) / 100.0f residualCapacity = bytesToShort(bytes[8], bytes[9]) / 100.0f nominalCapacity = bytesToShort(bytes[10], bytes[11]) / 100.0f - cycles = bytesToShort(bytes[12], bytes[13]) + + // Pack Cycle +// cycles = bytesToShort(bytes[12], bytes[13]) + cycles = bytesToShort(bytes[6], bytes[7]) // 14 & 15 = production date // 16 & 17 = balance low @@ -37,7 +45,9 @@ class BmsGeneralInfoResponse(bytes: ByteArray) { // 20 & 21 = protection status // 22 = version - capacity = bytes[23].toInt() / 100.0f + // Pack Capacity +// capacity = bytes[23].toInt() / 100.0f + capacity = bytesToShort(bytes[4], bytes[5]) / 10.0f // 24 = MOS status // 25 = number of cells @@ -48,6 +58,9 @@ class BmsGeneralInfoResponse(bytes: ByteArray) { for (i in 0 until temperatureProbeCount) { temperatureProbeValues.add((bytesToShort(bytes[27 + (i * 2)], bytes[27 + (i * 2) + 1]) - 2731) / 10.0f) } + + // System temperature + temperature = bytesToShort(bytes[24], bytes[25]) / 10.0f } private fun bytesToShort(h: Byte, l: Byte, order: ByteOrder = ByteOrder.BIG_ENDIAN): Short { diff --git a/app/src/main/java/de/jnns/bmsmonitor/data/BatteryData.kt b/app/src/main/java/de/jnns/bmsmonitor/data/BatteryData.kt index f7f058c..817bec7 100644 --- a/app/src/main/java/de/jnns/bmsmonitor/data/BatteryData.kt +++ b/app/src/main/java/de/jnns/bmsmonitor/data/BatteryData.kt @@ -14,8 +14,13 @@ open class BatteryData( var temperatureCount: Int = 0, var cellCount: Int = 0, var temperatures: RealmList = RealmList(), - var cellVoltages: RealmList = RealmList() + var cellVoltages: RealmList = RealmList(), + var temperature: Float = 0.0f ) : RealmObject() { + val temp: Float + get() { + return temperature + } val voltage: Float get() { diff --git a/app/src/main/java/de/jnns/bmsmonitor/services/BmsService.kt b/app/src/main/java/de/jnns/bmsmonitor/services/BmsService.kt index 78739b1..209222d 100644 --- a/app/src/main/java/de/jnns/bmsmonitor/services/BmsService.kt +++ b/app/src/main/java/de/jnns/bmsmonitor/services/BmsService.kt @@ -22,7 +22,8 @@ import io.realm.Realm @ExperimentalUnsignedTypes class BmsService : Service() { // BMS commands, they won't change - private val cmdGeneralInfo: ByteArray = ubyteArrayOf(0xDDU, 0xA5U, 0x03U, 0x00U, 0xFFU, 0xFDU, 0x77U).toByteArray() +// private val cmdGeneralInfo: ByteArray = ubyteArrayOf(0xDDU, 0xA5U, 0x03U, 0x00U, 0xFFU, 0xFDU, 0x77U).toByteArray() + private val cmdGeneralInfo: ByteArray = ubyteArrayOf(0x10U, 0x00U, 0x00U, 0x64U).toByteArray() private val cmdCellInfo: ByteArray = ubyteArrayOf(0xDDU, 0xA5U, 0x04U, 0x00U, 0xFFU, 0xFCU, 0x77U).toByteArray() private val cmdBmsVersion: ByteArray = ubyteArrayOf(0xDDU, 0xA5U, 0x05U, 0x00U, 0xFFU, 0xFBU, 0x77U).toByteArray() @@ -35,7 +36,7 @@ class BmsService : Service() { // it is going to toggle "dataModeSwitch" and // request General or Cell data private val dataHandler: Handler = Handler() - private var dataModeSwitch = false +// private var dataModeSwitch = false private var dataPollDelay: Long = 0 // we need both datasets to update the view @@ -64,7 +65,8 @@ class BmsService : Service() { bleMac = PreferenceManager.getDefaultSharedPreferences(this).getString("macAddress", "")!! blePin = PreferenceManager.getDefaultSharedPreferences(this).getString("blePin", "")!! - dataPollDelay = PreferenceManager.getDefaultSharedPreferences(this).getString("refreshInterval", "1000")!!.toLong() / 2 +// dataPollDelay = PreferenceManager.getDefaultSharedPreferences(this).getString("refreshInterval", "1000")!!.toLong() / 2 + dataPollDelay = PreferenceManager.getDefaultSharedPreferences(this).getString("refreshInterval", "1000")!!.toLong() BleManager.i.onUpdateFunctions.add { searchForDeviceAndConnect() @@ -147,6 +149,8 @@ class BmsService : Service() { connectToDevice() } + private fun ByteArray.toHexString() = joinToString("") { "%02x".format(it) } + private fun onConnectionSucceeded() { isConnected = true isConnecting = false @@ -155,13 +159,15 @@ class BmsService : Service() { override fun run() { if (gattClientCallback.isConnected) { if (isInForeground) { - if (dataModeSwitch) { - writeBytes(cmdGeneralInfo) - } else { - writeBytes(cmdCellInfo) - } - - dataModeSwitch = !dataModeSwitch +// if (dataModeSwitch) { +// writeBytes(cmdGeneralInfo) +// } else { +// writeBytes(cmdCellInfo) +// } +// +// dataModeSwitch = !dataModeSwitch + Log.d("BMS", "writeBytes():" + cmdGeneralInfo.toHexString()) + writeBytes(cmdGeneralInfo) dataHandler.postDelayed(this, dataPollDelay) } From 32ebadb252d76c6699fe67772682f629f41ce6fe Mon Sep 17 00:00:00 2001 From: David Lee Date: Wed, 7 Jun 2023 10:24:27 +0800 Subject: [PATCH 03/21] Developing 20230607 --- .../de/jnns/bmsmonitor/BatteryFragment.kt | 3 +- .../bluetooth/BmsGattClientCallback.kt | 96 +++++++++++-------- .../bmsmonitor/bms/BmsGeneralInfoResponse.kt | 21 +++- .../de/jnns/bmsmonitor/data/BatteryData.kt | 12 ++- .../de/jnns/bmsmonitor/services/BmsService.kt | 28 ++++-- 5 files changed, 103 insertions(+), 57 deletions(-) diff --git a/app/src/main/java/de/jnns/bmsmonitor/BatteryFragment.kt b/app/src/main/java/de/jnns/bmsmonitor/BatteryFragment.kt index eda9f35..7aa69ef 100644 --- a/app/src/main/java/de/jnns/bmsmonitor/BatteryFragment.kt +++ b/app/src/main/java/de/jnns/bmsmonitor/BatteryFragment.kt @@ -221,7 +221,8 @@ class BatteryFragment : Fragment() { binding.labelCapacityWh.text = batteryData.watthours.roundToInt().toString() // Row 3 - binding.labelTemperature.text = roundTo(batteryData.avgTemperature, 1).toString() +// binding.labelTemperature.text = roundTo(batteryData.avgTemperature, 1).toString() + binding.labelTemperature.text = roundTo(batteryData.temperature, 1).toString() binding.labelTemperatureMax.text = roundTo(batteryData.maxTemperature, 1).toString() } } diff --git a/app/src/main/java/de/jnns/bmsmonitor/bluetooth/BmsGattClientCallback.kt b/app/src/main/java/de/jnns/bmsmonitor/bluetooth/BmsGattClientCallback.kt index 6169d04..0bbce26 100644 --- a/app/src/main/java/de/jnns/bmsmonitor/bluetooth/BmsGattClientCallback.kt +++ b/app/src/main/java/de/jnns/bmsmonitor/bluetooth/BmsGattClientCallback.kt @@ -23,11 +23,14 @@ class BmsGattClientCallback( lateinit var readCharacteristic: BluetoothGattCharacteristic lateinit var writeCharacteristic: BluetoothGattCharacteristic - private val uartUuid = UUID.fromString("0000ff00-0000-1000-8000-00805f9b34fb") - private val rxUuid = UUID.fromString("0000ff01-0000-1000-8000-00805f9b34fb") - private val txUuid = UUID.fromString("0000ff02-0000-1000-8000-00805f9b34fb") - - private val bufferSize: Int = 80 +// private val uartUuid = UUID.fromString("0000ff00-0000-1000-8000-00805f9b34fb") +// private val rxUuid = UUID.fromString("0000ff01-0000-1000-8000-00805f9b34fb") +// private val txUuid = UUID.fromString("0000ff02-0000-1000-8000-00805f9b34fb") + private val uartUuid = UUID.fromString("0000A002-0000-1000-8000-00805f9b34fb") + private val rxUuid = UUID.fromString("0000C305-0000-1000-8000-00805f9b34fb") + private val txUuid = UUID.fromString("0000C302-0000-1000-8000-00805f9b34fb") + + private val bufferSize: Int = 256 private var uartBuffer = ByteArray(bufferSize) private var uartBufferPos: Int = 0 private var uartBytesLeft: Int = 0 @@ -63,14 +66,17 @@ class BmsGattClientCallback( for(serv in gatt.services) { i++ - Log.d("BluetoothGatt", "Services" + i.toString() + ":" + serv.uuid.toString()) + Log.d("BMS", "Services" + i.toString() + ":" + serv.uuid.toString()) } val uartService = gatt.getService(uartUuid) + Log.d("BMS", "Got uartService:" + uartService.uuid.toString()) if (uartService != null) { readCharacteristic = uartService.getCharacteristic(rxUuid) writeCharacteristic = uartService.getCharacteristic(txUuid) + Log.d("BMS", "Got readCharacteristic:" + readCharacteristic.uuid.toString()) + Log.d("BMS", "Got writeCharacteristic:" + writeCharacteristic.uuid.toString()) gatt.setCharacteristicNotification(writeCharacteristic, true) gatt.setCharacteristicNotification(readCharacteristic, true) @@ -84,36 +90,43 @@ class BmsGattClientCallback( super.onCharacteristicChanged(gatt, characteristic) // Log.d("BluetoothGatt", "BLE Data (" + characteristic.value.size + "): " + characteristic.value.toHexString()) + Log.d("BMS", "BLE Data (" + characteristic.value.size + "): " + characteristic.value.toHexString()) for (byte: Byte in characteristic.value) { - if (uartBufferPos >= bufferSize) { - isInFrame = false - uartBufferPos = 0 - uartBytesLeft = 0 - } - +// if (uartBufferPos >= bufferSize) { +// isInFrame = false +// uartBufferPos = 0 +// uartBytesLeft = 0 +// } +// +// uartBuffer[uartBufferPos] = byte +// +// if (isInFrame) { +// if (uartBufferPos == 3) { +// uartBytesLeft = byte.toInt() +// } +// +// if (byte.toUByte() == 0x77.toUByte() && uartBytesLeft < 1) { +// isInFrame = false +// onFrameComplete(uartBufferPos) +// uartBufferPos = 0 +// uartBytesLeft = 0 +// } else { +// +// uartBufferPos++ +// uartBytesLeft-- +// } +// } else if (byte.toUByte() == 0xDD.toUByte()) { +// isInFrame = true +// uartBufferPos++ +// } uartBuffer[uartBufferPos] = byte - - if (isInFrame) { - if (uartBufferPos == 3) { - uartBytesLeft = byte.toInt() - } - - if (byte.toUByte() == 0x77.toUByte() && uartBytesLeft < 1) { - isInFrame = false - onFrameComplete(uartBufferPos) - uartBufferPos = 0 - uartBytesLeft = 0 - } else { - - uartBufferPos++ - uartBytesLeft-- - } - } else if (byte.toUByte() == 0xDD.toUByte()) { - isInFrame = true - uartBufferPos++ + uartBufferPos++ + if (uartBufferPos >= bufferSize) { + break } } + onFrameComplete(uartBufferPos) } private fun onFrameComplete(size: Int) { @@ -123,15 +136,18 @@ class BmsGattClientCallback( val frameBytes = uartBuffer.slice(IntRange(0, size)).toByteArray() - // Log.d("BluetoothGatt", "FrameData (" + frameBytes.size + "): " + frameBytes.toHexString()) - - if (frameBytes[1] == 0x3.toByte()) { - val generalInfo = BmsGeneralInfoResponse(frameBytes) - onGeneralInfoCallback(generalInfo) - } else if (frameBytes[1] == 0x4.toByte()) { - val cellInfo = BmsCellInfoResponse(frameBytes) - onCellInfoCallback(cellInfo) - } + Log.d("BMS", "FrameData (" + frameBytes.size + "): " + frameBytes.toHexString()) + +// if (frameBytes[1] == 0x3.toByte()) { +// val generalInfo = BmsGeneralInfoResponse(frameBytes) +// onGeneralInfoCallback(generalInfo) +// } else if (frameBytes[1] == 0x4.toByte()) { +// val cellInfo = BmsCellInfoResponse(frameBytes) +// onCellInfoCallback(cellInfo) +// } +// + val generalInfo = BmsGeneralInfoResponse(frameBytes) + onGeneralInfoCallback(generalInfo) } private fun ByteArray.toHexString() = joinToString("") { "%02x".format(it) } diff --git a/app/src/main/java/de/jnns/bmsmonitor/bms/BmsGeneralInfoResponse.kt b/app/src/main/java/de/jnns/bmsmonitor/bms/BmsGeneralInfoResponse.kt index 5cbba17..4344892 100644 --- a/app/src/main/java/de/jnns/bmsmonitor/bms/BmsGeneralInfoResponse.kt +++ b/app/src/main/java/de/jnns/bmsmonitor/bms/BmsGeneralInfoResponse.kt @@ -17,19 +17,27 @@ class BmsGeneralInfoResponse(bytes: ByteArray) { var temperatureProbeValues: RealmList var cycles: Short = 0 + var temperature: Float = 0.0f + init { command = bytes[1].toInt() status = bytes[2].toInt() dataLength = bytes[3].toInt() - totalVoltage = bytesToShort(bytes[4], bytes[5]) / 100.0f + // Pack Voltage +// totalVoltage = bytesToShort(bytes[4], bytes[5]) / 100.0f + totalVoltage = bytesToShort(bytes[8], bytes[9]) / 100.0f // discharging - totalCurrent = bytesToShort(bytes[6], bytes[7]) / 100.0f +// totalCurrent = bytesToShort(bytes[6], bytes[7]) / 100.0f + totalCurrent = bytesToShort(bytes[10], bytes[11]) / 100.0f residualCapacity = bytesToShort(bytes[8], bytes[9]) / 100.0f nominalCapacity = bytesToShort(bytes[10], bytes[11]) / 100.0f - cycles = bytesToShort(bytes[12], bytes[13]) + + // Pack Cycle +// cycles = bytesToShort(bytes[12], bytes[13]) + cycles = bytesToShort(bytes[6], bytes[7]) // 14 & 15 = production date // 16 & 17 = balance low @@ -37,7 +45,9 @@ class BmsGeneralInfoResponse(bytes: ByteArray) { // 20 & 21 = protection status // 22 = version - capacity = bytes[23].toInt() / 100.0f + // Pack Capacity +// capacity = bytes[23].toInt() / 100.0f + capacity = bytesToShort(bytes[4], bytes[5]) / 10.0f // 24 = MOS status // 25 = number of cells @@ -48,6 +58,9 @@ class BmsGeneralInfoResponse(bytes: ByteArray) { for (i in 0 until temperatureProbeCount) { temperatureProbeValues.add((bytesToShort(bytes[27 + (i * 2)], bytes[27 + (i * 2) + 1]) - 2731) / 10.0f) } + + // System temperature + temperature = bytesToShort(bytes[24], bytes[25]) / 10.0f } private fun bytesToShort(h: Byte, l: Byte, order: ByteOrder = ByteOrder.BIG_ENDIAN): Short { diff --git a/app/src/main/java/de/jnns/bmsmonitor/data/BatteryData.kt b/app/src/main/java/de/jnns/bmsmonitor/data/BatteryData.kt index f7f058c..a280415 100644 --- a/app/src/main/java/de/jnns/bmsmonitor/data/BatteryData.kt +++ b/app/src/main/java/de/jnns/bmsmonitor/data/BatteryData.kt @@ -14,12 +14,20 @@ open class BatteryData( var temperatureCount: Int = 0, var cellCount: Int = 0, var temperatures: RealmList = RealmList(), - var cellVoltages: RealmList = RealmList() + var cellVoltages: RealmList = RealmList(), + var temperature: Float = 0.0f ) : RealmObject() { + var vol: Float = 0.0f; + + val temp: Float + get() { + return temperature + } val voltage: Float get() { - return cellVoltages.sum() +// return cellVoltages.sum() + return vol } val avgTemperature: Float diff --git a/app/src/main/java/de/jnns/bmsmonitor/services/BmsService.kt b/app/src/main/java/de/jnns/bmsmonitor/services/BmsService.kt index 78739b1..7f41427 100644 --- a/app/src/main/java/de/jnns/bmsmonitor/services/BmsService.kt +++ b/app/src/main/java/de/jnns/bmsmonitor/services/BmsService.kt @@ -22,7 +22,8 @@ import io.realm.Realm @ExperimentalUnsignedTypes class BmsService : Service() { // BMS commands, they won't change - private val cmdGeneralInfo: ByteArray = ubyteArrayOf(0xDDU, 0xA5U, 0x03U, 0x00U, 0xFFU, 0xFDU, 0x77U).toByteArray() +// private val cmdGeneralInfo: ByteArray = ubyteArrayOf(0xDDU, 0xA5U, 0x03U, 0x00U, 0xFFU, 0xFDU, 0x77U).toByteArray() + private val cmdGeneralInfo: ByteArray = ubyteArrayOf(0x10U, 0x00U, 0x00U, 0x64U).toByteArray() private val cmdCellInfo: ByteArray = ubyteArrayOf(0xDDU, 0xA5U, 0x04U, 0x00U, 0xFFU, 0xFCU, 0x77U).toByteArray() private val cmdBmsVersion: ByteArray = ubyteArrayOf(0xDDU, 0xA5U, 0x05U, 0x00U, 0xFFU, 0xFBU, 0x77U).toByteArray() @@ -35,7 +36,7 @@ class BmsService : Service() { // it is going to toggle "dataModeSwitch" and // request General or Cell data private val dataHandler: Handler = Handler() - private var dataModeSwitch = false +// private var dataModeSwitch = false private var dataPollDelay: Long = 0 // we need both datasets to update the view @@ -64,7 +65,8 @@ class BmsService : Service() { bleMac = PreferenceManager.getDefaultSharedPreferences(this).getString("macAddress", "")!! blePin = PreferenceManager.getDefaultSharedPreferences(this).getString("blePin", "")!! - dataPollDelay = PreferenceManager.getDefaultSharedPreferences(this).getString("refreshInterval", "1000")!!.toLong() / 2 +// dataPollDelay = PreferenceManager.getDefaultSharedPreferences(this).getString("refreshInterval", "1000")!!.toLong() / 2 + dataPollDelay = PreferenceManager.getDefaultSharedPreferences(this).getString("refreshInterval", "1000")!!.toLong() BleManager.i.onUpdateFunctions.add { searchForDeviceAndConnect() @@ -100,6 +102,8 @@ class BmsService : Service() { batteryData.totalCapacity = generalInfo.nominalCapacity batteryData.temperatureCount = generalInfo.temperatureProbeCount batteryData.temperatures = generalInfo.temperatureProbeValues + batteryData.vol = generalInfo.totalVoltage + batteryData.temperature = generalInfo.temperature generalInfoReceived = true sendData() @@ -147,6 +151,8 @@ class BmsService : Service() { connectToDevice() } + private fun ByteArray.toHexString() = joinToString("") { "%02x".format(it) } + private fun onConnectionSucceeded() { isConnected = true isConnecting = false @@ -155,13 +161,15 @@ class BmsService : Service() { override fun run() { if (gattClientCallback.isConnected) { if (isInForeground) { - if (dataModeSwitch) { - writeBytes(cmdGeneralInfo) - } else { - writeBytes(cmdCellInfo) - } - - dataModeSwitch = !dataModeSwitch +// if (dataModeSwitch) { +// writeBytes(cmdGeneralInfo) +// } else { +// writeBytes(cmdCellInfo) +// } +// +// dataModeSwitch = !dataModeSwitch + Log.d("BMS", "writeBytes():" + cmdGeneralInfo.toHexString()) + writeBytes(cmdGeneralInfo) dataHandler.postDelayed(this, dataPollDelay) } From 228dff65ac05cefefd588d8161a3acfc0396dcad Mon Sep 17 00:00:00 2001 From: David Lee Date: Thu, 8 Jun 2023 16:21:59 +0800 Subject: [PATCH 04/21] Developing 202306081621 --- .../de/jnns/bmsmonitor/BatteryFragment.kt | 674 +++++++++--------- .../de/jnns/bmsmonitor/BatteryFragment1.kt | 362 ++++++++++ .../java/de/jnns/bmsmonitor/MainActivity.kt | 8 +- .../bmsmonitor/bms/BmsGeneralInfoResponse.kt | 91 +-- .../de/jnns/bmsmonitor/data/BatteryData.kt | 21 +- .../de/jnns/bmsmonitor/services/BmsService.kt | 18 +- app/src/main/res/layout/activity_main.xml | 2 +- app/src/main/res/layout/fragment_battery.xml | 32 +- app/src/main/res/layout/fragment_bms.xml | 356 +++++++++ app/src/main/res/navigation/nav_graph.xml | 15 +- app/src/main/res/values/strings.xml | 7 + 11 files changed, 1165 insertions(+), 421 deletions(-) create mode 100644 app/src/main/java/de/jnns/bmsmonitor/BatteryFragment1.kt create mode 100644 app/src/main/res/layout/fragment_bms.xml diff --git a/app/src/main/java/de/jnns/bmsmonitor/BatteryFragment.kt b/app/src/main/java/de/jnns/bmsmonitor/BatteryFragment.kt index 7aa69ef..b503644 100644 --- a/app/src/main/java/de/jnns/bmsmonitor/BatteryFragment.kt +++ b/app/src/main/java/de/jnns/bmsmonitor/BatteryFragment.kt @@ -1,339 +1,339 @@ package de.jnns.bmsmonitor -import android.content.BroadcastReceiver -import android.content.Context -import android.content.Intent -import android.content.IntentFilter -import android.content.res.ColorStateList -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import android.widget.ProgressBar -import android.widget.TextView -import androidx.core.content.ContextCompat -import androidx.fragment.app.Fragment -import androidx.localbroadcastmanager.content.LocalBroadcastManager -import androidx.navigation.findNavController -import androidx.preference.PreferenceManager -import com.github.anastr.speedviewlib.components.Section -import com.github.mikephil.charting.data.BarData -import com.github.mikephil.charting.data.BarDataSet -import com.github.mikephil.charting.data.BarEntry -import com.github.mikephil.charting.formatter.DefaultValueFormatter -import com.google.gson.Gson -import de.jnns.bmsmonitor.data.BatteryData -import de.jnns.bmsmonitor.databinding.FragmentBatteryBinding -import java.util.* -import kotlin.math.pow -import kotlin.math.roundToInt - - -@ExperimentalUnsignedTypes -class BatteryFragment : Fragment() { - private var _binding: FragmentBatteryBinding? = null - private val binding get() = _binding!! - - // no need to refresh data in the background - private var isInForeground = false - - // time calculation smoothing - // default 5 min - private var smoothCount = 60 * 5 - private var smoothIndex = 0 - private var wattValues = FloatArray(smoothCount) - - private var minCellVoltage = 0 - private var maxCellVoltage = 0 - - private val mMessageReceiver: BroadcastReceiver = object : BroadcastReceiver() { - override fun onReceive(context: Context, intent: Intent) { - try { - val msg: String = intent.getStringExtra("batteryData")!! - - binding.labelStatus.text = String.format(resources.getString(R.string.connectedToBms), intent.getStringExtra("deviceName")) - - if (msg.isNotEmpty()) { - updateUi(Gson().fromJson(msg, BatteryData::class.java)) - } - } catch (ex: Exception) { - // ignored - } - } - } - - override fun onCreateView( - inflater: LayoutInflater, - container: ViewGroup?, - savedInstanceState: Bundle? - ): View { - LocalBroadcastManager.getInstance(requireContext()).registerReceiver(mMessageReceiver, IntentFilter("bmsDataIntent")) - _binding = FragmentBatteryBinding.inflate(inflater, container, false) - return binding.root - } - - override fun onDestroy() { - super.onDestroy() - _binding = null - LocalBroadcastManager.getInstance(requireContext()).unregisterReceiver(mMessageReceiver) - } - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - - binding.speedViewSpeed.clearSections() - binding.speedViewSpeed.addSections( - Section(0.00000000f, 0.11111111f, ContextCompat.getColor(requireContext(), R.color.batteryChargeHigh), 72.0f), - Section(0.11111111f, 0.22222222f, ContextCompat.getColor(requireContext(), R.color.batteryChargeMedium), 72.0f), - Section(0.22222222f, 0.33333333f, ContextCompat.getColor(requireContext(), R.color.batteryChargeLow), 72.0f), - Section(0.33333333f, 0.44444444f, ContextCompat.getColor(requireContext(), R.color.batteryDischargeLow), 72.0f), - Section(0.44444444f, 0.66666666f, ContextCompat.getColor(requireContext(), R.color.batteryDischargeMedium), 72.0f), - Section(0.66666666f, 0.88888888f, ContextCompat.getColor(requireContext(), R.color.batteryDischargeHigh), 72.0f), - Section(0.88888888f, 1.00000000f, ContextCompat.getColor(requireContext(), R.color.batteryDischargeHighest), 72.0f) - ) - - binding.speedViewSpeed.minSpeed = PreferenceManager.getDefaultSharedPreferences(requireContext()).getString("minPower", "-500")!!.toFloat() - binding.speedViewSpeed.maxSpeed = PreferenceManager.getDefaultSharedPreferences(requireContext()).getString("maxPower", "1000")!!.toFloat() - - minCellVoltage = PreferenceManager.getDefaultSharedPreferences(requireContext()).getString("minCellVoltage", "2500")!!.toInt() - maxCellVoltage = PreferenceManager.getDefaultSharedPreferences(requireContext()).getString("maxCellVoltage", "4200")!!.toInt() - - binding.barchartCells.setNoDataTextColor(requireActivity().getColor(R.color.white)) - binding.barchartCells.setNoDataText("...") - - binding.barchartCells.setPinchZoom(false) - binding.barchartCells.setTouchEnabled(false) - binding.barchartCells.isClickable = false - binding.barchartCells.isDoubleTapToZoomEnabled = false - - binding.barchartCells.setDrawBorders(false) - binding.barchartCells.setDrawValueAboveBar(true) - binding.barchartCells.setDrawBorders(false) - binding.barchartCells.setDrawGridBackground(false) - - binding.barchartCells.description.isEnabled = false - binding.barchartCells.legend.isEnabled = false - - binding.barchartCells.axisLeft.setDrawGridLines(false) - binding.barchartCells.axisLeft.setDrawLabels(false) - binding.barchartCells.axisLeft.setDrawAxisLine(false) - - binding.barchartCells.xAxis.setDrawGridLines(false) - binding.barchartCells.xAxis.setDrawLabels(false) - binding.barchartCells.xAxis.setDrawAxisLine(false) - - binding.barchartCells.axisRight.isEnabled = false - } - - override fun onResume() { - super.onResume() - - isInForeground = true - - binding.speedViewSpeed.speedTo(0.0f, 0) - binding.labelStatus.text = getString(R.string.waitForBms) - } - - override fun onPause() { - super.onPause() - isInForeground = false - } - - private fun updateUi(batteryData: BatteryData) { - if (!isInForeground) { - return - } - - requireActivity().runOnUiThread { - // Power Gauge - val powerUsage = batteryData.power * -1.0f - - binding.speedViewSpeed.speedTo(powerUsage, 1000) - - if (powerUsage < 0.0f) { - binding.labelPower.text = "+${powerUsage.roundToInt() * -1}" - } else { - binding.labelPower.text = powerUsage.roundToInt().toString() - } - - // Remaining Time - wattValues[smoothIndex] = batteryData.power - ++smoothIndex - - if (smoothIndex == smoothCount) { - smoothIndex = 0 - } - - var averagePower = wattValues.filter { x -> x != 0.0f }.average().toFloat() - - if (averagePower < 0.0f) { - averagePower *= -1 - } - - val wattSeconds: Float = when { - batteryData.power < 0.0f -> { - (batteryData.watthours / averagePower) * 3600 - } - batteryData.power > 0.0f -> { - ((batteryData.totalWatthours - batteryData.watthours) / averagePower) * 3600 - } - else -> { - 0.0f - } - } - - if (batteryData.power == 0.0f) { - wattValues.fill(0.0f) - } - - val remainingMinutes = (wattSeconds % 3600) / 60 - val remainingHours = (wattSeconds - remainingMinutes) / 3600 - - binding.labelTime.text = String.format("%02d:%02d", remainingHours.roundToInt(), remainingMinutes.roundToInt()) - - // Cell Bar-Diagram - val cellBars = ArrayList() - - for ((i, cell) in batteryData.cellVoltages.withIndex()) { - cellBars.add(BarEntry(i.toFloat(), cell)) - } - - val barDataSetVoltage = BarDataSet(cellBars, "Cell Voltages") - barDataSetVoltage.valueTextColor = requireActivity().getColor(R.color.white) - barDataSetVoltage.valueTextSize = 12.0f - barDataSetVoltage.valueFormatter = DefaultValueFormatter(2) - barDataSetVoltage.setColors(requireActivity().getColor(R.color.primary)) - - val barData = BarData(barDataSetVoltage) - binding.barchartCells.data = barData - binding.barchartCells.invalidate() - - // Row 1 - val totalPercentage = ((batteryData.percentage) * 1000.0f).roundToInt() / 10.0f - - binding.labelVoltage.text = roundTo(batteryData.voltage, 1).toString() - binding.labelPercentage.text = totalPercentage.toString() - - uiBatteryCapacityBar(totalPercentage) - - // Row 2 - binding.labelCurrent.text = String.format(Locale.US, "%.1f", roundTo(batteryData.current, 1)) - binding.labelCapacityWh.text = batteryData.watthours.roundToInt().toString() - - // Row 3 -// binding.labelTemperature.text = roundTo(batteryData.avgTemperature, 1).toString() - binding.labelTemperature.text = roundTo(batteryData.temperature, 1).toString() - binding.labelTemperatureMax.text = roundTo(batteryData.maxTemperature, 1).toString() - } - } - - private fun uiColorCapacityProgressBar(value: Float, uiElement: ProgressBar) { - // 80% skipped here because battery only gets charged to ~80 percent - when { - value < 20 -> { - uiElement.progressTintList = ColorStateList.valueOf(ContextCompat.getColor(requireContext(), R.color.percentUnder20)) - } - value < 40 -> { - uiElement.progressTintList = ColorStateList.valueOf(ContextCompat.getColor(requireContext(), R.color.percentUnder40)) - } - value < 70 -> { - uiElement.progressTintList = ColorStateList.valueOf(ContextCompat.getColor(requireContext(), R.color.percentUnder70)) - } - else -> { - uiElement.progressTintList = ColorStateList.valueOf(ContextCompat.getColor(requireContext(), R.color.percentUnder100)) - } - } - } - - private fun uiBatteryCapacityBar(value: Float) { - when { - value == 0.0f -> { - binding.progressBarBattery1.progress = 0 - binding.progressBarBattery2.progress = 0 - binding.progressBarBattery3.progress = 0 - binding.progressBarBattery4.progress = 0 - binding.progressBarBattery5.progress = 0 - } - value < 20.0f -> { - val newValue = value * 5.0f - uiColorCapacityProgressBar(newValue, binding.progressBarBattery1) - binding.progressBarBattery1.progress = newValue.roundToInt() - binding.progressBarBattery2.progress = 0 - binding.progressBarBattery3.progress = 0 - binding.progressBarBattery4.progress = 0 - binding.progressBarBattery5.progress = 0 - } - value < 40.0f -> { - binding.progressBarBattery1.progressTintList = ColorStateList.valueOf(ContextCompat.getColor(requireContext(), R.color.percentUnder100)) - binding.progressBarBattery1.progress = 100 - val newValue = (value - 20.0f) * 5.0f - uiColorCapacityProgressBar(newValue, binding.progressBarBattery2) - binding.progressBarBattery2.progress = newValue.roundToInt() - binding.progressBarBattery3.progress = 0 - binding.progressBarBattery4.progress = 0 - binding.progressBarBattery5.progress = 0 - } - value < 60.0f -> { - binding.progressBarBattery1.progressTintList = ColorStateList.valueOf(ContextCompat.getColor(requireContext(), R.color.percentUnder100)) - binding.progressBarBattery1.progress = 100 - binding.progressBarBattery2.progressTintList = ColorStateList.valueOf(ContextCompat.getColor(requireContext(), R.color.percentUnder100)) - binding.progressBarBattery2.progress = 100 - val newValue = (value - 40.0f) * 5.0f - uiColorCapacityProgressBar(newValue, binding.progressBarBattery3) - binding.progressBarBattery3.progress = newValue.roundToInt() - binding.progressBarBattery4.progress = 0 - binding.progressBarBattery5.progress = 0 - } - value < 80.0f -> { - binding.progressBarBattery1.progressTintList = ColorStateList.valueOf(ContextCompat.getColor(requireContext(), R.color.percentUnder100)) - binding.progressBarBattery1.progress = 100 - binding.progressBarBattery2.progressTintList = ColorStateList.valueOf(ContextCompat.getColor(requireContext(), R.color.percentUnder100)) - binding.progressBarBattery2.progress = 100 - binding.progressBarBattery3.progressTintList = ColorStateList.valueOf(ContextCompat.getColor(requireContext(), R.color.percentUnder100)) - binding.progressBarBattery3.progress = 100 - val newValue = (value - 60.0f) * 5.0f - uiColorCapacityProgressBar(newValue, binding.progressBarBattery4) - binding.progressBarBattery4.progress = newValue.roundToInt() - binding.progressBarBattery5.progress = 0 - } - value < 100.0f -> { - binding.progressBarBattery1.progressTintList = ColorStateList.valueOf(ContextCompat.getColor(requireContext(), R.color.percentUnder100)) - binding.progressBarBattery1.progress = 100 - binding.progressBarBattery2.progressTintList = ColorStateList.valueOf(ContextCompat.getColor(requireContext(), R.color.percentUnder100)) - binding.progressBarBattery2.progress = 100 - binding.progressBarBattery3.progressTintList = ColorStateList.valueOf(ContextCompat.getColor(requireContext(), R.color.percentUnder100)) - binding.progressBarBattery3.progress = 100 - binding.progressBarBattery4.progressTintList = ColorStateList.valueOf(ContextCompat.getColor(requireContext(), R.color.percentUnder100)) - binding.progressBarBattery4.progress = 100 - val newValue = (value - 80.0f) * 5.0f - uiColorCapacityProgressBar(newValue, binding.progressBarBattery5) - binding.progressBarBattery5.progress = newValue.roundToInt() - } - else -> { - binding.progressBarBattery1.progressTintList = ColorStateList.valueOf(ContextCompat.getColor(requireContext(), R.color.percentUnder100)) - binding.progressBarBattery1.progress = 100 - binding.progressBarBattery2.progressTintList = ColorStateList.valueOf(ContextCompat.getColor(requireContext(), R.color.percentUnder100)) - binding.progressBarBattery2.progress = 100 - binding.progressBarBattery3.progressTintList = ColorStateList.valueOf(ContextCompat.getColor(requireContext(), R.color.percentUnder100)) - binding.progressBarBattery3.progress = 100 - binding.progressBarBattery4.progressTintList = ColorStateList.valueOf(ContextCompat.getColor(requireContext(), R.color.percentUnder100)) - binding.progressBarBattery4.progress = 100 - binding.progressBarBattery5.progressTintList = ColorStateList.valueOf(ContextCompat.getColor(requireContext(), R.color.percentUnder100)) - binding.progressBarBattery5.progress = 100 - } - } - } - - private fun roundTo(value: Float, decimals: Int): Float { - if (value.isNaN() || value.isInfinite()) { - return 0.0f - } - - if (decimals == 0) { - return value.roundToInt().toFloat() - } - - val factor = 10.0.pow(decimals.toDouble()).toFloat() - return (value * factor).roundToInt() / factor - } -} \ No newline at end of file +//import android.content.BroadcastReceiver +//import android.content.Context +//import android.content.Intent +//import android.content.IntentFilter +//import android.content.res.ColorStateList +//import android.os.Bundle +//import android.view.LayoutInflater +//import android.view.View +//import android.view.ViewGroup +//import android.widget.ProgressBar +//import android.widget.TextView +//import androidx.core.content.ContextCompat +//import androidx.fragment.app.Fragment +//import androidx.localbroadcastmanager.content.LocalBroadcastManager +//import androidx.navigation.findNavController +//import androidx.preference.PreferenceManager +//import com.github.anastr.speedviewlib.components.Section +//import com.github.mikephil.charting.data.BarData +//import com.github.mikephil.charting.data.BarDataSet +//import com.github.mikephil.charting.data.BarEntry +//import com.github.mikephil.charting.formatter.DefaultValueFormatter +//import com.google.gson.Gson +//import de.jnns.bmsmonitor.data.BatteryData +//import de.jnns.bmsmonitor.databinding.FragmentBatteryBinding +//import java.util.* +//import kotlin.math.pow +//import kotlin.math.roundToInt +// +// +//@ExperimentalUnsignedTypes +//class BatteryFragment : Fragment() { +// private var _binding: FragmentBatteryBinding? = null +// private val binding get() = _binding!! +// +// // no need to refresh data in the background +// private var isInForeground = false +// +// // time calculation smoothing +// // default 5 min +// private var smoothCount = 60 * 5 +// private var smoothIndex = 0 +// private var wattValues = FloatArray(smoothCount) +// +// private var minCellVoltage = 0 +// private var maxCellVoltage = 0 +// +// private val mMessageReceiver: BroadcastReceiver = object : BroadcastReceiver() { +// override fun onReceive(context: Context, intent: Intent) { +// try { +// val msg: String = intent.getStringExtra("batteryData")!! +// +// binding.labelStatus.text = String.format(resources.getString(R.string.connectedToBms), intent.getStringExtra("deviceName")) +// +// if (msg.isNotEmpty()) { +// updateUi(Gson().fromJson(msg, BatteryData::class.java)) +// } +// } catch (ex: Exception) { +// // ignored +// } +// } +// } +// +// override fun onCreateView( +// inflater: LayoutInflater, +// container: ViewGroup?, +// savedInstanceState: Bundle? +// ): View { +// LocalBroadcastManager.getInstance(requireContext()).registerReceiver(mMessageReceiver, IntentFilter("bmsDataIntent")) +// _binding = FragmentBatteryBinding.inflate(inflater, container, false) +// return binding.root +// } +// +// override fun onDestroy() { +// super.onDestroy() +// _binding = null +// LocalBroadcastManager.getInstance(requireContext()).unregisterReceiver(mMessageReceiver) +// } +// +// override fun onViewCreated(view: View, savedInstanceState: Bundle?) { +// super.onViewCreated(view, savedInstanceState) +// +// binding.speedViewSpeed.clearSections() +// binding.speedViewSpeed.addSections( +// Section(0.00000000f, 0.11111111f, ContextCompat.getColor(requireContext(), R.color.batteryChargeHigh), 72.0f), +// Section(0.11111111f, 0.22222222f, ContextCompat.getColor(requireContext(), R.color.batteryChargeMedium), 72.0f), +// Section(0.22222222f, 0.33333333f, ContextCompat.getColor(requireContext(), R.color.batteryChargeLow), 72.0f), +// Section(0.33333333f, 0.44444444f, ContextCompat.getColor(requireContext(), R.color.batteryDischargeLow), 72.0f), +// Section(0.44444444f, 0.66666666f, ContextCompat.getColor(requireContext(), R.color.batteryDischargeMedium), 72.0f), +// Section(0.66666666f, 0.88888888f, ContextCompat.getColor(requireContext(), R.color.batteryDischargeHigh), 72.0f), +// Section(0.88888888f, 1.00000000f, ContextCompat.getColor(requireContext(), R.color.batteryDischargeHighest), 72.0f) +// ) +// +// binding.speedViewSpeed.minSpeed = PreferenceManager.getDefaultSharedPreferences(requireContext()).getString("minPower", "-500")!!.toFloat() +// binding.speedViewSpeed.maxSpeed = PreferenceManager.getDefaultSharedPreferences(requireContext()).getString("maxPower", "1000")!!.toFloat() +// +// minCellVoltage = PreferenceManager.getDefaultSharedPreferences(requireContext()).getString("minCellVoltage", "2500")!!.toInt() +// maxCellVoltage = PreferenceManager.getDefaultSharedPreferences(requireContext()).getString("maxCellVoltage", "4200")!!.toInt() +// +// binding.barchartCells.setNoDataTextColor(requireActivity().getColor(R.color.white)) +// binding.barchartCells.setNoDataText("...") +// +// binding.barchartCells.setPinchZoom(false) +// binding.barchartCells.setTouchEnabled(false) +// binding.barchartCells.isClickable = false +// binding.barchartCells.isDoubleTapToZoomEnabled = false +// +// binding.barchartCells.setDrawBorders(false) +// binding.barchartCells.setDrawValueAboveBar(true) +// binding.barchartCells.setDrawBorders(false) +// binding.barchartCells.setDrawGridBackground(false) +// +// binding.barchartCells.description.isEnabled = false +// binding.barchartCells.legend.isEnabled = false +// +// binding.barchartCells.axisLeft.setDrawGridLines(false) +// binding.barchartCells.axisLeft.setDrawLabels(false) +// binding.barchartCells.axisLeft.setDrawAxisLine(false) +// +// binding.barchartCells.xAxis.setDrawGridLines(false) +// binding.barchartCells.xAxis.setDrawLabels(false) +// binding.barchartCells.xAxis.setDrawAxisLine(false) +// +// binding.barchartCells.axisRight.isEnabled = false +// } +// +// override fun onResume() { +// super.onResume() +// +// isInForeground = true +// +// binding.speedViewSpeed.speedTo(0.0f, 0) +// binding.labelStatus.text = getString(R.string.waitForBms) +// } +// +// override fun onPause() { +// super.onPause() +// isInForeground = false +// } +// +// private fun updateUi(batteryData: BatteryData) { +// if (!isInForeground) { +// return +// } +// +// requireActivity().runOnUiThread { +// // Power Gauge +// val powerUsage = batteryData.power * -1.0f +// +// binding.speedViewSpeed.speedTo(powerUsage, 1000) +// +// if (powerUsage < 0.0f) { +// binding.labelPower.text = "+${powerUsage.roundToInt() * -1}" +// } else { +// binding.labelPower.text = powerUsage.roundToInt().toString() +// } +// +// // Remaining Time +// wattValues[smoothIndex] = batteryData.power +// ++smoothIndex +// +// if (smoothIndex == smoothCount) { +// smoothIndex = 0 +// } +// +// var averagePower = wattValues.filter { x -> x != 0.0f }.average().toFloat() +// +// if (averagePower < 0.0f) { +// averagePower *= -1 +// } +// +// val wattSeconds: Float = when { +// batteryData.power < 0.0f -> { +// (batteryData.watthours / averagePower) * 3600 +// } +// batteryData.power > 0.0f -> { +// ((batteryData.totalWatthours - batteryData.watthours) / averagePower) * 3600 +// } +// else -> { +// 0.0f +// } +// } +// +// if (batteryData.power == 0.0f) { +// wattValues.fill(0.0f) +// } +// +// val remainingMinutes = (wattSeconds % 3600) / 60 +// val remainingHours = (wattSeconds - remainingMinutes) / 3600 +// +// binding.labelTime.text = String.format("%02d:%02d", remainingHours.roundToInt(), remainingMinutes.roundToInt()) +// +// // Cell Bar-Diagram +// val cellBars = ArrayList() +// +// for ((i, cell) in batteryData.cellVoltages.withIndex()) { +// cellBars.add(BarEntry(i.toFloat(), cell)) +// } +// +// val barDataSetVoltage = BarDataSet(cellBars, "Cell Voltages") +// barDataSetVoltage.valueTextColor = requireActivity().getColor(R.color.white) +// barDataSetVoltage.valueTextSize = 12.0f +// barDataSetVoltage.valueFormatter = DefaultValueFormatter(2) +// barDataSetVoltage.setColors(requireActivity().getColor(R.color.primary)) +// +// val barData = BarData(barDataSetVoltage) +// binding.barchartCells.data = barData +// binding.barchartCells.invalidate() +// +// // Row 1 +// val totalPercentage = ((batteryData.percentage) * 1000.0f).roundToInt() / 10.0f +// +// binding.labelVoltage.text = roundTo(batteryData.voltage, 1).toString() +// binding.labelPercentage.text = totalPercentage.toString() +// +// uiBatteryCapacityBar(totalPercentage) +// +// // Row 2 +// binding.labelCurrent.text = String.format(Locale.US, "%.1f", roundTo(batteryData.current, 1)) +// binding.labelCapacityWh.text = batteryData.watthours.roundToInt().toString() +// +// // Row 3 +//// binding.labelTemperature.text = roundTo(batteryData.avgTemperature, 1).toString() +// binding.labelTemperature.text = roundTo(batteryData.temperature, 1).toString() +// binding.labelTemperatureMax.text = roundTo(batteryData.maxTemperature, 1).toString() +// } +// } +// +// private fun uiColorCapacityProgressBar(value: Float, uiElement: ProgressBar) { +// // 80% skipped here because battery only gets charged to ~80 percent +// when { +// value < 20 -> { +// uiElement.progressTintList = ColorStateList.valueOf(ContextCompat.getColor(requireContext(), R.color.percentUnder20)) +// } +// value < 40 -> { +// uiElement.progressTintList = ColorStateList.valueOf(ContextCompat.getColor(requireContext(), R.color.percentUnder40)) +// } +// value < 70 -> { +// uiElement.progressTintList = ColorStateList.valueOf(ContextCompat.getColor(requireContext(), R.color.percentUnder70)) +// } +// else -> { +// uiElement.progressTintList = ColorStateList.valueOf(ContextCompat.getColor(requireContext(), R.color.percentUnder100)) +// } +// } +// } +// +// private fun uiBatteryCapacityBar(value: Float) { +// when { +// value == 0.0f -> { +// binding.progressBarBattery1.progress = 0 +// binding.progressBarBattery2.progress = 0 +// binding.progressBarBattery3.progress = 0 +// binding.progressBarBattery4.progress = 0 +// binding.progressBarBattery5.progress = 0 +// } +// value < 20.0f -> { +// val newValue = value * 5.0f +// uiColorCapacityProgressBar(newValue, binding.progressBarBattery1) +// binding.progressBarBattery1.progress = newValue.roundToInt() +// binding.progressBarBattery2.progress = 0 +// binding.progressBarBattery3.progress = 0 +// binding.progressBarBattery4.progress = 0 +// binding.progressBarBattery5.progress = 0 +// } +// value < 40.0f -> { +// binding.progressBarBattery1.progressTintList = ColorStateList.valueOf(ContextCompat.getColor(requireContext(), R.color.percentUnder100)) +// binding.progressBarBattery1.progress = 100 +// val newValue = (value - 20.0f) * 5.0f +// uiColorCapacityProgressBar(newValue, binding.progressBarBattery2) +// binding.progressBarBattery2.progress = newValue.roundToInt() +// binding.progressBarBattery3.progress = 0 +// binding.progressBarBattery4.progress = 0 +// binding.progressBarBattery5.progress = 0 +// } +// value < 60.0f -> { +// binding.progressBarBattery1.progressTintList = ColorStateList.valueOf(ContextCompat.getColor(requireContext(), R.color.percentUnder100)) +// binding.progressBarBattery1.progress = 100 +// binding.progressBarBattery2.progressTintList = ColorStateList.valueOf(ContextCompat.getColor(requireContext(), R.color.percentUnder100)) +// binding.progressBarBattery2.progress = 100 +// val newValue = (value - 40.0f) * 5.0f +// uiColorCapacityProgressBar(newValue, binding.progressBarBattery3) +// binding.progressBarBattery3.progress = newValue.roundToInt() +// binding.progressBarBattery4.progress = 0 +// binding.progressBarBattery5.progress = 0 +// } +// value < 80.0f -> { +// binding.progressBarBattery1.progressTintList = ColorStateList.valueOf(ContextCompat.getColor(requireContext(), R.color.percentUnder100)) +// binding.progressBarBattery1.progress = 100 +// binding.progressBarBattery2.progressTintList = ColorStateList.valueOf(ContextCompat.getColor(requireContext(), R.color.percentUnder100)) +// binding.progressBarBattery2.progress = 100 +// binding.progressBarBattery3.progressTintList = ColorStateList.valueOf(ContextCompat.getColor(requireContext(), R.color.percentUnder100)) +// binding.progressBarBattery3.progress = 100 +// val newValue = (value - 60.0f) * 5.0f +// uiColorCapacityProgressBar(newValue, binding.progressBarBattery4) +// binding.progressBarBattery4.progress = newValue.roundToInt() +// binding.progressBarBattery5.progress = 0 +// } +// value < 100.0f -> { +// binding.progressBarBattery1.progressTintList = ColorStateList.valueOf(ContextCompat.getColor(requireContext(), R.color.percentUnder100)) +// binding.progressBarBattery1.progress = 100 +// binding.progressBarBattery2.progressTintList = ColorStateList.valueOf(ContextCompat.getColor(requireContext(), R.color.percentUnder100)) +// binding.progressBarBattery2.progress = 100 +// binding.progressBarBattery3.progressTintList = ColorStateList.valueOf(ContextCompat.getColor(requireContext(), R.color.percentUnder100)) +// binding.progressBarBattery3.progress = 100 +// binding.progressBarBattery4.progressTintList = ColorStateList.valueOf(ContextCompat.getColor(requireContext(), R.color.percentUnder100)) +// binding.progressBarBattery4.progress = 100 +// val newValue = (value - 80.0f) * 5.0f +// uiColorCapacityProgressBar(newValue, binding.progressBarBattery5) +// binding.progressBarBattery5.progress = newValue.roundToInt() +// } +// else -> { +// binding.progressBarBattery1.progressTintList = ColorStateList.valueOf(ContextCompat.getColor(requireContext(), R.color.percentUnder100)) +// binding.progressBarBattery1.progress = 100 +// binding.progressBarBattery2.progressTintList = ColorStateList.valueOf(ContextCompat.getColor(requireContext(), R.color.percentUnder100)) +// binding.progressBarBattery2.progress = 100 +// binding.progressBarBattery3.progressTintList = ColorStateList.valueOf(ContextCompat.getColor(requireContext(), R.color.percentUnder100)) +// binding.progressBarBattery3.progress = 100 +// binding.progressBarBattery4.progressTintList = ColorStateList.valueOf(ContextCompat.getColor(requireContext(), R.color.percentUnder100)) +// binding.progressBarBattery4.progress = 100 +// binding.progressBarBattery5.progressTintList = ColorStateList.valueOf(ContextCompat.getColor(requireContext(), R.color.percentUnder100)) +// binding.progressBarBattery5.progress = 100 +// } +// } +// } +// +// private fun roundTo(value: Float, decimals: Int): Float { +// if (value.isNaN() || value.isInfinite()) { +// return 0.0f +// } +// +// if (decimals == 0) { +// return value.roundToInt().toFloat() +// } +// +// val factor = 10.0.pow(decimals.toDouble()).toFloat() +// return (value * factor).roundToInt() / factor +// } +//} \ No newline at end of file diff --git a/app/src/main/java/de/jnns/bmsmonitor/BatteryFragment1.kt b/app/src/main/java/de/jnns/bmsmonitor/BatteryFragment1.kt new file mode 100644 index 0000000..1c603e1 --- /dev/null +++ b/app/src/main/java/de/jnns/bmsmonitor/BatteryFragment1.kt @@ -0,0 +1,362 @@ +package de.jnns.bmsmonitor + +import android.content.BroadcastReceiver +import android.content.Context +import android.content.Intent +import android.content.IntentFilter +import android.content.res.ColorStateList +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.ProgressBar +import android.widget.TextView +import androidx.core.content.ContextCompat +import androidx.fragment.app.Fragment +import androidx.localbroadcastmanager.content.LocalBroadcastManager +import androidx.navigation.findNavController +import androidx.preference.PreferenceManager +import com.github.anastr.speedviewlib.components.Section +import com.github.mikephil.charting.data.BarData +import com.github.mikephil.charting.data.BarDataSet +import com.github.mikephil.charting.data.BarEntry +import com.github.mikephil.charting.formatter.DefaultValueFormatter +import com.google.gson.Gson +import de.jnns.bmsmonitor.R +import de.jnns.bmsmonitor.data.BatteryData +//import de.jnns.bmsmonitor.databinding.FragmentBatteryBinding +import de.jnns.bmsmonitor.databinding.FragmentBmsBinding +import java.util.* +import kotlin.math.pow +import kotlin.math.round +import kotlin.math.roundToInt + + +@ExperimentalUnsignedTypes +class BatteryFragment1 : Fragment() { +// private var _binding: FragmentBatteryBinding? = null + private var _binding: FragmentBmsBinding? = null + private val binding get() = _binding!! + + // no need to refresh data in the background + private var isInForeground = false + + // time calculation smoothing + // default 5 min + private var smoothCount = 60 * 5 + private var smoothIndex = 0 + private var wattValues = FloatArray(smoothCount) + + private var minCellVoltage = 0 + private var maxCellVoltage = 0 + + private val mMessageReceiver: BroadcastReceiver = object : BroadcastReceiver() { + override fun onReceive(context: Context, intent: Intent) { + try { + val msg: String = intent.getStringExtra("batteryData")!! + + // David Lee +// binding.labelStatus.text = String.format(resources.getString(R.string.connectedToBms), intent.getStringExtra("deviceName")) + + if (msg.isNotEmpty()) { + updateUi(Gson().fromJson(msg, BatteryData::class.java)) + } + } catch (ex: Exception) { + // ignored + } + } + } + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + LocalBroadcastManager.getInstance(requireContext()).registerReceiver(mMessageReceiver, IntentFilter("bmsDataIntent")) +// _binding = FragmentBatteryBinding.inflate(inflater, container, false) + _binding = FragmentBmsBinding.inflate(inflater, container, false) + return binding.root + } + + override fun onDestroy() { + super.onDestroy() + _binding = null + LocalBroadcastManager.getInstance(requireContext()).unregisterReceiver(mMessageReceiver) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + +// binding.speedViewSpeed.clearSections() +// binding.speedViewSpeed.addSections( +// Section(0.00000000f, 0.11111111f, ContextCompat.getColor(requireContext(), R.color.batteryChargeHigh), 72.0f), +// Section(0.11111111f, 0.22222222f, ContextCompat.getColor(requireContext(), R.color.batteryChargeMedium), 72.0f), +// Section(0.22222222f, 0.33333333f, ContextCompat.getColor(requireContext(), R.color.batteryChargeLow), 72.0f), +// Section(0.33333333f, 0.44444444f, ContextCompat.getColor(requireContext(), R.color.batteryDischargeLow), 72.0f), +// Section(0.44444444f, 0.66666666f, ContextCompat.getColor(requireContext(), R.color.batteryDischargeMedium), 72.0f), +// Section(0.66666666f, 0.88888888f, ContextCompat.getColor(requireContext(), R.color.batteryDischargeHigh), 72.0f), +// Section(0.88888888f, 1.00000000f, ContextCompat.getColor(requireContext(), R.color.batteryDischargeHighest), 72.0f) +// ) +// +// binding.speedViewSpeed.minSpeed = PreferenceManager.getDefaultSharedPreferences(requireContext()).getString("minPower", "-500")!!.toFloat() +// binding.speedViewSpeed.maxSpeed = PreferenceManager.getDefaultSharedPreferences(requireContext()).getString("maxPower", "1000")!!.toFloat() +// +// minCellVoltage = PreferenceManager.getDefaultSharedPreferences(requireContext()).getString("minCellVoltage", "2500")!!.toInt() +// maxCellVoltage = PreferenceManager.getDefaultSharedPreferences(requireContext()).getString("maxCellVoltage", "4200")!!.toInt() +// +// binding.barchartCells.setNoDataTextColor(requireActivity().getColor(R.color.white)) +// binding.barchartCells.setNoDataText("...") +// +// binding.barchartCells.setPinchZoom(false) +// binding.barchartCells.setTouchEnabled(false) +// binding.barchartCells.isClickable = false +// binding.barchartCells.isDoubleTapToZoomEnabled = false +// +// binding.barchartCells.setDrawBorders(false) +// binding.barchartCells.setDrawValueAboveBar(true) +// binding.barchartCells.setDrawBorders(false) +// binding.barchartCells.setDrawGridBackground(false) +// +// binding.barchartCells.description.isEnabled = false +// binding.barchartCells.legend.isEnabled = false +// +// binding.barchartCells.axisLeft.setDrawGridLines(false) +// binding.barchartCells.axisLeft.setDrawLabels(false) +// binding.barchartCells.axisLeft.setDrawAxisLine(false) +// +// binding.barchartCells.xAxis.setDrawGridLines(false) +// binding.barchartCells.xAxis.setDrawLabels(false) +// binding.barchartCells.xAxis.setDrawAxisLine(false) +// +// binding.barchartCells.axisRight.isEnabled = false + } + + override fun onResume() { + super.onResume() + + isInForeground = true + +// binding.speedViewSpeed.speedTo(0.0f, 0) +// binding.labelStatus.text = getString(R.string.waitForBms) + } + + override fun onPause() { + super.onPause() + isInForeground = false + } + + private fun updateUi(batteryData: BatteryData) { + if (!isInForeground) { + return + } + + requireActivity().runOnUiThread { +// binding.labelTemperature.text = roundTo(batteryData.temperature, 1).toString() + +// var cycles: Short = 0 +// var capacity: Float = 0.0f +// var temperature: Float = 0.0f +// var dischargeCurrent: Float = 0.0f +// var packVoltage: Float = 0.0f +// var chargeCurrent: Float = 0.0f +// var maxChargeCurrent: Float = 0.0f + + binding.labelCycle.text = batteryData.cycles.toString() + binding.labelPackCapacity.text = roundTo(batteryData.capacity, 1).toString() + binding.labelDischargeCurrent.text = roundTo(batteryData.dischargeCurrent, 1).toString() + binding.labelPackVoltage.text = roundTo(batteryData.packVoltage, 1).toString() + binding.labelChargeCurrent.text = roundTo(batteryData.chargeCurrent, 1).toString() + binding.labelMaxCC.text = roundTo(batteryData.maxChargeCurrent, 1).toString() + +// // Power Gauge +// val powerUsage = batteryData.power * -1.0f +// +// binding.speedViewSpeed.speedTo(powerUsage, 1000) +// +// if (powerUsage < 0.0f) { +// binding.labelPower.text = "+${powerUsage.roundToInt() * -1}" +// } else { +// binding.labelPower.text = powerUsage.roundToInt().toString() +// } +// +// // Remaining Time +// wattValues[smoothIndex] = batteryData.power +// ++smoothIndex +// +// if (smoothIndex == smoothCount) { +// smoothIndex = 0 +// } +// +// var averagePower = wattValues.filter { x -> x != 0.0f }.average().toFloat() +// +// if (averagePower < 0.0f) { +// averagePower *= -1 +// } +// +// val wattSeconds: Float = when { +// batteryData.power < 0.0f -> { +// (batteryData.watthours / averagePower) * 3600 +// } +// batteryData.power > 0.0f -> { +// ((batteryData.totalWatthours - batteryData.watthours) / averagePower) * 3600 +// } +// else -> { +// 0.0f +// } +// } +// +// if (batteryData.power == 0.0f) { +// wattValues.fill(0.0f) +// } +// +// val remainingMinutes = (wattSeconds % 3600) / 60 +// val remainingHours = (wattSeconds - remainingMinutes) / 3600 +// +// binding.labelTime.text = String.format("%02d:%02d", remainingHours.roundToInt(), remainingMinutes.roundToInt()) +// +// // Cell Bar-Diagram +// val cellBars = ArrayList() +// +// for ((i, cell) in batteryData.cellVoltages.withIndex()) { +// cellBars.add(BarEntry(i.toFloat(), cell)) +// } +// +// val barDataSetVoltage = BarDataSet(cellBars, "Cell Voltages") +// barDataSetVoltage.valueTextColor = requireActivity().getColor(R.color.white) +// barDataSetVoltage.valueTextSize = 12.0f +// barDataSetVoltage.valueFormatter = DefaultValueFormatter(2) +// barDataSetVoltage.setColors(requireActivity().getColor(R.color.primary)) +// +// val barData = BarData(barDataSetVoltage) +// binding.barchartCells.data = barData +// binding.barchartCells.invalidate() +// +// // Row 1 +// val totalPercentage = ((batteryData.percentage) * 1000.0f).roundToInt() / 10.0f +// +// binding.labelVoltage.text = roundTo(batteryData.voltage, 1).toString() +// binding.labelPercentage.text = totalPercentage.toString() +// +// uiBatteryCapacityBar(totalPercentage) +// +// // Row 2 +// binding.labelCurrent.text = String.format(Locale.US, "%.1f", roundTo(batteryData.current, 1)) +// binding.labelCapacityWh.text = batteryData.watthours.roundToInt().toString() +// +// // Row 3 +//// binding.labelTemperature.text = roundTo(batteryData.avgTemperature, 1).toString() +// binding.labelTemperature.text = roundTo(batteryData.temperature, 1).toString() +// binding.labelTemperatureMax.text = roundTo(batteryData.maxTemperature, 1).toString() + } + } + +// private fun uiColorCapacityProgressBar(value: Float, uiElement: ProgressBar) { +// // 80% skipped here because battery only gets charged to ~80 percent +// when { +// value < 20 -> { +// uiElement.progressTintList = ColorStateList.valueOf(ContextCompat.getColor(requireContext(), R.color.percentUnder20)) +// } +// value < 40 -> { +// uiElement.progressTintList = ColorStateList.valueOf(ContextCompat.getColor(requireContext(), R.color.percentUnder40)) +// } +// value < 70 -> { +// uiElement.progressTintList = ColorStateList.valueOf(ContextCompat.getColor(requireContext(), R.color.percentUnder70)) +// } +// else -> { +// uiElement.progressTintList = ColorStateList.valueOf(ContextCompat.getColor(requireContext(), R.color.percentUnder100)) +// } +// } +// } + +// private fun uiBatteryCapacityBar(value: Float) { +// when { +// value == 0.0f -> { +// binding.progressBarBattery1.progress = 0 +// binding.progressBarBattery2.progress = 0 +// binding.progressBarBattery3.progress = 0 +// binding.progressBarBattery4.progress = 0 +// binding.progressBarBattery5.progress = 0 +// } +// value < 20.0f -> { +// val newValue = value * 5.0f +// uiColorCapacityProgressBar(newValue, binding.progressBarBattery1) +// binding.progressBarBattery1.progress = newValue.roundToInt() +// binding.progressBarBattery2.progress = 0 +// binding.progressBarBattery3.progress = 0 +// binding.progressBarBattery4.progress = 0 +// binding.progressBarBattery5.progress = 0 +// } +// value < 40.0f -> { +// binding.progressBarBattery1.progressTintList = ColorStateList.valueOf(ContextCompat.getColor(requireContext(), R.color.percentUnder100)) +// binding.progressBarBattery1.progress = 100 +// val newValue = (value - 20.0f) * 5.0f +// uiColorCapacityProgressBar(newValue, binding.progressBarBattery2) +// binding.progressBarBattery2.progress = newValue.roundToInt() +// binding.progressBarBattery3.progress = 0 +// binding.progressBarBattery4.progress = 0 +// binding.progressBarBattery5.progress = 0 +// } +// value < 60.0f -> { +// binding.progressBarBattery1.progressTintList = ColorStateList.valueOf(ContextCompat.getColor(requireContext(), R.color.percentUnder100)) +// binding.progressBarBattery1.progress = 100 +// binding.progressBarBattery2.progressTintList = ColorStateList.valueOf(ContextCompat.getColor(requireContext(), R.color.percentUnder100)) +// binding.progressBarBattery2.progress = 100 +// val newValue = (value - 40.0f) * 5.0f +// uiColorCapacityProgressBar(newValue, binding.progressBarBattery3) +// binding.progressBarBattery3.progress = newValue.roundToInt() +// binding.progressBarBattery4.progress = 0 +// binding.progressBarBattery5.progress = 0 +// } +// value < 80.0f -> { +// binding.progressBarBattery1.progressTintList = ColorStateList.valueOf(ContextCompat.getColor(requireContext(), R.color.percentUnder100)) +// binding.progressBarBattery1.progress = 100 +// binding.progressBarBattery2.progressTintList = ColorStateList.valueOf(ContextCompat.getColor(requireContext(), R.color.percentUnder100)) +// binding.progressBarBattery2.progress = 100 +// binding.progressBarBattery3.progressTintList = ColorStateList.valueOf(ContextCompat.getColor(requireContext(), R.color.percentUnder100)) +// binding.progressBarBattery3.progress = 100 +// val newValue = (value - 60.0f) * 5.0f +// uiColorCapacityProgressBar(newValue, binding.progressBarBattery4) +// binding.progressBarBattery4.progress = newValue.roundToInt() +// binding.progressBarBattery5.progress = 0 +// } +// value < 100.0f -> { +// binding.progressBarBattery1.progressTintList = ColorStateList.valueOf(ContextCompat.getColor(requireContext(), R.color.percentUnder100)) +// binding.progressBarBattery1.progress = 100 +// binding.progressBarBattery2.progressTintList = ColorStateList.valueOf(ContextCompat.getColor(requireContext(), R.color.percentUnder100)) +// binding.progressBarBattery2.progress = 100 +// binding.progressBarBattery3.progressTintList = ColorStateList.valueOf(ContextCompat.getColor(requireContext(), R.color.percentUnder100)) +// binding.progressBarBattery3.progress = 100 +// binding.progressBarBattery4.progressTintList = ColorStateList.valueOf(ContextCompat.getColor(requireContext(), R.color.percentUnder100)) +// binding.progressBarBattery4.progress = 100 +// val newValue = (value - 80.0f) * 5.0f +// uiColorCapacityProgressBar(newValue, binding.progressBarBattery5) +// binding.progressBarBattery5.progress = newValue.roundToInt() +// } +// else -> { +// binding.progressBarBattery1.progressTintList = ColorStateList.valueOf(ContextCompat.getColor(requireContext(), R.color.percentUnder100)) +// binding.progressBarBattery1.progress = 100 +// binding.progressBarBattery2.progressTintList = ColorStateList.valueOf(ContextCompat.getColor(requireContext(), R.color.percentUnder100)) +// binding.progressBarBattery2.progress = 100 +// binding.progressBarBattery3.progressTintList = ColorStateList.valueOf(ContextCompat.getColor(requireContext(), R.color.percentUnder100)) +// binding.progressBarBattery3.progress = 100 +// binding.progressBarBattery4.progressTintList = ColorStateList.valueOf(ContextCompat.getColor(requireContext(), R.color.percentUnder100)) +// binding.progressBarBattery4.progress = 100 +// binding.progressBarBattery5.progressTintList = ColorStateList.valueOf(ContextCompat.getColor(requireContext(), R.color.percentUnder100)) +// binding.progressBarBattery5.progress = 100 +// } +// } +// } + + private fun roundTo(value: Float, decimals: Int): Float { + if (value.isNaN() || value.isInfinite()) { + return 0.0f + } + + if (decimals == 0) { + return value.roundToInt().toFloat() + } + + val factor = 10.0.pow(decimals.toDouble()).toFloat() + return (value * factor).roundToInt() / factor + } +} \ No newline at end of file diff --git a/app/src/main/java/de/jnns/bmsmonitor/MainActivity.kt b/app/src/main/java/de/jnns/bmsmonitor/MainActivity.kt index 873c37f..f017fea 100644 --- a/app/src/main/java/de/jnns/bmsmonitor/MainActivity.kt +++ b/app/src/main/java/de/jnns/bmsmonitor/MainActivity.kt @@ -11,6 +11,7 @@ import androidx.navigation.findNavController import androidx.preference.PreferenceManager import de.jnns.bmsmonitor.bluetooth.BleService import de.jnns.bmsmonitor.databinding.ActivityMainBinding +import de.jnns.bmsmonitor.BatteryFragment1 import de.jnns.bmsmonitor.services.BikeService import de.jnns.bmsmonitor.services.BmsService import io.realm.Realm @@ -23,6 +24,7 @@ class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) + Log.d("BMS", "MainActivity Start...") Realm.init(this) Realm.compactRealm(Realm.getDefaultConfiguration()!!) @@ -50,14 +52,16 @@ class MainActivity : AppCompatActivity() { Intent(this, BleService::class.java).also { intent -> startService(intent) } } - val batteryFragment = BatteryFragment() +// val batteryFragment = BatteryFragment() + val batteryFragment1 = BatteryFragment1() val bikeFragment = BikeFragment() val statsFragment = StatsFragment() val settingsFragment = SettingsFragment() binding.bottomNavigation.setOnNavigationItemSelectedListener { when (it.itemId) { - R.id.page_battery -> supportFragmentManager.beginTransaction().apply { replace(R.id.nav_host_fragment, batteryFragment).commit() } +// R.id.page_battery -> supportFragmentManager.beginTransaction().apply { replace(R.id.nav_host_fragment, batteryFragment).commit() } + R.id.page_battery -> supportFragmentManager.beginTransaction().apply { replace(R.id.nav_host_fragment, batteryFragment1).commit() } R.id.page_bike -> supportFragmentManager.beginTransaction().apply { replace(R.id.nav_host_fragment, bikeFragment).commit() } R.id.page_stats -> supportFragmentManager.beginTransaction().apply { replace(R.id.nav_host_fragment, statsFragment).commit() } R.id.page_settings -> supportFragmentManager.beginTransaction().apply { replace(R.id.nav_host_fragment, settingsFragment).commit() } diff --git a/app/src/main/java/de/jnns/bmsmonitor/bms/BmsGeneralInfoResponse.kt b/app/src/main/java/de/jnns/bmsmonitor/bms/BmsGeneralInfoResponse.kt index 4344892..84fef06 100644 --- a/app/src/main/java/de/jnns/bmsmonitor/bms/BmsGeneralInfoResponse.kt +++ b/app/src/main/java/de/jnns/bmsmonitor/bms/BmsGeneralInfoResponse.kt @@ -5,59 +5,64 @@ import java.nio.ByteBuffer import java.nio.ByteOrder class BmsGeneralInfoResponse(bytes: ByteArray) { - var command: Int = 0 - var status: Int = 0 - var dataLength: Int = 0 - var totalVoltage: Float = 0.0f - var totalCurrent: Float = 0.0f - var residualCapacity: Float = 0.0f - var capacity: Float = 0.0f - var nominalCapacity: Float = 0.0f - var temperatureProbeCount: Int = 0 - var temperatureProbeValues: RealmList - var cycles: Short = 0 +// var command: Int = 0 +// var status: Int = 0 +// var dataLength: Int = 0 +// var totalVoltage: Float = 0.0f +// var totalCurrent: Float = 0.0f +// var residualCapacity: Float = 0.0f +// var nominalCapacity: Float = 0.0f +// var temperatureProbeCount: Int = 0 +// var temperatureProbeValues: RealmList + var cycles: Short = 0 + var capacity: Float = 0.0f var temperature: Float = 0.0f + var dischargeCurrent: Float = 0.0f + var packVoltage: Float = 0.0f + var chargeCurrent: Float = 0.0f + var maxChargeCurrent: Float = 0.0f init { - command = bytes[1].toInt() - status = bytes[2].toInt() - dataLength = bytes[3].toInt() - - // Pack Voltage +// command = bytes[1].toInt() +// status = bytes[2].toInt() +// dataLength = bytes[3].toInt() +// // totalVoltage = bytesToShort(bytes[4], bytes[5]) / 100.0f - totalVoltage = bytesToShort(bytes[8], bytes[9]) / 100.0f - - // discharging +// // totalCurrent = bytesToShort(bytes[6], bytes[7]) / 100.0f - totalCurrent = bytesToShort(bytes[10], bytes[11]) / 100.0f - - residualCapacity = bytesToShort(bytes[8], bytes[9]) / 100.0f - nominalCapacity = bytesToShort(bytes[10], bytes[11]) / 100.0f - - // Pack Cycle +// +// residualCapacity = bytesToShort(bytes[8], bytes[9]) / 100.0f +// nominalCapacity = bytesToShort(bytes[10], bytes[11]) / 100.0f +// // cycles = bytesToShort(bytes[12], bytes[13]) - cycles = bytesToShort(bytes[6], bytes[7]) - - // 14 & 15 = production date - // 16 & 17 = balance low - // 18 & 19 = balance high - // 20 & 21 = protection status - // 22 = version - - // Pack Capacity +// +// // 14 & 15 = production date +// // 16 & 17 = balance low +// // 18 & 19 = balance high +// // 20 & 21 = protection status +// // 22 = version +// +// // Pack Capacity // capacity = bytes[23].toInt() / 100.0f +// +// // 24 = MOS status +// // 25 = number of cells +// +// temperatureProbeCount = bytes[26].toInt() +// temperatureProbeValues = RealmList() +// +// for (i in 0 until temperatureProbeCount) { +// temperatureProbeValues.add((bytesToShort(bytes[27 + (i * 2)], bytes[27 + (i * 2) + 1]) - 2731) / 10.0f) +// } + + packVoltage = bytesToShort(bytes[8], bytes[9]) / 100.0f + dischargeCurrent = bytesToShort(bytes[10], bytes[11]) / 10.0f + cycles = bytesToShort(bytes[6], bytes[7]) capacity = bytesToShort(bytes[4], bytes[5]) / 10.0f + chargeCurrent = bytesToShort(bytes[12], bytes[13]) / 10.0f + maxChargeCurrent = bytesToShort(bytes[14], bytes[15]) / 10.0f - // 24 = MOS status - // 25 = number of cells - - temperatureProbeCount = bytes[26].toInt() - temperatureProbeValues = RealmList() - - for (i in 0 until temperatureProbeCount) { - temperatureProbeValues.add((bytesToShort(bytes[27 + (i * 2)], bytes[27 + (i * 2) + 1]) - 2731) / 10.0f) - } // System temperature temperature = bytesToShort(bytes[24], bytes[25]) / 10.0f diff --git a/app/src/main/java/de/jnns/bmsmonitor/data/BatteryData.kt b/app/src/main/java/de/jnns/bmsmonitor/data/BatteryData.kt index 3a3ffd3..379f8e3 100644 --- a/app/src/main/java/de/jnns/bmsmonitor/data/BatteryData.kt +++ b/app/src/main/java/de/jnns/bmsmonitor/data/BatteryData.kt @@ -10,24 +10,22 @@ open class BatteryData( var current: Float = 0.0f, var totalCapacity: Float = 0.0f, var currentCapacity: Float = 0.0f, - var cycles: Int = 0, var temperatureCount: Int = 0, var cellCount: Int = 0, var temperatures: RealmList = RealmList(), var cellVoltages: RealmList = RealmList(), - var temperature: Float = 0.0f -) : RealmObject() { - var vol: Float = 0.0f; - - val temp: Float - get() { - return temperature - } + var cycles: Short = 0, + var capacity: Float = 0.0f, + var temperature: Float = 0.0f, + var dischargeCurrent: Float = 0.0f, + var packVoltage: Float = 0.0f, + var chargeCurrent: Float = 0.0f, + var maxChargeCurrent: Float = 0.0f +) : RealmObject() { val voltage: Float get() { -// return cellVoltages.sum() - return vol + return cellVoltages.sum() } val avgTemperature: Float @@ -59,4 +57,5 @@ open class BatteryData( get() { return current * voltage } + } diff --git a/app/src/main/java/de/jnns/bmsmonitor/services/BmsService.kt b/app/src/main/java/de/jnns/bmsmonitor/services/BmsService.kt index 7f41427..d959280 100644 --- a/app/src/main/java/de/jnns/bmsmonitor/services/BmsService.kt +++ b/app/src/main/java/de/jnns/bmsmonitor/services/BmsService.kt @@ -97,12 +97,18 @@ class BmsService : Service() { } private fun onGeneralInfoAvailable(generalInfo: BmsGeneralInfoResponse) { - batteryData.current = generalInfo.totalCurrent - batteryData.currentCapacity = generalInfo.residualCapacity - batteryData.totalCapacity = generalInfo.nominalCapacity - batteryData.temperatureCount = generalInfo.temperatureProbeCount - batteryData.temperatures = generalInfo.temperatureProbeValues - batteryData.vol = generalInfo.totalVoltage +// batteryData.current = generalInfo.totalCurrent +// batteryData.currentCapacity = generalInfo.residualCapacity +// batteryData.totalCapacity = generalInfo.nominalCapacity +// batteryData.temperatureCount = generalInfo.temperatureProbeCount +// batteryData.temperatures = generalInfo.temperatureProbeValues +// batteryData.vol = generalInfo.totalVoltage + batteryData.packVoltage = generalInfo.packVoltage + batteryData.dischargeCurrent = generalInfo.dischargeCurrent + batteryData.capacity = generalInfo.capacity + batteryData.cycles = generalInfo.cycles + batteryData.chargeCurrent = generalInfo.chargeCurrent + batteryData.maxChargeCurrent = generalInfo.maxChargeCurrent batteryData.temperature = generalInfo.temperature generalInfoReceived = true diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index 5c26e5f..c67c4d8 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -65,7 +65,7 @@ android:id="@+id/bottomNavigation" android:layout_width="0dp" android:layout_height="wrap_content" - android:background="@color/bgBase" + android:background="@color/bgBase9" android:hapticFeedbackEnabled="true" app:elevation="0dp" app:labelVisibilityMode="labeled" diff --git a/app/src/main/res/layout/fragment_battery.xml b/app/src/main/res/layout/fragment_battery.xml index c6cbfa5..ebf8669 100644 --- a/app/src/main/res/layout/fragment_battery.xml +++ b/app/src/main/res/layout/fragment_battery.xml @@ -307,7 +307,7 @@ app:layout_flexBasisPercent="50%"> + app:layout_constraintTop_toTopOf="@+id/labelPackCapacityTitle" /> + app:layout_constraintBottom_toBottomOf="@+id/labelPackCapacity" + app:layout_constraintStart_toEndOf="@+id/labelPackCapacity" /> @@ -375,7 +375,7 @@ app:layout_flexBasisPercent="50%"> + app:layout_constraintTop_toTopOf="@+id/labelCycleTitle" /> + app:layout_constraintBottom_toBottomOf="@+id/labelCycle" + app:layout_constraintStart_toEndOf="@+id/labelCycle" /> diff --git a/app/src/main/res/layout/fragment_bms.xml b/app/src/main/res/layout/fragment_bms.xml new file mode 100644 index 0000000..f2b354f --- /dev/null +++ b/app/src/main/res/layout/fragment_bms.xml @@ -0,0 +1,356 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/navigation/nav_graph.xml b/app/src/main/res/navigation/nav_graph.xml index 970cb83..3ca88d2 100644 --- a/app/src/main/res/navigation/nav_graph.xml +++ b/app/src/main/res/navigation/nav_graph.xml @@ -3,13 +3,18 @@ xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/nav_graph" - app:startDestination="@id/navigation_battery"> + app:startDestination="@id/navigation_bms"> + + + + + + android:id="@+id/navigation_bms" + android:name="de.jnns.bmsmonitor.BatteryFragment1" + android:label="fragment_bms" + tools:layout="@layout/fragment_bms" /> Max Speed (Km/h) Cell Balance + + Pack Cap.: + Cycle: + Pack Vol.: + Disch. Curr: + Charge Curr: + Max. CC: \ No newline at end of file From 594eb9b124d36b05ff024679d5c9d2f8cf02e6ad Mon Sep 17 00:00:00 2001 From: Jcheng Lee Date: Thu, 8 Jun 2023 21:09:07 +0800 Subject: [PATCH 05/21] Developing 202306082109 --- .../de/jnns/bmsmonitor/bluetooth/BmsGattClientCallback.kt | 4 ++-- app/src/main/java/de/jnns/bmsmonitor/services/BmsService.kt | 5 +++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/de/jnns/bmsmonitor/bluetooth/BmsGattClientCallback.kt b/app/src/main/java/de/jnns/bmsmonitor/bluetooth/BmsGattClientCallback.kt index 0bbce26..a14ca86 100644 --- a/app/src/main/java/de/jnns/bmsmonitor/bluetooth/BmsGattClientCallback.kt +++ b/app/src/main/java/de/jnns/bmsmonitor/bluetooth/BmsGattClientCallback.kt @@ -30,7 +30,7 @@ class BmsGattClientCallback( private val rxUuid = UUID.fromString("0000C305-0000-1000-8000-00805f9b34fb") private val txUuid = UUID.fromString("0000C302-0000-1000-8000-00805f9b34fb") - private val bufferSize: Int = 256 + private val bufferSize: Int = 128 private var uartBuffer = ByteArray(bufferSize) private var uartBufferPos: Int = 0 private var uartBytesLeft: Int = 0 @@ -136,7 +136,7 @@ class BmsGattClientCallback( val frameBytes = uartBuffer.slice(IntRange(0, size)).toByteArray() - Log.d("BMS", "FrameData (" + frameBytes.size + "): " + frameBytes.toHexString()) +// Log.d("BMS", "FrameData (" + frameBytes.size + "): " + frameBytes.toHexString()) // if (frameBytes[1] == 0x3.toByte()) { // val generalInfo = BmsGeneralInfoResponse(frameBytes) diff --git a/app/src/main/java/de/jnns/bmsmonitor/services/BmsService.kt b/app/src/main/java/de/jnns/bmsmonitor/services/BmsService.kt index d959280..ab15bc0 100644 --- a/app/src/main/java/de/jnns/bmsmonitor/services/BmsService.kt +++ b/app/src/main/java/de/jnns/bmsmonitor/services/BmsService.kt @@ -23,7 +23,7 @@ import io.realm.Realm class BmsService : Service() { // BMS commands, they won't change // private val cmdGeneralInfo: ByteArray = ubyteArrayOf(0xDDU, 0xA5U, 0x03U, 0x00U, 0xFFU, 0xFDU, 0x77U).toByteArray() - private val cmdGeneralInfo: ByteArray = ubyteArrayOf(0x10U, 0x00U, 0x00U, 0x64U).toByteArray() + private val cmdGeneralInfo: ByteArray = ubyteArrayOf(0x10U, 0x00U, 0x00U, 0x40U).toByteArray() private val cmdCellInfo: ByteArray = ubyteArrayOf(0xDDU, 0xA5U, 0x04U, 0x00U, 0xFFU, 0xFCU, 0x77U).toByteArray() private val cmdBmsVersion: ByteArray = ubyteArrayOf(0xDDU, 0xA5U, 0x05U, 0x00U, 0xFFU, 0xFBU, 0x77U).toByteArray() @@ -124,7 +124,8 @@ class BmsService : Service() { } private fun sendData() { - if (cellInfoReceived && generalInfoReceived) { +// if (cellInfoReceived && generalInfoReceived) { + if (generalInfoReceived) { cellInfoReceived = false generalInfoReceived = false From 11858f9e0de4aaa249efd592d0414fd49dd8ff60 Mon Sep 17 00:00:00 2001 From: David Lee Date: Fri, 9 Jun 2023 08:26:13 +0800 Subject: [PATCH 06/21] Developing 202306090826 --- .../bluetooth/BmsGattClientCallback.kt | 18 ++++++++++++++++-- .../de/jnns/bmsmonitor/services/BmsService.kt | 5 ++++- 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/de/jnns/bmsmonitor/bluetooth/BmsGattClientCallback.kt b/app/src/main/java/de/jnns/bmsmonitor/bluetooth/BmsGattClientCallback.kt index a14ca86..ba12856 100644 --- a/app/src/main/java/de/jnns/bmsmonitor/bluetooth/BmsGattClientCallback.kt +++ b/app/src/main/java/de/jnns/bmsmonitor/bluetooth/BmsGattClientCallback.kt @@ -30,6 +30,8 @@ class BmsGattClientCallback( private val rxUuid = UUID.fromString("0000C305-0000-1000-8000-00805f9b34fb") private val txUuid = UUID.fromString("0000C302-0000-1000-8000-00805f9b34fb") + private var isInTrans: Boolean = false + private var receLen: Int = 0 private val bufferSize: Int = 128 private var uartBuffer = ByteArray(bufferSize) private var uartBufferPos: Int = 0 @@ -86,6 +88,16 @@ class BmsGattClientCallback( } } + fun setReceivingLen(receiveLen: Int) { + receLen = receiveLen + isInTrans = true + uartBufferPos = 0 + } + + fun isInTransaction(): Boolean { + return isInTrans + } + override fun onCharacteristicChanged(gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic) { super.onCharacteristicChanged(gatt, characteristic) @@ -122,11 +134,13 @@ class BmsGattClientCallback( // } uartBuffer[uartBufferPos] = byte uartBufferPos++ - if (uartBufferPos >= bufferSize) { + if (uartBufferPos >= bufferSize || uartBufferPos >= receLen) { + isInTrans = false + uartBufferPos = 0 + onFrameComplete(uartBufferPos) break } } - onFrameComplete(uartBufferPos) } private fun onFrameComplete(size: Int) { diff --git a/app/src/main/java/de/jnns/bmsmonitor/services/BmsService.kt b/app/src/main/java/de/jnns/bmsmonitor/services/BmsService.kt index ab15bc0..37028be 100644 --- a/app/src/main/java/de/jnns/bmsmonitor/services/BmsService.kt +++ b/app/src/main/java/de/jnns/bmsmonitor/services/BmsService.kt @@ -176,7 +176,9 @@ class BmsService : Service() { // // dataModeSwitch = !dataModeSwitch Log.d("BMS", "writeBytes():" + cmdGeneralInfo.toHexString()) - writeBytes(cmdGeneralInfo) + if (gattClientCallback.isInTransaction() == false) { + writeBytes(cmdGeneralInfo) + } dataHandler.postDelayed(this, dataPollDelay) } @@ -224,6 +226,7 @@ class BmsService : Service() { } private fun writeBytes(bytes: ByteArray) { + gattClientCallback.setReceivingLen(128); gattClientCallback.writeCharacteristic.value = bytes bluetoothGatt.writeCharacteristic(gattClientCallback.writeCharacteristic) } From 3c79eb32f83c88eda900cf31ef25275141adff72 Mon Sep 17 00:00:00 2001 From: David Lee Date: Fri, 9 Jun 2023 08:29:43 +0800 Subject: [PATCH 07/21] Developing 202306090829 --- .../de/jnns/bmsmonitor/bluetooth/BmsGattClientCallback.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/de/jnns/bmsmonitor/bluetooth/BmsGattClientCallback.kt b/app/src/main/java/de/jnns/bmsmonitor/bluetooth/BmsGattClientCallback.kt index ba12856..8a84766 100644 --- a/app/src/main/java/de/jnns/bmsmonitor/bluetooth/BmsGattClientCallback.kt +++ b/app/src/main/java/de/jnns/bmsmonitor/bluetooth/BmsGattClientCallback.kt @@ -135,9 +135,9 @@ class BmsGattClientCallback( uartBuffer[uartBufferPos] = byte uartBufferPos++ if (uartBufferPos >= bufferSize || uartBufferPos >= receLen) { - isInTrans = false - uartBufferPos = 0 onFrameComplete(uartBufferPos) + uartBufferPos = 0 + isInTrans = false break } } From 06659a1cb9f8907572c441d44127f37bd42c59dd Mon Sep 17 00:00:00 2001 From: David Lee Date: Fri, 9 Jun 2023 10:32:00 +0800 Subject: [PATCH 08/21] Developing 202306091031 --- .../java/de/jnns/bmsmonitor/bluetooth/BmsGattClientCallback.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/main/java/de/jnns/bmsmonitor/bluetooth/BmsGattClientCallback.kt b/app/src/main/java/de/jnns/bmsmonitor/bluetooth/BmsGattClientCallback.kt index 8a84766..08ac286 100644 --- a/app/src/main/java/de/jnns/bmsmonitor/bluetooth/BmsGattClientCallback.kt +++ b/app/src/main/java/de/jnns/bmsmonitor/bluetooth/BmsGattClientCallback.kt @@ -138,6 +138,7 @@ class BmsGattClientCallback( onFrameComplete(uartBufferPos) uartBufferPos = 0 isInTrans = false + Log.d("BMS", "Transaction Done.") break } } From f8f67cadc4082240c385112690ff08593f940104 Mon Sep 17 00:00:00 2001 From: David Lee Date: Fri, 9 Jun 2023 11:13:18 +0800 Subject: [PATCH 09/21] Developing 202306091113 --- app/src/main/java/de/jnns/bmsmonitor/bluetooth/BleService.kt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/de/jnns/bmsmonitor/bluetooth/BleService.kt b/app/src/main/java/de/jnns/bmsmonitor/bluetooth/BleService.kt index 007c334..133ea8d 100644 --- a/app/src/main/java/de/jnns/bmsmonitor/bluetooth/BleService.kt +++ b/app/src/main/java/de/jnns/bmsmonitor/bluetooth/BleService.kt @@ -49,7 +49,9 @@ class BleService : Service() { super.onScanResult(callbackType, result) if (result.device != null) { - BleManager.i.addDevice(result.device) + if(result.device.name.startsWith("BMS")) { + BleManager.i.addDevice(result.device) + } } } } From 88eef2b8baf840a0ce4ce570ece22898df4553c1 Mon Sep 17 00:00:00 2001 From: Jcheng Lee Date: Fri, 9 Jun 2023 15:54:53 +0800 Subject: [PATCH 10/21] Developing 202306091554 --- .../de/jnns/bmsmonitor/BatteryFragment1.kt | 3 -- .../jnns/bmsmonitor/bluetooth/BleManager.kt | 4 +- .../jnns/bmsmonitor/bluetooth/BleService.kt | 8 ++- .../bluetooth/BmsGattClientCallback.kt | 53 +++---------------- .../de/jnns/bmsmonitor/services/BmsService.kt | 6 ++- 5 files changed, 20 insertions(+), 54 deletions(-) diff --git a/app/src/main/java/de/jnns/bmsmonitor/BatteryFragment1.kt b/app/src/main/java/de/jnns/bmsmonitor/BatteryFragment1.kt index 1c603e1..f910c1a 100644 --- a/app/src/main/java/de/jnns/bmsmonitor/BatteryFragment1.kt +++ b/app/src/main/java/de/jnns/bmsmonitor/BatteryFragment1.kt @@ -55,9 +55,6 @@ class BatteryFragment1 : Fragment() { try { val msg: String = intent.getStringExtra("batteryData")!! - // David Lee -// binding.labelStatus.text = String.format(resources.getString(R.string.connectedToBms), intent.getStringExtra("deviceName")) - if (msg.isNotEmpty()) { updateUi(Gson().fromJson(msg, BatteryData::class.java)) } diff --git a/app/src/main/java/de/jnns/bmsmonitor/bluetooth/BleManager.kt b/app/src/main/java/de/jnns/bmsmonitor/bluetooth/BleManager.kt index f0f00a1..ed7f05c 100644 --- a/app/src/main/java/de/jnns/bmsmonitor/bluetooth/BleManager.kt +++ b/app/src/main/java/de/jnns/bmsmonitor/bluetooth/BleManager.kt @@ -10,8 +10,8 @@ class BleManager { fun addDevice(device: BluetoothDevice) { if (!bleDevices.contains(device)) { - Log.d("BleManager", "Device Address:" + device.address) - Log.d("BleManager", "Device Name:" + device.name) +// Log.d("BleManager", "Device Address:" + device.address) +// Log.d("BleManager", "Device Name:" + device.name) bleDevices.add(device) for (func in onUpdateFunctions) diff --git a/app/src/main/java/de/jnns/bmsmonitor/bluetooth/BleService.kt b/app/src/main/java/de/jnns/bmsmonitor/bluetooth/BleService.kt index 133ea8d..4899de0 100644 --- a/app/src/main/java/de/jnns/bmsmonitor/bluetooth/BleService.kt +++ b/app/src/main/java/de/jnns/bmsmonitor/bluetooth/BleService.kt @@ -19,7 +19,8 @@ class BleService : Service() { super.onCreate() bluetoothLeScanner = BluetoothAdapter.getDefaultAdapter().bluetoothLeScanner - +// BluetoothAdapter.getDefaultAdapter().bondedDevices() +// BluetoothAdapter.getDefaultAdapter().getRemoteDevice() startBleScanning() } @@ -49,7 +50,10 @@ class BleService : Service() { super.onScanResult(callbackType, result) if (result.device != null) { - if(result.device.name.startsWith("BMS")) { + if( + result.device.name != null && + result.device.name.startsWith("UOOK-BMS") == true + ) { BleManager.i.addDevice(result.device) } } diff --git a/app/src/main/java/de/jnns/bmsmonitor/bluetooth/BmsGattClientCallback.kt b/app/src/main/java/de/jnns/bmsmonitor/bluetooth/BmsGattClientCallback.kt index 08ac286..a2bba4f 100644 --- a/app/src/main/java/de/jnns/bmsmonitor/bluetooth/BmsGattClientCallback.kt +++ b/app/src/main/java/de/jnns/bmsmonitor/bluetooth/BmsGattClientCallback.kt @@ -32,7 +32,7 @@ class BmsGattClientCallback( private var isInTrans: Boolean = false private var receLen: Int = 0 - private val bufferSize: Int = 128 + private val bufferSize: Int = 256 private var uartBuffer = ByteArray(bufferSize) private var uartBufferPos: Int = 0 private var uartBytesLeft: Int = 0 @@ -68,7 +68,7 @@ class BmsGattClientCallback( for(serv in gatt.services) { i++ - Log.d("BMS", "Services" + i.toString() + ":" + serv.uuid.toString()) +// Log.d("BMS", "Services" + i.toString() + ":" + serv.uuid.toString()) } val uartService = gatt.getService(uartUuid) @@ -77,8 +77,8 @@ class BmsGattClientCallback( if (uartService != null) { readCharacteristic = uartService.getCharacteristic(rxUuid) writeCharacteristic = uartService.getCharacteristic(txUuid) - Log.d("BMS", "Got readCharacteristic:" + readCharacteristic.uuid.toString()) - Log.d("BMS", "Got writeCharacteristic:" + writeCharacteristic.uuid.toString()) +// Log.d("BMS", "Got readCharacteristic:" + readCharacteristic.uuid.toString()) +// Log.d("BMS", "Got writeCharacteristic:" + writeCharacteristic.uuid.toString()) gatt.setCharacteristicNotification(writeCharacteristic, true) gatt.setCharacteristicNotification(readCharacteristic, true) @@ -101,44 +101,16 @@ class BmsGattClientCallback( override fun onCharacteristicChanged(gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic) { super.onCharacteristicChanged(gatt, characteristic) - // Log.d("BluetoothGatt", "BLE Data (" + characteristic.value.size + "): " + characteristic.value.toHexString()) Log.d("BMS", "BLE Data (" + characteristic.value.size + "): " + characteristic.value.toHexString()) for (byte: Byte in characteristic.value) { -// if (uartBufferPos >= bufferSize) { -// isInFrame = false -// uartBufferPos = 0 -// uartBytesLeft = 0 -// } -// -// uartBuffer[uartBufferPos] = byte -// -// if (isInFrame) { -// if (uartBufferPos == 3) { -// uartBytesLeft = byte.toInt() -// } -// -// if (byte.toUByte() == 0x77.toUByte() && uartBytesLeft < 1) { -// isInFrame = false -// onFrameComplete(uartBufferPos) -// uartBufferPos = 0 -// uartBytesLeft = 0 -// } else { -// -// uartBufferPos++ -// uartBytesLeft-- -// } -// } else if (byte.toUByte() == 0xDD.toUByte()) { -// isInFrame = true -// uartBufferPos++ -// } uartBuffer[uartBufferPos] = byte uartBufferPos++ if (uartBufferPos >= bufferSize || uartBufferPos >= receLen) { onFrameComplete(uartBufferPos) - uartBufferPos = 0 - isInTrans = false Log.d("BMS", "Transaction Done.") + isInTrans = false + uartBufferPos = 0 break } } @@ -149,18 +121,7 @@ class BmsGattClientCallback( return } - val frameBytes = uartBuffer.slice(IntRange(0, size)).toByteArray() - -// Log.d("BMS", "FrameData (" + frameBytes.size + "): " + frameBytes.toHexString()) - -// if (frameBytes[1] == 0x3.toByte()) { -// val generalInfo = BmsGeneralInfoResponse(frameBytes) -// onGeneralInfoCallback(generalInfo) -// } else if (frameBytes[1] == 0x4.toByte()) { -// val cellInfo = BmsCellInfoResponse(frameBytes) -// onCellInfoCallback(cellInfo) -// } -// + val frameBytes = uartBuffer.slice(IntRange(0, size - 1)).toByteArray() val generalInfo = BmsGeneralInfoResponse(frameBytes) onGeneralInfoCallback(generalInfo) } diff --git a/app/src/main/java/de/jnns/bmsmonitor/services/BmsService.kt b/app/src/main/java/de/jnns/bmsmonitor/services/BmsService.kt index 37028be..23f2a38 100644 --- a/app/src/main/java/de/jnns/bmsmonitor/services/BmsService.kt +++ b/app/src/main/java/de/jnns/bmsmonitor/services/BmsService.kt @@ -96,6 +96,10 @@ class BmsService : Service() { return null } + override fun onDestroy() { + disconnectFromDevice() + } + private fun onGeneralInfoAvailable(generalInfo: BmsGeneralInfoResponse) { // batteryData.current = generalInfo.totalCurrent // batteryData.currentCapacity = generalInfo.residualCapacity @@ -175,8 +179,8 @@ class BmsService : Service() { // } // // dataModeSwitch = !dataModeSwitch - Log.d("BMS", "writeBytes():" + cmdGeneralInfo.toHexString()) if (gattClientCallback.isInTransaction() == false) { + Log.d("BMS", "writeBytes():" + cmdGeneralInfo.toHexString()) writeBytes(cmdGeneralInfo) } From ca2cfd81e7a2a0da571a3a934eb3eaa5d842ab57 Mon Sep 17 00:00:00 2001 From: David Lee Date: Fri, 9 Jun 2023 17:17:56 +0800 Subject: [PATCH 11/21] Developing 202306091718 1. Adjust UI, remove un-needed. --- .../java/de/jnns/bmsmonitor/MainActivity.kt | 5 +- .../de/jnns/bmsmonitor/SettingsFragment.kt | 6 +- .../res/drawable/selector_bottom_nav_item.xml | 6 + app/src/main/res/layout/activity_main.xml | 4 +- .../main/res/menu/main_navigation_menu.xml | 10 +- app/src/main/res/values/themes.xml | 3 + app/src/main/res/xml/preferences.xml | 167 +++++++++--------- 7 files changed, 109 insertions(+), 92 deletions(-) create mode 100644 app/src/main/res/drawable/selector_bottom_nav_item.xml diff --git a/app/src/main/java/de/jnns/bmsmonitor/MainActivity.kt b/app/src/main/java/de/jnns/bmsmonitor/MainActivity.kt index f017fea..1768cdb 100644 --- a/app/src/main/java/de/jnns/bmsmonitor/MainActivity.kt +++ b/app/src/main/java/de/jnns/bmsmonitor/MainActivity.kt @@ -2,6 +2,7 @@ package de.jnns.bmsmonitor import android.Manifest import android.content.Intent +import android.content.SharedPreferences import android.content.pm.PackageManager import android.os.Bundle import android.util.Log @@ -35,7 +36,8 @@ class MainActivity : AppCompatActivity() { _binding = ActivityMainBinding.inflate(layoutInflater) setContentView(binding.root) - val batteryEnabled = PreferenceManager.getDefaultSharedPreferences(this).getBoolean("batteryEnabled", false) +// val batteryEnabled = PreferenceManager.getDefaultSharedPreferences(this).getBoolean("batteryEnabled", false) + val batteryEnabled = PreferenceManager.getDefaultSharedPreferences(this).getBoolean("batteryEnabled", true) val bikeEnabled = PreferenceManager.getDefaultSharedPreferences(this).getBoolean("bikeEnabled", false) if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) { @@ -81,6 +83,7 @@ class MainActivity : AppCompatActivity() { when (requestCode) { 4711 -> { if ((grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED)) { + Intent(this, BleService::class.java).also { intent -> startService(intent) } Intent(this, BmsService::class.java).also { intent -> startService(intent) } diff --git a/app/src/main/java/de/jnns/bmsmonitor/SettingsFragment.kt b/app/src/main/java/de/jnns/bmsmonitor/SettingsFragment.kt index f4f30cd..5a8a515 100644 --- a/app/src/main/java/de/jnns/bmsmonitor/SettingsFragment.kt +++ b/app/src/main/java/de/jnns/bmsmonitor/SettingsFragment.kt @@ -17,16 +17,16 @@ class SettingsFragment : PreferenceFragmentCompat() { super.onViewCreated(view, savedInstanceState) val btPreference = findPreference("macAddress") as ListPreference - val btPreferenceBike = findPreference("macAddressBike") as ListPreference +// val btPreferenceBike = findPreference("macAddressBike") as ListPreference val bleNames = BleManager.i.getBleNames() bleNames.add("None") btPreference.entries = bleNames.toTypedArray() - btPreferenceBike.entries = bleNames.toTypedArray() +// btPreferenceBike.entries = bleNames.toTypedArray() val bleAddresses = BleManager.i.getBleAddresses() bleAddresses.add("0") btPreference.entryValues = bleAddresses.toTypedArray() - btPreferenceBike.entryValues = bleAddresses.toTypedArray() +// btPreferenceBike.entryValues = bleAddresses.toTypedArray() } } \ No newline at end of file diff --git a/app/src/main/res/drawable/selector_bottom_nav_item.xml b/app/src/main/res/drawable/selector_bottom_nav_item.xml new file mode 100644 index 0000000..1df24fb --- /dev/null +++ b/app/src/main/res/drawable/selector_bottom_nav_item.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index c67c4d8..e2377d6 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -65,13 +65,15 @@ android:id="@+id/bottomNavigation" android:layout_width="0dp" android:layout_height="wrap_content" - android:background="@color/bgBase9" + android:background="@color/bgBase" android:hapticFeedbackEnabled="true" app:elevation="0dp" app:labelVisibilityMode="labeled" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" + app:itemIconTint="@drawable/selector_bottom_nav_item" + app:itemTextColor="@drawable/selector_bottom_nav_item" app:menu="@menu/main_navigation_menu" /> + android:title="@string/navBike" + android:visible="false" /> + android:title="@string/navStats" + android:visible="false" /> @color/primary @color/bgBase1 @color/bgBase + + @android:color/white + @android:color/white \ No newline at end of file diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml index d683e86..a58e377 100644 --- a/app/src/main/res/xml/preferences.xml +++ b/app/src/main/res/xml/preferences.xml @@ -7,6 +7,7 @@ app:title="Bluetooth Low Energy"> - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + @@ -58,66 +59,66 @@ app:title="@string/settingsRefreshInterval" app:useSimpleSummaryProvider="true" /> - - - - - + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file From a15c6c6304c09d0007ef70d16c15c4787ed865a9 Mon Sep 17 00:00:00 2001 From: Jcheng Lee Date: Wed, 28 Jun 2023 14:50:18 +0800 Subject: [PATCH 12/21] Developing --- .../de/jnns/bmsmonitor/BatteryFragment.kt | 628 ++++++++++-------- .../de/jnns/bmsmonitor/BatteryFragment1.kt | 12 +- .../java/de/jnns/bmsmonitor/MainActivity.kt | 11 +- .../bluetooth/BmsGattClientCallback.kt | 53 ++ .../bmsmonitor/bms/BmsGeneralInfoResponse.kt | 62 +- .../de/jnns/bmsmonitor/data/BatteryData.kt | 44 +- .../de/jnns/bmsmonitor/services/BmsService.kt | 39 +- app/src/main/res/layout/fragment_battery.xml | 67 +- app/src/main/res/navigation/nav_graph.xml | 4 +- app/src/main/res/values/strings.xml | 4 +- 10 files changed, 572 insertions(+), 352 deletions(-) diff --git a/app/src/main/java/de/jnns/bmsmonitor/BatteryFragment.kt b/app/src/main/java/de/jnns/bmsmonitor/BatteryFragment.kt index b503644..f783322 100644 --- a/app/src/main/java/de/jnns/bmsmonitor/BatteryFragment.kt +++ b/app/src/main/java/de/jnns/bmsmonitor/BatteryFragment.kt @@ -1,161 +1,185 @@ package de.jnns.bmsmonitor -//import android.content.BroadcastReceiver -//import android.content.Context -//import android.content.Intent -//import android.content.IntentFilter -//import android.content.res.ColorStateList -//import android.os.Bundle -//import android.view.LayoutInflater -//import android.view.View -//import android.view.ViewGroup -//import android.widget.ProgressBar -//import android.widget.TextView -//import androidx.core.content.ContextCompat -//import androidx.fragment.app.Fragment -//import androidx.localbroadcastmanager.content.LocalBroadcastManager -//import androidx.navigation.findNavController -//import androidx.preference.PreferenceManager -//import com.github.anastr.speedviewlib.components.Section -//import com.github.mikephil.charting.data.BarData -//import com.github.mikephil.charting.data.BarDataSet -//import com.github.mikephil.charting.data.BarEntry -//import com.github.mikephil.charting.formatter.DefaultValueFormatter -//import com.google.gson.Gson -//import de.jnns.bmsmonitor.data.BatteryData -//import de.jnns.bmsmonitor.databinding.FragmentBatteryBinding -//import java.util.* -//import kotlin.math.pow -//import kotlin.math.roundToInt -// -// -//@ExperimentalUnsignedTypes -//class BatteryFragment : Fragment() { -// private var _binding: FragmentBatteryBinding? = null -// private val binding get() = _binding!! -// -// // no need to refresh data in the background -// private var isInForeground = false -// -// // time calculation smoothing -// // default 5 min -// private var smoothCount = 60 * 5 -// private var smoothIndex = 0 -// private var wattValues = FloatArray(smoothCount) -// -// private var minCellVoltage = 0 -// private var maxCellVoltage = 0 -// -// private val mMessageReceiver: BroadcastReceiver = object : BroadcastReceiver() { -// override fun onReceive(context: Context, intent: Intent) { -// try { -// val msg: String = intent.getStringExtra("batteryData")!! -// +import android.content.BroadcastReceiver +import android.content.Context +import android.content.Intent +import android.content.IntentFilter +import android.content.res.ColorStateList +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.ProgressBar +import android.widget.TextView +import androidx.core.content.ContextCompat +import androidx.fragment.app.Fragment +import androidx.localbroadcastmanager.content.LocalBroadcastManager +import androidx.navigation.findNavController +import androidx.preference.PreferenceManager +import com.github.anastr.speedviewlib.components.Section +import com.github.mikephil.charting.data.BarData +import com.github.mikephil.charting.data.BarDataSet +import com.github.mikephil.charting.data.BarEntry +import com.github.mikephil.charting.formatter.DefaultValueFormatter +import com.google.gson.Gson +import de.jnns.bmsmonitor.data.BatteryData +import de.jnns.bmsmonitor.databinding.FragmentBatteryBinding +import java.util.* +import kotlin.math.pow +import kotlin.math.roundToInt + + +@ExperimentalUnsignedTypes +class BatteryFragment : Fragment() { + private var _binding: FragmentBatteryBinding? = null + private val binding get() = _binding!! + + // no need to refresh data in the background + private var isInForeground = false + + // time calculation smoothing + // default 5 min + private var smoothCount = 60 * 5 + private var smoothIndex = 0 + private var wattValues = FloatArray(smoothCount) + + private var minCellVoltage = 0 + private var maxCellVoltage = 0 + + private val mMessageReceiver: BroadcastReceiver = object : BroadcastReceiver() { + override fun onReceive(context: Context, intent: Intent) { + try { + val msg: String = intent.getStringExtra("batteryData")!! + // binding.labelStatus.text = String.format(resources.getString(R.string.connectedToBms), intent.getStringExtra("deviceName")) -// -// if (msg.isNotEmpty()) { -// updateUi(Gson().fromJson(msg, BatteryData::class.java)) -// } -// } catch (ex: Exception) { -// // ignored -// } -// } -// } -// -// override fun onCreateView( -// inflater: LayoutInflater, -// container: ViewGroup?, -// savedInstanceState: Bundle? -// ): View { -// LocalBroadcastManager.getInstance(requireContext()).registerReceiver(mMessageReceiver, IntentFilter("bmsDataIntent")) -// _binding = FragmentBatteryBinding.inflate(inflater, container, false) -// return binding.root -// } -// -// override fun onDestroy() { -// super.onDestroy() -// _binding = null -// LocalBroadcastManager.getInstance(requireContext()).unregisterReceiver(mMessageReceiver) -// } -// -// override fun onViewCreated(view: View, savedInstanceState: Bundle?) { -// super.onViewCreated(view, savedInstanceState) -// -// binding.speedViewSpeed.clearSections() -// binding.speedViewSpeed.addSections( -// Section(0.00000000f, 0.11111111f, ContextCompat.getColor(requireContext(), R.color.batteryChargeHigh), 72.0f), -// Section(0.11111111f, 0.22222222f, ContextCompat.getColor(requireContext(), R.color.batteryChargeMedium), 72.0f), -// Section(0.22222222f, 0.33333333f, ContextCompat.getColor(requireContext(), R.color.batteryChargeLow), 72.0f), -// Section(0.33333333f, 0.44444444f, ContextCompat.getColor(requireContext(), R.color.batteryDischargeLow), 72.0f), -// Section(0.44444444f, 0.66666666f, ContextCompat.getColor(requireContext(), R.color.batteryDischargeMedium), 72.0f), -// Section(0.66666666f, 0.88888888f, ContextCompat.getColor(requireContext(), R.color.batteryDischargeHigh), 72.0f), -// Section(0.88888888f, 1.00000000f, ContextCompat.getColor(requireContext(), R.color.batteryDischargeHighest), 72.0f) -// ) -// -// binding.speedViewSpeed.minSpeed = PreferenceManager.getDefaultSharedPreferences(requireContext()).getString("minPower", "-500")!!.toFloat() -// binding.speedViewSpeed.maxSpeed = PreferenceManager.getDefaultSharedPreferences(requireContext()).getString("maxPower", "1000")!!.toFloat() -// -// minCellVoltage = PreferenceManager.getDefaultSharedPreferences(requireContext()).getString("minCellVoltage", "2500")!!.toInt() -// maxCellVoltage = PreferenceManager.getDefaultSharedPreferences(requireContext()).getString("maxCellVoltage", "4200")!!.toInt() -// -// binding.barchartCells.setNoDataTextColor(requireActivity().getColor(R.color.white)) -// binding.barchartCells.setNoDataText("...") -// -// binding.barchartCells.setPinchZoom(false) -// binding.barchartCells.setTouchEnabled(false) -// binding.barchartCells.isClickable = false -// binding.barchartCells.isDoubleTapToZoomEnabled = false -// -// binding.barchartCells.setDrawBorders(false) -// binding.barchartCells.setDrawValueAboveBar(true) -// binding.barchartCells.setDrawBorders(false) -// binding.barchartCells.setDrawGridBackground(false) -// -// binding.barchartCells.description.isEnabled = false -// binding.barchartCells.legend.isEnabled = false -// -// binding.barchartCells.axisLeft.setDrawGridLines(false) -// binding.barchartCells.axisLeft.setDrawLabels(false) -// binding.barchartCells.axisLeft.setDrawAxisLine(false) -// -// binding.barchartCells.xAxis.setDrawGridLines(false) -// binding.barchartCells.xAxis.setDrawLabels(false) -// binding.barchartCells.xAxis.setDrawAxisLine(false) -// -// binding.barchartCells.axisRight.isEnabled = false -// } -// -// override fun onResume() { -// super.onResume() -// -// isInForeground = true -// -// binding.speedViewSpeed.speedTo(0.0f, 0) -// binding.labelStatus.text = getString(R.string.waitForBms) -// } -// -// override fun onPause() { -// super.onPause() -// isInForeground = false -// } -// -// private fun updateUi(batteryData: BatteryData) { -// if (!isInForeground) { -// return -// } -// -// requireActivity().runOnUiThread { -// // Power Gauge + + if (msg.isNotEmpty()) { + updateUi(Gson().fromJson(msg, BatteryData::class.java)) + } + } catch (ex: Exception) { + // ignored + } + } + } + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + LocalBroadcastManager.getInstance(requireContext()).registerReceiver(mMessageReceiver, IntentFilter("bmsDataIntent")) + _binding = FragmentBatteryBinding.inflate(inflater, container, false) + return binding.root + } + + override fun onDestroy() { + super.onDestroy() + _binding = null + LocalBroadcastManager.getInstance(requireContext()).unregisterReceiver(mMessageReceiver) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + binding.speedViewSpeed.clearSections() + binding.speedViewSpeed.addSections( + Section(0.00000000f, 0.11111111f, ContextCompat.getColor(requireContext(), R.color.batteryChargeHigh), 72.0f), + Section(0.11111111f, 0.22222222f, ContextCompat.getColor(requireContext(), R.color.batteryChargeMedium), 72.0f), + Section(0.22222222f, 0.33333333f, ContextCompat.getColor(requireContext(), R.color.batteryChargeLow), 72.0f), + Section(0.33333333f, 0.44444444f, ContextCompat.getColor(requireContext(), R.color.batteryDischargeLow), 72.0f), + Section(0.44444444f, 0.66666666f, ContextCompat.getColor(requireContext(), R.color.batteryDischargeMedium), 72.0f), + Section(0.66666666f, 0.88888888f, ContextCompat.getColor(requireContext(), R.color.batteryDischargeHigh), 72.0f), + Section(0.88888888f, 1.00000000f, ContextCompat.getColor(requireContext(), R.color.batteryDischargeHighest), 72.0f) + ) + + binding.speedViewSpeed.minSpeed = PreferenceManager.getDefaultSharedPreferences(requireContext()).getString("minPower", "-500")!!.toFloat() + binding.speedViewSpeed.maxSpeed = PreferenceManager.getDefaultSharedPreferences(requireContext()).getString("maxPower", "1000")!!.toFloat() + + minCellVoltage = PreferenceManager.getDefaultSharedPreferences(requireContext()).getString("minCellVoltage", "2500")!!.toInt() + maxCellVoltage = PreferenceManager.getDefaultSharedPreferences(requireContext()).getString("maxCellVoltage", "4200")!!.toInt() + + binding.barchartCells.setNoDataTextColor(requireActivity().getColor(R.color.white)) + binding.barchartCells.setNoDataText("...") + + binding.barchartCells.setPinchZoom(false) + binding.barchartCells.setTouchEnabled(false) + binding.barchartCells.isClickable = false + binding.barchartCells.isDoubleTapToZoomEnabled = false + + binding.barchartCells.setDrawBorders(false) + binding.barchartCells.setDrawValueAboveBar(true) + binding.barchartCells.setDrawBorders(false) + binding.barchartCells.setDrawGridBackground(false) + + binding.barchartCells.description.isEnabled = false + binding.barchartCells.legend.isEnabled = false + + binding.barchartCells.axisLeft.setDrawGridLines(false) + binding.barchartCells.axisLeft.setDrawLabels(false) + binding.barchartCells.axisLeft.setDrawAxisLine(false) + + binding.barchartCells.xAxis.setDrawGridLines(false) + binding.barchartCells.xAxis.setDrawLabels(false) + binding.barchartCells.xAxis.setDrawAxisLine(false) + + binding.barchartCells.axisRight.isEnabled = false + } + + override fun onResume() { + super.onResume() + + isInForeground = true + + binding.speedViewSpeed.speedTo(0.0f, 0) + binding.labelStatus.text = getString(R.string.waitForBms) + } + + override fun onPause() { + super.onPause() + isInForeground = false + } + + private fun updateUi(batteryData: BatteryData) { + if (!isInForeground) { + return + } + + requireActivity().runOnUiThread { + binding.labelStatus.text = String.format(resources.getString(R.string.connectedToBms), batteryData.bpVersion, batteryData.bpNumber) + + // Power Gauge // val powerUsage = batteryData.power * -1.0f -// -// binding.speedViewSpeed.speedTo(powerUsage, 1000) -// -// if (powerUsage < 0.0f) { -// binding.labelPower.text = "+${powerUsage.roundToInt() * -1}" -// } else { -// binding.labelPower.text = powerUsage.roundToInt().toString() -// } -// + val powerUsage = batteryData.packCurrent * batteryData.packVoltage + + var chargePower:Float = 0.0f + + if(batteryData.packCurrent >= 0.0f) + { + chargePower = batteryData.packCurrent / batteryData.maxChargeCurrent +// if(chargePower > 1.0f) { +// chargePower = 1.0f * -500 +// } else { + chargePower = chargePower * -500 +// } + } else { + chargePower = batteryData.packCurrent / batteryData.maxDischargeCurrent +// if(chargePower > 1.0f) { +// chargePower = 1.0f * -1000 +// } else { + chargePower = chargePower * -1000 +// } + } +// chargePower = chargePower * -1 + binding.speedViewSpeed.speedTo(chargePower, 1000) +// binding.speedViewSpeed.speedTo(1000.0f, 1000) + + if (powerUsage < 0.0f) { + binding.labelPower.text = "+${powerUsage.roundToInt() * -1}" + } else { + binding.labelPower.text = powerUsage.roundToInt().toString() + } + + // TODO: David Review // // Remaining Time // wattValues[smoothIndex] = batteryData.power // ++smoothIndex @@ -188,10 +212,42 @@ package de.jnns.bmsmonitor // // val remainingMinutes = (wattSeconds % 3600) / 60 // val remainingHours = (wattSeconds - remainingMinutes) / 3600 -// + // binding.labelTime.text = String.format("%02d:%02d", remainingHours.roundToInt(), remainingMinutes.roundToInt()) -// -// // Cell Bar-Diagram + binding.labelCycle.text = String.format("Cycle:%s", batteryData.packDischargCycle.toString()) + + // TODO: David Modify + // Cell Bar-Diagram + val cellBars = ArrayList() + cellBars.add(BarEntry(0.toFloat(), batteryData.cellVoltage1)) + cellBars.add(BarEntry(1.toFloat(), batteryData.cellVoltage2)) + cellBars.add(BarEntry(2.toFloat(), batteryData.cellVoltage3)) + cellBars.add(BarEntry(3.toFloat(), batteryData.cellVoltage4)) + cellBars.add(BarEntry(4.toFloat(), batteryData.cellVoltage5)) + cellBars.add(BarEntry(5.toFloat(), batteryData.cellVoltage6)) + cellBars.add(BarEntry(6.toFloat(), batteryData.cellVoltage7)) + cellBars.add(BarEntry(7.toFloat(), batteryData.cellVoltage8)) + cellBars.add(BarEntry(8.toFloat(), batteryData.cellVoltage9)) + cellBars.add(BarEntry(9.toFloat(), batteryData.cellVoltage10)) + cellBars.add(BarEntry(10.toFloat(), batteryData.cellVoltage11)) + cellBars.add(BarEntry(11.toFloat(), batteryData.cellVoltage12)) + cellBars.add(BarEntry(12.toFloat(), batteryData.cellVoltage13)) + cellBars.add(BarEntry(13.toFloat(), batteryData.cellVoltage14)) + cellBars.add(BarEntry(14.toFloat(), batteryData.cellVoltage15)) + cellBars.add(BarEntry(15.toFloat(), batteryData.cellVoltage16)) + + val barDataSetVoltage = BarDataSet(cellBars, "Cell Voltages") + barDataSetVoltage.valueTextColor = requireActivity().getColor(R.color.white) + barDataSetVoltage.valueTextSize = 12.0f + barDataSetVoltage.valueFormatter = DefaultValueFormatter(2) + barDataSetVoltage.setColors(requireActivity().getColor(R.color.primary)) + + val barData = BarData(barDataSetVoltage) + binding.barchartCells.data = barData + binding.barchartCells.invalidate() + + + // val cellBars = ArrayList() // // for ((i, cell) in batteryData.cellVoltages.withIndex()) { @@ -207,133 +263,141 @@ package de.jnns.bmsmonitor // val barData = BarData(barDataSetVoltage) // binding.barchartCells.data = barData // binding.barchartCells.invalidate() -// -// // Row 1 + + // Row 1 // val totalPercentage = ((batteryData.percentage) * 1000.0f).roundToInt() / 10.0f -// -// binding.labelVoltage.text = roundTo(batteryData.voltage, 1).toString() -// binding.labelPercentage.text = totalPercentage.toString() -// -// uiBatteryCapacityBar(totalPercentage) -// -// // Row 2 + var totalPercentage = batteryData.packRSOC.toUByte() + if(totalPercentage > 100U) { + totalPercentage = 100U + } + + binding.labelVoltage.text = roundTo(batteryData.packVoltage, 1).toString() + binding.labelPercentage.text = totalPercentage.toString() + + uiBatteryCapacityBar(totalPercentage.toFloat()) + + // Row 2 // binding.labelCurrent.text = String.format(Locale.US, "%.1f", roundTo(batteryData.current, 1)) + binding.labelCurrent.text = String.format(Locale.US, "%.1f", roundTo(batteryData.packCurrent, 1)) // binding.labelCapacityWh.text = batteryData.watthours.roundToInt().toString() -// -// // Row 3 -//// binding.labelTemperature.text = roundTo(batteryData.avgTemperature, 1).toString() + binding.labelCapacityWh.text = batteryData.capacity.roundToInt().toString() + + // Row 3 +// binding.labelTemperature.text = roundTo(batteryData.avgTemperature, 1).toString() // binding.labelTemperature.text = roundTo(batteryData.temperature, 1).toString() + binding.labelTemperature.text = roundTo(batteryData.sysTemperature, 1).toString() // binding.labelTemperatureMax.text = roundTo(batteryData.maxTemperature, 1).toString() -// } -// } -// -// private fun uiColorCapacityProgressBar(value: Float, uiElement: ProgressBar) { -// // 80% skipped here because battery only gets charged to ~80 percent -// when { -// value < 20 -> { -// uiElement.progressTintList = ColorStateList.valueOf(ContextCompat.getColor(requireContext(), R.color.percentUnder20)) -// } -// value < 40 -> { -// uiElement.progressTintList = ColorStateList.valueOf(ContextCompat.getColor(requireContext(), R.color.percentUnder40)) -// } -// value < 70 -> { -// uiElement.progressTintList = ColorStateList.valueOf(ContextCompat.getColor(requireContext(), R.color.percentUnder70)) -// } -// else -> { -// uiElement.progressTintList = ColorStateList.valueOf(ContextCompat.getColor(requireContext(), R.color.percentUnder100)) -// } -// } -// } -// -// private fun uiBatteryCapacityBar(value: Float) { -// when { -// value == 0.0f -> { -// binding.progressBarBattery1.progress = 0 -// binding.progressBarBattery2.progress = 0 -// binding.progressBarBattery3.progress = 0 -// binding.progressBarBattery4.progress = 0 -// binding.progressBarBattery5.progress = 0 -// } -// value < 20.0f -> { -// val newValue = value * 5.0f -// uiColorCapacityProgressBar(newValue, binding.progressBarBattery1) -// binding.progressBarBattery1.progress = newValue.roundToInt() -// binding.progressBarBattery2.progress = 0 -// binding.progressBarBattery3.progress = 0 -// binding.progressBarBattery4.progress = 0 -// binding.progressBarBattery5.progress = 0 -// } -// value < 40.0f -> { -// binding.progressBarBattery1.progressTintList = ColorStateList.valueOf(ContextCompat.getColor(requireContext(), R.color.percentUnder100)) -// binding.progressBarBattery1.progress = 100 -// val newValue = (value - 20.0f) * 5.0f -// uiColorCapacityProgressBar(newValue, binding.progressBarBattery2) -// binding.progressBarBattery2.progress = newValue.roundToInt() -// binding.progressBarBattery3.progress = 0 -// binding.progressBarBattery4.progress = 0 -// binding.progressBarBattery5.progress = 0 -// } -// value < 60.0f -> { -// binding.progressBarBattery1.progressTintList = ColorStateList.valueOf(ContextCompat.getColor(requireContext(), R.color.percentUnder100)) -// binding.progressBarBattery1.progress = 100 -// binding.progressBarBattery2.progressTintList = ColorStateList.valueOf(ContextCompat.getColor(requireContext(), R.color.percentUnder100)) -// binding.progressBarBattery2.progress = 100 -// val newValue = (value - 40.0f) * 5.0f -// uiColorCapacityProgressBar(newValue, binding.progressBarBattery3) -// binding.progressBarBattery3.progress = newValue.roundToInt() -// binding.progressBarBattery4.progress = 0 -// binding.progressBarBattery5.progress = 0 -// } -// value < 80.0f -> { -// binding.progressBarBattery1.progressTintList = ColorStateList.valueOf(ContextCompat.getColor(requireContext(), R.color.percentUnder100)) -// binding.progressBarBattery1.progress = 100 -// binding.progressBarBattery2.progressTintList = ColorStateList.valueOf(ContextCompat.getColor(requireContext(), R.color.percentUnder100)) -// binding.progressBarBattery2.progress = 100 -// binding.progressBarBattery3.progressTintList = ColorStateList.valueOf(ContextCompat.getColor(requireContext(), R.color.percentUnder100)) -// binding.progressBarBattery3.progress = 100 -// val newValue = (value - 60.0f) * 5.0f -// uiColorCapacityProgressBar(newValue, binding.progressBarBattery4) -// binding.progressBarBattery4.progress = newValue.roundToInt() -// binding.progressBarBattery5.progress = 0 -// } -// value < 100.0f -> { -// binding.progressBarBattery1.progressTintList = ColorStateList.valueOf(ContextCompat.getColor(requireContext(), R.color.percentUnder100)) -// binding.progressBarBattery1.progress = 100 -// binding.progressBarBattery2.progressTintList = ColorStateList.valueOf(ContextCompat.getColor(requireContext(), R.color.percentUnder100)) -// binding.progressBarBattery2.progress = 100 -// binding.progressBarBattery3.progressTintList = ColorStateList.valueOf(ContextCompat.getColor(requireContext(), R.color.percentUnder100)) -// binding.progressBarBattery3.progress = 100 -// binding.progressBarBattery4.progressTintList = ColorStateList.valueOf(ContextCompat.getColor(requireContext(), R.color.percentUnder100)) -// binding.progressBarBattery4.progress = 100 -// val newValue = (value - 80.0f) * 5.0f -// uiColorCapacityProgressBar(newValue, binding.progressBarBattery5) -// binding.progressBarBattery5.progress = newValue.roundToInt() -// } -// else -> { -// binding.progressBarBattery1.progressTintList = ColorStateList.valueOf(ContextCompat.getColor(requireContext(), R.color.percentUnder100)) -// binding.progressBarBattery1.progress = 100 -// binding.progressBarBattery2.progressTintList = ColorStateList.valueOf(ContextCompat.getColor(requireContext(), R.color.percentUnder100)) -// binding.progressBarBattery2.progress = 100 -// binding.progressBarBattery3.progressTintList = ColorStateList.valueOf(ContextCompat.getColor(requireContext(), R.color.percentUnder100)) -// binding.progressBarBattery3.progress = 100 -// binding.progressBarBattery4.progressTintList = ColorStateList.valueOf(ContextCompat.getColor(requireContext(), R.color.percentUnder100)) -// binding.progressBarBattery4.progress = 100 -// binding.progressBarBattery5.progressTintList = ColorStateList.valueOf(ContextCompat.getColor(requireContext(), R.color.percentUnder100)) -// binding.progressBarBattery5.progress = 100 -// } -// } -// } -// -// private fun roundTo(value: Float, decimals: Int): Float { -// if (value.isNaN() || value.isInfinite()) { -// return 0.0f -// } -// -// if (decimals == 0) { -// return value.roundToInt().toFloat() -// } -// -// val factor = 10.0.pow(decimals.toDouble()).toFloat() -// return (value * factor).roundToInt() / factor -// } -//} \ No newline at end of file + binding.labelTemperatureMax.text = roundTo(batteryData.heatsinkTemperature, 1).toString() + } + } + + private fun uiColorCapacityProgressBar(value: Float, uiElement: ProgressBar) { + // 80% skipped here because battery only gets charged to ~80 percent + when { + value < 20 -> { + uiElement.progressTintList = ColorStateList.valueOf(ContextCompat.getColor(requireContext(), R.color.percentUnder20)) + } + value < 40 -> { + uiElement.progressTintList = ColorStateList.valueOf(ContextCompat.getColor(requireContext(), R.color.percentUnder40)) + } + value < 70 -> { + uiElement.progressTintList = ColorStateList.valueOf(ContextCompat.getColor(requireContext(), R.color.percentUnder70)) + } + else -> { + uiElement.progressTintList = ColorStateList.valueOf(ContextCompat.getColor(requireContext(), R.color.percentUnder100)) + } + } + } + + private fun uiBatteryCapacityBar(value: Float) { + when { + value == 0.0f -> { + binding.progressBarBattery1.progress = 0 + binding.progressBarBattery2.progress = 0 + binding.progressBarBattery3.progress = 0 + binding.progressBarBattery4.progress = 0 + binding.progressBarBattery5.progress = 0 + } + value < 20.0f -> { + val newValue = value * 5.0f + uiColorCapacityProgressBar(newValue, binding.progressBarBattery1) + binding.progressBarBattery1.progress = newValue.roundToInt() + binding.progressBarBattery2.progress = 0 + binding.progressBarBattery3.progress = 0 + binding.progressBarBattery4.progress = 0 + binding.progressBarBattery5.progress = 0 + } + value < 40.0f -> { + binding.progressBarBattery1.progressTintList = ColorStateList.valueOf(ContextCompat.getColor(requireContext(), R.color.percentUnder100)) + binding.progressBarBattery1.progress = 100 + val newValue = (value - 20.0f) * 5.0f + uiColorCapacityProgressBar(newValue, binding.progressBarBattery2) + binding.progressBarBattery2.progress = newValue.roundToInt() + binding.progressBarBattery3.progress = 0 + binding.progressBarBattery4.progress = 0 + binding.progressBarBattery5.progress = 0 + } + value < 60.0f -> { + binding.progressBarBattery1.progressTintList = ColorStateList.valueOf(ContextCompat.getColor(requireContext(), R.color.percentUnder100)) + binding.progressBarBattery1.progress = 100 + binding.progressBarBattery2.progressTintList = ColorStateList.valueOf(ContextCompat.getColor(requireContext(), R.color.percentUnder100)) + binding.progressBarBattery2.progress = 100 + val newValue = (value - 40.0f) * 5.0f + uiColorCapacityProgressBar(newValue, binding.progressBarBattery3) + binding.progressBarBattery3.progress = newValue.roundToInt() + binding.progressBarBattery4.progress = 0 + binding.progressBarBattery5.progress = 0 + } + value < 80.0f -> { + binding.progressBarBattery1.progressTintList = ColorStateList.valueOf(ContextCompat.getColor(requireContext(), R.color.percentUnder100)) + binding.progressBarBattery1.progress = 100 + binding.progressBarBattery2.progressTintList = ColorStateList.valueOf(ContextCompat.getColor(requireContext(), R.color.percentUnder100)) + binding.progressBarBattery2.progress = 100 + binding.progressBarBattery3.progressTintList = ColorStateList.valueOf(ContextCompat.getColor(requireContext(), R.color.percentUnder100)) + binding.progressBarBattery3.progress = 100 + val newValue = (value - 60.0f) * 5.0f + uiColorCapacityProgressBar(newValue, binding.progressBarBattery4) + binding.progressBarBattery4.progress = newValue.roundToInt() + binding.progressBarBattery5.progress = 0 + } + value < 100.0f -> { + binding.progressBarBattery1.progressTintList = ColorStateList.valueOf(ContextCompat.getColor(requireContext(), R.color.percentUnder100)) + binding.progressBarBattery1.progress = 100 + binding.progressBarBattery2.progressTintList = ColorStateList.valueOf(ContextCompat.getColor(requireContext(), R.color.percentUnder100)) + binding.progressBarBattery2.progress = 100 + binding.progressBarBattery3.progressTintList = ColorStateList.valueOf(ContextCompat.getColor(requireContext(), R.color.percentUnder100)) + binding.progressBarBattery3.progress = 100 + binding.progressBarBattery4.progressTintList = ColorStateList.valueOf(ContextCompat.getColor(requireContext(), R.color.percentUnder100)) + binding.progressBarBattery4.progress = 100 + val newValue = (value - 80.0f) * 5.0f + uiColorCapacityProgressBar(newValue, binding.progressBarBattery5) + binding.progressBarBattery5.progress = newValue.roundToInt() + } + else -> { + binding.progressBarBattery1.progressTintList = ColorStateList.valueOf(ContextCompat.getColor(requireContext(), R.color.percentUnder100)) + binding.progressBarBattery1.progress = 100 + binding.progressBarBattery2.progressTintList = ColorStateList.valueOf(ContextCompat.getColor(requireContext(), R.color.percentUnder100)) + binding.progressBarBattery2.progress = 100 + binding.progressBarBattery3.progressTintList = ColorStateList.valueOf(ContextCompat.getColor(requireContext(), R.color.percentUnder100)) + binding.progressBarBattery3.progress = 100 + binding.progressBarBattery4.progressTintList = ColorStateList.valueOf(ContextCompat.getColor(requireContext(), R.color.percentUnder100)) + binding.progressBarBattery4.progress = 100 + binding.progressBarBattery5.progressTintList = ColorStateList.valueOf(ContextCompat.getColor(requireContext(), R.color.percentUnder100)) + binding.progressBarBattery5.progress = 100 + } + } + } + + private fun roundTo(value: Float, decimals: Int): Float { + if (value.isNaN() || value.isInfinite()) { + return 0.0f + } + + if (decimals == 0) { + return value.roundToInt().toFloat() + } + + val factor = 10.0.pow(decimals.toDouble()).toFloat() + return (value * factor).roundToInt() / factor + } +} \ No newline at end of file diff --git a/app/src/main/java/de/jnns/bmsmonitor/BatteryFragment1.kt b/app/src/main/java/de/jnns/bmsmonitor/BatteryFragment1.kt index f910c1a..042d400 100644 --- a/app/src/main/java/de/jnns/bmsmonitor/BatteryFragment1.kt +++ b/app/src/main/java/de/jnns/bmsmonitor/BatteryFragment1.kt @@ -158,12 +158,12 @@ class BatteryFragment1 : Fragment() { // var chargeCurrent: Float = 0.0f // var maxChargeCurrent: Float = 0.0f - binding.labelCycle.text = batteryData.cycles.toString() - binding.labelPackCapacity.text = roundTo(batteryData.capacity, 1).toString() - binding.labelDischargeCurrent.text = roundTo(batteryData.dischargeCurrent, 1).toString() - binding.labelPackVoltage.text = roundTo(batteryData.packVoltage, 1).toString() - binding.labelChargeCurrent.text = roundTo(batteryData.chargeCurrent, 1).toString() - binding.labelMaxCC.text = roundTo(batteryData.maxChargeCurrent, 1).toString() +// binding.labelCycle.text = batteryData.cycles.toString() +// binding.labelPackCapacity.text = roundTo(batteryData.capacity, 1).toString() +// binding.labelDischargeCurrent.text = roundTo(batteryData.dischargeCurrent, 1).toString() +// binding.labelPackVoltage.text = roundTo(batteryData.packVoltage, 1).toString() +// binding.labelChargeCurrent.text = roundTo(batteryData.chargeCurrent, 1).toString() +// binding.labelMaxCC.text = roundTo(batteryData.maxChargeCurrent, 1).toString() // // Power Gauge // val powerUsage = batteryData.power * -1.0f diff --git a/app/src/main/java/de/jnns/bmsmonitor/MainActivity.kt b/app/src/main/java/de/jnns/bmsmonitor/MainActivity.kt index 1768cdb..bbb760e 100644 --- a/app/src/main/java/de/jnns/bmsmonitor/MainActivity.kt +++ b/app/src/main/java/de/jnns/bmsmonitor/MainActivity.kt @@ -12,7 +12,8 @@ import androidx.navigation.findNavController import androidx.preference.PreferenceManager import de.jnns.bmsmonitor.bluetooth.BleService import de.jnns.bmsmonitor.databinding.ActivityMainBinding -import de.jnns.bmsmonitor.BatteryFragment1 +//import de.jnns.bmsmonitor.BatteryFragment1 +import de.jnns.bmsmonitor.BatteryFragment import de.jnns.bmsmonitor.services.BikeService import de.jnns.bmsmonitor.services.BmsService import io.realm.Realm @@ -54,16 +55,16 @@ class MainActivity : AppCompatActivity() { Intent(this, BleService::class.java).also { intent -> startService(intent) } } -// val batteryFragment = BatteryFragment() - val batteryFragment1 = BatteryFragment1() + val batteryFragment = BatteryFragment() +// val batteryFragment1 = BatteryFragment1() val bikeFragment = BikeFragment() val statsFragment = StatsFragment() val settingsFragment = SettingsFragment() binding.bottomNavigation.setOnNavigationItemSelectedListener { when (it.itemId) { -// R.id.page_battery -> supportFragmentManager.beginTransaction().apply { replace(R.id.nav_host_fragment, batteryFragment).commit() } - R.id.page_battery -> supportFragmentManager.beginTransaction().apply { replace(R.id.nav_host_fragment, batteryFragment1).commit() } + R.id.page_battery -> supportFragmentManager.beginTransaction().apply { replace(R.id.nav_host_fragment, batteryFragment).commit() } +// R.id.page_battery -> supportFragmentManager.beginTransaction().apply { replace(R.id.nav_host_fragment, batteryFragment1).commit() } R.id.page_bike -> supportFragmentManager.beginTransaction().apply { replace(R.id.nav_host_fragment, bikeFragment).commit() } R.id.page_stats -> supportFragmentManager.beginTransaction().apply { replace(R.id.nav_host_fragment, statsFragment).commit() } R.id.page_settings -> supportFragmentManager.beginTransaction().apply { replace(R.id.nav_host_fragment, settingsFragment).commit() } diff --git a/app/src/main/java/de/jnns/bmsmonitor/bluetooth/BmsGattClientCallback.kt b/app/src/main/java/de/jnns/bmsmonitor/bluetooth/BmsGattClientCallback.kt index a2bba4f..1c78475 100644 --- a/app/src/main/java/de/jnns/bmsmonitor/bluetooth/BmsGattClientCallback.kt +++ b/app/src/main/java/de/jnns/bmsmonitor/bluetooth/BmsGattClientCallback.kt @@ -97,6 +97,59 @@ class BmsGattClientCallback( fun isInTransaction(): Boolean { return isInTrans } +// override fun onCharacteristicChanged(gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic) { +// super.onCharacteristicChanged(gatt, characteristic) +// +// // Log.d("BluetoothGatt", "BLE Data (" + characteristic.value.size + "): " + characteristic.value.toHexString()) +// +// for (byte: Byte in characteristic.value) { +// if (uartBufferPos >= bufferSize) { +// isInFrame = false +// uartBufferPos = 0 +// uartBytesLeft = 0 +// } +// +// uartBuffer[uartBufferPos] = byte +// +// if (isInFrame) { +// if (uartBufferPos == 3) { +// uartBytesLeft = byte.toInt() +// } +// +// if (byte.toUByte() == 0x77.toUByte() && uartBytesLeft < 1) { +// isInFrame = false +// onFrameComplete(uartBufferPos) +// uartBufferPos = 0 +// uartBytesLeft = 0 +// } else { +// +// uartBufferPos++ +// uartBytesLeft-- +// } +// } else if (byte.toUByte() == 0xDD.toUByte()) { +// isInFrame = true +// uartBufferPos++ +// } +// } +// } +// +// private fun onFrameComplete(size: Int) { +// if (size <= 0) { +// return +// } +// +// val frameBytes = uartBuffer.slice(IntRange(0, size)).toByteArray() +// +// // Log.d("BluetoothGatt", "FrameData (" + frameBytes.size + "): " + frameBytes.toHexString()) +// +// if (frameBytes[1] == 0x3.toByte()) { +// val generalInfo = BmsGeneralInfoResponse(frameBytes) +// onGeneralInfoCallback(generalInfo) +// } else if (frameBytes[1] == 0x4.toByte()) { +// val cellInfo = BmsCellInfoResponse(frameBytes) +// onCellInfoCallback(cellInfo) +// } +// } override fun onCharacteristicChanged(gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic) { super.onCharacteristicChanged(gatt, characteristic) diff --git a/app/src/main/java/de/jnns/bmsmonitor/bms/BmsGeneralInfoResponse.kt b/app/src/main/java/de/jnns/bmsmonitor/bms/BmsGeneralInfoResponse.kt index 84fef06..e3e5671 100644 --- a/app/src/main/java/de/jnns/bmsmonitor/bms/BmsGeneralInfoResponse.kt +++ b/app/src/main/java/de/jnns/bmsmonitor/bms/BmsGeneralInfoResponse.kt @@ -15,13 +15,34 @@ class BmsGeneralInfoResponse(bytes: ByteArray) { // var temperatureProbeCount: Int = 0 // var temperatureProbeValues: RealmList - var cycles: Short = 0 var capacity: Float = 0.0f - var temperature: Float = 0.0f - var dischargeCurrent: Float = 0.0f + var sysTemperature: Float = 0.0f + var packCurrent: Float = 0.0f var packVoltage: Float = 0.0f - var chargeCurrent: Float = 0.0f + var maxDischargeCurrent: Float = 0.0f var maxChargeCurrent: Float = 0.0f + var bpVersion: Byte = 0 + var bpNumber: Byte = 0 + var packRSOC: Byte = 0 + var packDischargCycle: Short = 0 + var heatsinkTemperature: Float = 0.0f + var cellVoltage1: Float = 0.0f + var cellVoltage2: Float = 0.0f + var cellVoltage3: Float = 0.0f + var cellVoltage4: Float = 0.0f + var cellVoltage5: Float = 0.0f + var cellVoltage6: Float = 0.0f + var cellVoltage7: Float = 0.0f + var cellVoltage8: Float = 0.0f + var cellVoltage9: Float = 0.0f + var cellVoltage10: Float = 0.0f + var cellVoltage11: Float = 0.0f + var cellVoltage12: Float = 0.0f + var cellVoltage13: Float = 0.0f + var cellVoltage14: Float = 0.0f + var cellVoltage15: Float = 0.0f + var cellVoltage16: Float = 0.0f + init { // command = bytes[1].toInt() @@ -56,16 +77,37 @@ class BmsGeneralInfoResponse(bytes: ByteArray) { // temperatureProbeValues.add((bytesToShort(bytes[27 + (i * 2)], bytes[27 + (i * 2) + 1]) - 2731) / 10.0f) // } - packVoltage = bytesToShort(bytes[8], bytes[9]) / 100.0f - dischargeCurrent = bytesToShort(bytes[10], bytes[11]) / 10.0f - cycles = bytesToShort(bytes[6], bytes[7]) + bpVersion = bytes[0] + bpNumber = bytes[1] + packRSOC = bytes[2] capacity = bytesToShort(bytes[4], bytes[5]) / 10.0f - chargeCurrent = bytesToShort(bytes[12], bytes[13]) / 10.0f - maxChargeCurrent = bytesToShort(bytes[14], bytes[15]) / 10.0f + packVoltage = bytesToShort(bytes[8], bytes[9]) / 100.0f + packCurrent = bytesToShort(bytes[10], bytes[11]) / 100.0f + maxDischargeCurrent = bytesToShort(bytes[12], bytes[13]) / 100.0f + maxChargeCurrent = bytesToShort(bytes[14], bytes[15]) / 100.0f + packDischargCycle = bytesToShort(bytes[74], bytes[75]) // System temperature - temperature = bytesToShort(bytes[24], bytes[25]) / 10.0f + sysTemperature = bytesToShort(bytes[52], bytes[53]) / 100.0f + heatsinkTemperature = bytesToShort(bytes[54], bytes[55]) / 100.0f + + cellVoltage1 = bytesToShort(bytes[96], bytes[97]) / 1000.0f + cellVoltage2 = bytesToShort(bytes[98], bytes[99]) / 1000.0f + cellVoltage3 = bytesToShort(bytes[100], bytes[101]) / 1000.0f + cellVoltage4 = bytesToShort(bytes[102], bytes[103]) / 1000.0f + cellVoltage5 = bytesToShort(bytes[104], bytes[105]) / 1000.0f + cellVoltage6 = bytesToShort(bytes[106], bytes[107]) / 1000.0f + cellVoltage7 = bytesToShort(bytes[108], bytes[109]) / 1000.0f + cellVoltage8 = bytesToShort(bytes[110], bytes[111]) / 1000.0f + cellVoltage9 = bytesToShort(bytes[112], bytes[113]) / 1000.0f + cellVoltage10 = bytesToShort(bytes[114], bytes[115]) / 1000.0f + cellVoltage11 = bytesToShort(bytes[116], bytes[117]) / 1000.0f + cellVoltage12 = bytesToShort(bytes[118], bytes[119]) / 1000.0f + cellVoltage13 = bytesToShort(bytes[120], bytes[121]) / 1000.0f + cellVoltage14 = bytesToShort(bytes[122], bytes[123]) / 1000.0f + cellVoltage15 = bytesToShort(bytes[124], bytes[125]) / 1000.0f + cellVoltage16 = bytesToShort(bytes[126], bytes[127]) / 1000.0f } private fun bytesToShort(h: Byte, l: Byte, order: ByteOrder = ByteOrder.BIG_ENDIAN): Short { diff --git a/app/src/main/java/de/jnns/bmsmonitor/data/BatteryData.kt b/app/src/main/java/de/jnns/bmsmonitor/data/BatteryData.kt index 379f8e3..1b7aaa3 100644 --- a/app/src/main/java/de/jnns/bmsmonitor/data/BatteryData.kt +++ b/app/src/main/java/de/jnns/bmsmonitor/data/BatteryData.kt @@ -15,14 +15,44 @@ open class BatteryData( var temperatures: RealmList = RealmList(), var cellVoltages: RealmList = RealmList(), - var cycles: Short = 0, - var capacity: Float = 0.0f, - var temperature: Float = 0.0f, - var dischargeCurrent: Float = 0.0f, - var packVoltage: Float = 0.0f, - var chargeCurrent: Float = 0.0f, - var maxChargeCurrent: Float = 0.0f +// var cycles: Short = 0, +// var capacity: Float = 0.0f, +// var temperature: Float = 0.0f, +// var dischargeCurrent: Float = 0.0f, +// var packVoltage: Float = 0.0f, +// var chargeCurrent: Float = 0.0f, +// var maxChargeCurrent: Float = 0.0f + + ) : RealmObject() { + var capacity: Float = 0.0f + var sysTemperature: Float = 0.0f + var packCurrent: Float = 0.0f + var packVoltage: Float = 0.0f + var maxDischargeCurrent: Float = 0.0f + var maxChargeCurrent: Float = 0.0f + var bpVersion: Byte = 0 + var bpNumber: Byte = 0 + var packRSOC: Byte = 0 + var packDischargCycle: Short = 0 + var heatsinkTemperature: Float = 0.0f + var cellVoltage1: Float = 0.0f + var cellVoltage2: Float = 0.0f + var cellVoltage3: Float = 0.0f + var cellVoltage4: Float = 0.0f + var cellVoltage5: Float = 0.0f + var cellVoltage6: Float = 0.0f + var cellVoltage7: Float = 0.0f + var cellVoltage8: Float = 0.0f + var cellVoltage9: Float = 0.0f + var cellVoltage10: Float = 0.0f + var cellVoltage11: Float = 0.0f + var cellVoltage12: Float = 0.0f + var cellVoltage13: Float = 0.0f + var cellVoltage14: Float = 0.0f + var cellVoltage15: Float = 0.0f + var cellVoltage16: Float = 0.0f + val voltage: Float get() { return cellVoltages.sum() diff --git a/app/src/main/java/de/jnns/bmsmonitor/services/BmsService.kt b/app/src/main/java/de/jnns/bmsmonitor/services/BmsService.kt index 23f2a38..d00c8fd 100644 --- a/app/src/main/java/de/jnns/bmsmonitor/services/BmsService.kt +++ b/app/src/main/java/de/jnns/bmsmonitor/services/BmsService.kt @@ -107,13 +107,42 @@ class BmsService : Service() { // batteryData.temperatureCount = generalInfo.temperatureProbeCount // batteryData.temperatures = generalInfo.temperatureProbeValues // batteryData.vol = generalInfo.totalVoltage - batteryData.packVoltage = generalInfo.packVoltage - batteryData.dischargeCurrent = generalInfo.dischargeCurrent + batteryData.capacity = generalInfo.capacity - batteryData.cycles = generalInfo.cycles - batteryData.chargeCurrent = generalInfo.chargeCurrent + batteryData.sysTemperature = generalInfo.sysTemperature + batteryData.packCurrent = generalInfo.packCurrent + batteryData.packVoltage = generalInfo.packVoltage + batteryData.maxDischargeCurrent = generalInfo.maxDischargeCurrent batteryData.maxChargeCurrent = generalInfo.maxChargeCurrent - batteryData.temperature = generalInfo.temperature + batteryData.bpVersion = generalInfo.bpVersion + batteryData.bpNumber = generalInfo.bpNumber + batteryData.packRSOC = generalInfo.packRSOC + batteryData.packDischargCycle = generalInfo.packDischargCycle + batteryData.heatsinkTemperature = generalInfo.heatsinkTemperature + batteryData.cellVoltage1 = generalInfo.cellVoltage1 + batteryData.cellVoltage2 = generalInfo.cellVoltage2 + batteryData.cellVoltage3 = generalInfo.cellVoltage3 + batteryData.cellVoltage4 = generalInfo.cellVoltage4 + batteryData.cellVoltage5 = generalInfo.cellVoltage5 + batteryData.cellVoltage6 = generalInfo.cellVoltage6 + batteryData.cellVoltage7 = generalInfo.cellVoltage7 + batteryData.cellVoltage8 = generalInfo.cellVoltage8 + batteryData.cellVoltage9 = generalInfo.cellVoltage9 + batteryData.cellVoltage10 = generalInfo.cellVoltage10 + batteryData.cellVoltage11 = generalInfo.cellVoltage11 + batteryData.cellVoltage12 = generalInfo.cellVoltage12 + batteryData.cellVoltage13 = generalInfo.cellVoltage13 + batteryData.cellVoltage14 = generalInfo.cellVoltage14 + batteryData.cellVoltage15 = generalInfo.cellVoltage15 + batteryData.cellVoltage16 = generalInfo.cellVoltage16 + +// batteryData.packVoltage = generalInfo.packVoltage +// batteryData.dischargeCurrent = generalInfo.dischargeCurrent +// batteryData.capacity = generalInfo.capacity +// batteryData.cycles = generalInfo.cycles +// batteryData.chargeCurrent = generalInfo.chargeCurrent +// batteryData.maxChargeCurrent = generalInfo.maxChargeCurrent +// batteryData.temperature = generalInfo.temperature generalInfoReceived = true sendData() diff --git a/app/src/main/res/layout/fragment_battery.xml b/app/src/main/res/layout/fragment_battery.xml index ebf8669..300cc91 100644 --- a/app/src/main/res/layout/fragment_battery.xml +++ b/app/src/main/res/layout/fragment_battery.xml @@ -80,16 +80,17 @@ app:layout_constraintTop_toTopOf="@+id/speedViewSpeed" /> + app:layout_constraintEnd_toStartOf="@+id/labelPower" + /> - + + + + + + + + + + + + + + app:layout_constraintTop_toTopOf="@+id/imageViewBattery" /> + app:layout_constraintBottom_toBottomOf="@+id/labelVoltage" + app:layout_constraintStart_toEndOf="@+id/labelVoltage" /> @@ -375,7 +376,7 @@ app:layout_flexBasisPercent="50%"> + app:layout_constraintTop_toTopOf="@+id/imageViewCapacityPercent" /> + app:layout_constraintBottom_toBottomOf="@+id/labelCurrent" + app:layout_constraintStart_toEndOf="@+id/labelCurrent" /> @@ -467,7 +468,7 @@ android:layout_marginStart="8dp" android:layout_marginBottom="3dp" android:fontFamily="@font/aldrich" - android:text="@string/unitDisplayWattHours" + android:text="@string/unitDisplayAmpereHours" android:textColor="@color/darkBorder" android:textSize="24sp" android:typeface="monospace" diff --git a/app/src/main/res/navigation/nav_graph.xml b/app/src/main/res/navigation/nav_graph.xml index 3ca88d2..662c865 100644 --- a/app/src/main/res/navigation/nav_graph.xml +++ b/app/src/main/res/navigation/nav_graph.xml @@ -12,9 +12,9 @@ + tools:layout="@layout/fragment_battery" /> BMS Monitor - Stats Waiting for BMS… - BMS: %1$s + BMS: %1$s/%2$s Bike: %1$s Power @@ -26,7 +26,7 @@ 0 0.0 - 00:00 + Cycle: W V From bcf256cabee86b0dbda4e34af225911005a96b4a Mon Sep 17 00:00:00 2001 From: Jcheng Lee Date: Thu, 29 Jun 2023 22:28:50 +0800 Subject: [PATCH 13/21] Phase one release --- .../de/jnns/bmsmonitor/BatteryFragment.kt | 107 ++++++++++++------ .../de/jnns/bmsmonitor/services/BmsService.kt | 5 +- app/src/main/res/layout/fragment_battery.xml | 57 ++++++---- app/src/main/res/values/strings.xml | 4 +- 4 files changed, 116 insertions(+), 57 deletions(-) diff --git a/app/src/main/java/de/jnns/bmsmonitor/BatteryFragment.kt b/app/src/main/java/de/jnns/bmsmonitor/BatteryFragment.kt index f783322..695ad83 100644 --- a/app/src/main/java/de/jnns/bmsmonitor/BatteryFragment.kt +++ b/app/src/main/java/de/jnns/bmsmonitor/BatteryFragment.kt @@ -98,31 +98,58 @@ class BatteryFragment : Fragment() { minCellVoltage = PreferenceManager.getDefaultSharedPreferences(requireContext()).getString("minCellVoltage", "2500")!!.toInt() maxCellVoltage = PreferenceManager.getDefaultSharedPreferences(requireContext()).getString("maxCellVoltage", "4200")!!.toInt() - binding.barchartCells.setNoDataTextColor(requireActivity().getColor(R.color.white)) - binding.barchartCells.setNoDataText("...") + binding.barchartCells1.setNoDataTextColor(requireActivity().getColor(R.color.white)) + binding.barchartCells1.setNoDataText("...") - binding.barchartCells.setPinchZoom(false) - binding.barchartCells.setTouchEnabled(false) - binding.barchartCells.isClickable = false - binding.barchartCells.isDoubleTapToZoomEnabled = false + binding.barchartCells1.setPinchZoom(false) + binding.barchartCells1.setTouchEnabled(false) + binding.barchartCells1.isClickable = false + binding.barchartCells1.isDoubleTapToZoomEnabled = false - binding.barchartCells.setDrawBorders(false) - binding.barchartCells.setDrawValueAboveBar(true) - binding.barchartCells.setDrawBorders(false) - binding.barchartCells.setDrawGridBackground(false) + binding.barchartCells1.setDrawBorders(false) + binding.barchartCells1.setDrawValueAboveBar(true) + binding.barchartCells1.setDrawBorders(false) + binding.barchartCells1.setDrawGridBackground(false) - binding.barchartCells.description.isEnabled = false - binding.barchartCells.legend.isEnabled = false + binding.barchartCells1.description.isEnabled = false + binding.barchartCells1.legend.isEnabled = false - binding.barchartCells.axisLeft.setDrawGridLines(false) - binding.barchartCells.axisLeft.setDrawLabels(false) - binding.barchartCells.axisLeft.setDrawAxisLine(false) + binding.barchartCells1.axisLeft.setDrawGridLines(false) + binding.barchartCells1.axisLeft.setDrawLabels(false) + binding.barchartCells1.axisLeft.setDrawAxisLine(false) - binding.barchartCells.xAxis.setDrawGridLines(false) - binding.barchartCells.xAxis.setDrawLabels(false) - binding.barchartCells.xAxis.setDrawAxisLine(false) + binding.barchartCells1.xAxis.setDrawGridLines(false) + binding.barchartCells1.xAxis.setDrawLabels(false) + binding.barchartCells1.xAxis.setDrawAxisLine(false) - binding.barchartCells.axisRight.isEnabled = false + binding.barchartCells1.axisRight.isEnabled = false + + + binding.barchartCells2.setNoDataTextColor(requireActivity().getColor(R.color.white)) + binding.barchartCells2.setNoDataText("...") + + binding.barchartCells2.setPinchZoom(false) + binding.barchartCells2.setTouchEnabled(false) + binding.barchartCells2.isClickable = false + binding.barchartCells2.isDoubleTapToZoomEnabled = false + + binding.barchartCells2.setDrawBorders(false) + binding.barchartCells2.setDrawValueAboveBar(true) + binding.barchartCells2.setDrawBorders(false) + binding.barchartCells2.setDrawGridBackground(false) + + binding.barchartCells2.description.isEnabled = false + binding.barchartCells2.legend.isEnabled = false + + binding.barchartCells2.axisLeft.setDrawGridLines(false) + binding.barchartCells2.axisLeft.setDrawLabels(false) + binding.barchartCells2.axisLeft.setDrawAxisLine(false) + + binding.barchartCells2.xAxis.setDrawGridLines(false) + binding.barchartCells2.xAxis.setDrawLabels(false) + binding.barchartCells2.xAxis.setDrawAxisLine(false) + + binding.barchartCells2.axisRight.isEnabled = false } override fun onResume() { @@ -227,25 +254,38 @@ class BatteryFragment : Fragment() { cellBars.add(BarEntry(5.toFloat(), batteryData.cellVoltage6)) cellBars.add(BarEntry(6.toFloat(), batteryData.cellVoltage7)) cellBars.add(BarEntry(7.toFloat(), batteryData.cellVoltage8)) - cellBars.add(BarEntry(8.toFloat(), batteryData.cellVoltage9)) - cellBars.add(BarEntry(9.toFloat(), batteryData.cellVoltage10)) - cellBars.add(BarEntry(10.toFloat(), batteryData.cellVoltage11)) - cellBars.add(BarEntry(11.toFloat(), batteryData.cellVoltage12)) - cellBars.add(BarEntry(12.toFloat(), batteryData.cellVoltage13)) - cellBars.add(BarEntry(13.toFloat(), batteryData.cellVoltage14)) - cellBars.add(BarEntry(14.toFloat(), batteryData.cellVoltage15)) - cellBars.add(BarEntry(15.toFloat(), batteryData.cellVoltage16)) val barDataSetVoltage = BarDataSet(cellBars, "Cell Voltages") barDataSetVoltage.valueTextColor = requireActivity().getColor(R.color.white) barDataSetVoltage.valueTextSize = 12.0f - barDataSetVoltage.valueFormatter = DefaultValueFormatter(2) + barDataSetVoltage.valueFormatter = DefaultValueFormatter(3) barDataSetVoltage.setColors(requireActivity().getColor(R.color.primary)) val barData = BarData(barDataSetVoltage) - binding.barchartCells.data = barData - binding.barchartCells.invalidate() + binding.barchartCells1.data = barData + binding.barchartCells1.invalidate() + + + val cellBars1 = ArrayList() + + cellBars1.add(BarEntry(8.toFloat(), batteryData.cellVoltage9)) + cellBars1.add(BarEntry(9.toFloat(), batteryData.cellVoltage10)) + cellBars1.add(BarEntry(10.toFloat(), batteryData.cellVoltage11)) + cellBars1.add(BarEntry(11.toFloat(), batteryData.cellVoltage12)) + cellBars1.add(BarEntry(12.toFloat(), batteryData.cellVoltage13)) + cellBars1.add(BarEntry(13.toFloat(), batteryData.cellVoltage14)) + cellBars1.add(BarEntry(14.toFloat(), batteryData.cellVoltage15)) + cellBars1.add(BarEntry(15.toFloat(), batteryData.cellVoltage16)) + + val barDataSetVoltage1 = BarDataSet(cellBars, "Cell Voltages") + barDataSetVoltage1.valueTextColor = requireActivity().getColor(R.color.white) + barDataSetVoltage1.valueTextSize = 12.0f + barDataSetVoltage1.valueFormatter = DefaultValueFormatter(3) + barDataSetVoltage1.setColors(requireActivity().getColor(R.color.primary)) + val barData1 = BarData(barDataSetVoltage) + binding.barchartCells2.data = barData1 + binding.barchartCells2.invalidate() // val cellBars = ArrayList() @@ -278,9 +318,12 @@ class BatteryFragment : Fragment() { // Row 2 // binding.labelCurrent.text = String.format(Locale.US, "%.1f", roundTo(batteryData.current, 1)) - binding.labelCurrent.text = String.format(Locale.US, "%.1f", roundTo(batteryData.packCurrent, 1)) +// binding.labelCurrent.text = String.format(Locale.US, "%.1f", roundTo(batteryData.packCurrent, 1)) +// batteryData.packCurrent = 3.14f + binding.labelCurrent.text = roundTo(batteryData.packCurrent, 1).toString() // binding.labelCapacityWh.text = batteryData.watthours.roundToInt().toString() - binding.labelCapacityWh.text = batteryData.capacity.roundToInt().toString() +// binding.labelCapacityWh.text = batteryData.capacity.roundToInt().toString() + binding.labelCapacityWh.text = roundTo(batteryData.capacity, 1).toString() // Row 3 // binding.labelTemperature.text = roundTo(batteryData.avgTemperature, 1).toString() diff --git a/app/src/main/java/de/jnns/bmsmonitor/services/BmsService.kt b/app/src/main/java/de/jnns/bmsmonitor/services/BmsService.kt index d00c8fd..2f8280b 100644 --- a/app/src/main/java/de/jnns/bmsmonitor/services/BmsService.kt +++ b/app/src/main/java/de/jnns/bmsmonitor/services/BmsService.kt @@ -208,10 +208,11 @@ class BmsService : Service() { // } // // dataModeSwitch = !dataModeSwitch - if (gattClientCallback.isInTransaction() == false) { + +// if (gattClientCallback.isInTransaction() == false) { Log.d("BMS", "writeBytes():" + cmdGeneralInfo.toHexString()) writeBytes(cmdGeneralInfo) - } +// } dataHandler.postDelayed(this, dataPollDelay) } diff --git a/app/src/main/res/layout/fragment_battery.xml b/app/src/main/res/layout/fragment_battery.xml index 300cc91..2cd64ec 100644 --- a/app/src/main/res/layout/fragment_battery.xml +++ b/app/src/main/res/layout/fragment_battery.xml @@ -242,9 +242,9 @@ app:layout_constraintTop_toTopOf="@+id/progressBarBattery3" /> + @@ -372,7 +387,7 @@ @@ -440,7 +455,7 @@ @@ -508,7 +523,7 @@ @@ -576,7 +591,7 @@ @@ -644,7 +659,7 @@ diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 7a5721a..e62eda8 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1,12 +1,12 @@ BMS Monitor - BMS + UOOK BMS Monitor BMS Monitor - Settings BMS Monitor - Stats Waiting for BMS… - BMS: %1$s/%2$s + BMS: %2$02x%1$02x Bike: %1$s Power From 89a1029bd2a1d9167f2e2ee5877ad0ec957c3f58 Mon Sep 17 00:00:00 2001 From: Jcheng Lee Date: Fri, 30 Jun 2023 14:28:51 +0800 Subject: [PATCH 14/21] Version with code pruned. --- .../de/jnns/bmsmonitor/BatteryFragment.kt | 85 +---- .../de/jnns/bmsmonitor/BatteryFragment1.kt | 359 ------------------ .../java/de/jnns/bmsmonitor/MainActivity.kt | 2 - .../bluetooth/BikeGattClientCallback.kt | 4 - .../jnns/bmsmonitor/bluetooth/BleManager.kt | 2 - .../jnns/bmsmonitor/bluetooth/BleService.kt | 2 - .../bluetooth/BmsGattClientCallback.kt | 67 ---- .../bmsmonitor/bms/BmsGeneralInfoResponse.kt | 42 -- .../de/jnns/bmsmonitor/services/BmsService.kt | 34 +- app/src/main/res/layout/fragment_bms.xml | 356 ----------------- 10 files changed, 5 insertions(+), 948 deletions(-) delete mode 100644 app/src/main/java/de/jnns/bmsmonitor/BatteryFragment1.kt delete mode 100644 app/src/main/res/layout/fragment_bms.xml diff --git a/app/src/main/java/de/jnns/bmsmonitor/BatteryFragment.kt b/app/src/main/java/de/jnns/bmsmonitor/BatteryFragment.kt index 695ad83..5e397c6 100644 --- a/app/src/main/java/de/jnns/bmsmonitor/BatteryFragment.kt +++ b/app/src/main/java/de/jnns/bmsmonitor/BatteryFragment.kt @@ -50,9 +50,6 @@ class BatteryFragment : Fragment() { override fun onReceive(context: Context, intent: Intent) { try { val msg: String = intent.getStringExtra("batteryData")!! - -// binding.labelStatus.text = String.format(resources.getString(R.string.connectedToBms), intent.getStringExtra("deviceName")) - if (msg.isNotEmpty()) { updateUi(Gson().fromJson(msg, BatteryData::class.java)) } @@ -175,7 +172,6 @@ class BatteryFragment : Fragment() { binding.labelStatus.text = String.format(resources.getString(R.string.connectedToBms), batteryData.bpVersion, batteryData.bpNumber) // Power Gauge -// val powerUsage = batteryData.power * -1.0f val powerUsage = batteryData.packCurrent * batteryData.packVoltage var chargePower:Float = 0.0f @@ -183,22 +179,13 @@ class BatteryFragment : Fragment() { if(batteryData.packCurrent >= 0.0f) { chargePower = batteryData.packCurrent / batteryData.maxChargeCurrent -// if(chargePower > 1.0f) { -// chargePower = 1.0f * -500 -// } else { - chargePower = chargePower * -500 -// } + chargePower = chargePower * -500 } else { chargePower = batteryData.packCurrent / batteryData.maxDischargeCurrent -// if(chargePower > 1.0f) { -// chargePower = 1.0f * -1000 -// } else { - chargePower = chargePower * -1000 -// } + chargePower = chargePower * -1000 } -// chargePower = chargePower * -1 + binding.speedViewSpeed.speedTo(chargePower, 1000) -// binding.speedViewSpeed.speedTo(1000.0f, 1000) if (powerUsage < 0.0f) { binding.labelPower.text = "+${powerUsage.roundToInt() * -1}" @@ -206,44 +193,8 @@ class BatteryFragment : Fragment() { binding.labelPower.text = powerUsage.roundToInt().toString() } - // TODO: David Review -// // Remaining Time -// wattValues[smoothIndex] = batteryData.power -// ++smoothIndex -// -// if (smoothIndex == smoothCount) { -// smoothIndex = 0 -// } -// -// var averagePower = wattValues.filter { x -> x != 0.0f }.average().toFloat() -// -// if (averagePower < 0.0f) { -// averagePower *= -1 -// } -// -// val wattSeconds: Float = when { -// batteryData.power < 0.0f -> { -// (batteryData.watthours / averagePower) * 3600 -// } -// batteryData.power > 0.0f -> { -// ((batteryData.totalWatthours - batteryData.watthours) / averagePower) * 3600 -// } -// else -> { -// 0.0f -// } -// } -// -// if (batteryData.power == 0.0f) { -// wattValues.fill(0.0f) -// } -// -// val remainingMinutes = (wattSeconds % 3600) / 60 -// val remainingHours = (wattSeconds - remainingMinutes) / 3600 - -// binding.labelTime.text = String.format("%02d:%02d", remainingHours.roundToInt(), remainingMinutes.roundToInt()) binding.labelCycle.text = String.format("Cycle:%s", batteryData.packDischargCycle.toString()) - // TODO: David Modify // Cell Bar-Diagram val cellBars = ArrayList() cellBars.add(BarEntry(0.toFloat(), batteryData.cellVoltage1)) @@ -265,7 +216,6 @@ class BatteryFragment : Fragment() { binding.barchartCells1.data = barData binding.barchartCells1.invalidate() - val cellBars1 = ArrayList() cellBars1.add(BarEntry(8.toFloat(), batteryData.cellVoltage9)) @@ -287,25 +237,6 @@ class BatteryFragment : Fragment() { binding.barchartCells2.data = barData1 binding.barchartCells2.invalidate() - -// val cellBars = ArrayList() -// -// for ((i, cell) in batteryData.cellVoltages.withIndex()) { -// cellBars.add(BarEntry(i.toFloat(), cell)) -// } -// -// val barDataSetVoltage = BarDataSet(cellBars, "Cell Voltages") -// barDataSetVoltage.valueTextColor = requireActivity().getColor(R.color.white) -// barDataSetVoltage.valueTextSize = 12.0f -// barDataSetVoltage.valueFormatter = DefaultValueFormatter(2) -// barDataSetVoltage.setColors(requireActivity().getColor(R.color.primary)) -// -// val barData = BarData(barDataSetVoltage) -// binding.barchartCells.data = barData -// binding.barchartCells.invalidate() - - // Row 1 -// val totalPercentage = ((batteryData.percentage) * 1000.0f).roundToInt() / 10.0f var totalPercentage = batteryData.packRSOC.toUByte() if(totalPercentage > 100U) { totalPercentage = 100U @@ -316,20 +247,10 @@ class BatteryFragment : Fragment() { uiBatteryCapacityBar(totalPercentage.toFloat()) - // Row 2 -// binding.labelCurrent.text = String.format(Locale.US, "%.1f", roundTo(batteryData.current, 1)) -// binding.labelCurrent.text = String.format(Locale.US, "%.1f", roundTo(batteryData.packCurrent, 1)) -// batteryData.packCurrent = 3.14f binding.labelCurrent.text = roundTo(batteryData.packCurrent, 1).toString() -// binding.labelCapacityWh.text = batteryData.watthours.roundToInt().toString() -// binding.labelCapacityWh.text = batteryData.capacity.roundToInt().toString() binding.labelCapacityWh.text = roundTo(batteryData.capacity, 1).toString() - // Row 3 -// binding.labelTemperature.text = roundTo(batteryData.avgTemperature, 1).toString() -// binding.labelTemperature.text = roundTo(batteryData.temperature, 1).toString() binding.labelTemperature.text = roundTo(batteryData.sysTemperature, 1).toString() -// binding.labelTemperatureMax.text = roundTo(batteryData.maxTemperature, 1).toString() binding.labelTemperatureMax.text = roundTo(batteryData.heatsinkTemperature, 1).toString() } } diff --git a/app/src/main/java/de/jnns/bmsmonitor/BatteryFragment1.kt b/app/src/main/java/de/jnns/bmsmonitor/BatteryFragment1.kt deleted file mode 100644 index 042d400..0000000 --- a/app/src/main/java/de/jnns/bmsmonitor/BatteryFragment1.kt +++ /dev/null @@ -1,359 +0,0 @@ -package de.jnns.bmsmonitor - -import android.content.BroadcastReceiver -import android.content.Context -import android.content.Intent -import android.content.IntentFilter -import android.content.res.ColorStateList -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import android.widget.ProgressBar -import android.widget.TextView -import androidx.core.content.ContextCompat -import androidx.fragment.app.Fragment -import androidx.localbroadcastmanager.content.LocalBroadcastManager -import androidx.navigation.findNavController -import androidx.preference.PreferenceManager -import com.github.anastr.speedviewlib.components.Section -import com.github.mikephil.charting.data.BarData -import com.github.mikephil.charting.data.BarDataSet -import com.github.mikephil.charting.data.BarEntry -import com.github.mikephil.charting.formatter.DefaultValueFormatter -import com.google.gson.Gson -import de.jnns.bmsmonitor.R -import de.jnns.bmsmonitor.data.BatteryData -//import de.jnns.bmsmonitor.databinding.FragmentBatteryBinding -import de.jnns.bmsmonitor.databinding.FragmentBmsBinding -import java.util.* -import kotlin.math.pow -import kotlin.math.round -import kotlin.math.roundToInt - - -@ExperimentalUnsignedTypes -class BatteryFragment1 : Fragment() { -// private var _binding: FragmentBatteryBinding? = null - private var _binding: FragmentBmsBinding? = null - private val binding get() = _binding!! - - // no need to refresh data in the background - private var isInForeground = false - - // time calculation smoothing - // default 5 min - private var smoothCount = 60 * 5 - private var smoothIndex = 0 - private var wattValues = FloatArray(smoothCount) - - private var minCellVoltage = 0 - private var maxCellVoltage = 0 - - private val mMessageReceiver: BroadcastReceiver = object : BroadcastReceiver() { - override fun onReceive(context: Context, intent: Intent) { - try { - val msg: String = intent.getStringExtra("batteryData")!! - - if (msg.isNotEmpty()) { - updateUi(Gson().fromJson(msg, BatteryData::class.java)) - } - } catch (ex: Exception) { - // ignored - } - } - } - - override fun onCreateView( - inflater: LayoutInflater, - container: ViewGroup?, - savedInstanceState: Bundle? - ): View { - LocalBroadcastManager.getInstance(requireContext()).registerReceiver(mMessageReceiver, IntentFilter("bmsDataIntent")) -// _binding = FragmentBatteryBinding.inflate(inflater, container, false) - _binding = FragmentBmsBinding.inflate(inflater, container, false) - return binding.root - } - - override fun onDestroy() { - super.onDestroy() - _binding = null - LocalBroadcastManager.getInstance(requireContext()).unregisterReceiver(mMessageReceiver) - } - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - -// binding.speedViewSpeed.clearSections() -// binding.speedViewSpeed.addSections( -// Section(0.00000000f, 0.11111111f, ContextCompat.getColor(requireContext(), R.color.batteryChargeHigh), 72.0f), -// Section(0.11111111f, 0.22222222f, ContextCompat.getColor(requireContext(), R.color.batteryChargeMedium), 72.0f), -// Section(0.22222222f, 0.33333333f, ContextCompat.getColor(requireContext(), R.color.batteryChargeLow), 72.0f), -// Section(0.33333333f, 0.44444444f, ContextCompat.getColor(requireContext(), R.color.batteryDischargeLow), 72.0f), -// Section(0.44444444f, 0.66666666f, ContextCompat.getColor(requireContext(), R.color.batteryDischargeMedium), 72.0f), -// Section(0.66666666f, 0.88888888f, ContextCompat.getColor(requireContext(), R.color.batteryDischargeHigh), 72.0f), -// Section(0.88888888f, 1.00000000f, ContextCompat.getColor(requireContext(), R.color.batteryDischargeHighest), 72.0f) -// ) -// -// binding.speedViewSpeed.minSpeed = PreferenceManager.getDefaultSharedPreferences(requireContext()).getString("minPower", "-500")!!.toFloat() -// binding.speedViewSpeed.maxSpeed = PreferenceManager.getDefaultSharedPreferences(requireContext()).getString("maxPower", "1000")!!.toFloat() -// -// minCellVoltage = PreferenceManager.getDefaultSharedPreferences(requireContext()).getString("minCellVoltage", "2500")!!.toInt() -// maxCellVoltage = PreferenceManager.getDefaultSharedPreferences(requireContext()).getString("maxCellVoltage", "4200")!!.toInt() -// -// binding.barchartCells.setNoDataTextColor(requireActivity().getColor(R.color.white)) -// binding.barchartCells.setNoDataText("...") -// -// binding.barchartCells.setPinchZoom(false) -// binding.barchartCells.setTouchEnabled(false) -// binding.barchartCells.isClickable = false -// binding.barchartCells.isDoubleTapToZoomEnabled = false -// -// binding.barchartCells.setDrawBorders(false) -// binding.barchartCells.setDrawValueAboveBar(true) -// binding.barchartCells.setDrawBorders(false) -// binding.barchartCells.setDrawGridBackground(false) -// -// binding.barchartCells.description.isEnabled = false -// binding.barchartCells.legend.isEnabled = false -// -// binding.barchartCells.axisLeft.setDrawGridLines(false) -// binding.barchartCells.axisLeft.setDrawLabels(false) -// binding.barchartCells.axisLeft.setDrawAxisLine(false) -// -// binding.barchartCells.xAxis.setDrawGridLines(false) -// binding.barchartCells.xAxis.setDrawLabels(false) -// binding.barchartCells.xAxis.setDrawAxisLine(false) -// -// binding.barchartCells.axisRight.isEnabled = false - } - - override fun onResume() { - super.onResume() - - isInForeground = true - -// binding.speedViewSpeed.speedTo(0.0f, 0) -// binding.labelStatus.text = getString(R.string.waitForBms) - } - - override fun onPause() { - super.onPause() - isInForeground = false - } - - private fun updateUi(batteryData: BatteryData) { - if (!isInForeground) { - return - } - - requireActivity().runOnUiThread { -// binding.labelTemperature.text = roundTo(batteryData.temperature, 1).toString() - -// var cycles: Short = 0 -// var capacity: Float = 0.0f -// var temperature: Float = 0.0f -// var dischargeCurrent: Float = 0.0f -// var packVoltage: Float = 0.0f -// var chargeCurrent: Float = 0.0f -// var maxChargeCurrent: Float = 0.0f - -// binding.labelCycle.text = batteryData.cycles.toString() -// binding.labelPackCapacity.text = roundTo(batteryData.capacity, 1).toString() -// binding.labelDischargeCurrent.text = roundTo(batteryData.dischargeCurrent, 1).toString() -// binding.labelPackVoltage.text = roundTo(batteryData.packVoltage, 1).toString() -// binding.labelChargeCurrent.text = roundTo(batteryData.chargeCurrent, 1).toString() -// binding.labelMaxCC.text = roundTo(batteryData.maxChargeCurrent, 1).toString() - -// // Power Gauge -// val powerUsage = batteryData.power * -1.0f -// -// binding.speedViewSpeed.speedTo(powerUsage, 1000) -// -// if (powerUsage < 0.0f) { -// binding.labelPower.text = "+${powerUsage.roundToInt() * -1}" -// } else { -// binding.labelPower.text = powerUsage.roundToInt().toString() -// } -// -// // Remaining Time -// wattValues[smoothIndex] = batteryData.power -// ++smoothIndex -// -// if (smoothIndex == smoothCount) { -// smoothIndex = 0 -// } -// -// var averagePower = wattValues.filter { x -> x != 0.0f }.average().toFloat() -// -// if (averagePower < 0.0f) { -// averagePower *= -1 -// } -// -// val wattSeconds: Float = when { -// batteryData.power < 0.0f -> { -// (batteryData.watthours / averagePower) * 3600 -// } -// batteryData.power > 0.0f -> { -// ((batteryData.totalWatthours - batteryData.watthours) / averagePower) * 3600 -// } -// else -> { -// 0.0f -// } -// } -// -// if (batteryData.power == 0.0f) { -// wattValues.fill(0.0f) -// } -// -// val remainingMinutes = (wattSeconds % 3600) / 60 -// val remainingHours = (wattSeconds - remainingMinutes) / 3600 -// -// binding.labelTime.text = String.format("%02d:%02d", remainingHours.roundToInt(), remainingMinutes.roundToInt()) -// -// // Cell Bar-Diagram -// val cellBars = ArrayList() -// -// for ((i, cell) in batteryData.cellVoltages.withIndex()) { -// cellBars.add(BarEntry(i.toFloat(), cell)) -// } -// -// val barDataSetVoltage = BarDataSet(cellBars, "Cell Voltages") -// barDataSetVoltage.valueTextColor = requireActivity().getColor(R.color.white) -// barDataSetVoltage.valueTextSize = 12.0f -// barDataSetVoltage.valueFormatter = DefaultValueFormatter(2) -// barDataSetVoltage.setColors(requireActivity().getColor(R.color.primary)) -// -// val barData = BarData(barDataSetVoltage) -// binding.barchartCells.data = barData -// binding.barchartCells.invalidate() -// -// // Row 1 -// val totalPercentage = ((batteryData.percentage) * 1000.0f).roundToInt() / 10.0f -// -// binding.labelVoltage.text = roundTo(batteryData.voltage, 1).toString() -// binding.labelPercentage.text = totalPercentage.toString() -// -// uiBatteryCapacityBar(totalPercentage) -// -// // Row 2 -// binding.labelCurrent.text = String.format(Locale.US, "%.1f", roundTo(batteryData.current, 1)) -// binding.labelCapacityWh.text = batteryData.watthours.roundToInt().toString() -// -// // Row 3 -//// binding.labelTemperature.text = roundTo(batteryData.avgTemperature, 1).toString() -// binding.labelTemperature.text = roundTo(batteryData.temperature, 1).toString() -// binding.labelTemperatureMax.text = roundTo(batteryData.maxTemperature, 1).toString() - } - } - -// private fun uiColorCapacityProgressBar(value: Float, uiElement: ProgressBar) { -// // 80% skipped here because battery only gets charged to ~80 percent -// when { -// value < 20 -> { -// uiElement.progressTintList = ColorStateList.valueOf(ContextCompat.getColor(requireContext(), R.color.percentUnder20)) -// } -// value < 40 -> { -// uiElement.progressTintList = ColorStateList.valueOf(ContextCompat.getColor(requireContext(), R.color.percentUnder40)) -// } -// value < 70 -> { -// uiElement.progressTintList = ColorStateList.valueOf(ContextCompat.getColor(requireContext(), R.color.percentUnder70)) -// } -// else -> { -// uiElement.progressTintList = ColorStateList.valueOf(ContextCompat.getColor(requireContext(), R.color.percentUnder100)) -// } -// } -// } - -// private fun uiBatteryCapacityBar(value: Float) { -// when { -// value == 0.0f -> { -// binding.progressBarBattery1.progress = 0 -// binding.progressBarBattery2.progress = 0 -// binding.progressBarBattery3.progress = 0 -// binding.progressBarBattery4.progress = 0 -// binding.progressBarBattery5.progress = 0 -// } -// value < 20.0f -> { -// val newValue = value * 5.0f -// uiColorCapacityProgressBar(newValue, binding.progressBarBattery1) -// binding.progressBarBattery1.progress = newValue.roundToInt() -// binding.progressBarBattery2.progress = 0 -// binding.progressBarBattery3.progress = 0 -// binding.progressBarBattery4.progress = 0 -// binding.progressBarBattery5.progress = 0 -// } -// value < 40.0f -> { -// binding.progressBarBattery1.progressTintList = ColorStateList.valueOf(ContextCompat.getColor(requireContext(), R.color.percentUnder100)) -// binding.progressBarBattery1.progress = 100 -// val newValue = (value - 20.0f) * 5.0f -// uiColorCapacityProgressBar(newValue, binding.progressBarBattery2) -// binding.progressBarBattery2.progress = newValue.roundToInt() -// binding.progressBarBattery3.progress = 0 -// binding.progressBarBattery4.progress = 0 -// binding.progressBarBattery5.progress = 0 -// } -// value < 60.0f -> { -// binding.progressBarBattery1.progressTintList = ColorStateList.valueOf(ContextCompat.getColor(requireContext(), R.color.percentUnder100)) -// binding.progressBarBattery1.progress = 100 -// binding.progressBarBattery2.progressTintList = ColorStateList.valueOf(ContextCompat.getColor(requireContext(), R.color.percentUnder100)) -// binding.progressBarBattery2.progress = 100 -// val newValue = (value - 40.0f) * 5.0f -// uiColorCapacityProgressBar(newValue, binding.progressBarBattery3) -// binding.progressBarBattery3.progress = newValue.roundToInt() -// binding.progressBarBattery4.progress = 0 -// binding.progressBarBattery5.progress = 0 -// } -// value < 80.0f -> { -// binding.progressBarBattery1.progressTintList = ColorStateList.valueOf(ContextCompat.getColor(requireContext(), R.color.percentUnder100)) -// binding.progressBarBattery1.progress = 100 -// binding.progressBarBattery2.progressTintList = ColorStateList.valueOf(ContextCompat.getColor(requireContext(), R.color.percentUnder100)) -// binding.progressBarBattery2.progress = 100 -// binding.progressBarBattery3.progressTintList = ColorStateList.valueOf(ContextCompat.getColor(requireContext(), R.color.percentUnder100)) -// binding.progressBarBattery3.progress = 100 -// val newValue = (value - 60.0f) * 5.0f -// uiColorCapacityProgressBar(newValue, binding.progressBarBattery4) -// binding.progressBarBattery4.progress = newValue.roundToInt() -// binding.progressBarBattery5.progress = 0 -// } -// value < 100.0f -> { -// binding.progressBarBattery1.progressTintList = ColorStateList.valueOf(ContextCompat.getColor(requireContext(), R.color.percentUnder100)) -// binding.progressBarBattery1.progress = 100 -// binding.progressBarBattery2.progressTintList = ColorStateList.valueOf(ContextCompat.getColor(requireContext(), R.color.percentUnder100)) -// binding.progressBarBattery2.progress = 100 -// binding.progressBarBattery3.progressTintList = ColorStateList.valueOf(ContextCompat.getColor(requireContext(), R.color.percentUnder100)) -// binding.progressBarBattery3.progress = 100 -// binding.progressBarBattery4.progressTintList = ColorStateList.valueOf(ContextCompat.getColor(requireContext(), R.color.percentUnder100)) -// binding.progressBarBattery4.progress = 100 -// val newValue = (value - 80.0f) * 5.0f -// uiColorCapacityProgressBar(newValue, binding.progressBarBattery5) -// binding.progressBarBattery5.progress = newValue.roundToInt() -// } -// else -> { -// binding.progressBarBattery1.progressTintList = ColorStateList.valueOf(ContextCompat.getColor(requireContext(), R.color.percentUnder100)) -// binding.progressBarBattery1.progress = 100 -// binding.progressBarBattery2.progressTintList = ColorStateList.valueOf(ContextCompat.getColor(requireContext(), R.color.percentUnder100)) -// binding.progressBarBattery2.progress = 100 -// binding.progressBarBattery3.progressTintList = ColorStateList.valueOf(ContextCompat.getColor(requireContext(), R.color.percentUnder100)) -// binding.progressBarBattery3.progress = 100 -// binding.progressBarBattery4.progressTintList = ColorStateList.valueOf(ContextCompat.getColor(requireContext(), R.color.percentUnder100)) -// binding.progressBarBattery4.progress = 100 -// binding.progressBarBattery5.progressTintList = ColorStateList.valueOf(ContextCompat.getColor(requireContext(), R.color.percentUnder100)) -// binding.progressBarBattery5.progress = 100 -// } -// } -// } - - private fun roundTo(value: Float, decimals: Int): Float { - if (value.isNaN() || value.isInfinite()) { - return 0.0f - } - - if (decimals == 0) { - return value.roundToInt().toFloat() - } - - val factor = 10.0.pow(decimals.toDouble()).toFloat() - return (value * factor).roundToInt() / factor - } -} \ No newline at end of file diff --git a/app/src/main/java/de/jnns/bmsmonitor/MainActivity.kt b/app/src/main/java/de/jnns/bmsmonitor/MainActivity.kt index bbb760e..b440db9 100644 --- a/app/src/main/java/de/jnns/bmsmonitor/MainActivity.kt +++ b/app/src/main/java/de/jnns/bmsmonitor/MainActivity.kt @@ -56,7 +56,6 @@ class MainActivity : AppCompatActivity() { } val batteryFragment = BatteryFragment() -// val batteryFragment1 = BatteryFragment1() val bikeFragment = BikeFragment() val statsFragment = StatsFragment() val settingsFragment = SettingsFragment() @@ -64,7 +63,6 @@ class MainActivity : AppCompatActivity() { binding.bottomNavigation.setOnNavigationItemSelectedListener { when (it.itemId) { R.id.page_battery -> supportFragmentManager.beginTransaction().apply { replace(R.id.nav_host_fragment, batteryFragment).commit() } -// R.id.page_battery -> supportFragmentManager.beginTransaction().apply { replace(R.id.nav_host_fragment, batteryFragment1).commit() } R.id.page_bike -> supportFragmentManager.beginTransaction().apply { replace(R.id.nav_host_fragment, bikeFragment).commit() } R.id.page_stats -> supportFragmentManager.beginTransaction().apply { replace(R.id.nav_host_fragment, statsFragment).commit() } R.id.page_settings -> supportFragmentManager.beginTransaction().apply { replace(R.id.nav_host_fragment, settingsFragment).commit() } diff --git a/app/src/main/java/de/jnns/bmsmonitor/bluetooth/BikeGattClientCallback.kt b/app/src/main/java/de/jnns/bmsmonitor/bluetooth/BikeGattClientCallback.kt index 80bdb94..3053234 100644 --- a/app/src/main/java/de/jnns/bmsmonitor/bluetooth/BikeGattClientCallback.kt +++ b/app/src/main/java/de/jnns/bmsmonitor/bluetooth/BikeGattClientCallback.kt @@ -69,13 +69,9 @@ class BikeGattClientCallback( super.onCharacteristicChanged(gatt, characteristic) if (characteristic.uuid == lcdToMcuUuid) { - // Log.d("BluetoothGatt", "FrameData (LCD): " + characteristic.value.toHexString()) - val data = LcdToMcuResponse(characteristic.value) onLcdToMcuDataAvailable(data) } else if (characteristic.uuid == mcuToLcdUuid) { - // Log.d("BluetoothGatt", "FrameData (MCU): " + characteristic.value.toHexString()) - val data = McuToLcdResponse(characteristic.value) onMcuToLcdDataAvailable(data) } diff --git a/app/src/main/java/de/jnns/bmsmonitor/bluetooth/BleManager.kt b/app/src/main/java/de/jnns/bmsmonitor/bluetooth/BleManager.kt index ed7f05c..cebd823 100644 --- a/app/src/main/java/de/jnns/bmsmonitor/bluetooth/BleManager.kt +++ b/app/src/main/java/de/jnns/bmsmonitor/bluetooth/BleManager.kt @@ -10,8 +10,6 @@ class BleManager { fun addDevice(device: BluetoothDevice) { if (!bleDevices.contains(device)) { -// Log.d("BleManager", "Device Address:" + device.address) -// Log.d("BleManager", "Device Name:" + device.name) bleDevices.add(device) for (func in onUpdateFunctions) diff --git a/app/src/main/java/de/jnns/bmsmonitor/bluetooth/BleService.kt b/app/src/main/java/de/jnns/bmsmonitor/bluetooth/BleService.kt index 4899de0..4548cfb 100644 --- a/app/src/main/java/de/jnns/bmsmonitor/bluetooth/BleService.kt +++ b/app/src/main/java/de/jnns/bmsmonitor/bluetooth/BleService.kt @@ -19,8 +19,6 @@ class BleService : Service() { super.onCreate() bluetoothLeScanner = BluetoothAdapter.getDefaultAdapter().bluetoothLeScanner -// BluetoothAdapter.getDefaultAdapter().bondedDevices() -// BluetoothAdapter.getDefaultAdapter().getRemoteDevice() startBleScanning() } diff --git a/app/src/main/java/de/jnns/bmsmonitor/bluetooth/BmsGattClientCallback.kt b/app/src/main/java/de/jnns/bmsmonitor/bluetooth/BmsGattClientCallback.kt index 1c78475..4493e24 100644 --- a/app/src/main/java/de/jnns/bmsmonitor/bluetooth/BmsGattClientCallback.kt +++ b/app/src/main/java/de/jnns/bmsmonitor/bluetooth/BmsGattClientCallback.kt @@ -23,9 +23,6 @@ class BmsGattClientCallback( lateinit var readCharacteristic: BluetoothGattCharacteristic lateinit var writeCharacteristic: BluetoothGattCharacteristic -// private val uartUuid = UUID.fromString("0000ff00-0000-1000-8000-00805f9b34fb") -// private val rxUuid = UUID.fromString("0000ff01-0000-1000-8000-00805f9b34fb") -// private val txUuid = UUID.fromString("0000ff02-0000-1000-8000-00805f9b34fb") private val uartUuid = UUID.fromString("0000A002-0000-1000-8000-00805f9b34fb") private val rxUuid = UUID.fromString("0000C305-0000-1000-8000-00805f9b34fb") private val txUuid = UUID.fromString("0000C302-0000-1000-8000-00805f9b34fb") @@ -64,22 +61,11 @@ class BmsGattClientCallback( return } - var i: Int = 0 - - for(serv in gatt.services) { - i++ -// Log.d("BMS", "Services" + i.toString() + ":" + serv.uuid.toString()) - } - val uartService = gatt.getService(uartUuid) - Log.d("BMS", "Got uartService:" + uartService.uuid.toString()) if (uartService != null) { readCharacteristic = uartService.getCharacteristic(rxUuid) writeCharacteristic = uartService.getCharacteristic(txUuid) -// Log.d("BMS", "Got readCharacteristic:" + readCharacteristic.uuid.toString()) -// Log.d("BMS", "Got writeCharacteristic:" + writeCharacteristic.uuid.toString()) - gatt.setCharacteristicNotification(writeCharacteristic, true) gatt.setCharacteristicNotification(readCharacteristic, true) @@ -97,59 +83,6 @@ class BmsGattClientCallback( fun isInTransaction(): Boolean { return isInTrans } -// override fun onCharacteristicChanged(gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic) { -// super.onCharacteristicChanged(gatt, characteristic) -// -// // Log.d("BluetoothGatt", "BLE Data (" + characteristic.value.size + "): " + characteristic.value.toHexString()) -// -// for (byte: Byte in characteristic.value) { -// if (uartBufferPos >= bufferSize) { -// isInFrame = false -// uartBufferPos = 0 -// uartBytesLeft = 0 -// } -// -// uartBuffer[uartBufferPos] = byte -// -// if (isInFrame) { -// if (uartBufferPos == 3) { -// uartBytesLeft = byte.toInt() -// } -// -// if (byte.toUByte() == 0x77.toUByte() && uartBytesLeft < 1) { -// isInFrame = false -// onFrameComplete(uartBufferPos) -// uartBufferPos = 0 -// uartBytesLeft = 0 -// } else { -// -// uartBufferPos++ -// uartBytesLeft-- -// } -// } else if (byte.toUByte() == 0xDD.toUByte()) { -// isInFrame = true -// uartBufferPos++ -// } -// } -// } -// -// private fun onFrameComplete(size: Int) { -// if (size <= 0) { -// return -// } -// -// val frameBytes = uartBuffer.slice(IntRange(0, size)).toByteArray() -// -// // Log.d("BluetoothGatt", "FrameData (" + frameBytes.size + "): " + frameBytes.toHexString()) -// -// if (frameBytes[1] == 0x3.toByte()) { -// val generalInfo = BmsGeneralInfoResponse(frameBytes) -// onGeneralInfoCallback(generalInfo) -// } else if (frameBytes[1] == 0x4.toByte()) { -// val cellInfo = BmsCellInfoResponse(frameBytes) -// onCellInfoCallback(cellInfo) -// } -// } override fun onCharacteristicChanged(gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic) { super.onCharacteristicChanged(gatt, characteristic) diff --git a/app/src/main/java/de/jnns/bmsmonitor/bms/BmsGeneralInfoResponse.kt b/app/src/main/java/de/jnns/bmsmonitor/bms/BmsGeneralInfoResponse.kt index e3e5671..c50fc57 100644 --- a/app/src/main/java/de/jnns/bmsmonitor/bms/BmsGeneralInfoResponse.kt +++ b/app/src/main/java/de/jnns/bmsmonitor/bms/BmsGeneralInfoResponse.kt @@ -5,16 +5,6 @@ import java.nio.ByteBuffer import java.nio.ByteOrder class BmsGeneralInfoResponse(bytes: ByteArray) { -// var command: Int = 0 -// var status: Int = 0 -// var dataLength: Int = 0 -// var totalVoltage: Float = 0.0f -// var totalCurrent: Float = 0.0f -// var residualCapacity: Float = 0.0f -// var nominalCapacity: Float = 0.0f -// var temperatureProbeCount: Int = 0 -// var temperatureProbeValues: RealmList - var capacity: Float = 0.0f var sysTemperature: Float = 0.0f var packCurrent: Float = 0.0f @@ -45,38 +35,6 @@ class BmsGeneralInfoResponse(bytes: ByteArray) { init { -// command = bytes[1].toInt() -// status = bytes[2].toInt() -// dataLength = bytes[3].toInt() -// -// totalVoltage = bytesToShort(bytes[4], bytes[5]) / 100.0f -// -// totalCurrent = bytesToShort(bytes[6], bytes[7]) / 100.0f -// -// residualCapacity = bytesToShort(bytes[8], bytes[9]) / 100.0f -// nominalCapacity = bytesToShort(bytes[10], bytes[11]) / 100.0f -// -// cycles = bytesToShort(bytes[12], bytes[13]) -// -// // 14 & 15 = production date -// // 16 & 17 = balance low -// // 18 & 19 = balance high -// // 20 & 21 = protection status -// // 22 = version -// -// // Pack Capacity -// capacity = bytes[23].toInt() / 100.0f -// -// // 24 = MOS status -// // 25 = number of cells -// -// temperatureProbeCount = bytes[26].toInt() -// temperatureProbeValues = RealmList() -// -// for (i in 0 until temperatureProbeCount) { -// temperatureProbeValues.add((bytesToShort(bytes[27 + (i * 2)], bytes[27 + (i * 2) + 1]) - 2731) / 10.0f) -// } - bpVersion = bytes[0] bpNumber = bytes[1] packRSOC = bytes[2] diff --git a/app/src/main/java/de/jnns/bmsmonitor/services/BmsService.kt b/app/src/main/java/de/jnns/bmsmonitor/services/BmsService.kt index 2f8280b..20de067 100644 --- a/app/src/main/java/de/jnns/bmsmonitor/services/BmsService.kt +++ b/app/src/main/java/de/jnns/bmsmonitor/services/BmsService.kt @@ -22,7 +22,6 @@ import io.realm.Realm @ExperimentalUnsignedTypes class BmsService : Service() { // BMS commands, they won't change -// private val cmdGeneralInfo: ByteArray = ubyteArrayOf(0xDDU, 0xA5U, 0x03U, 0x00U, 0xFFU, 0xFDU, 0x77U).toByteArray() private val cmdGeneralInfo: ByteArray = ubyteArrayOf(0x10U, 0x00U, 0x00U, 0x40U).toByteArray() private val cmdCellInfo: ByteArray = ubyteArrayOf(0xDDU, 0xA5U, 0x04U, 0x00U, 0xFFU, 0xFCU, 0x77U).toByteArray() private val cmdBmsVersion: ByteArray = ubyteArrayOf(0xDDU, 0xA5U, 0x05U, 0x00U, 0xFFU, 0xFBU, 0x77U).toByteArray() @@ -65,7 +64,6 @@ class BmsService : Service() { bleMac = PreferenceManager.getDefaultSharedPreferences(this).getString("macAddress", "")!! blePin = PreferenceManager.getDefaultSharedPreferences(this).getString("blePin", "")!! -// dataPollDelay = PreferenceManager.getDefaultSharedPreferences(this).getString("refreshInterval", "1000")!!.toLong() / 2 dataPollDelay = PreferenceManager.getDefaultSharedPreferences(this).getString("refreshInterval", "1000")!!.toLong() BleManager.i.onUpdateFunctions.add { @@ -101,13 +99,6 @@ class BmsService : Service() { } private fun onGeneralInfoAvailable(generalInfo: BmsGeneralInfoResponse) { -// batteryData.current = generalInfo.totalCurrent -// batteryData.currentCapacity = generalInfo.residualCapacity -// batteryData.totalCapacity = generalInfo.nominalCapacity -// batteryData.temperatureCount = generalInfo.temperatureProbeCount -// batteryData.temperatures = generalInfo.temperatureProbeValues -// batteryData.vol = generalInfo.totalVoltage - batteryData.capacity = generalInfo.capacity batteryData.sysTemperature = generalInfo.sysTemperature batteryData.packCurrent = generalInfo.packCurrent @@ -135,15 +126,6 @@ class BmsService : Service() { batteryData.cellVoltage14 = generalInfo.cellVoltage14 batteryData.cellVoltage15 = generalInfo.cellVoltage15 batteryData.cellVoltage16 = generalInfo.cellVoltage16 - -// batteryData.packVoltage = generalInfo.packVoltage -// batteryData.dischargeCurrent = generalInfo.dischargeCurrent -// batteryData.capacity = generalInfo.capacity -// batteryData.cycles = generalInfo.cycles -// batteryData.chargeCurrent = generalInfo.chargeCurrent -// batteryData.maxChargeCurrent = generalInfo.maxChargeCurrent -// batteryData.temperature = generalInfo.temperature - generalInfoReceived = true sendData() } @@ -157,7 +139,6 @@ class BmsService : Service() { } private fun sendData() { -// if (cellInfoReceived && generalInfoReceived) { if (generalInfoReceived) { cellInfoReceived = false generalInfoReceived = false @@ -201,19 +182,8 @@ class BmsService : Service() { override fun run() { if (gattClientCallback.isConnected) { if (isInForeground) { -// if (dataModeSwitch) { -// writeBytes(cmdGeneralInfo) -// } else { -// writeBytes(cmdCellInfo) -// } -// -// dataModeSwitch = !dataModeSwitch - -// if (gattClientCallback.isInTransaction() == false) { - Log.d("BMS", "writeBytes():" + cmdGeneralInfo.toHexString()) - writeBytes(cmdGeneralInfo) -// } - + Log.d("BMS", "writeBytes():" + cmdGeneralInfo.toHexString()) + writeBytes(cmdGeneralInfo) dataHandler.postDelayed(this, dataPollDelay) } } else { diff --git a/app/src/main/res/layout/fragment_bms.xml b/app/src/main/res/layout/fragment_bms.xml deleted file mode 100644 index f2b354f..0000000 --- a/app/src/main/res/layout/fragment_bms.xml +++ /dev/null @@ -1,356 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file From 48b7a1bc08cc113a894bc98fdd54d6500e6ee5c0 Mon Sep 17 00:00:00 2001 From: Jcheng Lee Date: Wed, 30 Aug 2023 21:49:38 +0800 Subject: [PATCH 15/21] Gradle Upgrade OK 1. --- build.gradle | 2 +- gradle/wrapper/gradle-wrapper.properties | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/build.gradle b/build.gradle index 924b026..9eb84c4 100644 --- a/build.gradle +++ b/build.gradle @@ -7,7 +7,7 @@ buildscript { } dependencies { - classpath 'com.android.tools.build:gradle:4.1.1' + classpath 'com.android.tools.build:gradle:4.2.2' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" classpath "io.realm:realm-gradle-plugin:10.2.0" diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index fc56cb7..dd5211d 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-all.zip From 8bcffdb2faee02f03a9d9325b70caf51e428a5b4 Mon Sep 17 00:00:00 2001 From: Jcheng Lee Date: Wed, 30 Aug 2023 22:16:09 +0800 Subject: [PATCH 16/21] Gradle Upgrade OK 2. --- app/build.gradle | 2 +- app/src/main/AndroidManifest.xml | 3 +-- build.gradle | 4 ++-- gradle.properties | 9 +++++++-- gradle/wrapper/gradle-wrapper.properties | 2 +- 5 files changed, 12 insertions(+), 8 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 15b7d72..ab2c10d 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -7,7 +7,6 @@ plugins { android { compileSdkVersion 29 - buildToolsVersion "30.0.2" defaultConfig { applicationId "de.jnns.bmsmonitor" @@ -38,6 +37,7 @@ android { kotlinOptions { jvmTarget = '1.8' } + namespace 'de.jnns.bmsmonitor' } dependencies { diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index a0bd31e..2de5da5 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,6 +1,5 @@ - + diff --git a/build.gradle b/build.gradle index 9eb84c4..a03ea65 100644 --- a/build.gradle +++ b/build.gradle @@ -1,5 +1,5 @@ buildscript { - ext.kotlin_version = "1.4.21" + ext.kotlin_version = '1.6.21' repositories { google() @@ -7,7 +7,7 @@ buildscript { } dependencies { - classpath 'com.android.tools.build:gradle:4.2.2' + classpath 'com.android.tools.build:gradle:7.4.2' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" classpath "io.realm:realm-gradle-plugin:10.2.0" diff --git a/gradle.properties b/gradle.properties index 98bed16..c730713 100644 --- a/gradle.properties +++ b/gradle.properties @@ -6,7 +6,11 @@ # http://www.gradle.org/docs/current/userguide/build_environment.html # Specifies the JVM arguments used for the daemon process. # The setting is particularly useful for tweaking memory settings. -org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 +#org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 +#org.gradle.jvmargs=-Xmx6g +org.gradle.jvmargs=-Xmx6g -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 -XX:+UseParallelGC -XX:MaxMetaspaceSize=1g + + # When configured, Gradle will run in incubating parallel mode. # This option should only be used with decoupled projects. More details, visit # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects @@ -18,4 +22,5 @@ android.useAndroidX=true # Automatically convert third-party libraries to use AndroidX android.enableJetifier=true # Kotlin code style for this project: "official" or "obsolete": -kotlin.code.style=official \ No newline at end of file +kotlin.code.style=official +# android.debug.obsoleteApi=true \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index dd5211d..745d67d 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-all.zip From 639d70263c226b3c86b05bcd39399feb99008092 Mon Sep 17 00:00:00 2001 From: Jcheng Lee Date: Thu, 31 Aug 2023 22:06:09 +0800 Subject: [PATCH 17/21] Developing --- .../jnns/bmsmonitor/bluetooth/BleService.kt | 160 ++++++++++++++++++ 1 file changed, 160 insertions(+) diff --git a/app/src/main/java/de/jnns/bmsmonitor/bluetooth/BleService.kt b/app/src/main/java/de/jnns/bmsmonitor/bluetooth/BleService.kt index 4548cfb..51f620f 100644 --- a/app/src/main/java/de/jnns/bmsmonitor/bluetooth/BleService.kt +++ b/app/src/main/java/de/jnns/bmsmonitor/bluetooth/BleService.kt @@ -2,26 +2,82 @@ package de.jnns.bmsmonitor.bluetooth import android.app.Service import android.bluetooth.BluetoothAdapter +import android.bluetooth.BluetoothDevice +import android.bluetooth.BluetoothGatt +import android.bluetooth.BluetoothGattCharacteristic +import android.bluetooth.BluetoothGattDescriptor +import android.bluetooth.BluetoothGattServer +import android.bluetooth.BluetoothGattServerCallback +import android.bluetooth.BluetoothGattService +import android.bluetooth.BluetoothManager +import android.bluetooth.BluetoothProfile import android.bluetooth.le.BluetoothLeScanner import android.bluetooth.le.ScanCallback import android.bluetooth.le.ScanResult +import android.content.Context import android.content.Intent +import android.content.pm.PackageManager import android.os.Handler import android.os.IBinder +import android.util.Log +import de.jnns.bmsmonitor.R +import java.util.* @ExperimentalUnsignedTypes class BleService : Service() { + private var TAG: String = "BMS" // bluetooth stuff private var isScanning = false private lateinit var bluetoothLeScanner: BluetoothLeScanner + private lateinit var mBluetoothManager: BluetoothManager + private lateinit var mGattServer: BluetoothGattServer + val UART_SERVICE: UUID = UUID.fromString("00001805-0000-1000-8000-00805f9b34fb") + val UART_CHAR: UUID = UUID.fromString("00002a2b-0000-1000-8000-00805f9b34fb") + + val service = BluetoothGattService(UART_SERVICE, BluetoothGattService.SERVICE_TYPE_PRIMARY) + + val currentTime = BluetoothGattCharacteristic(UART_CHAR, + //Read-only characteristic, supports notifications + BluetoothGattCharacteristic.PROPERTY_WRITE, + BluetoothGattCharacteristic.PERMISSION_WRITE) override fun onCreate() { super.onCreate() + mBluetoothManager = getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager +// Log.d("BMS", "writeBytes():" + cmdGeneralInfo.toHexString()) + Log.d("BMS", "mBluetoothManager:" + mBluetoothManager) + if (mBluetoothManager != null) { + mGattServer = mBluetoothManager.openGattServer(this, mGattServerCallback); + } + val bluetoothAdapter = mBluetoothManager.adapter + // We can't continue without proper Bluetooth support + checkBluetoothSupport(bluetoothAdapter) + bluetoothLeScanner = BluetoothAdapter.getDefaultAdapter().bluetoothLeScanner startBleScanning() } + /** + * Verify the level of Bluetooth support provided by the hardware. + * @param bluetoothAdapter System [BluetoothAdapter]. + * @return true if Bluetooth is properly supported, false otherwise. + */ + private fun checkBluetoothSupport(bluetoothAdapter: BluetoothAdapter?): Boolean { + + if (bluetoothAdapter == null) { + Log.w(TAG, "Bluetooth is not supported") + return false + } + + if (!packageManager.hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) { + Log.w(TAG, "Bluetooth LE is not supported") + return false + } + + return true + } + override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int { return START_STICKY } @@ -57,4 +113,108 @@ class BleService : Service() { } } } + + /** + * Callback to handle incoming requests to the GATT server. + * All read/write requests for characteristics and descriptors are handled here. + */ + private val mGattServerCallback = object : BluetoothGattServerCallback() { + + override fun onConnectionStateChange(device: BluetoothDevice, status: Int, newState: Int) { +// if (newState == BluetoothProfile.STATE_CONNECTED) { +// Log.i(TAG, "BluetoothDevice CONNECTED: $device") +// } else if (newState == BluetoothProfile.STATE_DISCONNECTED) { +// Log.i(TAG, "BluetoothDevice DISCONNECTED: $device") +// //Remove device from any active subscriptions +// registeredDevices.remove(device) +// } + } + + override fun onCharacteristicReadRequest(device: BluetoothDevice, requestId: Int, offset: Int, + characteristic: BluetoothGattCharacteristic + ) { +// val now = System.currentTimeMillis() +// when { +// TimeProfile.CURRENT_TIME == characteristic.uuid -> { +// Log.i(TAG, "Read CurrentTime") +// bluetoothGattServer?.sendResponse(device, +// requestId, +// BluetoothGatt.GATT_SUCCESS, +// 0, +// TimeProfile.getExactTime(now, TimeProfile.ADJUST_NONE)) +// } +// TimeProfile.LOCAL_TIME_INFO == characteristic.uuid -> { +// Log.i(TAG, "Read LocalTimeInfo") +// bluetoothGattServer?.sendResponse(device, +// requestId, +// BluetoothGatt.GATT_SUCCESS, +// 0, +// TimeProfile.getLocalTimeInfo(now)) +// } +// else -> { +// // Invalid characteristic +// Log.w(TAG, "Invalid Characteristic Read: " + characteristic.uuid) +// bluetoothGattServer?.sendResponse(device, +// requestId, +// BluetoothGatt.GATT_FAILURE, +// 0, +// null) +// } +// } + } + + override fun onDescriptorReadRequest(device: BluetoothDevice, requestId: Int, offset: Int, + descriptor: BluetoothGattDescriptor + ) { +// if (TimeProfile.CLIENT_CONFIG == descriptor.uuid) { +// Log.d(TAG, "Config descriptor read") +// val returnValue = if (registeredDevices.contains(device)) { +// BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE +// } else { +// BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE +// } +// bluetoothGattServer?.sendResponse(device, +// requestId, +// BluetoothGatt.GATT_SUCCESS, +// 0, +// returnValue) +// } else { +// Log.w(TAG, "Unknown descriptor read request") +// bluetoothGattServer?.sendResponse(device, +// requestId, +// BluetoothGatt.GATT_FAILURE, +// 0, null) +// } + } + + override fun onDescriptorWriteRequest(device: BluetoothDevice, requestId: Int, + descriptor: BluetoothGattDescriptor, + preparedWrite: Boolean, responseNeeded: Boolean, + offset: Int, value: ByteArray) { +// if (TimeProfile.CLIENT_CONFIG == descriptor.uuid) { +// if (Arrays.equals(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE, value)) { +// Log.d(TAG, "Subscribe device to notifications: $device") +// registeredDevices.add(device) +// } else if (Arrays.equals(BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE, value)) { +// Log.d(TAG, "Unsubscribe device from notifications: $device") +// registeredDevices.remove(device) +// } +// +// if (responseNeeded) { +// bluetoothGattServer?.sendResponse(device, +// requestId, +// BluetoothGatt.GATT_SUCCESS, +// 0, null) +// } +// } else { +// Log.w(TAG, "Unknown descriptor write request") +// if (responseNeeded) { +// bluetoothGattServer?.sendResponse(device, +// requestId, +// BluetoothGatt.GATT_FAILURE, +// 0, null) +// } +// } + } + } } \ No newline at end of file From 7aa712e490970eabba99465d3a68d6842848160a Mon Sep 17 00:00:00 2001 From: Jcheng Lee Date: Thu, 31 Aug 2023 22:18:06 +0800 Subject: [PATCH 18/21] Developing --- .../main/java/de/jnns/bmsmonitor/bluetooth/BleService.kt | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/de/jnns/bmsmonitor/bluetooth/BleService.kt b/app/src/main/java/de/jnns/bmsmonitor/bluetooth/BleService.kt index 51f620f..430c1bc 100644 --- a/app/src/main/java/de/jnns/bmsmonitor/bluetooth/BleService.kt +++ b/app/src/main/java/de/jnns/bmsmonitor/bluetooth/BleService.kt @@ -37,8 +37,7 @@ class BleService : Service() { val service = BluetoothGattService(UART_SERVICE, BluetoothGattService.SERVICE_TYPE_PRIMARY) - val currentTime = BluetoothGattCharacteristic(UART_CHAR, - //Read-only characteristic, supports notifications + val uartCharacteristic = BluetoothGattCharacteristic(UART_CHAR, BluetoothGattCharacteristic.PROPERTY_WRITE, BluetoothGattCharacteristic.PERMISSION_WRITE) override fun onCreate() { @@ -54,6 +53,10 @@ class BleService : Service() { // We can't continue without proper Bluetooth support checkBluetoothSupport(bluetoothAdapter) + service.addCharacteristic(uartCharacteristic) + + mGattServer?.addService(service) ?: Log.w(TAG, "Unable to create GATT server") + bluetoothLeScanner = BluetoothAdapter.getDefaultAdapter().bluetoothLeScanner startBleScanning() } From a64e33706734da086e8f41b05127880da2b86622 Mon Sep 17 00:00:00 2001 From: David Lee Date: Fri, 1 Sep 2023 15:27:10 +0800 Subject: [PATCH 19/21] Connecting to NXP Wireless UARK OK. --- .idea/misc.xml | 2 +- .../jnns/bmsmonitor/bluetooth/BleService.kt | 69 ++++++++++++++++--- .../bluetooth/BmsGattClientCallback.kt | 34 +++++++-- .../de/jnns/bmsmonitor/services/BmsService.kt | 14 ++-- 4 files changed, 98 insertions(+), 21 deletions(-) diff --git a/.idea/misc.xml b/.idea/misc.xml index 9b6d99b..26fb6d1 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,5 +1,5 @@ - + diff --git a/app/src/main/java/de/jnns/bmsmonitor/bluetooth/BleService.kt b/app/src/main/java/de/jnns/bmsmonitor/bluetooth/BleService.kt index 430c1bc..aebaba3 100644 --- a/app/src/main/java/de/jnns/bmsmonitor/bluetooth/BleService.kt +++ b/app/src/main/java/de/jnns/bmsmonitor/bluetooth/BleService.kt @@ -4,6 +4,7 @@ import android.app.Service import android.bluetooth.BluetoothAdapter import android.bluetooth.BluetoothDevice import android.bluetooth.BluetoothGatt +import android.bluetooth.BluetoothGatt.GATT_SUCCESS import android.bluetooth.BluetoothGattCharacteristic import android.bluetooth.BluetoothGattDescriptor import android.bluetooth.BluetoothGattServer @@ -32,8 +33,13 @@ class BleService : Service() { private lateinit var mBluetoothManager: BluetoothManager private lateinit var mGattServer: BluetoothGattServer - val UART_SERVICE: UUID = UUID.fromString("00001805-0000-1000-8000-00805f9b34fb") - val UART_CHAR: UUID = UUID.fromString("00002a2b-0000-1000-8000-00805f9b34fb") +// UUID128(uuid_service_wireless_uart, 0xE0, 0x1C, 0x4B, 0x5E, 0x1E, 0xEB, 0xA1, 0x5C, 0xEE, 0xF4, 0x5E, 0xBA, 0x00, 0x01, 0xFF, 0x01) +// "01FF0100-BA5E-F4EE-5CA1-EB1E5E4B1CE0" +// UUID128(uuid_uart_stream, 0xE0, 0x1C, 0x4B, 0x5E, 0x1E, 0xEB, 0xA1, 0x5C, 0xEE, 0xF4, 0x5E, 0xBA, 0x01, 0x01, 0xFF, 0x01) +// "01FF0101-BA5E-F4EE-5CA1-EB1E5E4B1CE0" + + val UART_SERVICE: UUID = UUID.fromString("01FF0100-BA5E-F4EE-5CA1-EB1E5E4B1CE0") + val UART_CHAR: UUID = UUID.fromString("01FF0101-BA5E-F4EE-5CA1-EB1E5E4B1CE0") val service = BluetoothGattService(UART_SERVICE, BluetoothGattService.SERVICE_TYPE_PRIMARY) @@ -107,12 +113,13 @@ class BleService : Service() { super.onScanResult(callbackType, result) if (result.device != null) { - if( - result.device.name != null && - result.device.name.startsWith("UOOK-BMS") == true - ) { - BleManager.i.addDevice(result.device) - } +// if( +// result.device.name != null && +// result.device.name.startsWith("UOOK-BMS") == true +// ) { +// BleManager.i.addDevice(result.device) +// } + BleManager.i.addDevice(result.device) } } } @@ -133,6 +140,52 @@ class BleService : Service() { // } } + override fun onCharacteristicWriteRequest( + device: BluetoothDevice?, + requestId: Int, + characteristic: BluetoothGattCharacteristic?, + preparedWrite: Boolean, + responseNeeded: Boolean, + offset: Int, + value: ByteArray? + ) + { + super.onCharacteristicWriteRequest(device, requestId, characteristic, preparedWrite, responseNeeded, offset, value) + + var str: String = "" + + if (value != null) { + for(vl in value) { + str = String.format("%02X", vl) + Log.d("BMS", str) + } + } + mGattServer?.sendResponse( + device, + requestId, + GATT_SUCCESS, + 0, + null + ) + } +// public void onCharacteristicWriteRequest(BluetoothDevice device, int requestId, BluetoothGattCharacteristic characteristic, boolean preparedWrite, boolean responseNeeded, int offset, byte[] value) { +// throw new RuntimeException("Stub!"); +// } +// public void onCharacteristicWriteRequest(BluetoothDevice device, int requestId, BluetoothGattCharacteristic characteristic, boolean preparedWrite, boolean responseNeeded, int offset, byte[] value) { +// super.onCharacteristicWriteRequest(device, requestId, characteristic, preparedWrite, responseNeeded, offset, value); +// String s = ""; +// for (byte vl: value) { +// s = String.format("%02X ", vl); +// } +// Log.v(TAG, "Char: " + characteristic.getUuid().toString() + " offset " + offset + " Value " + s); +// +// if(tmp == mCurrentServiceFragment.getCharacteristicUUID().get(1)) +// { +// +// } +// mGattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, 0, null); +// } + override fun onCharacteristicReadRequest(device: BluetoothDevice, requestId: Int, offset: Int, characteristic: BluetoothGattCharacteristic ) { diff --git a/app/src/main/java/de/jnns/bmsmonitor/bluetooth/BmsGattClientCallback.kt b/app/src/main/java/de/jnns/bmsmonitor/bluetooth/BmsGattClientCallback.kt index 4493e24..af0bf4a 100644 --- a/app/src/main/java/de/jnns/bmsmonitor/bluetooth/BmsGattClientCallback.kt +++ b/app/src/main/java/de/jnns/bmsmonitor/bluetooth/BmsGattClientCallback.kt @@ -4,6 +4,7 @@ import android.bluetooth.BluetoothGatt import android.bluetooth.BluetoothGattCallback import android.bluetooth.BluetoothGattCharacteristic import android.bluetooth.BluetoothProfile +import android.os.Build import android.util.Log import de.jnns.bmsmonitor.bms.BmsCellInfoResponse import de.jnns.bmsmonitor.bms.BmsGeneralInfoResponse @@ -20,12 +21,14 @@ class BmsGattClientCallback( var isConnected = false - lateinit var readCharacteristic: BluetoothGattCharacteristic +// lateinit var readCharacteristic: BluetoothGattCharacteristic lateinit var writeCharacteristic: BluetoothGattCharacteristic - private val uartUuid = UUID.fromString("0000A002-0000-1000-8000-00805f9b34fb") - private val rxUuid = UUID.fromString("0000C305-0000-1000-8000-00805f9b34fb") - private val txUuid = UUID.fromString("0000C302-0000-1000-8000-00805f9b34fb") +// private val uartUuid = UUID.fromString("0000A002-0000-1000-8000-00805f9b34fb") +// private val rxUuid = UUID.fromString("0000C305-0000-1000-8000-00805f9b34fb") +// private val txUuid = UUID.fromString("0000C302-0000-1000-8000-00805f9b34fb") + private val uartUuid = UUID.fromString("01FF0100-BA5E-F4EE-5CA1-EB1E5E4B1CE0") + private val txUuid = UUID.fromString("01FF0101-BA5E-F4EE-5CA1-EB1E5E4B1CE0") private var isInTrans: Boolean = false private var receLen: Int = 0 @@ -47,7 +50,10 @@ class BmsGattClientCallback( } if (newState == BluetoothProfile.STATE_CONNECTED) { - gatt.discoverServices() + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + gatt.requestMtu(247); + } +// gatt.discoverServices() } else if (newState == BluetoothProfile.STATE_DISCONNECTED) { isConnected = false } @@ -64,10 +70,12 @@ class BmsGattClientCallback( val uartService = gatt.getService(uartUuid) if (uartService != null) { - readCharacteristic = uartService.getCharacteristic(rxUuid) + Log.d("BMS", "uartService:" + uartService) +// readCharacteristic = uartService.getCharacteristic(rxUuid) writeCharacteristic = uartService.getCharacteristic(txUuid) + Log.d("BMS", "writeCharacteristic:" + writeCharacteristic) gatt.setCharacteristicNotification(writeCharacteristic, true) - gatt.setCharacteristicNotification(readCharacteristic, true) +// gatt.setCharacteristicNotification(readCharacteristic, true) onConnectionSucceeded() isConnected = true @@ -102,6 +110,18 @@ class BmsGattClientCallback( } } + override fun onMtuChanged(gatt: BluetoothGatt, mtu: Int, status: Int) { + var mtu_size: Int = mtu + // Handle MTU change request from the central device +// if (mtu_size > 247) { +// mtu_size = 247 +// } +// // Respond to the MTU change request +// bluetoothGattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, 0, null); + Log.d("BMS", "Negotiated mtu_size=" + mtu_size) + gatt.discoverServices() + } + private fun onFrameComplete(size: Int) { if (size <= 0) { return diff --git a/app/src/main/java/de/jnns/bmsmonitor/services/BmsService.kt b/app/src/main/java/de/jnns/bmsmonitor/services/BmsService.kt index 20de067..5a2fbf6 100644 --- a/app/src/main/java/de/jnns/bmsmonitor/services/BmsService.kt +++ b/app/src/main/java/de/jnns/bmsmonitor/services/BmsService.kt @@ -5,6 +5,7 @@ import android.bluetooth.BluetoothDevice import android.bluetooth.BluetoothGatt import android.content.Intent import android.content.SharedPreferences.OnSharedPreferenceChangeListener +import android.os.Build import android.os.Handler import android.os.IBinder import android.util.Log @@ -22,7 +23,8 @@ import io.realm.Realm @ExperimentalUnsignedTypes class BmsService : Service() { // BMS commands, they won't change - private val cmdGeneralInfo: ByteArray = ubyteArrayOf(0x10U, 0x00U, 0x00U, 0x40U).toByteArray() +// private val cmdGeneralInfo: ByteArray = ubyteArrayOf(0x10U, 0x00U, 0x00U, 0x40U).toByteArray() + private val cmdGeneralInfo: ByteArray = ubyteArrayOf(0xA5U, 0x01U, 0x61U, 0x62U).toByteArray() private val cmdCellInfo: ByteArray = ubyteArrayOf(0xDDU, 0xA5U, 0x04U, 0x00U, 0xFFU, 0xFCU, 0x77U).toByteArray() private val cmdBmsVersion: ByteArray = ubyteArrayOf(0xDDU, 0xA5U, 0x05U, 0x00U, 0xFFU, 0xFBU, 0x77U).toByteArray() @@ -64,7 +66,8 @@ class BmsService : Service() { bleMac = PreferenceManager.getDefaultSharedPreferences(this).getString("macAddress", "")!! blePin = PreferenceManager.getDefaultSharedPreferences(this).getString("blePin", "")!! - dataPollDelay = PreferenceManager.getDefaultSharedPreferences(this).getString("refreshInterval", "1000")!!.toLong() + dataPollDelay = PreferenceManager.getDefaultSharedPreferences(this).getString("refreshInterval", "5000")!!.toLong() + dataPollDelay = 5000 BleManager.i.onUpdateFunctions.add { searchForDeviceAndConnect() @@ -182,7 +185,7 @@ class BmsService : Service() { override fun run() { if (gattClientCallback.isConnected) { if (isInForeground) { - Log.d("BMS", "writeBytes():" + cmdGeneralInfo.toHexString()) + Log.d("BMS", "writeBytes()1:" + cmdGeneralInfo.toHexString()) writeBytes(cmdGeneralInfo) dataHandler.postDelayed(this, dataPollDelay) } @@ -214,8 +217,9 @@ class BmsService : Service() { ::onConnectionFailed // on connection fails ) - currentBleDevice.setPin(blePin.toByteArray()) - currentBleDevice.createBond() +// currentBleDevice.setPin(blePin.toByteArray()) +// currentBleDevice.createBond() +// currentBleDevice.setPairingConfirmation(false); bluetoothGatt = currentBleDevice.connectGatt(this, false, gattClientCallback) } From 0c11154ae4fb89cecafab69c5bf33fc0301df803 Mon Sep 17 00:00:00 2001 From: Jcheng Lee Date: Sat, 31 Aug 2024 23:02:30 +0800 Subject: [PATCH 20/21] add tmp.txt --- tmp.txt | 1 + 1 file changed, 1 insertion(+) create mode 100644 tmp.txt diff --git a/tmp.txt b/tmp.txt new file mode 100644 index 0000000..5d89782 --- /dev/null +++ b/tmp.txt @@ -0,0 +1 @@ +temp 0111 From aaf0e58a39b2c1571c5cba68ec10220c56d8e779 Mon Sep 17 00:00:00 2001 From: Jcheng Lee Date: Sat, 31 Aug 2024 23:42:45 +0800 Subject: [PATCH 21/21] delete tmp.txt --- tmp.txt | 1 - 1 file changed, 1 deletion(-) delete mode 100644 tmp.txt diff --git a/tmp.txt b/tmp.txt deleted file mode 100644 index 5d89782..0000000 --- a/tmp.txt +++ /dev/null @@ -1 +0,0 @@ -temp 0111