Architecture

Modules

asimov-manager/
├── main.c              # Entry point, 100 Hz main loop
├── app_context/        # Global config singleton
├── config/             # YAML config parser
├── control/            # UDP to asimov-firmware
├── media/              # GStreamer pipelines
│   ├── publisher.c     # Video/audio → LiveKit
│   ├── subscriber.c    # LiveKit → speaker, DataChannel → commands
│   ├── video_track.c   # Camera capture (v4l2src)
│   ├── mic_track.c     # Microphone capture (alsasrc)
│   └── speaker_track.c # Speaker playback (alsasink)
├── teleop/             # Command/state binary packing
└── telemetry/          # Prometheus metrics

Command Flow (Operator → Robot)

        flowchart TD
    op["Operator UI"] -->|"TeleopCommand_S (22 bytes)"| dc["LiveKit DataChannel (reliable)"]
    dc --> sub["subscriber.c::on_data_received()"]
    sub --> teleop["TELEOP_handle_command()"]
    teleop -->|"Convert to RobotCommand_S"| ctrl["CONTROL_send_command()"]
    ctrl -->|"Serialize to nanopb"| udp["UDP :8888"]
    udp --> fw["asimov-firmware"]
    

State Flow (Robot → Operator @ 50 Hz)

        flowchart TD
    fw["asimov-firmware"] -->|"RobotState (nanopb)"| udp["UDP :8889"]
    udp --> ctrl["CONTROL_process()::deserialize_state()"]
    ctrl --> teleop["TELEOP_send_state()"]
    teleop -->|"Pack to TeleopState_S"| pub["MEDIA_publisher_send_data()"]
    pub --> dc["LiveKit DataChannel (lossy)"]
    dc --> op["Operator UI"]
    

Binary Formats

TeleopCommand_S (22 bytes)

Operator → Robot. Reliable delivery.

struct {
    uint8_t mode;        // ControlMode_E
    float vx;            // Forward velocity [m/s]
    float vy;            // Lateral velocity [m/s]
    float vyaw;          // Yaw rate [rad/s]
    uint8_t gait_mode;   // GaitMode_E
    uint8_t enable;      // Motors enabled
    uint8_t e_stop;      // Emergency stop
} __attribute__((packed));

TeleopState_S (~170 bytes)

Robot → Operator @ 50 Hz. Lossy delivery.

struct {
    uint64_t timestamp_us;
    uint32_t sequence;
    uint8_t mode;
    uint8_t motors_enabled;
    uint8_t emergency_stop;
    float joint_pos[12];
    float joint_vel[12];
    float base_ang_vel[3];
    float projected_gravity[3];
    float gait_phase;
    float battery_voltage;
    uint8_t battery_percent;
} __attribute__((packed));

GStreamer Pipelines

Video (per camera)

        flowchart LR
    v4l2["v4l2src device=/dev/video0"] --> conv["videoconvert"]
    conv --> sink["livekitwebrtcsink (VP8/H264)"]
    sink --> lk["LiveKit Server"]
    

Audio

        flowchart LR
    subgraph capture["Mic Capture"]
        alsa1["alsasrc device=hw:0,0"] --> conv1["audioconvert"]
        conv1 --> sink1["livekitwebrtcsink"]
    end
    subgraph playback["Speaker Playback"]
        src2["livekitwebrtcsrc"] --> conv2["audioconvert"]
        conv2 --> alsa2["alsasink device=plughw:0,0"]
    end
    

Bandwidth

Stream

Rate

Bandwidth

Video (1920x1200 VP8)

50 fps

~2-4 Mbps

Audio (Opus)

48 kHz

~64 kbps

State

50 Hz

~8.5 KB/s

Commands

On demand

Negligible

Total upstream: ~3-5 Mbps per camera.

Source Files

File

What it does

main.c

Entry point, 100 Hz main loop

app_context/app_context.c

Global config singleton

config/config.c

YAML config parser

control/control.c

UDP to firmware, nanopb serialization

media/publisher.c

Video/audio capture, LiveKit streaming

media/subscriber.c

Audio playback, DataChannel commands

media/video_track.c

Camera capture pipeline

media/mic_track.c

Microphone capture pipeline

media/speaker_track.c

Speaker playback pipeline

teleop/teleop.c

Command/state binary packing

telemetry/telemetry.c

Prometheus metrics