如何为基于libgpiod的GPIO关机监控代码添加LED心跳闪烁功能
为libgpiod GPIO关机监听程序添加LED心跳闪烁功能
需求背景
现有代码通过libgpiod监听GPIO按钮长按事件触发系统关机,需新增LED心跳闪烁功能(如每隔1秒亮灭一次),同时保留原按钮监听的低CPU占用特性。
实现思路
利用gpiod_line_event_wait的超时参数打破原无限阻塞循环,每次超时后切换LED状态实现心跳效果,同时不影响按钮事件的实时响应。
修改后的完整代码
/* * Monitor external button on GPIO pin, shutdown machine if pressed * Adds LED heartbeat blink functionality using libgpiod timeout */ #define SHUTDOWN_CHIP 2 #define SHUTDOWN_LINE 21 // NEO3 pin 23 #define SHUTDOWN_HOLD 3 // seconds to hold down button #define LED_CHIP 2 // LED所在GPIO芯片编号,可与关机按钮复用同一片芯片 #define LED_LINE 24 // LED对应的GPIO引脚编号 #define HEARTBEAT_INTERVAL 1 // 心跳间隔(秒) #include <gpiod.h> // sudo apt install libgpiod-dev #include <stdio.h> #include <unistd.h> #include <syslog.h> #include <stdbool.h> void shut_down(struct gpiod_line *led_line) { syslog(LOG_NOTICE, "Safe shutdown triggered from GPIO button"); // 关机前熄灭LED gpiod_line_set_value(led_line, 0); system("/usr/bin/sudo /bin/shutdown -h now"); exit(EXIT_SUCCESS); }; int main(int argc, char *argv[]) { struct gpiod_chip *chip; struct gpiod_line *button; struct gpiod_line *led_line; bool led_state = false; // LED初始状态为熄灭 // 资源清理函数 void cleanup_resources() { if (led_line) { gpiod_line_set_value(led_line, 0); gpiod_line_release(led_line); } if (button) gpiod_line_release(button); if (chip) gpiod_chip_close(chip); } // 打开GPIO芯片 chip = gpiod_chip_open_by_number(SHUTDOWN_CHIP); if (!chip) { perror("Failed to open GPIO chip"); exit(EXIT_FAILURE); } // 初始化关机按钮引脚 button = gpiod_chip_get_line(chip, SHUTDOWN_LINE); if (!button) { perror("Failed to get shutdown button line"); cleanup_resources(); exit(EXIT_FAILURE); } // 若硬件无默认上拉电阻,可启用下方带flags的请求函数(libgpiod v1.5+支持) // if (gpiod_line_request_both_edges_events_flags(button, "gpio-shutdown", GPIOD_LINE_REQUEST_FLAG_BIAS_PULL_UP) < 0) { if (gpiod_line_request_both_edges_events(button, "gpio-shutdown") < 0) { perror("Can't claim shutdown button GPIO pin"); cleanup_resources(); exit(EXIT_FAILURE); } // 初始化LED引脚为输出模式 led_line = gpiod_chip_get_line(chip, LED_LINE); if (!led_line) { perror("Failed to get LED line"); cleanup_resources(); exit(EXIT_FAILURE); } if (gpiod_line_request_output(led_line, "gpio-heartbeat", 0) < 0) { perror("Can't claim LED GPIO pin"); cleanup_resources(); exit(EXIT_FAILURE); } syslog(LOG_INFO, "Monitoring GPIO button for shutdown requests, LED heartbeat active"); atexit(cleanup_resources); struct timespec heartbeat_timeout = {HEARTBEAT_INTERVAL, 0}; while (true) { // 等待按钮事件或心跳超时 int wait_result = gpiod_line_event_wait(button, &heartbeat_timeout); if (wait_result == 1) { // 捕获到按钮边缘事件 struct gpiod_line_event ev; struct timespec t_held = {SHUTDOWN_HOLD, 0}; if (gpiod_line_event_read(button, &ev) == 0) { if (ev.event_type == GPIOD_LINE_EVENT_FALLING_EDGE) { // 若按钮长按超时未释放,触发关机 if (!gpiod_line_event_wait(button, &t_held)) { shut_down(led_line); } } } } else if (wait_result == 0) { // 心跳超时,切换LED状态 led_state = !led_state; gpiod_line_set_value(led_line, led_state ? 1 : 0); } else { // GPIO事件等待出错 perror("Error waiting for GPIO event"); cleanup_resources(); exit(EXIT_FAILURE); } }; return EXIT_SUCCESS; }
关键修改说明
- LED资源初始化:新增LED引脚的定义与输出模式配置,初始状态设为熄灭。
- 超时机制应用:给
gpiod_line_event_wait设置心跳间隔超时,让循环定期执行LED状态切换逻辑。 - 资源清理扩展:在退出函数中添加LED熄灭与资源释放逻辑,避免设备残留异常状态。
- 关机逻辑优化:触发关机前先熄灭LED,明确设备即将关机的状态。
编译与运行
编译时需链接libgpiod库:
gcc gpio-shutdown-led.c -o gpio-shutdown-led -lgpiod
以root权限运行程序:
sudo ./gpio-shutdown-led
可将程序配置为系统服务,实现开机自动启动。
内容的提问来源于stack exchange,提问作者Andrejka ThinkPad
相关产品推荐
相关产品推荐

