IoT Pi & ESP8266 MQTT 设备开发
本章将介绍如何在 IoT Pi 上使用 MS-RTOS 和 paho-mqtt-embedded-c 以及在 ESP8266 上使用 FreeRTOS 和 esp-mqtt 开发能接入到 EdgerOS 的 MQTT 物联网设备,其中 IoT Pi 作为发布者,ESP8266 作为订阅者。
IoT Pi MQTT Publisher 开发
IoT Pi 开发环境搭建
参考《IoT Pi 快速入门》,完成以下步骤:
完成 MS-RTOS 开发工具下载和安装
在 MS-RTOS 云开发平台上完成
msrtos_base_sdk
配置和下载, 配置 MS-RTOS 时,需要手动勾选 libmsdriver、esp_at_net、fatfs 这几个组件,如下图所示:在 IoT studio 上完成
msrtos_base_sdk
工程导入和编译在 IoT studio 上完成
bspstm32f4xx
工程的下载和导入,如下图所示:
获取 paho-mqtt-embedded-c 组件
进入 msrtos_base_sdk
根目录,使用 git 工具从 MS-RTOS github 社区 下载 paho-mqtt-embedded-c 组件,命令如下:
git clone https://github.com/ms-rtos/paho-mqtt-embedded-c.git
下载完成后右键 msrtos_base_sdk
工程选择 Refresh
菜单项刷新工程,如下图所示:
在 msrtos_base_sdk
根目录下的 Makefile
中添加 paho-mqtt-embedded-c
,如下图所示:
paho-mqtt-embedded-c 配置
paho-mqtt-embedded-c 组件在 msrtos_base_sdk\paho-mqtt-embedded-c\src\paho.mqtt.embedded-c\MQTTClient-C\samples\MS-RTOS\main.c
文件中进行连接 EdgerOS MQTT Broker 服务器地址相关信息配置,如下所示:
// 配置 EdgerOS MQTT Broker 地址
char* address = "192.168.128.1";
// 连接 MQTT Broker
if ((rc = NetworkConnect(&network, address, 1883)) != 0) {
ms_printf("Return code from network connect is %d\n", rc);
exit(0);
}
connectData.MQTTVersion = 3; // 配置 MQTT 版本
connectData.clientID.cstring = "MS-RTOS_sample"; // 配置 clientID
connectData.username.cstring = "user"; // 配置 MQTT Broker 用户名
connectData.password.cstring = "passwd"; // 配置 MQTT Broker 密码
配置 MQTT 发布内容,如下所示:
message.qos = 1;
message.retained = 0;
message.payload = payload;
ms_snprintf(payload, sizeof(payload), "message number %d", count);
message.payloadlen = strlen(payload);
// 发布主题为 MS-RTOS/sample/a 的消息,消息为 message number 累加常数
if ((rc = MQTTPublish(&client, "MS-RTOS/sample/a", &message)) != 0) {
ms_printf("Return code from MQTT publish is %d\n", rc);
break;
}
bspstm32f4xx 配置
Wi-Fi AP 连接模式配置
bspstm32f4xx/src/board/IOT_PI/iot_pi_cfg.h
文件为 bspstm32f4xx
工程的 IoT Pi 开发板的配置头文件,与 IoT Pi 连接 Wi-Fi AP 相关的配置宏如下:
宏 | 含义 |
---|---|
BSP_ESP8266_AUTO_JOIN | 自动连接模式,尝试连接上一次连接的 Wi-Fi AP |
BSP_ESP8266_SMART_CFG | SmartConfig 模式 |
BSP_ESP8266_MANUAL_CFG | 手动连接模式,连接 ap_list[] 中指定的 Wi-Fi AP |
BSP_CFG_ESP8266_MODE | 连接模式, 以上三种连接模式的组合,默认模式为自动连接模式 |
Wi-Fi AP 列表配置
bspstm32f4xx/src/board/IOT_PI/iot_pi_init.c
为 IoT Pi 开发板的初始化源文件,在此源文件中的 ap_list[]
变量用于指定手动连接模式下尝试连接到的 Wi-Fi AP 列表:
/**
* WiFi AP list.
*/
static const ms_esp_at_net_ap_t ap_list[] = {
{ "EOS-00000F", "123456789" }, // Spirit 1 的 Wi-Fi AP SSID 与密码
};
工程编译
paho-mqtt-embedded-c 编译
选中 msrtos_base_sdk
工程,点击编译按钮,将编译 msrtos_base_sdk
工程的组件,编译完成后,会在 paho-mqtt-embedded-c/Debug
目录生成 mqtt-client-example.bin
文件:
bspstm32f4xx 编译
选中 bspstm32f4xx
工程,点击编译按钮,将编译 bspstm32f4xx
工程,编译完成后,会在 Debug
目录生成 bspiotpi.bin
文件:
烧写镜像
使用 MS-RTOS AutoTester 烧写镜像,请参考《IoT Pi 快速入门》完成 bspiotpi.bin
和 mqtt-client-example.bin
镜像烧写,注意不同的镜像需要烧写到不同的地址,如下表所示:
镜像 | 烧写地址 |
---|---|
bspiotpi.bin | 0x08000000 |
mqtt-client-example.bin | 0x08040000 |
ESP8266 MQTT Subscriber 开发
ESP8266 开发环境搭建
参考《ESP8266 SDDC 设备开发》,完成以下步骤:
完成 IDE 软件包下载并安装
在 windows 上调整 IDE PATH,并下载 ESP8266 SDK
代码修改
在 examples\protocols\mqtt\tcp\main\app_main.c
文件中修改 MQTT 的配置信息内容参考《MQTT 介绍》,具体代码如下:
static void mqtt_app_start(void)
{
esp_mqtt_client_config_t mqtt_cfg = {
.uri = "mqtt://192.168.128.1:1883", // 配置 EdgerOS MQTT Broker 地址及端口
.username = "user", // 配置 MQTT Broker 用户名
.password = "passwd", // 配置 MQTT Broker 密码
.event_handle = mqtt_event_handler, // 配置回调函数
};
在 examples\protocols\mqtt\tcp\main\app_main.c
文件中修改 MQTT 订阅的主题为 MS-RTOS/sample/a
,具体代码如下:
switch (event->event_id) {
case MQTT_EVENT_CONNECTED:
ESP_LOGI(TAG, "MQTT_EVENT_CONNECTED");
// 发布消息主题为 /topic/qos1,内容为 data_3
msg_id = esp_mqtt_client_publish(client, "/topic/qos1", "data_3", 0, 1, 0);
ESP_LOGI(TAG, "sent publish successful, msg_id=%d", msg_id);
// 订阅消息主题为 MS-RTOS/sample/a
msg_id = esp_mqtt_client_subscribe(client, "MS-RTOS/sample/a", 0);
ESP_LOGI(TAG, "sent subscribe successful, msg_id=%d", msg_id);
// 订阅消息主题为 /topic/qos1
msg_id = esp_mqtt_client_subscribe(client, "/topic/qos1", 1);
ESP_LOGI(TAG, "sent subscribe successful, msg_id=%d", msg_id);
// 取消订阅消息主题为 /topic/qos1
msg_id = esp_mqtt_client_unsubscribe(client, "/topic/qos1");
ESP_LOGI(TAG, "sent unsubscribe successful, msg_id=%d", msg_id);
break;
配置 SDK 工程
- 参考《ESP8266 SDDC 设备开发》,配置
IDF_PATH
变量,配置构建路径为${workspace_loc:/AiThinkerProjectForESP}/examples/protocols/mqtt/tcp
,如下图所示:
- 参考《ESP8266 SDDC 设备开发》,在 IDE 中配置 menuconfig 目标。
工程编译
在 IDE 中编译 SDK 和例程:
烧写镜像
参考《ESP8266 SDDC 设备开发》,配置 FLASH 编程目标,完成镜像烧写,如下图所示:
验证功能
IoT Pi 连接 EdgerOS
按下 IoT Pi 开发板的 RESET 按键,MS-RTOS 操作系统启动后,将自动运行 0x08040000
地址处的 mqtt-client-example
demo 程序,该程序周期性发布主题名为 MS-RTOS/sample/a
,内容为 message number 累加常数
的消息,如下图所示:
ESP8266 连接 EdgerOS
使用 PuTTY 作为串口调试工具,新建一个 Serial 类型的 Session,按如下图所示配置串口,注意 Speed 为 74880,点击 Save 按钮保存 Session 方便以后直接打开,最后点击 Open 按钮:
按下 ESP8266 开发板的 RESET 按键,将看到 MQTT 程序运行起来,并收到主题为 MS-RTOS/sample/a
的消息,如下图所示:
代码解析
IoT Pi MQTT Publisher
IoT Pi MQTT demo 代码位于 msrtos_base_sdk\paho-mqtt-embedded-c\src\paho.mqtt.embedded-c\MQTTClient-C\samples\MS-RTOS\main.c
:
// 消息处理回调函数
static void messageArrived(MessageData* data)
{
char tmpbuf[80];
// 打印收到消息的主题
memcpy(tmpbuf, data->topicName->lenstring.data, data->topicName->lenstring.len);
tmpbuf[data->topicName->lenstring.len] = 0;
ms_printf("Message arrived on topic %s", tmpbuf);
// 打印收到消息的内容
memcpy(tmpbuf, data->message->payload, data->message->payloadlen);
tmpbuf[data->message->payloadlen] = 0;
ms_printf(": %s\n", tmpbuf);
}
int main(int argc, char **argv)
{
/* connect to 192.168.128.1, subscribe to a topic, send and receive messages regularly every 10 sec */
MQTTClient client;
Network network;
unsigned char sendbuf[80], readbuf[80];
int rc = 0, count = 0;
MQTTPacket_connectData connectData = MQTTPacket_connectData_initializer;
// 初始化网络
NetworkInit(&network);
// 初始化 MQTT 客户端
MQTTClientInit(&client, &network, 30000, sendbuf, sizeof(sendbuf), readbuf, sizeof(readbuf));
// 连接 EdgerOS 的 MQTT Broker
char* address = "192.168.128.1";
if ((rc = NetworkConnect(&network, address, 1883)) != 0) {
ms_printf("Return code from network connect is %d\n", rc);
exit(0);
}
#if defined(MQTT_TASK)
// 如果定义了 MQTT_TASK,则启动 MQTT 协议任务
if ((rc = MQTTStartTask(&client)) != MS_ERR_NONE) {
ms_printf("Return code from start tasks is %d\n", rc);
exit(0);
}
#endif
connectData.MQTTVersion = 3; // 配置 MQTT 版本
connectData.clientID.cstring = "MS-RTOS_sample"; // 配置 clientID
connectData.username.cstring = "user"; // 配置 MQTT Broker 用户名
connectData.password.cstring = "passwd"; // 配置 MQTT Broker 密码
// 通过 MQTT 相关配置进行连接
if ((rc = MQTTConnect(&client, &connectData)) != 0) {
ms_printf("Return code from MQTT connect is %d\n", rc);
exit(0);
}
ms_printf("MQTT Connected\n");
// 订阅主题为 MS-RTOS/sample/a 的消息,设置处理消息的回调函数 messageArrived()
if ((rc = MQTTSubscribe(&client, "MS-RTOS/sample/a", 2, messageArrived)) != 0) {
ms_printf("Return code from MQTT subscribe is %d\n", rc);
exit(0);
}
while (++count) {
MQTTMessage message;
char payload[30];
message.qos = 1;
message.retained = 0;
message.payload = payload;
ms_snprintf(payload, sizeof(payload), "message number %d", count);
message.payloadlen = strlen(payload);
// 发布主题为 MS-RTOS/sample/a 的消息,消息为累加常数
if ((rc = MQTTPublish(&client, "MS-RTOS/sample/a", &message)) != 0) {
ms_printf("Return code from MQTT publish is %d\n", rc);
break;
}
#if !defined(MQTT_TASK)
// 如果没有定义 MQTT_TASK,则处理 MQTT 协议
if ((rc = MQTTYield(&client, 10000)) != 0) {
ms_printf("Return code from yield is %d\n", rc);
break;
}
#else
ms_thread_sleep_s(10); // 10s 延时
#endif
}
ms_printf("MQTT example exit!\n");
// MQTT 断开连接
MQTTDisconnect(&client);
// 断开网络连接
NetworkDisconnect(&network);
return 0;
}
ESP8266 MQTT Subscriber
ESP8266 MQTT demo 代码位于 AiThinkerProjectForESP\examples\protocols\mqtt\tcp\main\app_main.c
:
// 消息处理回调函数
static esp_err_t mqtt_event_handler(esp_mqtt_event_handle_t event)
{
esp_mqtt_client_handle_t client = event->client;
int msg_id;
// your_context_t *context = event->context;
switch (event->event_id) {
case MQTT_EVENT_CONNECTED: // 已经连接
ESP_LOGI(TAG, "MQTT_EVENT_CONNECTED");
// 发布消息主题为 /topic/qos1,内容为 data_3
msg_id = esp_mqtt_client_publish(client, "/topic/qos1", "data_3", 0, 1, 0);
ESP_LOGI(TAG, "sent publish successful, msg_id=%d", msg_id);
// 订阅消息主题为 MS-RTOS/sample/a
msg_id = esp_mqtt_client_subscribe(client, "MS-RTOS/sample/a", 0);
ESP_LOGI(TAG, "sent subscribe successful, msg_id=%d", msg_id);
// 订阅消息主题为 /topic/qos1
msg_id = esp_mqtt_client_subscribe(client, "/topic/qos1", 1);
ESP_LOGI(TAG, "sent subscribe successful, msg_id=%d", msg_id);
// 取消订阅消息主题为 /topic/qos1
msg_id = esp_mqtt_client_unsubscribe(client, "/topic/qos1");
ESP_LOGI(TAG, "sent unsubscribe successful, msg_id=%d", msg_id);
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);
msg_id = esp_mqtt_client_publish(client, "/topic/qos0", "data", 0, 0, 0);
ESP_LOGI(TAG, "sent publish successful, msg_id=%d", msg_id);
break;
case MQTT_EVENT_UNSUBSCRIBED: // 取消订阅
ESP_LOGI(TAG, "MQTT_EVENT_UNSUBSCRIBED, msg_id=%d", event->msg_id);
break;
case MQTT_EVENT_PUBLISHED: // 发布成功
ESP_LOGI(TAG, "MQTT_EVENT_PUBLISHED, msg_id=%d", event->msg_id);
break;
case MQTT_EVENT_DATA: // 消息数据
ESP_LOGI(TAG, "MQTT_EVENT_DATA");
printf("TOPIC=%.*s\r\n", event->topic_len, event->topic);
printf("DATA=%.*s\r\n", event->data_len, event->data);
break;
case MQTT_EVENT_ERROR:
ESP_LOGI(TAG, "MQTT_EVENT_ERROR");
break;
}
return ESP_OK;
}
// 启动 MQTT 应用
static void mqtt_app_start(void)
{
esp_mqtt_client_config_t mqtt_cfg = {
.uri = "mqtt://192.168.128.1:1883", // 配置 EdgerOS MQTT Broker 地址及端口
.username = "user", // 配置 MQTT Broker 用户名
.password = "passwd", // 配置 MQTT Broker 密码
.event_handle = mqtt_event_handler, // 配置回调函数
// .user_context = (void *)your_context
};
#if CONFIG_BROKER_URL_FROM_STDIN
// MQTT Broker URL 配置从标准输入获
char line[128];
if (strcmp(mqtt_cfg.uri, "FROM_STDIN") == 0) {
int count = 0;
printf("Please enter url of mqtt broker\n");
while (count < 128) {
int c = fgetc(stdin);
if (c == '\n') {
line[count] = '\0';
break;
} else if (c > 0 && c < 127) {
line[count] = c;
++count;
}
vTaskDelay(10 / portTICK_PERIOD_MS);
}
mqtt_cfg.uri = line;
printf("Broker url: %s\n", line);
} else {
ESP_LOGE(TAG, "Configuration mismatch: wrong broker url");
abort();
}
#endif /* CONFIG_BROKER_URL_FROM_STDIN */
// 初始化 MQTT 客户端
esp_mqtt_client_handle_t client = esp_mqtt_client_init(&mqtt_cfg);
// 开启 MQTT 客户端
esp_mqtt_client_start(client);
}
// 应用入口函数
void app_main()
{
// 初始化 FLASH
ESP_ERROR_CHECK(nvs_flash_init());
// 初始化 NETIF
ESP_ERROR_CHECK(esp_netif_init());
// 创建缺省的事件循环
ESP_ERROR_CHECK(esp_event_loop_create_default());
// 进行网络连接
ESP_ERROR_CHECK(example_connect());
ESP_LOGI(TAG, "[APP] Startup..");
ESP_LOGI(TAG, "[APP] Free memory: %d bytes", esp_get_free_heap_size());
ESP_LOGI(TAG, "[APP] IDF version: %s", esp_get_idf_version());
esp_log_level_set("*", ESP_LOG_INFO);
esp_log_level_set("MQTT_CLIENT", ESP_LOG_VERBOSE);
esp_log_level_set("TRANSPORT_TCP", ESP_LOG_VERBOSE);
esp_log_level_set("TRANSPORT_SSL", ESP_LOG_VERBOSE);
esp_log_level_set("TRANSPORT", ESP_LOG_VERBOSE);
esp_log_level_set("OUTBOX", ESP_LOG_VERBOSE);
// 启动 MQTT 应用
mqtt_app_start();
}