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..26fb6d1 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,6 +1,5 @@ - - + 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/app/src/main/java/de/jnns/bmsmonitor/BatteryFragment.kt b/app/src/main/java/de/jnns/bmsmonitor/BatteryFragment.kt index eda9f35..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)) } @@ -98,31 +95,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.barchartCells1.setPinchZoom(false) + binding.barchartCells1.setTouchEnabled(false) + binding.barchartCells1.isClickable = false + binding.barchartCells1.isDoubleTapToZoomEnabled = false + + binding.barchartCells1.setDrawBorders(false) + binding.barchartCells1.setDrawValueAboveBar(true) + binding.barchartCells1.setDrawBorders(false) + binding.barchartCells1.setDrawGridBackground(false) + + binding.barchartCells1.description.isEnabled = false + binding.barchartCells1.legend.isEnabled = false + + binding.barchartCells1.axisLeft.setDrawGridLines(false) + binding.barchartCells1.axisLeft.setDrawLabels(false) + binding.barchartCells1.axisLeft.setDrawAxisLine(false) + + binding.barchartCells1.xAxis.setDrawGridLines(false) + binding.barchartCells1.xAxis.setDrawLabels(false) + binding.barchartCells1.xAxis.setDrawAxisLine(false) + + binding.barchartCells1.axisRight.isEnabled = false + - binding.barchartCells.setPinchZoom(false) - binding.barchartCells.setTouchEnabled(false) - binding.barchartCells.isClickable = false - binding.barchartCells.isDoubleTapToZoomEnabled = false + binding.barchartCells2.setNoDataTextColor(requireActivity().getColor(R.color.white)) + binding.barchartCells2.setNoDataText("...") - binding.barchartCells.setDrawBorders(false) - binding.barchartCells.setDrawValueAboveBar(true) - binding.barchartCells.setDrawBorders(false) - binding.barchartCells.setDrawGridBackground(false) + binding.barchartCells2.setPinchZoom(false) + binding.barchartCells2.setTouchEnabled(false) + binding.barchartCells2.isClickable = false + binding.barchartCells2.isDoubleTapToZoomEnabled = false - binding.barchartCells.description.isEnabled = false - binding.barchartCells.legend.isEnabled = false + binding.barchartCells2.setDrawBorders(false) + binding.barchartCells2.setDrawValueAboveBar(true) + binding.barchartCells2.setDrawBorders(false) + binding.barchartCells2.setDrawGridBackground(false) - binding.barchartCells.axisLeft.setDrawGridLines(false) - binding.barchartCells.axisLeft.setDrawLabels(false) - binding.barchartCells.axisLeft.setDrawAxisLine(false) + binding.barchartCells2.description.isEnabled = false + binding.barchartCells2.legend.isEnabled = false - binding.barchartCells.xAxis.setDrawGridLines(false) - binding.barchartCells.xAxis.setDrawLabels(false) - binding.barchartCells.xAxis.setDrawAxisLine(false) + binding.barchartCells2.axisLeft.setDrawGridLines(false) + binding.barchartCells2.axisLeft.setDrawLabels(false) + binding.barchartCells2.axisLeft.setDrawAxisLine(false) - binding.barchartCells.axisRight.isEnabled = false + binding.barchartCells2.xAxis.setDrawGridLines(false) + binding.barchartCells2.xAxis.setDrawLabels(false) + binding.barchartCells2.xAxis.setDrawAxisLine(false) + + binding.barchartCells2.axisRight.isEnabled = false } override fun onResume() { @@ -145,84 +169,89 @@ class BatteryFragment : Fragment() { } requireActivity().runOnUiThread { + 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 - binding.speedViewSpeed.speedTo(powerUsage, 1000) + var chargePower:Float = 0.0f - if (powerUsage < 0.0f) { - binding.labelPower.text = "+${powerUsage.roundToInt() * -1}" + if(batteryData.packCurrent >= 0.0f) + { + chargePower = batteryData.packCurrent / batteryData.maxChargeCurrent + chargePower = chargePower * -500 } else { - binding.labelPower.text = powerUsage.roundToInt().toString() - } - - // Remaining Time - wattValues[smoothIndex] = batteryData.power - ++smoothIndex - - if (smoothIndex == smoothCount) { - smoothIndex = 0 + chargePower = batteryData.packCurrent / batteryData.maxDischargeCurrent + chargePower = chargePower * -1000 } - var averagePower = wattValues.filter { x -> x != 0.0f }.average().toFloat() + binding.speedViewSpeed.speedTo(chargePower, 1000) - 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) + if (powerUsage < 0.0f) { + binding.labelPower.text = "+${powerUsage.roundToInt() * -1}" + } else { + binding.labelPower.text = powerUsage.roundToInt().toString() } - 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()) // Cell Bar-Diagram val cellBars = ArrayList() - - for ((i, cell) in batteryData.cellVoltages.withIndex()) { - cellBars.add(BarEntry(i.toFloat(), cell)) - } + 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)) 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() - - // Row 1 - val totalPercentage = ((batteryData.percentage) * 1000.0f).roundToInt() / 10.0f + 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() + + var totalPercentage = batteryData.packRSOC.toUByte() + if(totalPercentage > 100U) { + totalPercentage = 100U + } - binding.labelVoltage.text = roundTo(batteryData.voltage, 1).toString() + binding.labelVoltage.text = roundTo(batteryData.packVoltage, 1).toString() binding.labelPercentage.text = totalPercentage.toString() - uiBatteryCapacityBar(totalPercentage) + uiBatteryCapacityBar(totalPercentage.toFloat()) - // Row 2 - binding.labelCurrent.text = String.format(Locale.US, "%.1f", roundTo(batteryData.current, 1)) - binding.labelCapacityWh.text = batteryData.watthours.roundToInt().toString() + binding.labelCurrent.text = roundTo(batteryData.packCurrent, 1).toString() + binding.labelCapacityWh.text = roundTo(batteryData.capacity, 1).toString() - // Row 3 - binding.labelTemperature.text = roundTo(batteryData.avgTemperature, 1).toString() - binding.labelTemperatureMax.text = roundTo(batteryData.maxTemperature, 1).toString() + binding.labelTemperature.text = roundTo(batteryData.sysTemperature, 1).toString() + binding.labelTemperatureMax.text = roundTo(batteryData.heatsinkTemperature, 1).toString() } } diff --git a/app/src/main/java/de/jnns/bmsmonitor/MainActivity.kt b/app/src/main/java/de/jnns/bmsmonitor/MainActivity.kt index f84ae65..b440db9 100644 --- a/app/src/main/java/de/jnns/bmsmonitor/MainActivity.kt +++ b/app/src/main/java/de/jnns/bmsmonitor/MainActivity.kt @@ -2,14 +2,18 @@ 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 import androidx.appcompat.app.AppCompatActivity import androidx.core.content.ContextCompat 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.BatteryFragment import de.jnns.bmsmonitor.services.BikeService import de.jnns.bmsmonitor.services.BmsService import io.realm.Realm @@ -22,7 +26,7 @@ class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - + Log.d("BMS", "MainActivity Start...") Realm.init(this) Realm.compactRealm(Realm.getDefaultConfiguration()!!) @@ -33,7 +37,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) { @@ -77,6 +82,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/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 1539980..cebd823 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 { 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..aebaba3 100644 --- a/app/src/main/java/de/jnns/bmsmonitor/bluetooth/BleService.kt +++ b/app/src/main/java/de/jnns/bmsmonitor/bluetooth/BleService.kt @@ -2,27 +2,91 @@ package de.jnns.bmsmonitor.bluetooth 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 +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 +// 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) + + val uartCharacteristic = BluetoothGattCharacteristic(UART_CHAR, + BluetoothGattCharacteristic.PROPERTY_WRITE, + BluetoothGattCharacteristic.PERMISSION_WRITE) override fun onCreate() { super.onCreate() - bluetoothLeScanner = BluetoothAdapter.getDefaultAdapter().bluetoothLeScanner + 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) + + service.addCharacteristic(uartCharacteristic) + mGattServer?.addService(service) ?: Log.w(TAG, "Unable to create GATT server") + + 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 } @@ -49,8 +113,164 @@ 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) +// } BleManager.i.addDevice(result.device) } } } + + /** + * 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 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 + ) { +// 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 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..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,14 +21,18 @@ class BmsGattClientCallback( var isConnected = false - lateinit var readCharacteristic: BluetoothGattCharacteristic +// 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") + private val uartUuid = UUID.fromString("01FF0100-BA5E-F4EE-5CA1-EB1E5E4B1CE0") + private val txUuid = UUID.fromString("01FF0101-BA5E-F4EE-5CA1-EB1E5E4B1CE0") - private val bufferSize: Int = 80 + private var isInTrans: Boolean = false + private var receLen: Int = 0 + private val bufferSize: Int = 256 private var uartBuffer = ByteArray(bufferSize) private var uartBufferPos: Int = 0 private var uartBytesLeft: Int = 0 @@ -45,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 } @@ -55,75 +63,73 @@ class BmsGattClientCallback( super.onServicesDiscovered(gatt, status) if (status != BluetoothGatt.GATT_SUCCESS) { + Log.d("BluetoothGatt", "onServicesDiscovered failed") return } 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 } } + 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) - // 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++ + uartBufferPos++ + if (uartBufferPos >= bufferSize || uartBufferPos >= receLen) { + onFrameComplete(uartBufferPos) + Log.d("BMS", "Transaction Done.") + isInTrans = false + uartBufferPos = 0 + break } } } + 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 } - 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) - } + val frameBytes = uartBuffer.slice(IntRange(0, size - 1)).toByteArray() + 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..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,49 +5,67 @@ 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 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 - init { - command = bytes[1].toInt() - status = bytes[2].toInt() - dataLength = bytes[3].toInt() - - totalVoltage = bytesToShort(bytes[4], bytes[5]) / 100.0f - - // discharging - 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 - - capacity = bytes[23].toInt() / 100.0f + init { + bpVersion = bytes[0] + bpNumber = bytes[1] + packRSOC = bytes[2] + capacity = bytesToShort(bytes[4], bytes[5]) / 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 - // 24 = MOS status - // 25 = number of cells + packDischargCycle = bytesToShort(bytes[74], bytes[75]) - temperatureProbeCount = bytes[26].toInt() - temperatureProbeValues = RealmList() + // System temperature + sysTemperature = bytesToShort(bytes[52], bytes[53]) / 100.0f + heatsinkTemperature = bytesToShort(bytes[54], bytes[55]) / 100.0f - for (i in 0 until temperatureProbeCount) { - temperatureProbeValues.add((bytesToShort(bytes[27 + (i * 2)], bytes[27 + (i * 2) + 1]) - 2731) / 10.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 f7f058c..1b7aaa3 100644 --- a/app/src/main/java/de/jnns/bmsmonitor/data/BatteryData.kt +++ b/app/src/main/java/de/jnns/bmsmonitor/data/BatteryData.kt @@ -10,12 +10,48 @@ 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 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 + + ) : 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() { @@ -51,4 +87,5 @@ open class BatteryData( get() { return current * voltage } -} \ No newline at end of file + +} 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..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(0xDDU, 0xA5U, 0x03U, 0x00U, 0xFFU, 0xFDU, 0x77U).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() @@ -35,7 +37,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 +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() / 2 + dataPollDelay = PreferenceManager.getDefaultSharedPreferences(this).getString("refreshInterval", "5000")!!.toLong() + dataPollDelay = 5000 BleManager.i.onUpdateFunctions.add { searchForDeviceAndConnect() @@ -94,13 +97,38 @@ class BmsService : Service() { return null } - 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 + override fun onDestroy() { + disconnectFromDevice() + } + private fun onGeneralInfoAvailable(generalInfo: BmsGeneralInfoResponse) { + batteryData.capacity = generalInfo.capacity + batteryData.sysTemperature = generalInfo.sysTemperature + batteryData.packCurrent = generalInfo.packCurrent + batteryData.packVoltage = generalInfo.packVoltage + batteryData.maxDischargeCurrent = generalInfo.maxDischargeCurrent + batteryData.maxChargeCurrent = generalInfo.maxChargeCurrent + 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 generalInfoReceived = true sendData() } @@ -114,7 +142,7 @@ class BmsService : Service() { } private fun sendData() { - if (cellInfoReceived && generalInfoReceived) { + if (generalInfoReceived) { cellInfoReceived = false generalInfoReceived = false @@ -147,6 +175,8 @@ class BmsService : Service() { connectToDevice() } + private fun ByteArray.toHexString() = joinToString("") { "%02x".format(it) } + private fun onConnectionSucceeded() { isConnected = true isConnecting = false @@ -155,14 +185,8 @@ class BmsService : Service() { override fun run() { if (gattClientCallback.isConnected) { if (isInForeground) { - if (dataModeSwitch) { - writeBytes(cmdGeneralInfo) - } else { - writeBytes(cmdCellInfo) - } - - dataModeSwitch = !dataModeSwitch - + Log.d("BMS", "writeBytes()1:" + cmdGeneralInfo.toHexString()) + writeBytes(cmdGeneralInfo) dataHandler.postDelayed(this, dataPollDelay) } } else { @@ -193,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) } @@ -209,6 +234,7 @@ class BmsService : Service() { } private fun writeBytes(bytes: ByteArray) { + gattClientCallback.setReceivingLen(128); gattClientCallback.writeCharacteristic.value = bytes bluetoothGatt.writeCharacteristic(gattClientCallback.writeCharacteristic) } 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 5c26e5f..e2377d6 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -72,6 +72,8 @@ 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" /> + app:layout_constraintEnd_toStartOf="@+id/labelPower" + /> - + + + + + + + + + + + + + + @@ -371,7 +387,7 @@ @@ -439,7 +455,7 @@ @@ -507,7 +523,7 @@ @@ -575,7 +591,7 @@ @@ -643,7 +659,7 @@ diff --git a/app/src/main/res/menu/main_navigation_menu.xml b/app/src/main/res/menu/main_navigation_menu.xml index a79e6ff..5709988 100644 --- a/app/src/main/res/menu/main_navigation_menu.xml +++ b/app/src/main/res/menu/main_navigation_menu.xml @@ -7,14 +7,16 @@ android:title="@string/navBattery"/> + android:title="@string/navBike" + android:visible="false" /> + android:title="@string/navStats" + android:visible="false" /> + app:startDestination="@id/navigation_bms"> + + + + + BMS Monitor - BMS + UOOK BMS Monitor BMS Monitor - Settings BMS Monitor - Stats Waiting for BMS… - BMS: %1$s + BMS: %2$02x%1$02x Bike: %1$s Power @@ -26,7 +26,7 @@ 0 0.0 - 00:00 + Cycle: W V @@ -60,4 +60,11 @@ Max Speed (Km/h) Cell Balance + + Pack Cap.: + Cycle: + Pack Vol.: + Disch. Curr: + Charge Curr: + Max. CC: \ No newline at end of file diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml index ccba39e..f0640c7 100644 --- a/app/src/main/res/values/themes.xml +++ b/app/src/main/res/values/themes.xml @@ -7,5 +7,8 @@ @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 diff --git a/build.gradle b/build.gradle index 924b026..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.1.1' + 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 fc56cb7..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.5-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-all.zip 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