🥑 meltybrain avocado
0

Configure Feed

Select the types of activity you want to include in your feed.

feat: add blinking led

+85
+1
firmware/CMakeLists.txt
··· 36 36 src/control/drift.c 37 37 src/control/pid.c 38 38 src/app/control_loop.c 39 + src/led/head_led.c 39 40 src/safety/watchdog.c 40 41 src/safety/battery.c 41 42 src/safety/rc_health.c
+53
firmware/src/led/head_led.c
··· 1 + #include "head_led.h" 2 + #include "hw_pins.h" 3 + #include "hardware/gpio.h" 4 + #include <math.h> 5 + 6 + /* Tunables. */ 7 + #define SPIN_OMEGA_MIN 10.0f /* rad/s; below this the robot is "idle" */ 8 + #define STROBE_WINDOW 0.35f /* rad (~20 deg) total width of the dot */ 9 + #define REF_HEADING 0.0f /* heading at which the dot appears */ 10 + #define STALE_US 200000u /* no estimate for this long => idle */ 11 + #define IDLE_BLINK_US 250000u /* idle heartbeat half-period (2 Hz) */ 12 + 13 + static float s_heading; 14 + static float s_omega; 15 + static uint32_t s_update_us; 16 + static bool s_have; 17 + 18 + void head_led_init(void) { 19 + gpio_init(PIN_HEAD_LED); 20 + gpio_set_dir(PIN_HEAD_LED, GPIO_OUT); 21 + gpio_put(PIN_HEAD_LED, 0); 22 + s_have = false; 23 + } 24 + 25 + void head_led_on_estimate(const heading_estimate_t *est, uint32_t now_us) { 26 + s_heading = est->heading; 27 + s_omega = est->omega; 28 + s_update_us = now_us; 29 + s_have = true; 30 + } 31 + 32 + static float wrap_pi(float a) { 33 + const float pi = (float)M_PI; 34 + while (a >= pi) a -= 2.0f * pi; 35 + while (a < -pi) a += 2.0f * pi; 36 + return a; 37 + } 38 + 39 + void head_led_tick(uint32_t now_us) { 40 + bool stale = !s_have || (now_us - s_update_us) > STALE_US; 41 + 42 + if (!stale && fabsf(s_omega) > SPIN_OMEGA_MIN) { 43 + /* Extrapolate heading to "now" so the dot stays put between the 44 + * (slower) estimate updates, then light the LED within a small 45 + * angular window around the reference heading. */ 46 + float dt = (float)(now_us - s_update_us) * 1e-6f; 47 + float err = wrap_pi(s_heading + s_omega * dt - REF_HEADING); 48 + gpio_put(PIN_HEAD_LED, fabsf(err) < (STROBE_WINDOW * 0.5f)); 49 + } else { 50 + /* Idle heartbeat. */ 51 + gpio_put(PIN_HEAD_LED, ((now_us / IDLE_BLINK_US) & 1u) == 0u); 52 + } 53 + }
+26
firmware/src/led/head_led.h
··· 1 + #pragma once 2 + 3 + /* 4 + * Heading-indicator LED (meltybrain "stationary dot"). 5 + * 6 + * The LED sits on PIN_HEAD_LED via a PN2222A (drive high = lit). When the 7 + * robot is spinning with a live heading estimate, the LED is pulsed once per 8 + * revolution as the estimated heading sweeps past a fixed reference angle. 9 + * To the eye that paints a single stationary bright dot the driver can use as 10 + * "front." When idle (not spinning, or no fresh estimate), it slow-blinks as 11 + * a heartbeat so you can see the firmware is alive. 12 + * 13 + * Heading is extrapolated from the last estimate using omega so the dot stays 14 + * crisp even though estimates arrive slower than the control loop. 15 + */ 16 + 17 + #include <stdint.h> 18 + #include "estimation/heading_estimate.h" 19 + 20 + void head_led_init(void); 21 + 22 + /* Feed a fresh heading estimate (call when a new one is available). */ 23 + void head_led_on_estimate(const heading_estimate_t *est, uint32_t now_us); 24 + 25 + /* Update the LED output. Call every control-loop iteration. */ 26 + void head_led_tick(uint32_t now_us);
+5
firmware/src/main.c
··· 15 15 #include "safety/watchdog.h" 16 16 #include "safety/battery.h" 17 17 #include "safety/rc_health.h" 18 + #include "led/head_led.h" 18 19 19 20 #define RAD_TO_DEG 57.2958f 20 21 #define CRSF_LINK_TIMEOUT_MS 500 ··· 50 51 51 52 crsf_init(); 52 53 rc_health_init(); 54 + head_led_init(); 53 55 crsf_state_t rc = {0}; 54 56 55 57 printf("Core 1 ready [%s]. Waiting for RC link...\n", sensor_primary.name); ··· 66 68 if (shared_est.ready) { 67 69 est = shared_est.est; 68 70 shared_est.ready = false; 71 + head_led_on_estimate(&est, time_us_32()); 69 72 } 70 73 spin_unlock(est_lock, irq); 71 74 ··· 108 111 battery_low() ? " LOW-BAT" : "", 109 112 !rc_health_ok() ? " STALE-RC" : ""); 110 113 } 114 + 115 + head_led_tick(time_us_32()); 111 116 112 117 watchdog_feed_core1(); 113 118 sleep_us(CONTROL_LOOP_SLEEP_US);