我正在熟悉使用ESP-IDF框架在ESP32上进行开发,这也是我第一次在freeRTOS上进行开发。
我的简单探索性应用程序将涉及响应来自两个不同来源的输入:到我的开发计算机的串行连接,以及到MQTT客户机的连接(例如)。
阅读freeRTOS上的介绍性材料似乎建议我应该创建单独的任务来监视和处理每个输入源。
然而,ESP-IDF中的项目示例(构建在freeRTOS之上)不包含对xTaskCreate
或vTaskStartScheduler()
的调用。相反,我看到事件句柄和回调被注册在事件循环中:
static esp_err_t mqtt_event_handler_cb(esp_mqtt_event_handle_t event)
{
esp_mqtt_client_handle_t client = event->client;
int msg_id;
static int cnt = 0;
// your_context_t *context = event->context;
switch (event->event_id) {
case MQTT_EVENT_CONNECTED:
ESP_LOGI(TAG, "MQTT_EVENT_CONNECTED");
//... Some other actions ...
break;
case MQTT_EVENT_DISCONNECTED:
ESP_LOGI(TAG, "MQTT_EVENT_DISCONNECTED");
break;
case MQTT_EVENT_SUBSCRIBED:
ESP_LOGI(TAG, "MQTT_EVENT_SUBSCRIBED, msg_id=%d", event->msg_id);
//... Some other actions ...
break;
default:
ESP_LOGI(TAG, "Other event id:%d", event->event_id);
break;
}
return ESP_OK;
}
static void mqtt_event_handler(void *handler_args, esp_event_base_t base, int32_t event_id, void *event_data) {
ESP_LOGD(TAG, "Event dispatched from event loop base=%s, event_id=%d", base, event_id);
mqtt_event_handler_cb(event_data);
}
static void mqtt_app_start(void)
{
const esp_mqtt_client_config_t mqtt_cfg = {
.uri = CONFIG_BROKER_URI,
.cert_pem = (const char *)mqtt_eclipse_org_pem_start,
};
esp_mqtt_client_handle_t client = esp_mqtt_client_init(&mqtt_cfg);
esp_mqtt_client_register_event(client, ESP_EVENT_ANY_ID, mqtt_event_handler, client);
esp_mqtt_client_start(client);
}
void app_main(void)
{
ESP_ERROR_CHECK(nvs_flash_init());
ESP_ERROR_CHECK(esp_netif_init());
ESP_ERROR_CHECK(esp_event_loop_create_default());
/* This helper function configures Wi-Fi or Ethernet, as selected in menuconfig.
* Read "Establishing Wi-Fi or Ethernet Connection" section in
* examples/protocols/README.md for more information about this function.
*/
ESP_ERROR_CHECK(example_connect());
mqtt_app_start();
}
我不明白为什么ESP-IDF会将事件循环结构强加于看似相当专用的方式之上,以管理响应外部事件的代码块之间共享处理器时间。我怀疑我错过了一个关键的理解,使每个工具(事件循环和任务管理器)适用于什么范围/上下文变得清晰。
最终,我的下一步将是创建我自己的任务或创建我自己的事件来处理来自我的开发机器的串行输入。哪一个?
请记住,ESP IDF示例的目的是以尽可能简单的方式演示特定子系统API的使用。为此,他们忽略了不必要的细节,而这些细节在构建真正的软件架构时可能变得很重要:)
在这种情况下,MQTT库使用事件循环库(ELL),它是由espresso提供的抽象事件处理程序服务,以简化您的生活。它在一个专门的任务中执行。任何由ELL发出的事件回调都在这个专用任务中运行,由所有从ELL接收回调的模块共享。这是一个妥协。如果你有很多轻量级(即不阻塞它们运行的任务)事件处理程序,这些事件处理程序不是实时关键的,那么在单个任务中执行它们是节省资源的好方法。它还允许相当简单的体系结构。
如果你需要做一些"重要的"事情(即阻塞任务)作为对事件的响应,那么您不应该在ELL任务中执行此操作。创建您自己的处理线程,并让ELL事件处理程序简单地在那里发布消息以开始处理。
这与UART基本相同- express的驱动程序创建一个任务和"接收到的数据";处理程序在那里执行。因此,您的问题的答案是:如果您需要阻塞一个任务,请为此目的创建您自己的任务,并将处理从事件处理程序推迟到那里。如果没有,请随意使用UART或ELL任务的事件处理程序。