kopia lustrzana https://github.com/meshtastic/Meshtastic-Android
bluetooth cleanup wip
rodzic
52440d499e
commit
6edc89e2aa
11
TODO.md
11
TODO.md
|
@ -1,11 +1,17 @@
|
|||
|
||||
|
||||
# High priority
|
||||
* fix // FIXME hack for now - throw IdNotFoundException(id) in MeshService
|
||||
* implement android side of mesh radio bluetooth link
|
||||
* investigate the Signal SMS message flow path, see if I could just make Mesh a third peer to signal & sms?
|
||||
* make signal work when there is no internet up
|
||||
* make Signal rx path work
|
||||
* send Signal message type. It seems to be? " public static final int WHISPER_TYPE = 2;
|
||||
public static final int PREKEY_TYPE = 3;
|
||||
public static final int SENDERKEY_TYPE = 4;
|
||||
public static final int SENDERKEY_DISTRIBUTION_TYPE = 5;"
|
||||
* optionally turn off crypto in signal
|
||||
* clean up sw update code in device side
|
||||
|
||||
* change signal package ID
|
||||
* make compose based access show mesh state
|
||||
* add real messaging code/protobufs
|
||||
|
@ -29,7 +35,7 @@ nanopb binaries available here: https://jpa.kapsi.fi/nanopb/download/ use nanopb
|
|||
* use platform theme (dark or light)
|
||||
* remove mixpanel analytics
|
||||
* require user auth to pair with the device (i.e. press button on device to allow a new phone to pair with it).
|
||||
Don't leave device discoverable. Don't let unpaired users do thing with device
|
||||
Don't leave device discoverable. Don't let unpaired users do things with device
|
||||
* remove example code boilerplate from the service
|
||||
* switch from protobuf-java to protobuf-javalite - much faster and smaller, just no JSON debug printing
|
||||
|
||||
|
@ -64,3 +70,4 @@ Don't leave device discoverable. Don't let unpaired users do thing with device
|
|||
* DONE add broadcasters for use by signal (node changes and packet received)
|
||||
|
||||
* DONE have signal declare receivers: https://developer.android.com/guide/components/broadcasts#manifest-declared-receivers
|
||||
* fix // FIXME hack for now - throw IdNotFoundException(id) in MeshService
|
||||
|
|
|
@ -36,7 +36,6 @@ class MeshService : Service(), Logging {
|
|||
private const val NODE_NUM_NO_MESH = -1
|
||||
}
|
||||
|
||||
|
||||
/// A mapping of receiver class name to package name - used for explicit broadcasts
|
||||
private val clientPackages = mutableMapOf<String, String>()
|
||||
|
||||
|
|
|
@ -1,11 +1,61 @@
|
|||
package com.geeksville.mesh
|
||||
|
||||
import android.bluetooth.BluetoothAdapter
|
||||
import android.bluetooth.BluetoothDevice
|
||||
import android.bluetooth.BluetoothManager
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import androidx.core.app.JobIntentService
|
||||
import com.geeksville.android.DebugLogFile
|
||||
import com.geeksville.android.Logging
|
||||
import com.google.protobuf.util.JsonFormat
|
||||
import java.util.*
|
||||
|
||||
/* Info for the esp32 device side code. See that source for the 'gold' standard docs on this interface.
|
||||
|
||||
MeshBluetoothService UUID 6ba1b218-15a8-461f-9fa8-5dcae273eafd
|
||||
|
||||
FIXME - notify vs indication for fromradio output. Using notify for now, not sure if that is best
|
||||
FIXME - in the esp32 mesh management code, occasionally mirror the current net db to flash, so that if we reboot we still have a good guess of users who are out there.
|
||||
FIXME - make sure this protocol is guaranteed robust and won't drop packets
|
||||
|
||||
"According to the BLE specification the notification length can be max ATT_MTU - 3. The 3 bytes subtracted is the 3-byte header(OP-code (operation, 1 byte) and the attribute handle (2 bytes)).
|
||||
In BLE 4.1 the ATT_MTU is 23 bytes (20 bytes for payload), but in BLE 4.2 the ATT_MTU can be negotiated up to 247 bytes."
|
||||
|
||||
MAXPACKET is 256? look into what the lora lib uses. FIXME
|
||||
|
||||
Characteristics:
|
||||
UUID
|
||||
properties
|
||||
description
|
||||
|
||||
8ba2bcc2-ee02-4a55-a531-c525c5e454d5
|
||||
read
|
||||
fromradio - contains a newly received packet destined towards the phone (up to MAXPACKET bytes? per packet).
|
||||
After reading the esp32 will put the next packet in this mailbox. If the FIFO is empty it will put an empty packet in this
|
||||
mailbox.
|
||||
|
||||
f75c76d2-129e-4dad-a1dd-7866124401e7
|
||||
write
|
||||
toradio - write ToRadio protobufs to this charstic to send them (up to MAXPACKET len)
|
||||
|
||||
ed9da18c-a800-4f66-a670-aa7547e34453
|
||||
read|notify|write
|
||||
fromnum - the current packet # in the message waiting inside fromradio, if the phone sees this notify it should read messages
|
||||
until it catches up with this number.
|
||||
The phone can write to this register to go backwards up to FIXME packets, to handle the rare case of a fromradio packet was dropped after the esp32
|
||||
callback was called, but before it arrives at the phone. If the phone writes to this register the esp32 will discard older packets and put the next packet >= fromnum in fromradio.
|
||||
When the esp32 advances fromnum, it will delay doing the notify by 100ms, in the hopes that the notify will never actally need to be sent if the phone is already pulling from fromradio.
|
||||
Note: that if the phone ever sees this number decrease, it means the esp32 has rebooted.
|
||||
|
||||
Re: queue management
|
||||
Not all messages are kept in the fromradio queue (filtered based on SubPacket):
|
||||
* only the most recent Position and User messages for a particular node are kept
|
||||
* all Data SubPackets are kept
|
||||
* No WantNodeNum / DenyNodeNum messages are kept
|
||||
A variable keepAllPackets, if set to true will suppress this behavior and instead keep everything for forwarding to the phone (for debugging)
|
||||
|
||||
*/
|
||||
|
||||
/**
|
||||
* Handles the bluetooth link with a mesh radio device. Does not cache any device state,
|
||||
|
@ -36,6 +86,14 @@ class RadioInterfaceService : JobIntentService(), Logging {
|
|||
*/
|
||||
const val RECEIVE_FROMRADIO_ACTION = "$prefix.RECEIVE_FROMRADIO"
|
||||
|
||||
private val BTM_SERVICE_UUID = UUID.fromString("6ba1b218-15a8-461f-9fa8-5dcae273eafd")
|
||||
private val BTM_FROMRADIO_CHARACTER =
|
||||
UUID.fromString("8ba2bcc2-ee02-4a55-a531-c525c5e454d5")
|
||||
private val BTM_TORADIO_CHARACTER =
|
||||
UUID.fromString("f75c76d2-129e-4dad-a1dd-7866124401e7")
|
||||
private val BTM_FROMNUM_CHARACTER =
|
||||
UUID.fromString("ed9da18c-a800-4f66-a670-aa7547e34453")
|
||||
|
||||
/**
|
||||
* Convenience method for enqueuing work in to this service.
|
||||
*/
|
||||
|
@ -64,6 +122,15 @@ class RadioInterfaceService : JobIntentService(), Logging {
|
|||
}
|
||||
}
|
||||
|
||||
private val bluetoothAdapter: BluetoothAdapter by lazy(LazyThreadSafetyMode.NONE) {
|
||||
val bluetoothManager = getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager
|
||||
bluetoothManager.adapter!!
|
||||
}
|
||||
|
||||
// Both of these are created in onCreate()
|
||||
private lateinit var device: BluetoothDevice
|
||||
private lateinit var safe: SafeBluetooth
|
||||
|
||||
lateinit var sentPacketsLog: DebugLogFile // inited in onCreate
|
||||
|
||||
fun broadcastConnectionChanged(isConnected: Boolean) {
|
||||
|
@ -91,6 +158,23 @@ class RadioInterfaceService : JobIntentService(), Logging {
|
|||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
|
||||
// FIXME, the lifecycle is wrong for jobintentservice, change to a regular service
|
||||
// FIXME, let user GUI select which device we are talking to
|
||||
|
||||
// Note: this call does no comms, it just creates the device object (even if the
|
||||
// device is off/not connected)
|
||||
device = bluetoothAdapter.getRemoteDevice("B4:E6:2D:EA:32:B7")
|
||||
// Note this constructor also does no comm
|
||||
safe = SafeBluetooth(this, device)
|
||||
|
||||
// FIXME, pass in true for autoconnect - so we will autoconnect whenever the radio
|
||||
// comes in range (even if we made this connect call long ago when we got powered on)
|
||||
// see https://stackoverflow.com/questions/40156699/which-correct-flag-of-autoconnect-in-connectgatt-of-ble for
|
||||
// more info
|
||||
// FIXME, can't use sync connect here - because it could take a LONG time
|
||||
// FIXME, don't use sync api at all - because our operations are so simple and atomic
|
||||
safe.connect(true)
|
||||
|
||||
sentPacketsLog = DebugLogFile(this, "sent_log.json")
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,232 @@
|
|||
package com.geeksville.mesh
|
||||
|
||||
import android.bluetooth.*
|
||||
import android.content.Context
|
||||
import com.geeksville.android.Logging
|
||||
import com.geeksville.concurrent.CallbackContinuation
|
||||
import com.geeksville.concurrent.Continuation
|
||||
import com.geeksville.concurrent.SyncContinuation
|
||||
import java.io.IOException
|
||||
|
||||
|
||||
/**
|
||||
* Uses coroutines to safely access a bluetooth GATT device with a synchronous API
|
||||
*
|
||||
* The BTLE API on android is dumb. You can only have one outstanding operation in flight to
|
||||
* the device. If you try to do something when something is pending, the operation just returns
|
||||
* false. You are expected to chain your operations from the results callbacks.
|
||||
*
|
||||
* This class fixes the API by using coroutines to let you safely do a series of BTLE operations.
|
||||
*/
|
||||
class SafeBluetooth(private val context: Context, private val device: BluetoothDevice) :
|
||||
Logging {
|
||||
|
||||
/// Timeout before we declare a bluetooth operation failed
|
||||
private val timeoutMsec = 30 * 1000L
|
||||
|
||||
/// Users can access the GATT directly as needed
|
||||
lateinit var gatt: BluetoothGatt
|
||||
|
||||
var state = BluetoothProfile.STATE_DISCONNECTED
|
||||
private var currentWork: BluetoothContinuation? = null
|
||||
private val workQueue = mutableListOf<BluetoothContinuation>()
|
||||
|
||||
/**
|
||||
* a schedulable bit of bluetooth work, includes both the closure to call to start the operation
|
||||
* and the completion (either async or sync) to call when it completes
|
||||
*/
|
||||
class BluetoothContinuation(
|
||||
val completion: com.geeksville.concurrent.Continuation<*>,
|
||||
private val startWorkFn: () -> Boolean
|
||||
) {
|
||||
|
||||
/// Start running a queued bit of work, return true for success or false for fatal bluetooth error
|
||||
fun startWork() = startWorkFn()
|
||||
}
|
||||
|
||||
private val gattCallback = object : BluetoothGattCallback() {
|
||||
|
||||
override fun onConnectionStateChange(
|
||||
gatt: BluetoothGatt,
|
||||
status: Int,
|
||||
newState: Int
|
||||
) {
|
||||
info("new bluetooth connection state $newState")
|
||||
state = newState
|
||||
when (newState) {
|
||||
BluetoothProfile.STATE_CONNECTED -> {
|
||||
//logAssert(workQueue.isNotEmpty())
|
||||
//val work = workQueue.removeAt(0)
|
||||
completeWork(status, Unit)
|
||||
}
|
||||
BluetoothProfile.STATE_DISCONNECTED -> {
|
||||
// cancel any ops
|
||||
failAllWork(IOException("Lost connection"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onServicesDiscovered(gatt: BluetoothGatt, status: Int) {
|
||||
completeWork(status, Unit)
|
||||
}
|
||||
|
||||
override fun onCharacteristicRead(
|
||||
gatt: BluetoothGatt,
|
||||
characteristic: BluetoothGattCharacteristic,
|
||||
status: Int
|
||||
) {
|
||||
completeWork(status, characteristic)
|
||||
}
|
||||
|
||||
override fun onCharacteristicWrite(
|
||||
gatt: BluetoothGatt,
|
||||
characteristic: BluetoothGattCharacteristic,
|
||||
status: Int
|
||||
) {
|
||||
completeWork(status, characteristic)
|
||||
}
|
||||
|
||||
override fun onMtuChanged(gatt: BluetoothGatt, mtu: Int, status: Int) {
|
||||
completeWork(status, mtu)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// If we have work we can do, start doing it.
|
||||
private fun startNewWork() {
|
||||
logAssert(currentWork == null)
|
||||
|
||||
if (workQueue.isNotEmpty()) {
|
||||
val newWork = workQueue.removeAt(0)
|
||||
currentWork = newWork
|
||||
newWork.startWork()
|
||||
}
|
||||
}
|
||||
|
||||
private fun <T> queueWork(cont: Continuation<T>, initFn: () -> Boolean) {
|
||||
val btCont = BluetoothContinuation(cont, initFn)
|
||||
|
||||
synchronized(workQueue) {
|
||||
workQueue.add(btCont)
|
||||
|
||||
// if we don't have any outstanding operations, run first item in queue
|
||||
if (currentWork == null)
|
||||
startNewWork()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called from our big GATT callback, completes the current job and then schedules a new one
|
||||
*/
|
||||
private fun <T : Any> completeWork(status: Int, res: T) {
|
||||
|
||||
// startup next job in queue before calling the completion handler
|
||||
val work =
|
||||
synchronized(workQueue) {
|
||||
val w = currentWork!! // will throw if null, which is helpful
|
||||
currentWork = null // We are now no longer working on anything
|
||||
|
||||
startNewWork()
|
||||
w
|
||||
}
|
||||
|
||||
if (status != 0)
|
||||
work.completion.resumeWithException(IOException("Bluetooth status=$status"))
|
||||
else
|
||||
work.completion.resume(Result.success(res) as Result<Nothing>) // FIXME, will this work?
|
||||
}
|
||||
|
||||
/**
|
||||
* Something went wrong, abort all queued
|
||||
*/
|
||||
private fun failAllWork(ex: Exception) {
|
||||
synchronized(workQueue) {
|
||||
workQueue.forEach {
|
||||
it.completion.resumeWithException(ex)
|
||||
}
|
||||
workQueue.clear()
|
||||
}
|
||||
}
|
||||
|
||||
/// helper glue to make sync continuations and then wait for the result
|
||||
private fun <T> makeSync(wrappedFn: (SyncContinuation<T>) -> Unit): T {
|
||||
val cont = SyncContinuation<T>()
|
||||
wrappedFn(cont)
|
||||
return cont.await(timeoutMsec)
|
||||
}
|
||||
|
||||
// FIXME, pass in true for autoconnect - so we will autoconnect whenever the radio
|
||||
// comes in range (even if we made this connect call long ago when we got powered on)
|
||||
// see https://stackoverflow.com/questions/40156699/which-correct-flag-of-autoconnect-in-connectgatt-of-ble for
|
||||
// more info.
|
||||
// Otherwise if you pass in false, it will try to connect now and will timeout and fail in 30 seconds.
|
||||
private fun queueConnect(autoConnect: Boolean = false, cont: Continuation<Unit>) {
|
||||
queueWork(cont) {
|
||||
val g = device.connectGatt(context, autoConnect, gattCallback)
|
||||
if (g != null)
|
||||
gatt = g
|
||||
g != null
|
||||
}
|
||||
}
|
||||
|
||||
fun asyncConnect(autoConnect: Boolean = false, cb: (Result<Unit>) -> Unit) {
|
||||
queueConnect(autoConnect, CallbackContinuation(cb))
|
||||
}
|
||||
|
||||
fun connect(autoConnect: Boolean = false) = makeSync<Unit> { queueConnect(autoConnect, it) }
|
||||
|
||||
private fun queueReadCharacteristic(
|
||||
c: BluetoothGattCharacteristic,
|
||||
cont: Continuation<BluetoothGattCharacteristic>
|
||||
) = queueWork(cont) { gatt.readCharacteristic(c) }
|
||||
|
||||
fun asyncReadCharacteristic(
|
||||
c: BluetoothGattCharacteristic,
|
||||
cb: (Result<BluetoothGattCharacteristic>) -> Unit
|
||||
) = queueReadCharacteristic(c, CallbackContinuation(cb))
|
||||
|
||||
fun readCharacteristic(c: BluetoothGattCharacteristic): BluetoothGattCharacteristic =
|
||||
makeSync { queueReadCharacteristic(c, it) }
|
||||
|
||||
private fun queueDiscoverServices(cont: Continuation<Unit>) {
|
||||
queueWork(cont) {
|
||||
gatt.discoverServices()
|
||||
}
|
||||
}
|
||||
|
||||
fun asyncDiscoverServices(cb: (Result<Unit>) -> Unit) {
|
||||
queueDiscoverServices(CallbackContinuation(cb))
|
||||
}
|
||||
|
||||
fun discoverServices() = makeSync<Unit> { queueDiscoverServices(it) }
|
||||
|
||||
private fun queueRequestMtu(
|
||||
len: Int,
|
||||
cont: Continuation<Int>
|
||||
) = queueWork(cont) { gatt.requestMtu(len) }
|
||||
|
||||
fun asyncRequestMtu(
|
||||
len: Int,
|
||||
cb: (Result<Int>) -> Unit
|
||||
) = queueRequestMtu(len, CallbackContinuation(cb))
|
||||
|
||||
fun requestMtu(len: Int): Int =
|
||||
makeSync { queueRequestMtu(len, it) }
|
||||
|
||||
private fun queueWriteCharacteristic(
|
||||
c: BluetoothGattCharacteristic,
|
||||
cont: Continuation<BluetoothGattCharacteristic>
|
||||
) = queueWork(cont) { gatt.writeCharacteristic(c) }
|
||||
|
||||
fun asyncWriteCharacteristic(
|
||||
c: BluetoothGattCharacteristic,
|
||||
cb: (Result<BluetoothGattCharacteristic>) -> Unit
|
||||
) = queueWriteCharacteristic(c, CallbackContinuation(cb))
|
||||
|
||||
fun writeCharacteristic(c: BluetoothGattCharacteristic): BluetoothGattCharacteristic =
|
||||
makeSync { queueWriteCharacteristic(c, it) }
|
||||
|
||||
fun disconnect() {
|
||||
gatt.disconnect()
|
||||
}
|
||||
}
|
|
@ -40,7 +40,7 @@ class SoftwareUpdateService : JobIntentService(), Logging {
|
|||
fun startUpdate() {
|
||||
info("starting update")
|
||||
|
||||
val sync = SyncBluetoothDevice(this@SoftwareUpdateService, device)
|
||||
val sync = SafeBluetooth(this@SoftwareUpdateService, device)
|
||||
|
||||
val firmwareStream = assets.open("firmware.bin")
|
||||
val firmwareCrc = CRC32()
|
||||
|
|
|
@ -1,142 +0,0 @@
|
|||
package com.geeksville.mesh
|
||||
|
||||
import android.bluetooth.*
|
||||
import android.content.Context
|
||||
import com.geeksville.android.Logging
|
||||
import com.geeksville.concurrent.SyncContinuation
|
||||
import com.geeksville.concurrent.suspend
|
||||
import java.io.IOException
|
||||
|
||||
|
||||
/**
|
||||
* Uses coroutines to safely access a bluetooth GATT device with a synchronous API
|
||||
*
|
||||
* The BTLE API on android is dumb. You can only have one outstanding operation in flight to
|
||||
* the device. If you try to do something when something is pending, the operation just returns
|
||||
* false. You are expected to chain your operations from the results callbacks.
|
||||
*
|
||||
* This class fixes the API by using coroutines to let you safely do a series of BTLE operations.
|
||||
*/
|
||||
class SyncBluetoothDevice(private val context: Context, private val device: BluetoothDevice) :
|
||||
Logging {
|
||||
|
||||
private var pendingServiceDesc: SyncContinuation<Unit>? = null
|
||||
private var pendingMtu: SyncContinuation<Int>? = null
|
||||
private var pendingWriteC: SyncContinuation<Unit>? = null
|
||||
private var pendingReadC: SyncContinuation<BluetoothGattCharacteristic>? = null
|
||||
private var pendingConnect: SyncContinuation<Unit>? = null
|
||||
|
||||
/// Timeout before we declare a bluetooth operation failed
|
||||
private val timeoutMsec = 30 * 1000L
|
||||
|
||||
var state = BluetoothProfile.STATE_DISCONNECTED
|
||||
|
||||
private val gattCallback = object : BluetoothGattCallback() {
|
||||
override fun onConnectionStateChange(
|
||||
gatt: BluetoothGatt,
|
||||
status: Int,
|
||||
newState: Int
|
||||
) {
|
||||
info("new bluetooth connection state $newState")
|
||||
state = newState
|
||||
when (newState) {
|
||||
BluetoothProfile.STATE_CONNECTED -> {
|
||||
if (pendingConnect != null) { // If someone was waiting to connect unblock them
|
||||
pendingConnect!!.resume(Unit)
|
||||
pendingConnect = null
|
||||
}
|
||||
}
|
||||
BluetoothProfile.STATE_DISCONNECTED -> {
|
||||
// cancel any ops
|
||||
|
||||
val pendings = listOf(
|
||||
pendingMtu,
|
||||
pendingServiceDesc,
|
||||
pendingWriteC,
|
||||
pendingReadC,
|
||||
pendingConnect
|
||||
)
|
||||
pendings.filterNotNull().forEach {
|
||||
it.resumeWithException(IOException("Lost connection"))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onServicesDiscovered(gatt: BluetoothGatt, status: Int) {
|
||||
if (status != 0)
|
||||
pendingServiceDesc!!.resumeWithException(IOException("Bluetooth status=$status"))
|
||||
else
|
||||
pendingServiceDesc!!.resume(Unit)
|
||||
pendingServiceDesc = null
|
||||
}
|
||||
|
||||
override fun onCharacteristicRead(
|
||||
gatt: BluetoothGatt,
|
||||
characteristic: BluetoothGattCharacteristic,
|
||||
status: Int
|
||||
) {
|
||||
if (status != 0)
|
||||
pendingReadC!!.resumeWithException(IOException("Bluetooth status=$status"))
|
||||
else
|
||||
pendingReadC!!.resume(characteristic)
|
||||
pendingReadC = null
|
||||
}
|
||||
|
||||
override fun onCharacteristicWrite(
|
||||
gatt: BluetoothGatt,
|
||||
characteristic: BluetoothGattCharacteristic,
|
||||
status: Int
|
||||
) {
|
||||
if (status != 0)
|
||||
pendingWriteC!!.resumeWithException(IOException("Bluetooth status=$status"))
|
||||
else
|
||||
pendingWriteC!!.resume(Unit)
|
||||
}
|
||||
|
||||
override fun onMtuChanged(gatt: BluetoothGatt, mtu: Int, status: Int) {
|
||||
if (status != 0)
|
||||
pendingMtu!!.resumeWithException(IOException("Bluetooth status=$status"))
|
||||
else
|
||||
pendingMtu!!.resume(mtu)
|
||||
pendingMtu = null
|
||||
}
|
||||
}
|
||||
|
||||
/// Users can access the GATT directly as needed
|
||||
lateinit var gatt: BluetoothGatt
|
||||
|
||||
fun connect() =
|
||||
suspend<Unit>(timeoutMsec) { cont ->
|
||||
pendingConnect = cont
|
||||
gatt = device.connectGatt(context, false, gattCallback)!!
|
||||
}
|
||||
|
||||
fun discoverServices() =
|
||||
suspend<Unit>(timeoutMsec) { cont ->
|
||||
pendingServiceDesc = cont
|
||||
logAssert(gatt.discoverServices())
|
||||
}
|
||||
|
||||
/// Returns the actual MTU size used
|
||||
fun requestMtu(len: Int) = suspend<Int>(timeoutMsec) { cont ->
|
||||
pendingMtu = cont
|
||||
logAssert(gatt.requestMtu(len))
|
||||
}
|
||||
|
||||
fun writeCharacteristic(c: BluetoothGattCharacteristic) =
|
||||
suspend<Unit>(timeoutMsec) { cont ->
|
||||
pendingWriteC = cont
|
||||
logAssert(gatt.writeCharacteristic(c))
|
||||
}
|
||||
|
||||
fun readCharacteristic(c: BluetoothGattCharacteristic) =
|
||||
suspend<BluetoothGattCharacteristic>(timeoutMsec) { cont ->
|
||||
pendingReadC = cont
|
||||
logAssert(gatt.readCharacteristic(c))
|
||||
}
|
||||
|
||||
fun disconnect() {
|
||||
gatt.disconnect()
|
||||
}
|
||||
}
|
|
@ -45,6 +45,7 @@ message Position {
|
|||
double latitude = 1;
|
||||
double longitude = 2;
|
||||
int32 altitude = 3;
|
||||
int32 battery_level = 4; // 0-100
|
||||
}
|
||||
|
||||
// Times are typically not sent over the mesh, but they will be added to any Packet (chain of SubPacket)
|
||||
|
@ -121,6 +122,12 @@ message MeshPacket {
|
|||
// Full settings (center freq, spread factor, pre-shared secret key etc...) needed to configure a radio
|
||||
message RadioConfig {
|
||||
// FIXME
|
||||
|
||||
// If true, radio should not try to be smart about what packets to queue to the phone
|
||||
bool keep_all_packets = 100;
|
||||
|
||||
// If true, we will try to capture all the packets sent on the mesh, not just the ones destined to our node.
|
||||
bool promiscuous_mode = 101;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -149,7 +156,6 @@ SET_CONFIG (switches device to a new set of radio params and preshared key, drop
|
|||
message NodeInfo {
|
||||
int32 num = 1; // the node number
|
||||
User user = 2;
|
||||
int32 battery_level = 3; // 0-100
|
||||
Position position = 4;
|
||||
Time last_seen = 5;
|
||||
}
|
||||
|
@ -159,14 +165,17 @@ message NodeInfo {
|
|||
// it will sit in that descriptor until consumed by the phone, at which point the next item in the FIFO
|
||||
// will be populated. FIXME
|
||||
message FromRadio {
|
||||
// The packet num, used to allow the phone to request missing read packets from the FIFO, see our bluetooth docs
|
||||
uint32 num = 1;
|
||||
|
||||
oneof variant {
|
||||
MeshPacket packet = 1;
|
||||
MeshPacket packet = 2;
|
||||
|
||||
/// Tells the phone what our node number is, can be -1 if we've not yet joined a mesh.
|
||||
sint32 my_node_num = 2;
|
||||
sint32 my_node_num = 3;
|
||||
|
||||
/// One packet is sent for each node in the on radio DB
|
||||
NodeInfo node_info = 3;
|
||||
NodeInfo node_info = 4;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Ładowanie…
Reference in New Issue