bluetooth better

pull/8/head
geeksville 2020-01-22 16:45:27 -08:00
rodzic 151c9e59bc
commit 397640151f
3 zmienionych plików z 64 dodań i 46 usunięć

Wyświetl plik

@ -1,7 +1,8 @@
* fix bluetooth update
* add real messaging code/protobufs
* use https://codelabs.developers.google.com/codelabs/jetpack-compose-basics/#4 to show service state
*fix bluetooth
* connect to bluetooth device automatically using minimum power
# Medium priority

Wyświetl plik

@ -27,7 +27,6 @@ import androidx.ui.tooling.preview.Preview
import com.geeksville.android.Logging
class MainActivity : AppCompatActivity(), Logging {
companion object {
@ -43,13 +42,20 @@ class MainActivity : AppCompatActivity(), Logging {
fun requestPermission() {
debug("Checking permissions")
val perms = arrayOf(Manifest.permission.ACCESS_FINE_LOCATION,
val perms = arrayOf(
Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.ACCESS_BACKGROUND_LOCATION,
Manifest.permission.BLUETOOTH,
Manifest.permission.BLUETOOTH_ADMIN,
Manifest.permission.WAKE_LOCK)
Manifest.permission.WAKE_LOCK
)
val missingPerms = perms.filter { ContextCompat.checkSelfPermission(this, it) != PackageManager.PERMISSION_GRANTED }
val missingPerms = perms.filter {
ContextCompat.checkSelfPermission(
this,
it
) != PackageManager.PERMISSION_GRANTED
}
if (missingPerms.isNotEmpty()) {
missingPerms.forEach {
// Permission is not granted
@ -73,25 +79,24 @@ class MainActivity : AppCompatActivity(), Logging {
}
}
@Preview
@Composable
fun composeView() {
MaterialTheme {
Column {
Text(text = "MeshUtil Ugly UI", modifier = Spacing(16.dp))
Text(text = "MeshUtil Ugly UI", modifier = Spacing(8.dp))
Button(text = "Start scan",
onClick = {
if (bluetoothAdapter != null) {
SoftwareUpdateService.enqueueWork(this@MainActivity, SoftwareUpdateService.scanDevicesIntent)
SoftwareUpdateService.enqueueWork(
this@MainActivity,
SoftwareUpdateService.scanDevicesIntent
)
}
})
}
}}
@Preview
@Composable
fun defaultPreview() {
composeView()
}
}
override fun onCreate(savedInstanceState: Bundle?) {
@ -102,13 +107,12 @@ class MainActivity : AppCompatActivity(), Logging {
// Ensures Bluetooth is available on the device and it is enabled. If not,
// displays a dialog requesting user permission to enable Bluetooth.
if(bluetoothAdapter != null) {
if (bluetoothAdapter != null) {
bluetoothAdapter!!.takeIf { !it.isEnabled }?.apply {
val enableBtIntent = Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE)
startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT)
}
}
else {
} else {
Toast.makeText(this, "Error - this app requires bluetooth", Toast.LENGTH_LONG).show()
}

Wyświetl plik

@ -36,27 +36,18 @@ class SoftwareUpdateService : JobIntentService(), Logging {
bluetoothManager.adapter!!
}
lateinit var updateGatt: BluetoothGatt // the gatt api used to talk to our device
lateinit var updateService: BluetoothGattService // The service we are currently talking to to do the update
lateinit var totalSizeDesc: BluetoothGattCharacteristic
lateinit var dataDesc: BluetoothGattCharacteristic
lateinit var firmwareStream: InputStream
fun startUpdate() {
totalSizeDesc = updateService.getCharacteristic(SW_UPDATE_TOTALSIZE_CHARACTER)!!
firmwareStream = assets.open("firmware.bin")
firmwareStream = assets.open("firmware.bin")
// Start the update by writing the # of bytes in the image
val numBytes = firmwareStream.available()
logAssert(totalSizeDesc.setValue(numBytes, BluetoothGattCharacteristic.FORMAT_UINT32, 0))
logAssert(updateGatt.writeCharacteristic(totalSizeDesc))
logAssert(updateGatt.readCharacteristic(totalSizeDesc))
// Start the update by writing the # of bytes in the image
val numBytes = firmwareStream.available()
logAssert(totalSizeDesc.setValue(numBytes, BluetoothGattCharacteristic.FORMAT_UINT32, 0))
logAssert(updateGatt.writeCharacteristic(totalSizeDesc))
}
// Send the next block of our file to the device
fun sendNextBlock() {
if(firmwareStream.available() > 0) {
if (firmwareStream.available() > 0) {
var blockSize = 512
if (blockSize > firmwareStream.available())
@ -66,12 +57,10 @@ class SoftwareUpdateService : JobIntentService(), Logging {
// slightly expensive to keep reallocing this buffer, but whatever
logAssert(firmwareStream.read(buffer) == blockSize)
dataDesc = updateService.getCharacteristic(SW_UPDATE_DATA_CHARACTER)!!
// updateGatt.beginReliableWrite()
dataDesc.value = buffer
logAssert(updateGatt.writeCharacteristic(dataDesc))
}
else {
} else {
logAssert(false) // fixme
}
}
@ -99,11 +88,13 @@ class SoftwareUpdateService : JobIntentService(), Logging {
// Various callback methods defined by the BLE API.
val gattCallback = object : BluetoothGattCallback() {
override fun onConnectionStateChange(
gatt: BluetoothGatt,
status: Int,
newState: Int
) {
info("new bluetooth connection state $newState")
//val intentAction: String
when (newState) {
BluetoothProfile.STATE_CONNECTED -> {
@ -130,6 +121,11 @@ class SoftwareUpdateService : JobIntentService(), Logging {
if (service != null) {
// FIXME instead of slamming in the target device here, instead make it a param for startUpdate
updateService = service
totalSizeDesc = service.getCharacteristic(SW_UPDATE_TOTALSIZE_CHARACTER)
dataDesc = service.getCharacteristic(SW_UPDATE_DATA_CHARACTER)
crc32Desc = service.getCharacteristic(SW_UPDATE_CRC32_CHARACTER)
updateResultDesc = service.getCharacteristic(SW_UPDATE_RESULT_CHARACTER)
// FIXME instead of keeping the connection open, make start update just reconnect (needed once user can choose devices)
updateGatt = bluetoothGatt
enqueueWork(this@SoftwareUpdateService, startUpdateIntent)
@ -162,19 +158,24 @@ class SoftwareUpdateService : JobIntentService(), Logging {
) {
logAssert(status == BluetoothGatt.GATT_SUCCESS)
if (characteristic == dataDesc) {
if(characteristic == totalSizeDesc) {
// Our write completed, queue up a readback
logAssert(updateGatt.readCharacteristic(totalSizeDesc))
} else if (characteristic == dataDesc) {
enqueueWork(this@SoftwareUpdateService, sendNextBlockIntent)
}
}
}
bluetoothGatt = result.device.connectGatt(this@SoftwareUpdateService, false, gattCallback)!!
bluetoothGatt =
result.device.connectGatt(this@SoftwareUpdateService.applicationContext, true, gattCallback)!!
toast("FISH " + bluetoothGatt)
logAssert(bluetoothGatt.discoverServices())
// too early to do this here
// logAssert(bluetoothGatt.discoverServices())
}
}
private fun scanLeDevice(enable: Boolean) {
when (enable) {
true -> {
@ -193,11 +194,11 @@ class SoftwareUpdateService : JobIntentService(), Logging {
/* ScanSettings.CALLBACK_TYPE_FIRST_MATCH seems to trigger a bug returning an error of
SCAN_FAILED_OUT_OF_HARDWARE_RESOURCES (error #5)
*/
val settings = ScanSettings.Builder().
setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY).
// setMatchMode(ScanSettings.MATCH_NUM_ONE_ADVERTISEMENT).
// setCallbackType(ScanSettings.CALLBACK_TYPE_FIRST_MATCH).
build()
val settings =
ScanSettings.Builder().setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY).
// setMatchMode(ScanSettings.MATCH_NUM_ONE_ADVERTISEMENT).
// setCallbackType(ScanSettings.CALLBACK_TYPE_FIRST_MATCH).
build()
scanner.startScan(listOf(filter), settings, scanCallback)
}
else -> {
@ -216,14 +217,15 @@ class SoftwareUpdateService : JobIntentService(), Logging {
}
toast("Executing: $label")
when(intent.action) {
when (intent.action) {
scanDevicesIntent.action -> scanLeDevice(true)
startUpdateIntent.action -> startUpdate()
sendNextBlockIntent.action -> sendNextBlock()
else -> logAssert(false)
}
info("Completed service @ " + SystemClock.elapsedRealtime()
info(
"Completed service @ " + SystemClock.elapsedRealtime()
)
}
@ -269,6 +271,17 @@ class SoftwareUpdateService : JobIntentService(), Logging {
private val SW_UPDATE_RESULT_CHARACTER =
UUID.fromString("5e134862-7411-4424-ac4a-210937432c77") // read|notify result code, readable but will notify when the OTA operation completes
// FIXME - this is state that really more properly goes with the serice instance, but
// it can go away if our work queue gets empty. So we keep it here instead. Not sure
// if there is a better approach?
lateinit var updateGatt: BluetoothGatt // the gatt api used to talk to our device
lateinit var updateService: BluetoothGattService // The service we are currently talking to to do the update
lateinit var totalSizeDesc: BluetoothGattCharacteristic
lateinit var dataDesc: BluetoothGattCharacteristic
lateinit var crc32Desc: BluetoothGattCharacteristic
lateinit var updateResultDesc: BluetoothGattCharacteristic
lateinit var firmwareStream: InputStream
/**
* Convenience method for enqueuing work in to this service.
*/