DEV Community

vmodal_ai
vmodal_ai

Posted on Originally published at v-modal.com

Building a Robot Diagnostics and Remote Debugging SDK for Android

Building a Robot Diagnostics and Remote Debugging SDK for Android

When a robot experiences a hardware fault or safety trip in the field, diagnostics data must be captured immediately for root cause analysis. A complete Android diagnostics SDK requires blackbox circular buffers (recording moments before a crash), dynamic diagnostic status trees (ROS DiagnosticArray equivalent), and remote debugging telemetry streams.

This tutorial guides you through building a diagnostic flight recorder and status aggregation engine in Kotlin.


1. Diagnostics System Topology

+-------------------------------------------------------------+
|                 Subsystem Diagnostic Nodes                  |
|       [Motors]        [Lidar]        [Battery]              |
+-------------------------------------------------------------+
                            | Periodic Telemetry / Faults
                            v
+-------------------------------------------------------------+
|                 Diagnostic Aggregator Engine                |
|           - Aggregates OK / WARN / ERROR States             |
+-------------------------------------------------------------+
                            |
                            v
+-------------------------------------------------------------+
|           In-Memory Flight Recorder (Circular Buffer)        |
|               (Saves 30s pre-crash snapshot)                |
+-------------------------------------------------------------+
Enter fullscreen mode Exit fullscreen mode

2. Diagnostic Status Model Definitions

package com.vmodal.sdk.diagnostics.model

enum class DiagnosticLevel {
    OK,
    WARN,
    ERROR,
    STALE
}

data class KeyValueVal(val key: String, val value: String)

data class DiagnosticStatus(
    val hardwareId: String,
    val name: String,
    val level: DiagnosticLevel,
    val message: String,
    val values: List<KeyValueVal> = emptyList(),
    val timestampMs: Long = System.currentTimeMillis()
)
Enter fullscreen mode Exit fullscreen mode

3. Implementing the In-Memory Circular Flight Recorder

package com.vmodal.sdk.diagnostics.recorder

import com.vmodal.sdk.diagnostics.model.DiagnosticStatus
import java.util.ArrayDeque

class CircularFlightRecorder(private val maxCapacity: Int = 1000) {

    private val buffer = ArrayDeque<DiagnosticStatus>()

    fun record(status: DiagnosticStatus) {
        synchronized(buffer) {
            if (buffer.size >= maxCapacity) {
                buffer.removeFirst()
            }
            buffer.addLast(status)
        }
    }

    fun exportBlackboxSnapshot(): List<DiagnosticStatus> {
        synchronized(buffer) {
            return buffer.toList()
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

4. Building the Core Diagnostic Aggregator

package com.vmodal.sdk.diagnostics

import com.vmodal.sdk.diagnostics.model.DiagnosticLevel
import com.vmodal.sdk.diagnostics.model.DiagnosticStatus
import com.vmodal.sdk.diagnostics.recorder.CircularFlightRecorder
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow

class DiagnosticAggregator(
    private val flightRecorder: CircularFlightRecorder
) {
    private val hardwareMap = mutableMapOf<String, DiagnosticStatus>()

    private val _systemHealth = MutableStateFlow(DiagnosticLevel.OK)
    val systemHealth: StateFlow<DiagnosticLevel> = _systemHealth.asStateFlow()

    fun updateStatus(status: DiagnosticStatus) {
        flightRecorder.record(status)

        synchronized(hardwareMap) {
            hardwareMap[status.hardwareId] = status
            reevaluateOverallHealth()
        }
    }

    private fun reevaluateOverallHealth() {
        val worstLevel = hardwareMap.values.maxOfOrNull { it.level } ?: DiagnosticLevel.OK
        _systemHealth.value = worstLevel
    }
}
Enter fullscreen mode Exit fullscreen mode

5. End-to-End Test Script

import com.vmodal.sdk.diagnostics.DiagnosticAggregator
import com.vmodal.sdk.diagnostics.model.DiagnosticLevel
import com.vmodal.sdk.diagnostics.model.DiagnosticStatus
import com.vmodal.sdk.diagnostics.model.KeyValueVal
import com.vmodal.sdk.diagnostics.recorder.CircularFlightRecorder

fun main() {
    val recorder = CircularFlightRecorder(capacity = 500)
    val aggregator = DiagnosticAggregator(recorder)

    println("Reporting Nominal Telemetry...")
    aggregator.updateStatus(
        DiagnosticStatus(
            hardwareId = "drive_motor_left",
            name = "Left Wheel Actuator",
            level = DiagnosticLevel.OK,
            message = "Operating normally",
            values = listOf(KeyValueVal("temp_c", "38.2"))
        )
    )

    println("Overall Health: ${aggregator.systemHealth.value}")

    println("
Simulating Hardware Fault...")
    aggregator.updateStatus(
        DiagnosticStatus(
            hardwareId = "drive_motor_left",
            name = "Left Wheel Actuator",
            level = DiagnosticLevel.ERROR,
            message = "Over-current protection tripped!",
            values = listOf(KeyValueVal("current_a", "45.0"))
        )
    )

    println("Overall System Health post-fault: ${aggregator.systemHealth.value}")
    println("Dumped Blackbox Records Count: ${recorder.exportBlackboxSnapshot().size}")
}
Enter fullscreen mode Exit fullscreen mode

Conclusion

Combining circular buffer flight recorders with structured diagnostic status models enables robust remote telemetry monitoring and instant root-cause analysis for autonomous robot fleets.


Useful Links

Top comments (0)