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 <noreply@anthropic.com>
This commit is contained in:
miketsai 2026-06-12 18:09:54 +08:00
parent fd54f5cc0b
commit 286291fcfd
4 changed files with 190 additions and 0 deletions

View File

@ -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 */

151
src/host_stream/buzzer.c Normal file
View File

@ -0,0 +1,151 @@
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <pthread.h>
#include <time.h>
#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; }
}

View File

@ -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;
}

View File

@ -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;