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

编译与烧录

  1. 在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)
  1. 编译并烧录:
idf.py build
idf.py -p /dev/ttyUSB0 flash monitor

主机端运行与验证

  1. 启动micro-ROS Agent(默认UDP端口8888):
ros2 run micro_ros_agent micro_ros_agent udp4 --port 8888
  1. 新终端中订阅话题:
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赋值逻辑取反即可。
ESP32 按键 GPIO0 GND WiFi/UDP micro-ROS Agent /key_state ros2 topic echo 按下→true | 松开→false