如何在Yocto中实现GPIO按键触发syslog的systemd服务?
实现GPIO按键触发Syslog日志的systemd服务(Yocto环境)
在Yocto环境中开发了一个作为systemd服务的用户态应用,需求是当连接到GPIO 193引脚的按键按下时,在syslog中生成"button pressed"日志。以下是现有代码及完善的实现建议:
现有C代码
#include <stdio.h> #include <unistd.h> #include <syslog.h> #include <fcntl.h> #include <systemd/sd-event.h> #define GPIO_PIN "/sys/class/gpio/gpio1009/value" // 1009是GPIO193的导出编号(芯片编号816) int button_event_callback(sd_event_source *source, int fd, uint32_t revents, void *userdata) { char value; ssize_t bytes_read = read(fd, &value, 1); // 读取GPIO电平值 if (bytes_read == -1) { perror("Error reading GPIO pin value"); return -1; } if (value == '0') { syslog(LOG_INFO,"Button pressed!\n"); printf("Button Pressed!\n"); } return 0; } int main() { sd_event *event_loop = NULL; sd_event_new(&event_loop); int button_fd = open(GPIO_PIN, O_RDONLY); // 打开GPIO值文件 if (button_fd == -1) { perror("Error opening GPIO pin file"); return 1; } sd_event_add_io(event_loop, NULL, button_fd, EPOLLIN, button_event_callback, NULL); syslog(LOG_INFO,"Listening for button press events.\n"); sd_event_run(event_loop,-1); sd_event_unref(event_loop); return 0; }
现有systemd服务文件
[Unit] Description=Continuous Hello World Service After=syslog.target [Service] ExecStart=/usr/bin/buttonser StandardOutput=syslog Type=simple Restart=always [Install] WantedBy=multi-user.target
实现建议
GPIO硬件配置修正
- 确保GPIO193已导出为
gpio1009:可手动执行echo 1009 > /sys/class/gpio/export验证,或在Yocto中通过设备树、udev规则自动导出该GPIO。 - 配置GPIO方向与上下拉:执行
echo in > /sys/class/gpio/gpio1009/direction设置为输入,根据硬件电路执行echo pullup > /sys/class/gpio/gpio1009/pull(若支持)确保按键默认电平稳定。 - 启用边沿触发:sysfs GPIO默认不触发IO事件,需设置边沿检测规则,例如按键按下时电平从高变低,执行
echo falling > /sys/class/gpio/gpio1009/edge;若为低电平触发,根据实际选rising或both。
- 确保GPIO193已导出为
代码优化
- 添加非阻塞打开标志:修改
open(GPIO_PIN, O_RDONLY | O_NONBLOCK),避免read操作阻塞,适配sd-event事件驱动模型。 - 初始化syslog标识:在main函数开头添加
openlog("buttonser", LOG_PID | LOG_CONS, LOG_DAEMON);,让日志携带进程ID和明确服务标识,方便排查。 - 重置文件偏移量:在callback函数读取完成后添加
lseek(fd, 0, SEEK_SET);,因为sysfs的value文件读取后指针会停在末尾,重置后才能正常读取下一次电平变化。
- 添加非阻塞打开标志:修改
systemd服务文件修正
- 修正描述信息:将
Description改为GPIO Button Press Logger Service,明确服务用途。 - 添加权限配置:若GPIO文件权限不足,添加
User=root(或具有GPIO访问权限的用户),确保进程能正常读取GPIO节点。 - 调整重启策略:将
Restart=always改为Restart=on-failure,仅在服务异常退出时重启,避免无意义的重启操作。
- 修正描述信息:将
Yocto编译与部署
- 配置编译依赖:在.bb文件中添加
DEPENDS += "systemd"和LDFLAGS += "-lsystemd",确保链接systemd库。 - 安装服务文件:将服务文件放置到
${D}/lib/systemd/system/buttonser.service,并添加SYSTEMD_SERVICE_${PN} = "buttonser.service",让Yocto自动处理服务安装。 - 自动启用服务:添加
SYSTEMD_AUTO_ENABLE_${PN} = "enable",部署后自动启用服务。
- 配置编译依赖:在.bb文件中添加
功能验证
- 查看服务日志:执行
journalctl -u buttonser.service查看服务专属日志,确认按键按下时出现"Button pressed!"条目。 - 检查服务状态:
systemctl status buttonser.service确认服务正常运行,无启动错误。 - 手动触发测试:按下按键,观察journalctl输出是否有对应日志生成。
- 查看服务日志:执行
内容的提问来源于stack exchange,提问作者G baba
相关产品推荐
相关产品推荐

