mirror of
https://github.com/meshtastic/Meshtastic-Android.git
synced 2026-04-20 22:23:37 +00:00
feat: Add ESP32 Unified OTA update support (#4095)
Signed-off-by: James Rich <2199651+jamesarich@users.noreply.github.com> Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
This commit is contained in:
parent
6b5dd24249
commit
2a60480bd9
40 changed files with 3410 additions and 717 deletions
|
|
@ -4,21 +4,164 @@
|
|||
"hwModel": 18,
|
||||
"hwModelSlug": "NANO_G2_ULTRA",
|
||||
"requiresBootloaderUpgradeForOta": true,
|
||||
"infoUrl": "https://github.com/RAKWireless/WisBlock/tree/master/bootloader/RAK4630/Latest/WisCore_RAK4631_Bootloader"
|
||||
"infoUrl": "https://meshtastic.org/docs/getting-started/flashing-firmware/nrf52/update-nrf52-bootloader/"
|
||||
},
|
||||
{
|
||||
"hwModel": 9,
|
||||
"hwModelSlug": "RAK4631",
|
||||
"requiresBootloaderUpgradeForOta": true,
|
||||
"infoUrl": "https://github.com/RAKWireless/WisBlock/tree/master/bootloader/RAK4630/Latest/WisCore_RAK4631_Bootloader"
|
||||
"infoUrl": "https://meshtastic.org/docs/getting-started/flashing-firmware/nrf52/update-nrf52-bootloader/"
|
||||
},
|
||||
{
|
||||
"hwModel": 96,
|
||||
"hwModelSlug": "NOMADSTAR_METEOR_PRO",
|
||||
"requiresBootloaderUpgradeForOta": true,
|
||||
"infoUrl": "https://github.com/RAKWireless/WisBlock/tree/master/bootloader/RAK4630/Latest/WisCore_RAK4631_Bootloader"
|
||||
"infoUrl": "https://meshtastic.org/docs/getting-started/flashing-firmware/nrf52/update-nrf52-bootloader/"
|
||||
},
|
||||
{
|
||||
"hwModel": 12,
|
||||
"hwModelSlug": "LILYGO_TBEAM_S3_CORE",
|
||||
"supportsUnifiedOta": true
|
||||
},
|
||||
{
|
||||
"hwModel": 16,
|
||||
"hwModelSlug": "TLORA_T3_S3",
|
||||
"supportsUnifiedOta": true
|
||||
},
|
||||
{
|
||||
"hwModel": 28,
|
||||
"hwModelSlug": "SENSELORA_S3",
|
||||
"supportsUnifiedOta": true
|
||||
},
|
||||
{
|
||||
"hwModel": 43,
|
||||
"hwModelSlug": "HELTEC_V3",
|
||||
"supportsUnifiedOta": true
|
||||
},
|
||||
{
|
||||
"hwModel": 44,
|
||||
"hwModelSlug": "HELTEC_WSL_V3",
|
||||
"supportsUnifiedOta": true
|
||||
},
|
||||
{
|
||||
"hwModel": 45,
|
||||
"hwModelSlug": "BETAFPV_2400_TX",
|
||||
"supportsUnifiedOta": true
|
||||
},
|
||||
{
|
||||
"hwModel": 46,
|
||||
"hwModelSlug": "BETAFPV_900_NANO_TX",
|
||||
"supportsUnifiedOta": true
|
||||
},
|
||||
{
|
||||
"hwModel": 48,
|
||||
"hwModelSlug": "HELTEC_WIRELESS_TRACKER",
|
||||
"supportsUnifiedOta": true
|
||||
},
|
||||
{
|
||||
"hwModel": 49,
|
||||
"hwModelSlug": "HELTEC_WIRELESS_PAPER",
|
||||
"supportsUnifiedOta": true
|
||||
},
|
||||
{
|
||||
"hwModel": 50,
|
||||
"hwModelSlug": "T_DECK",
|
||||
"supportsUnifiedOta": true
|
||||
},
|
||||
{
|
||||
"hwModel": 51,
|
||||
"hwModelSlug": "T_WATCH_S3",
|
||||
"supportsUnifiedOta": true
|
||||
},
|
||||
{
|
||||
"hwModel": 52,
|
||||
"hwModelSlug": "PICOMPUTER_S3",
|
||||
"supportsUnifiedOta": true
|
||||
},
|
||||
{
|
||||
"hwModel": 53,
|
||||
"hwModelSlug": "HELTEC_HT62",
|
||||
"supportsUnifiedOta": true
|
||||
},
|
||||
{
|
||||
"hwModel": 54,
|
||||
"hwModelSlug": "EBYTE_ESP32_S3",
|
||||
"supportsUnifiedOta": true
|
||||
},
|
||||
{
|
||||
"hwModel": 55,
|
||||
"hwModelSlug": "ESP32_S3_PICO",
|
||||
"supportsUnifiedOta": true
|
||||
},
|
||||
{
|
||||
"hwModel": 56,
|
||||
"hwModelSlug": "CHATTER_2",
|
||||
"supportsUnifiedOta": true
|
||||
},
|
||||
{
|
||||
"hwModel": 57,
|
||||
"hwModelSlug": "HELTEC_WIRELESS_PAPER_V1_0",
|
||||
"supportsUnifiedOta": true
|
||||
},
|
||||
{
|
||||
"hwModel": 58,
|
||||
"hwModelSlug": "HELTEC_WIRELESS_TRACKER_V1_0",
|
||||
"supportsUnifiedOta": true
|
||||
},
|
||||
{
|
||||
"hwModel": 59,
|
||||
"hwModelSlug": "UNPHONE",
|
||||
"supportsUnifiedOta": true
|
||||
},
|
||||
{
|
||||
"hwModel": 61,
|
||||
"hwModelSlug": "CDEBYTE_EORA_S3",
|
||||
"supportsUnifiedOta": true
|
||||
},
|
||||
{
|
||||
"hwModel": 64,
|
||||
"hwModelSlug": "RADIOMASTER_900_BANDIT_NANO",
|
||||
"supportsUnifiedOta": true
|
||||
},
|
||||
{
|
||||
"hwModel": 65,
|
||||
"hwModelSlug": "HELTEC_CAPSULE_SENSOR_V3",
|
||||
"supportsUnifiedOta": true
|
||||
},
|
||||
{
|
||||
"hwModel": 66,
|
||||
"hwModelSlug": "HELTEC_VISION_MASTER_T190",
|
||||
"supportsUnifiedOta": true
|
||||
},
|
||||
{
|
||||
"hwModel": 67,
|
||||
"hwModelSlug": "HELTEC_VISION_MASTER_E213",
|
||||
"supportsUnifiedOta": true
|
||||
},
|
||||
{
|
||||
"hwModel": 68,
|
||||
"hwModelSlug": "HELTEC_VISION_MASTER_E290",
|
||||
"supportsUnifiedOta": true
|
||||
},
|
||||
{
|
||||
"hwModel": 70,
|
||||
"hwModelSlug": "SENSECAP_INDICATOR",
|
||||
"supportsUnifiedOta": true
|
||||
},
|
||||
{
|
||||
"hwModel": 74,
|
||||
"hwModelSlug": "RADIOMASTER_900_BANDIT",
|
||||
"supportsUnifiedOta": true
|
||||
},
|
||||
{
|
||||
"hwModel": 107,
|
||||
"hwModelSlug": "THINKNODE_M5",
|
||||
"supportsUnifiedOta": true
|
||||
},
|
||||
{
|
||||
"hwModel": 110,
|
||||
"hwModelSlug": "HELTEC_V4",
|
||||
"supportsUnifiedOta": true
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -151,69 +151,76 @@ constructor(
|
|||
val attemptStart = System.currentTimeMillis()
|
||||
Logger.i { "[$address] TCP connection attempt starting..." }
|
||||
|
||||
val (host, port) =
|
||||
address.split(":", limit = 2).let { it[0] to (it.getOrNull(1)?.toIntOrNull() ?: SERVICE_PORT) }
|
||||
val parts = address.split(":", limit = 2)
|
||||
val host = parts[0]
|
||||
val port = parts.getOrNull(1)?.toIntOrNull() ?: SERVICE_PORT
|
||||
Logger.i { "[$address] Parsed address. Host: $host, Port: $port" }
|
||||
|
||||
Logger.d { "[$address] Resolving host '$host' and connecting to port $port..." }
|
||||
|
||||
Socket(InetAddress.getByName(host), port).use { socket ->
|
||||
socket.tcpNoDelay = true
|
||||
socket.soTimeout = SOCKET_TIMEOUT
|
||||
this@TCPInterface.socket = socket
|
||||
try {
|
||||
Socket(InetAddress.getByName(host), port).use { socket ->
|
||||
socket.tcpNoDelay = true
|
||||
socket.soTimeout = SOCKET_TIMEOUT
|
||||
this@TCPInterface.socket = socket
|
||||
|
||||
val connectTime = System.currentTimeMillis() - attemptStart
|
||||
connectionStartTime = System.currentTimeMillis()
|
||||
Logger.i {
|
||||
"[$address] TCP socket connected in ${connectTime}ms - " +
|
||||
"Local: ${socket.localSocketAddress}, Remote: ${socket.remoteSocketAddress}"
|
||||
}
|
||||
val connectTime = System.currentTimeMillis() - attemptStart
|
||||
connectionStartTime = System.currentTimeMillis()
|
||||
Logger.i {
|
||||
"[$address] TCP socket connected in ${connectTime}ms - " +
|
||||
"Local: ${socket.localSocketAddress}, Remote: ${socket.remoteSocketAddress}"
|
||||
}
|
||||
|
||||
BufferedOutputStream(socket.getOutputStream()).use { outputStream ->
|
||||
outStream = outputStream
|
||||
BufferedOutputStream(socket.getOutputStream()).use { outputStream ->
|
||||
outStream = outputStream
|
||||
|
||||
BufferedInputStream(socket.getInputStream()).use { inputStream ->
|
||||
super.connect()
|
||||
BufferedInputStream(socket.getInputStream()).use { inputStream ->
|
||||
super.connect()
|
||||
|
||||
retryCount = 1
|
||||
backoffDelay = MIN_BACKOFF_MILLIS
|
||||
retryCount = 1
|
||||
backoffDelay = MIN_BACKOFF_MILLIS
|
||||
|
||||
var timeoutCount = 0
|
||||
while (timeoutCount < SOCKET_RETRIES) {
|
||||
try { // close after 90s of inactivity
|
||||
val c = inputStream.read()
|
||||
if (c == -1) {
|
||||
Logger.w {
|
||||
"[$address] TCP got EOF on stream after $packetsReceived packets received"
|
||||
var timeoutCount = 0
|
||||
while (timeoutCount < SOCKET_RETRIES) {
|
||||
try { // close after 90s of inactivity
|
||||
val c = inputStream.read()
|
||||
if (c == -1) {
|
||||
Logger.w {
|
||||
"[$address] TCP got EOF on stream after $packetsReceived packets received"
|
||||
}
|
||||
break
|
||||
} else {
|
||||
timeoutCount = 0
|
||||
packetsReceived++
|
||||
bytesReceived++
|
||||
readChar(c.toByte())
|
||||
}
|
||||
break
|
||||
} else {
|
||||
timeoutCount = 0
|
||||
packetsReceived++
|
||||
bytesReceived++
|
||||
readChar(c.toByte())
|
||||
}
|
||||
} catch (ex: SocketTimeoutException) {
|
||||
timeoutCount++
|
||||
timeoutEvents++
|
||||
if (timeoutCount % TIMEOUT_LOG_INTERVAL == 0) {
|
||||
Logger.d {
|
||||
"[$address] TCP socket timeout count: $timeoutCount/$SOCKET_RETRIES " +
|
||||
"(total timeouts: $timeoutEvents)"
|
||||
} catch (ex: SocketTimeoutException) {
|
||||
timeoutCount++
|
||||
timeoutEvents++
|
||||
if (timeoutCount % TIMEOUT_LOG_INTERVAL == 0) {
|
||||
Logger.d {
|
||||
"[$address] TCP socket timeout count: $timeoutCount/$SOCKET_RETRIES " +
|
||||
"(total timeouts: $timeoutEvents)"
|
||||
}
|
||||
}
|
||||
// Ignore and start another read
|
||||
}
|
||||
// Ignore and start another read
|
||||
}
|
||||
}
|
||||
if (timeoutCount >= SOCKET_RETRIES) {
|
||||
val inactivityMs = SOCKET_RETRIES * SOCKET_TIMEOUT
|
||||
Logger.w {
|
||||
"[$address] TCP closing connection due to $SOCKET_RETRIES consecutive timeouts " +
|
||||
"(${inactivityMs}ms of inactivity)"
|
||||
if (timeoutCount >= SOCKET_RETRIES) {
|
||||
val inactivityMs = SOCKET_RETRIES * SOCKET_TIMEOUT
|
||||
Logger.w {
|
||||
"[$address] TCP closing connection due to $SOCKET_RETRIES consecutive timeouts " +
|
||||
"(${inactivityMs}ms of inactivity)"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
onDeviceDisconnect(false)
|
||||
}
|
||||
onDeviceDisconnect(false)
|
||||
} catch (e: IOException) {
|
||||
Logger.e(e) { "[$address] Error connecting to $host:$port" }
|
||||
throw e
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ package com.geeksville.mesh.service
|
|||
|
||||
import com.geeksville.mesh.concurrent.handledLaunch
|
||||
import com.geeksville.mesh.util.ignoreException
|
||||
import com.google.protobuf.ByteString
|
||||
import dagger.Lazy
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
|
|
@ -302,6 +303,16 @@ constructor(
|
|||
commandSender.sendAdmin(destNum, requestId) { rebootSeconds = DEFAULT_REBOOT_DELAY }
|
||||
}
|
||||
|
||||
fun handleRequestRebootOta(requestId: Int, destNum: Int, mode: Int, hash: ByteArray?) {
|
||||
val otaMode = AdminProtos.OTAMode.forNumber(mode) ?: AdminProtos.OTAMode.NO_REBOOT_OTA
|
||||
val otaEventBuilder = AdminProtos.AdminMessage.OTAEvent.newBuilder()
|
||||
otaEventBuilder.rebootOtaMode = otaMode
|
||||
if (hash != null) {
|
||||
otaEventBuilder.otaHash = ByteString.copyFrom(hash)
|
||||
}
|
||||
commandSender.sendAdmin(destNum, requestId) { otaRequest = otaEventBuilder.build() }
|
||||
}
|
||||
|
||||
fun handleRequestFactoryReset(requestId: Int, destNum: Int) {
|
||||
commandSender.sendAdmin(destNum, requestId) { factoryResetDevice = 1 }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -52,6 +52,7 @@ import org.meshtastic.core.service.ServiceRepository
|
|||
import javax.inject.Inject
|
||||
|
||||
@AndroidEntryPoint
|
||||
@Suppress("TooManyFunctions", "LargeClass")
|
||||
class MeshService : Service() {
|
||||
|
||||
@Inject lateinit var radioInterfaceService: RadioInterfaceService
|
||||
|
|
@ -342,5 +343,10 @@ class MeshService : Service() {
|
|||
override fun requestTelemetry(requestId: Int, destNum: Int, type: Int) = toRemoteExceptions {
|
||||
router.actionHandler.handleRequestTelemetry(requestId, destNum, type)
|
||||
}
|
||||
|
||||
override fun requestRebootOta(requestId: Int, destNum: Int, mode: Int, hash: ByteArray?) =
|
||||
toRemoteExceptions {
|
||||
router.actionHandler.handleRequestRebootOta(requestId, destNum, mode, hash)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -87,6 +87,7 @@ class FakeMeshServiceNotifications : MeshServiceNotifications {
|
|||
message: String,
|
||||
isBroadcast: Boolean,
|
||||
channelName: String?,
|
||||
isSilent: Boolean,
|
||||
) {}
|
||||
|
||||
override suspend fun updateWaypointNotification(
|
||||
|
|
@ -94,6 +95,7 @@ class FakeMeshServiceNotifications : MeshServiceNotifications {
|
|||
name: String,
|
||||
message: String,
|
||||
waypointId: Int,
|
||||
isSilent: Boolean,
|
||||
) {}
|
||||
|
||||
override suspend fun updateReactionNotification(
|
||||
|
|
@ -102,6 +104,7 @@ class FakeMeshServiceNotifications : MeshServiceNotifications {
|
|||
emoji: String,
|
||||
isBroadcast: Boolean,
|
||||
channelName: String?,
|
||||
isSilent: Boolean,
|
||||
) {}
|
||||
|
||||
override fun showAlertNotification(contactKey: String, name: String, alert: String) {}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue