Skip to main content

drone-hud

:::warning [UNTESTED] — v0.5 paper-correct Builds clean on native_sim/native/64 and cross-compiles to the AEN target. Real-silicon bring-up + airframe integration land later. :::

A live attitude / GPS / battery HUD for a drone, rendered with LVGL on an ST7789 TFT.

Source: examples/display/drone-hud/.

Components

  • LSM6DSO IMU — 6-axis sensor read via <alp/chips/lsm6dso.h>.
  • u-blox NEO-M9N GNSS — UART NMEA via <alp/chips/ublox_neo_m9n.h>.
  • INA236 battery monitor — voltage / current via <alp/chips/ina236.h>.
  • ST7789 TFT — 240×320 SPI panel via <alp/chips/st7789.h>.
  • <alp/ahrs.h> — Madgwick fusion of IMU samples into attitude.
  • LVGL — composes the artificial horizon + GPS readout + battery telemetry + flight-mode selector.

Attitude fusion

Attitude comes from the portable <alp/ahrs.h> Madgwick filter, which owns the quaternion state and the Euler conversion — there is no hand-rolled fusion in the app:

static alp_ahrs_t s_ahrs; /* Madgwick orientation filter */

alp_ahrs_init(&s_ahrs, NULL); /* default gains */

/* ... per IMU sample: */
alp_ahrs_update_imu(&s_ahrs, gx, gy, gz, a.x, a.y, a.z, dt_s);
alp_ahrs_euler(&s_ahrs, &telem->roll_deg, &telem->pitch_deg, &telem->yaw_deg);

Fusing the accelerometer pins roll and pitch against gravity, so the HUD attitude no longer drifts without bound the way the earlier gyro-only integrator did.

Thread model

Three threads at independent rates:

  • Render — 30 fps screen update
  • IMU + AHRS — 100 Hz attitude fusion
  • Slow telemetry — 5 Hz GPS + battery refresh

Display bring-up

The app owns the panel init sequence rather than letting LVGL auto-initialise it. It opens the display through <alp/display.h> and binds LVGL to it with <alp/gui.h>:

alp_display_config_t display_cfg = ALP_DISPLAY_CONFIG_DEFAULT(0);
alp_display_t *display = alp_display_open(&display_cfg);
if (display == NULL) { /* alp_last_error() */ }

lv_init();
lv_tick_set_cb(k_uptime_get_32);
alp_status_t attach_status = alp_gui_lvgl_attach(display);

This requires CONFIG_LV_Z_AUTO_INIT=n in prj.conf — see lvgl-widgets-demo for the full rationale.

board.yaml

libraries:
- name: lvgl
cores: [m55_hp]
- name: madgwick-ahrs
cores: [m55_hp]

som:
sku: E1M-AEN801

preset: e1m-evk

pins:
- { e1m: E1M_I2C0, macro: EVK_I2C_BUS_SENSORS, doc: "Shared sensor + IO-expander + INA236 bus" }
- { e1m: E1M_UART0, macro: EVK_UART_PORT_DEBUG, doc: "Console UART exposed on the JTAG/SWD-side debug header" }
- { e1m: E1M_SPI1, macro: EVK_SPI_BUS_ARDUINO, doc: "Arduino UNO header SPI" }

cores:
a32_cluster:
os: "off"
m55_hp:
app: ./src
peripherals:
- i2c # IMU + battery monitor.
- uart # GNSS NMEA.
- spi # Display.
- gpio # Display D/C# + reset.

chips:
- lsm6dso # 6-axis IMU.
- ublox_neo_m9n # Multi-constellation GNSS.
- ina236 # Battery V/I monitor.
- st7789 # 240x320 TFT.

diagnostics:
log_level: info

See also

Questions about this page? Discuss in Community Forum