DEV Community

vmodal_ai
vmodal_ai

Posted on Originally published at v-modal.com

Android SDK for Robot Battery, Power, and Charging Management

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)            |
+-------------------------------------------------------------+
Enter fullscreen mode Exit fullscreen mode

Core Architecture Components:

  1. BatteryState Data Carrier: Reactive model representing real-time battery voltage, current draw, state of charge (SoC), temperature, and health parameters.
  2. BatteryTelemetryManager: Kotlin StateFlow-based stream for high-frequency battery telemetry updates.
  3. DockingOrchestrator: Finite State Machine (FSM) guiding the robot through approach, contact alignment, handshake, charging, and release phases.
  4. 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
}
Enter fullscreen mode Exit fullscreen mode

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)
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

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
}
Enter fullscreen mode Exit fullscreen mode

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
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

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()
                }
            }
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

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

Top comments (0)