gracefully handle when an esp32 bluetooth link slowly browns out

1.2-legacy
geeksville 2020-02-17 18:46:20 -08:00
rodzic b3026ba6be
commit bdd6e5de6c
6 zmienionych plików z 104 dodań i 89 usunięć

11
TODO.md
Wyświetl plik

@ -1,10 +1,6 @@
# High priority
MVP features required for first public alpha
* show real ID of me when I sent texts
* keep text entry box at bottom of screen
* show user icons in chat
* when I enter texts, send them to the device
* let user set name and shortname
* take video
@ -60,6 +56,8 @@ Do this "Signal app compatible" release relatively soon after the alpha release
# Medium priority
Things for the betaish period.
* show user icons in chat
* keep past messages in db, one db per channel
* spend some quality power consumption tuning with https://developer.android.com/studio/profile/energy-profiler and https://developer.android.com/topic/performance/power/battery-historian
* only publish gps positions once every 5 mins while we are connected to our radio _and_ someone else is in the mesh
* Do PRIORITY_BALANCED_POWER_ACCURACY for our gps updates when no one in the mesh is nearer than 200 meters
@ -130,4 +128,7 @@ Don't leave device discoverable. Don't let unpaired users do things with device
* warn user to bt pair
* suppress logging output if running a release build (required for play store)
* provide gps location for devices that don't have it
* prompt user to turnon bluetooth and bind
* prompt user to turnon bluetooth and bind
* show real ID of me when I sent texts
* keep text entry box at bottom of screen
* when I enter texts, send them to the device

Wyświetl plik

@ -259,7 +259,7 @@ class MainActivity : AppCompatActivity(), Logging,
filter.addAction(MeshService.ACTION_NODE_CHANGE)
filter.addAction(MeshService.ACTION_RECEIVED_DATA)
registerReceiver(meshServiceReceiver, filter)
receiverRegistered = true;
}
private fun unregisterMeshReceiver() {

Wyświetl plik

@ -1,7 +1,10 @@
package com.geeksville.mesh.model
import android.os.RemoteException
import androidx.compose.mutableStateOf
import com.geeksville.android.Logging
import com.geeksville.mesh.MeshProtos
import com.geeksville.mesh.utf8
import java.util.*
/**
@ -35,9 +38,36 @@ object MessagesState : Logging {
a.size == b.size // If the # of messages changes, consider it important for rerender
})
/// add a message our GUI list of past msgs
fun addMessage(m: TextMessage) {
val l = messages.value.toMutableList()
l.add(m)
messages.value = l
}
/// Send a message and added it to our GUI log
fun sendMessage(str: String, dest: String? = null) {
var error: String? = null
val service = UIState.meshService
if (service != null)
try {
service.sendData(
dest,
str.toByteArray(utf8),
MeshProtos.Data.Type.CLEAR_TEXT_VALUE
)
} catch (ex: RemoteException) {
error = "Error: ${ex.message}"
}
else
error = "Error: No Mesh service"
MessagesState.addMessage(
TextMessage(
NodeDB.myId.value,
str,
errorMessage = error
)
)
}
}

Wyświetl plik

@ -9,6 +9,7 @@ import android.content.*
import android.graphics.Color
import android.os.Build
import android.os.IBinder
import android.os.RemoteException
import androidx.annotation.RequiresApi
import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationCompat.PRIORITY_MIN
@ -538,58 +539,64 @@ class MeshService : Service(), Logging {
isConnected = c
if (c) {
// Do our startup init
try {
// FIXME - don't do this until after we see that the radio is connected to the phone
//val sim = SimRadio(this@MeshService)
//sim.start() // Fake up our node id info and some past packets from other nodes
// FIXME - don't do this until after we see that the radio is connected to the phone
//val sim = SimRadio(this@MeshService)
//sim.start() // Fake up our node id info and some past packets from other nodes
val myInfo = MeshProtos.MyNodeInfo.parseFrom(
connectedRadio.readMyNode()
)
val myInfo = MeshProtos.MyNodeInfo.parseFrom(
connectedRadio.readMyNode()
)
val mynodeinfo = MyNodeInfo(myInfo.myNodeNum, myInfo.hasGps)
myNodeInfo = mynodeinfo
// Ask for the current node DB
connectedRadio.restartNodeInfo()
val mynodeinfo = MyNodeInfo(myInfo.myNodeNum, myInfo.hasGps)
myNodeInfo = mynodeinfo
// read all the infos until we get back null
var infoBytes = connectedRadio.readNodeInfo()
while (infoBytes != null) {
val info =
MeshProtos.NodeInfo.parseFrom(infoBytes)
debug("Received initial nodeinfo $info")
// Ask for the current node DB
connectedRadio.restartNodeInfo()
// Just replace/add any entry
updateNodeInfo(info.num) {
if (info.hasUser())
it.user =
MeshUser(
info.user.id,
info.user.longName,
info.user.shortName
)
// read all the infos until we get back null
var infoBytes = connectedRadio.readNodeInfo()
while (infoBytes != null) {
val info =
MeshProtos.NodeInfo.parseFrom(infoBytes)
debug("Received initial nodeinfo $info")
// Just replace/add any entry
updateNodeInfo(info.num) {
if (info.hasUser())
it.user =
MeshUser(
info.user.id,
info.user.longName,
info.user.shortName
if (info.hasPosition())
it.position = Position(
info.position.latitude,
info.position.longitude,
info.position.altitude
)
if (info.hasPosition())
it.position = Position(
info.position.latitude,
info.position.longitude,
info.position.altitude
)
it.lastSeen = info.lastSeen
}
it.lastSeen = info.lastSeen
// advance to next
infoBytes = connectedRadio.readNodeInfo()
}
// advance to next
infoBytes = connectedRadio.readNodeInfo()
// we don't ask for GPS locations from android if our device has a built in GPS
if (!mynodeinfo.hasGPS)
startLocationRequests()
else
debug("Our radio has a built in GPS, so not reading GPS in phone")
} catch (ex: RemoteException) {
// It seems that when the ESP32 goes offline it can briefly come back for a 100ms ish which
// causes the phone to try and reconnect. If we fail downloading our initial radio state we don't want to
// claim we have a valid connection still
isConnected = false;
throw ex; // Important to rethrow so that we don't tell the app all is well
}
// we don't ask for GPS locations from android if our device has a built in GPS
if (!mynodeinfo.hasGPS)
startLocationRequests()
else
debug("Our radio has a built in GPS, so not reading GPS in phone")
} else {
// lost radio connection, therefore no need to keep listening to GPS
stopLocationRequests()
@ -703,8 +710,7 @@ class MeshService : Service(), Logging {
// encapsulate our payload in the proper protobufs and fire it off
val packet = buildMeshPacket(destId) {
data = MeshProtos.Data.newBuilder().also {
it.typ =
MeshProtos.Data.Type.SIGNAL_OPAQUE
it.typ = MeshProtos.Data.Type.forNumber(typ)
it.payload = ByteString.copyFrom(payloadIn)
}.build()
}

Wyświetl plik

@ -265,22 +265,25 @@ class SafeBluetooth(private val context: Context, private val device: BluetoothD
* 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) {
exceptionReporter {
// We might unexpectedly fail inside here, but we don't want to pass that exception back up to the bluetooth GATT layer
// 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
// 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
}
startNewWork()
w
}
debug("work ${work.tag} is completed, resuming status=$status, res=$res")
if (status != 0)
work.completion.resumeWithException(IOException("Bluetooth status=$status"))
else
work.completion.resume(Result.success(res) as Result<Nothing>)
debug("work ${work.tag} is completed, resuming status=$status, res=$res")
if (status != 0)
work.completion.resumeWithException(IOException("Bluetooth status=$status while doing ${work.tag}"))
else
work.completion.resume(Result.success(res) as Result<Nothing>)
}
}
/**

Wyświetl plik

@ -1,6 +1,5 @@
package com.geeksville.mesh.ui
import android.os.RemoteException
import androidx.compose.Composable
import androidx.compose.state
import androidx.ui.core.Modifier
@ -21,20 +20,17 @@ import androidx.ui.material.surface.Surface
import androidx.ui.text.TextStyle
import androidx.ui.tooling.preview.Preview
import androidx.ui.unit.dp
import com.geeksville.mesh.MeshProtos
import com.geeksville.mesh.model.MessagesState
import com.geeksville.mesh.model.MessagesState.messages
import com.geeksville.mesh.model.NodeDB
import com.geeksville.mesh.model.TextMessage
import com.geeksville.mesh.model.UIState
import com.geeksville.mesh.utf8
import java.text.SimpleDateFormat
private val dateFormat = SimpleDateFormat("h:mm a")
val TimestampEmphasis = object : Emphasis {
override fun emphasize(color: Color) = color.copy(alpha = 0.12f)
override fun emphasize(color: Color) = color.copy(alpha = 0.25f)
}
@ -115,29 +111,8 @@ fun MessagesContent() {
MessagesState.info("did IME action")
val str = message.value
var error: String? = null
val service = UIState.meshService
if (service != null)
try {
service.sendData(
null,
str.toByteArray(utf8),
MeshProtos.Data.Type.CLEAR_TEXT_VALUE
)
} catch (ex: RemoteException) {
error = "Error: ${ex.message}"
}
else
error = "Error: No Mesh service"
MessagesState.addMessage(
TextMessage(
NodeDB.myId.value,
str,
errorMessage = error
)
)
MessagesState.sendMessage(str)
message.value = "" // blow away the string the user just entered
},
modifier = LayoutPadding(4.dp)
)