Android SDK for Robot Battery, Power, and Charging Management
In autonomous mobile robotics (AMR) and industrial automation, power lifecycle management is critical. A robot that unexpectedly exhausts its power mid-mission risks operational downtime, physical asset lockups, or safety hazards. Building an Android-based SDK for robot battery, power, and charging management requires low-latency telemetry streaming, state-machine power transitions, thermal monitoring, and real-time charging dock orchestration.
In this tutorial, we will build a production-grade Kotlin SDK module for managing robot battery health, dynamic low-power thresholds, dynamic thermal throttling, and docking state machines.
1. Architectural Overview
Managing power in a modern Android-driven robot involves low-level hardware communication over serial/CAN/DDS and high-level SDK interfaces exposed to mission planners and user interfaces.
+-------------------------------------------------------------+
| Android Application |
| (UI Dashboards / Autonomous Nav Apps) |
+-------------------------------------------------------------+
|
v
+-------------------------------------------------------------+
| VModal Power Management SDK |
| +-------------------+ +--------------------------------+ |
| | BatteryManager | | PowerStateController | |
| +-------------------+ +--------------------------------+ |
| | DockingOrchestration | | ThermalMonitor | |
| +-------------------+ +--------------------------------+ |
+-------------------------------------------------------------+
|
v
+-------------------------------------------------------------+
| Hardware Abstraction Layer (HAL) |
| (BMS / Serial / DDS Transport) |
+-------------------------------------------------------------+
Core Architecture Components:
-
BatteryStateData Carrier: Reactive model representing real-time battery voltage, current draw, state of charge (SoC), temperature, and health parameters. -
BatteryTelemetryManager: KotlinStateFlow-based stream for high-frequency battery telemetry updates. -
DockingOrchestrator: Finite State Machine (FSM) guiding the robot through approach, contact alignment, handshake, charging, and release phases. -
ThermalGovernor: Dynamic protection layer triggering emergency cooling or shutdown routines when battery cell temperatures breach safety limits.
2. Defining Battery Telemetry Data Models
First, define the core telemetry models capturing raw physical characteristics from the Battery Management System (BMS).
package com.vmodal.sdk.power.model
import java.time.Instant
enum class ChargingStatus {
DISCHARGING,
CHARGING_AC,
CHARGING_DOCK,
CHARGING_INDUCTION,
FULLY_CHARGED,
FAULT
}
enum class BatteryHealthStatus {
GOOD,
OVERHEAT,
DEAD,
OVER_VOLTAGE,
UNSPECIFIED_FAILURE,
COLD
}
data class BatteryState(
val percentage: Float, // State of Charge: 0.0f to 100.0f
val voltageVolts: Double, // Pack terminal voltage
val currentAmperes: Double, // Positive = charging, Negative = discharging
val temperatureCelsius: Double, // Internal cell pack temperature
val remainingCapacityAh: Double, // Remaining capacity in Ampere-hours
val fullCapacityAh: Double, // Nominal capacity in Ampere-hours
val chargingStatus: ChargingStatus,
val health: BatteryHealthStatus,
val timestamp: Instant = Instant.now()
) {
val isLowPower: Boolean get() = percentage <= 15.0f
val isCritical: Boolean get() = percentage <= 5.0f
}
3. Implementing the Battery Telemetry Manager
We use Kotlin StateFlow and SharedFlow to push high-frequency battery telemetry directly from hardware protocols to connected application listeners.
package com.vmodal.sdk.power
import com.vmodal.sdk.power.model.BatteryState
import com.vmodal.sdk.power.model.ChargingStatus
import com.vmodal.sdk.power.model.BatteryHealthStatus
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.launch
import java.nio.ByteBuffer
import java.nio.ByteOrder
class BatteryTelemetryManager(
private val scope: CoroutineScope
) {
private val _batteryState = MutableStateFlow<BatteryState?>(null)
val batteryState: StateFlow<BatteryState?> = _batteryState.asStateFlow()
/**
* Parse raw binary payload from hardware BMS bus (CAN / Serial frames)
*/
fun parseHardwarePayload(payload: ByteArray) {
val buffer = ByteBuffer.wrap(payload).order(ByteOrder.LITTLE_ENDIAN)
val soc = buffer.float
val voltage = buffer.double
val current = buffer.double
val temp = buffer.double
val remCap = buffer.double
val fullCap = buffer.double
val statusOrdinal = buffer.int
val healthOrdinal = buffer.int
val state = BatteryState(
percentage = soc,
voltageVolts = voltage,
currentAmperes = current,
temperatureCelsius = temp,
remainingCapacityAh = remCap,
fullCapacityAh = fullCap,
chargingStatus = ChargingStatus.values().getOrElse(statusOrdinal) { ChargingStatus.FAULT },
health = BatteryHealthStatus.values().getOrElse(healthOrdinal) { BatteryHealthStatus.UNSPECIFIED_FAILURE }
)
scope.launch(Dispatchers.Default) {
_batteryState.emit(state)
}
}
}
4. Designing Autonomous Charging Dock Orchestration
The charging orchestrator handles transition states when contacting physical charging docks, ensuring electrical contacts are safe before high-current power transfer begins.
package com.vmodal.sdk.power.docking
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
sealed class DockingState {
object Idle : DockingState()
object ApproachingDock : DockingState()
object ContactEstablished : DockingState()
object PerformingHandshake : DockingState()
object ChargingActive : DockingState()
data class DockingFailed(val reason: String) : DockingState()
object Disengaging : DockingState()
}
class DockingOrchestrator {
private val _currentState = MutableStateFlow<DockingState>(DockingState.Idle)
val currentState: StateFlow<DockingState> = _currentState.asStateFlow()
fun initiateDockingSequence() {
if (_currentState.value != DockingState.Idle) return
_currentState.value = DockingState.ApproachingDock
}
fun onContactDetected() {
if (_currentState.value is DockingState.ApproachingDock) {
_currentState.value = DockingState.ContactEstablished
performHardwareHandshake()
}
}
private fun performHardwareHandshake() {
_currentState.value = DockingState.PerformingHandshake
// Execute safety check logic (e.g., verifying charge relay engagement)
val success = verifyRelayPolarity()
if (success) {
_currentState.value = DockingState.ChargingActive
} else {
_currentState.value = DockingState.DockingFailed("Polarity check or communication handshake failed.")
}
}
fun undock() {
_currentState.value = DockingState.Disengaging
// Open main charging contractors/relays
_currentState.value = DockingState.Idle
}
private fun verifyRelayPolarity(): Boolean = true
}
5. Integrating Safety & Thermal Throttling
To prevent lithium cell degradation or thermal runaway, the ThermalGovernor continuously evaluates thermistor outputs and dynamically enforces power output limits.
package com.vmodal.sdk.power.safety
import com.vmodal.sdk.power.model.BatteryState
import kotlinx.coroutines.flow.Flow
class ThermalGovernor(
private val maxOperatingTempCelsius: Double = 55.0,
private val criticalShutdownTempCelsius: Double = 65.0
) {
sealed class PowerLimitMode {
object Nominal : PowerLimitMode()
data class Throttled(val maxSpeedScale: Float) : PowerLimitMode()
object EmergencyShutdownRequired : PowerLimitMode()
}
fun evaluateThermalSafety(state: BatteryState): PowerLimitMode {
return when {
state.temperatureCelsius >= criticalShutdownTempCelsius -> {
PowerLimitMode.EmergencyShutdownRequired
}
state.temperatureCelsius >= maxOperatingTempCelsius -> {
// Scale down motor output proportional to excess thermal budget
val excess = state.temperatureCelsius - maxOperatingTempCelsius
val scale = (1.0 - (excess / 10.0)).coerceIn(0.2, 0.8).toFloat()
PowerLimitMode.Throttled(maxSpeedScale = scale)
}
else -> PowerLimitMode.Nominal
}
}
}
6. Real-World Integration Example
Below is a complete Kotlin integration example using the VModal Power Management SDK inside an Android Service or Robot Mission Node.
import com.vmodal.sdk.power.BatteryTelemetryManager
import com.vmodal.sdk.power.docking.DockingOrchestrator
import com.vmodal.sdk.power.docking.DockingState
import com.vmodal.sdk.power.safety.ThermalGovernor
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch
fun main() {
val scope = CoroutineScope(Dispatchers.Default)
val batteryManager = BatteryTelemetryManager(scope)
val dockOrchestrator = DockingOrchestrator()
val thermalGovernor = ThermalGovernor()
// Monitor battery updates
scope.launch {
batteryManager.batteryState.collectLatest { state ->
state?.let {
println("Battery SoC: ${it.percentage}%, Temp: ${it.temperatureCelsius}°C")
// Check thermal safety
when (val limit = thermalGovernor.evaluateThermalSafety(it)) {
is ThermalGovernor.PowerLimitMode.Throttled -> {
println("WARNING: High temperature! Throttling drive system to scale: ${limit.maxSpeedScale}")
}
is ThermalGovernor.PowerLimitMode.EmergencyShutdownRequired -> {
println("CRITICAL: Overheating detected! Activating emergency stop.")
}
ThermalGovernor.PowerLimitMode.Nominal -> { /* Normal Operation */ }
}
// Low power docking trigger
if (it.isLowPower && dockOrchestrator.currentState.value is DockingState.Idle) {
println("Low battery detected. Auto-routing to charging dock...")
dockOrchestrator.initiateDockingSequence()
}
}
}
}
}
Conclusion
Building a production-ready Android SDK for robot power management requires a robust balance of reactive telemetry streams, defensive thermal throttling, and explicit state machines for autonomous docking. By implementing clean Kotlin primitives and robust safety routines, your robotic applications remain safe and operational.
Useful Links
- Website: www.v-modal.com
- SDK Flutter: v-modal/vmodal_sdk_flutter
- SDK Android: v-modal/vmodal_sdk_android
- Discord: https://discord.gg/K72z28KUx
Top comments (0)