3. 按键状态发布
任务目标
本案例实现:在ESP32开发板上读取一个按键的电平状态,通过micro-ROS以std_msgs/msg/Bool消息类型发布到key_state话题,主机端通过ros2 topic echo订阅并显示按键状态。按键按下时发布true,松开时发布false。
环境与依赖
- 硬件:ESP32开发板、按键模块(一端接GPIO,另一端接GND)、USB线
- 固件:micro_ros_espidf_programmer烧录的micro-ROS agent固件(已预置)
- 主机端:Ubuntu 22.04 + ROS 2 Humble + micro-ROS Agent
- 代码基于ESP-IDF框架,使用
esp-idf的GPIO驱动
完整实现代码
创建main.c,代码如下:
#include <stdio.h>
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include <driver/gpio.h>
#include <rcl/rcl.h>
#include <rclc/rclc.h>
#include <rclc/executor.h>
#include <std_msgs/msg/bool.h>
#define KEY_GPIO GPIO_NUM_0 // 按键接GPIO0,另一端接GND
#define RCCHECK(fn) { rcl_ret_t temp_rc = fn; if((temp_rc != RCL_RET_OK)){vTaskDelete(NULL);}}
#define RCSOFTCHECK(fn) { rcl_ret_t temp_rc = fn; if((temp_rc != RCL_RET_OK)){}}
rcl_publisher_t publisher;
std_msgs__msg__Bool msg;
void timer_callback(rcl_timer_t *timer, int64_t last_call_time)
{
(void)last_call_time;
if (timer != NULL) {
int level = gpio_get_level(KEY_GPIO);
// 按键按下时GPIO为低电平(0),发布true;松开为高电平(1),发布false
msg.data = (level == 0) ? true : false;
rcl_ret_t ret = rcl_publish(&publisher, &msg, NULL);
if (ret != RCL_RET_OK) {
printf("发布失败\n");
}
}
}
void app_main(void)
{
// 配置GPIO为输入,启用内部上拉
gpio_config_t io_conf = {
.pin_bit_mask = (1ULL << KEY_GPIO),
.mode = GPIO_MODE_INPUT,
.pull_up_en = GPIO_PULLUP_ENABLE,
.pull_down_en = GPIO_PULLDOWN_DISABLE,
.intr_type = GPIO_INTR_DISABLE
};
gpio_config(&io_conf);
// micro-ROS初始化
rcl_allocator_t allocator = rcl_get_default_allocator();
rclc_support_t support;
RCCHECK(rclc_support_init(&support, 0, NULL, &allocator));
rcl_node_t node;
RCCHECK(rclc_node_init_default(&node, "key_node", "", &support));
RCCHECK(rclc_publisher_init_default(
&publisher,
&node,
ROSIDL_GET_MSG_TYPE_SUPPORT(std_msgs, msg, Bool),
"key_state"));
// 创建定时器,每100ms发布一次
rcl_timer_t timer;
RCCHECK(rclc_timer_init_default(
&timer,
&support,
RCL_MS_TO_NS(100),
timer_callback));
rclc_executor_t executor;
RCCHECK(rclc_executor_init(&executor, &support.context, 1, &allocator));
RCCHECK(rclc_executor_add_timer(&executor, &timer));
printf("按键状态发布节点已启动\n");
while (1) {
rclc_executor_spin_some(&executor, RCL_MS_TO_NS(10));
vTaskDelay(pdMS_TO_TICKS(10));
}
// 清理资源(实际不会执行到)
RCCHECK(rcl_publisher_fini(&publisher, &node));
RCCHECK(rcl_node_fini(&node));
vTaskDelete(NULL);
}
编译与烧录
- 在ESP-IDF环境中创建项目,将上述代码放入
main/main.c,并在CMakeLists.txt中添加micro-ROS依赖:
idf_component_register(SRCS "main.c"
INCLUDE_DIRS ".")
target_link_libraries(${COMPONENT_LIB} INTERFACE micro_ros_espidf_component)
- 编译并烧录:
idf.py build
idf.py -p /dev/ttyUSB0 flash monitor
主机端运行与验证
- 启动micro-ROS Agent(默认UDP端口8888):
ros2 run micro_ros_agent micro_ros_agent udp4 --port 8888
- 新终端中订阅话题:
ros2 topic echo /key_state std_msgs/msg/Bool
通过条件
- 按下按键(GPIO0接地)时,终端显示
data: true;松开时显示data: false。 - 话题以约10Hz频率持续刷新,无断连报错。
常见问题
- 按键无反应:检查GPIO0是否被其他功能占用(如flash按钮),可更换其他GPIO并修改
KEY_GPIO宏。 - 发布失败:确认Agent已启动且ESP32已成功连接(串口日志显示
Micro-ROS initialized)。 - 电平逻辑相反:若按键按下时读到高电平,将
msg.data赋值逻辑取反即可。