From 286291fcfdba0b540f3abe2c793620a5dffe1d40 Mon Sep 17 00:00:00 2001 From: miketsai Date: Fri, 12 Jun 2026 18:09:54 +0800 Subject: [PATCH] feat(buzzer): add GPIO64 buzzer module with pattern-based alerts New buzzer.h/buzzer.c: background pthread drives gpio64 with three patterns (GRASS 500/500ms, ALERT 300/200ms, COLLISION 100/100ms). Integrated into fire_collision_warning(), fire_alert(), and grass state machine in event_recorder.c; buzzer_init() called after can_bus_init() in kp_firmware.c. Co-Authored-By: Claude Sonnet 4.6 --- include/host_stream/buzzer.h | 23 +++++ src/host_stream/buzzer.c | 151 +++++++++++++++++++++++++++++++ src/host_stream/event_recorder.c | 14 +++ src/host_stream/kp_firmware.c | 2 + 4 files changed, 190 insertions(+) create mode 100644 include/host_stream/buzzer.h create mode 100644 src/host_stream/buzzer.c diff --git a/include/host_stream/buzzer.h b/include/host_stream/buzzer.h new file mode 100644 index 0000000..e675497 --- /dev/null +++ b/include/host_stream/buzzer.h @@ -0,0 +1,23 @@ +/* + * buzzer.h — GPIO-driven buzzer control (gpio64) + * + * Background thread plays a continuous pattern until changed. + * Pattern priority (highest wins): COLLISION > ALERT > GRASS > OFF + * Callers set the desired pattern; the module handles GPIO toggling. + */ + +#ifndef BUZZER_H +#define BUZZER_H + +typedef enum { + BUZZER_PATTERN_OFF = 0, + BUZZER_PATTERN_GRASS = 1, /* 500ms on / 500ms off */ + BUZZER_PATTERN_ALERT = 2, /* 300ms on / 200ms off */ + BUZZER_PATTERN_COLLISION = 3, /* 100ms on / 100ms off */ +} buzzer_pattern_t; + +int buzzer_init(void); +void buzzer_set_pattern(buzzer_pattern_t pattern); +void buzzer_close(void); + +#endif /* BUZZER_H */ diff --git a/src/host_stream/buzzer.c b/src/host_stream/buzzer.c new file mode 100644 index 0000000..7cd0a72 --- /dev/null +++ b/src/host_stream/buzzer.c @@ -0,0 +1,151 @@ +#include +#include +#include +#include +#include +#include +#include +#include "buzzer.h" + +#define BUZZER_GPIO 64 + +static volatile buzzer_pattern_t s_pattern = BUZZER_PATTERN_OFF; +static volatile int s_running = 0; +static pthread_t s_tid; +static pthread_mutex_t s_mtx = PTHREAD_MUTEX_INITIALIZER; +static pthread_cond_t s_cond = PTHREAD_COND_INITIALIZER; +static int s_gpio_fd = -1; + +static void buzzer_gpio_write(int val) +{ + if (s_gpio_fd < 0) return; + lseek(s_gpio_fd, 0, SEEK_SET); + write(s_gpio_fd, val ? "1" : "0", 1); +} + +static int buzzer_gpio_init(void) +{ + char path[64], buf[8]; + int fd; + + fd = open("/sys/class/gpio/export", O_WRONLY); + if (fd >= 0) { + snprintf(buf, sizeof(buf), "%d", BUZZER_GPIO); + write(fd, buf, strlen(buf)); /* EBUSY = already exported, ok */ + close(fd); + } + + snprintf(path, sizeof(path), "/sys/class/gpio/gpio%d/direction", BUZZER_GPIO); + fd = open(path, O_WRONLY); + if (fd < 0) { perror("[BUZ] direction open"); return -1; } + write(fd, "out", 3); + close(fd); + + snprintf(path, sizeof(path), "/sys/class/gpio/gpio%d/value", BUZZER_GPIO); + fd = open(path, O_RDWR); + if (fd < 0) { perror("[BUZ] value open"); return -1; } + return fd; +} + +static void msleep(int ms) +{ + struct timespec ts = { ms / 1000, (ms % 1000) * 1000000L }; + nanosleep(&ts, NULL); +} + +static void cond_timedwait_ms(int ms) +{ + struct timespec abs; + clock_gettime(CLOCK_REALTIME, &abs); + abs.tv_sec += ms / 1000; + abs.tv_nsec += (ms % 1000) * 1000000L; + if (abs.tv_nsec >= 1000000000L) { abs.tv_sec++; abs.tv_nsec -= 1000000000L; } + pthread_cond_timedwait(&s_cond, &s_mtx, &abs); +} + +static void *buzzer_thread(void *arg) +{ + (void)arg; + printf("[BUZ] thread started gpio%d\n", BUZZER_GPIO); + + pthread_mutex_lock(&s_mtx); + while (s_running) { + buzzer_pattern_t pat = s_pattern; + pthread_mutex_unlock(&s_mtx); + + switch (pat) { + case BUZZER_PATTERN_GRASS: + buzzer_gpio_write(1); + msleep(500); + buzzer_gpio_write(0); + pthread_mutex_lock(&s_mtx); + if (s_pattern == pat) cond_timedwait_ms(500); + break; + + case BUZZER_PATTERN_ALERT: + buzzer_gpio_write(1); + msleep(300); + buzzer_gpio_write(0); + pthread_mutex_lock(&s_mtx); + if (s_pattern == pat) cond_timedwait_ms(200); + break; + + case BUZZER_PATTERN_COLLISION: + buzzer_gpio_write(1); + msleep(100); + buzzer_gpio_write(0); + pthread_mutex_lock(&s_mtx); + if (s_pattern == pat) cond_timedwait_ms(100); + break; + + default: /* BUZZER_PATTERN_OFF */ + buzzer_gpio_write(0); + pthread_mutex_lock(&s_mtx); + if (s_pattern == BUZZER_PATTERN_OFF) + pthread_cond_wait(&s_cond, &s_mtx); + break; + } + } + pthread_mutex_unlock(&s_mtx); + + buzzer_gpio_write(0); + printf("[BUZ] thread exit\n"); + return NULL; +} + +int buzzer_init(void) +{ + s_gpio_fd = buzzer_gpio_init(); + if (s_gpio_fd < 0) + printf("[BUZ] WARNING: gpio init failed, buzzer disabled\n"); + + s_running = 1; + if (pthread_create(&s_tid, NULL, buzzer_thread, NULL) != 0) { + perror("[BUZ] pthread_create"); + s_running = 0; + return -1; + } + pthread_setname_np(s_tid, "buzzer"); + printf("[BUZ] init done gpio%d\n", BUZZER_GPIO); + return 0; +} + +void buzzer_set_pattern(buzzer_pattern_t pattern) +{ + pthread_mutex_lock(&s_mtx); + if (s_pattern != pattern) { + s_pattern = pattern; + pthread_cond_signal(&s_cond); + } + pthread_mutex_unlock(&s_mtx); +} + +void buzzer_close(void) +{ + pthread_mutex_lock(&s_mtx); + s_running = 0; + pthread_cond_signal(&s_cond); + pthread_mutex_unlock(&s_mtx); + pthread_join(s_tid, NULL); + if (s_gpio_fd >= 0) { close(s_gpio_fd); s_gpio_fd = -1; } +} diff --git a/src/host_stream/event_recorder.c b/src/host_stream/event_recorder.c index 266d02d..065ed11 100644 --- a/src/host_stream/event_recorder.c +++ b/src/host_stream/event_recorder.c @@ -41,6 +41,7 @@ #include "event_recorder.h" #include "bt_uart.h" #include "can_bus.h" +#include "buzzer.h" #include "stdc_post_process.h" /* THR_*_COLLISION constants */ /* ── External (from kdp2_host_stream.c) ──────────────────────────────────── */ @@ -440,6 +441,7 @@ void fire_collision_warning(int level, const char *type) .keepalive_interval_ms = 0 }; can_bus_send_control_cmd(&ctrl); printf("[EVT-CAN] collision_warning speed=%d (level=%d)\n", speed, level); + buzzer_set_pattern(level ? BUZZER_PATTERN_COLLISION : BUZZER_PATTERN_OFF); } bt_uart_send_json(json); printf("[EVT] collision_warning level=%d type=%s\n", level, type ? type : "null"); @@ -471,6 +473,7 @@ static void fire_alert(int left_level, const char *left_type, .keepalive_interval_ms = 0 }; can_bus_send_control_cmd(&ctrl); printf("[EVT-CAN] alert speed=%d (left=%d right=%d)\n", speed, left_level, right_level); + buzzer_set_pattern((left_level || right_level) ? BUZZER_PATTERN_ALERT : BUZZER_PATTERN_OFF); } bt_uart_send_json(json); printf("[EVT] alert left=%d(%s) right=%d(%s)\n", @@ -694,6 +697,7 @@ void event_recorder_update(const stdc_analysis_t *ana) grass_enter_level(1); speed_val = SPEED_LIMIT_STOP; msg_send("/tmp/canbus/c0/command.fifo", "host_stream", "setSpeed",&speed_val, sizeof(speed_val), 0); + buzzer_set_pattern(BUZZER_PATTERN_GRASS); return; } break; @@ -708,11 +712,14 @@ void event_recorder_update(const stdc_analysis_t *ana) grass_enter_level(2); speed_val = SPEED_LIMIT_STOP; msg_send("/tmp/canbus/c0/command.fifo", "host_stream", "setSpeed",&speed_val, sizeof(speed_val), 0); + buzzer_set_pattern(BUZZER_PATTERN_GRASS); return; } } else { speed_val = SPEED_LIMIT_NORMAL; msg_send("/tmp/canbus/c0/command.fifo", "host_stream", "setSpeed",&speed_val, sizeof(speed_val), 0); + if (!g_last_collision_warning && !g_last_left_alert && !g_last_right_alert) + buzzer_set_pattern(BUZZER_PATTERN_OFF); if (elapsed_ms_tv(&g_grass.t_last_active) >= GRASS_EXIT_HYSTERESIS_MS) { g_grass.state = GRASS_DONE; gettimeofday(&g_grass.t_done, NULL); @@ -742,6 +749,8 @@ void event_recorder_update(const stdc_analysis_t *ana) } else { speed_val = SPEED_LIMIT_NORMAL; msg_send("/tmp/canbus/c0/command.fifo", "host_stream", "setSpeed",&speed_val, sizeof(speed_val), 0); + if (!g_last_collision_warning && !g_last_left_alert && !g_last_right_alert) + buzzer_set_pattern(BUZZER_PATTERN_OFF); if (elapsed_ms_tv(&g_grass.t_last_active) >= GRASS_EXIT_HYSTERESIS_MS) { g_grass.state = GRASS_DONE; gettimeofday(&g_grass.t_done, NULL); @@ -761,9 +770,12 @@ void event_recorder_update(const stdc_analysis_t *ana) gettimeofday(&g_grass.t_last_active, NULL); speed_val = SPEED_LIMIT_STOP; msg_send("/tmp/canbus/c0/command.fifo", "host_stream", "setSpeed",&speed_val, sizeof(speed_val), 0); + buzzer_set_pattern(BUZZER_PATTERN_GRASS); } else { speed_val = SPEED_LIMIT_NORMAL; msg_send("/tmp/canbus/c0/command.fifo", "host_stream", "setSpeed",&speed_val, sizeof(speed_val), 0); + if (!g_last_collision_warning && !g_last_left_alert && !g_last_right_alert) + buzzer_set_pattern(BUZZER_PATTERN_OFF); if (elapsed_ms_tv(&g_grass.t_last_active) >= GRASS_EXIT_HYSTERESIS_MS) { g_grass.state = GRASS_DONE; gettimeofday(&g_grass.t_done, NULL); @@ -784,6 +796,8 @@ void event_recorder_update(const stdc_analysis_t *ana) * (keepalive in can_bus.c also maintains this every 200ms). */ speed_val = SPEED_LIMIT_NORMAL; msg_send("/tmp/canbus/c0/command.fifo", "host_stream", "setSpeed",&speed_val, sizeof(speed_val), 0); + if (!g_last_collision_warning && !g_last_left_alert && !g_last_right_alert) + buzzer_set_pattern(BUZZER_PATTERN_OFF); break; } diff --git a/src/host_stream/kp_firmware.c b/src/host_stream/kp_firmware.c index 0f41381..6729aa4 100644 --- a/src/host_stream/kp_firmware.c +++ b/src/host_stream/kp_firmware.c @@ -43,6 +43,7 @@ #include "event_recorder.h" #include "bt_uart.h" #include "can_bus.h" +#include "buzzer.h" #include "handshake.h" //fifo queue buffer setting @@ -297,6 +298,7 @@ int loadConfig(HOST_STREAM_INIT_OPT_T* pHostStreamInit) if (can_enable) can_bus_init(can_spidev, can_speed, (uint32_t)can_id_raw); } + buzzer_init(); iniparser_freedict(ini); return 0;