IoT Pi & ESP8266 MQTT 设备开发

更新时间:
2023-08-09
下载文档

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 这几个组件,如下图所示:

    avatar
  • 在 IoT studio 上完成 msrtos_base_sdk 工程导入和编译

  • 在 IoT studio 上完成 bspstm32f4xx 工程的下载和导入,如下图所示:

    avatar

获取 paho-mqtt-embedded-c 组件

进入 msrtos_base_sdk 根目录,使用 git 工具从 MS-RTOS github 社区open in new window 下载 paho-mqtt-embedded-c 组件,命令如下:

git clone https://github.com/ms-rtos/paho-mqtt-embedded-c.git

下载完成后右键 msrtos_base_sdk 工程选择 Refresh 菜单项刷新工程,如下图所示:

avatar

msrtos_base_sdk 根目录下的 Makefile 中添加 paho-mqtt-embedded-c,如下图所示:

avatar

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 相关的配置宏如下:

avatar
含义
BSP_ESP8266_AUTO_JOIN自动连接模式,尝试连接上一次连接的 Wi-Fi AP
BSP_ESP8266_SMART_CFGSmartConfig 模式
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 文件:

avatar

bspstm32f4xx 编译

选中 bspstm32f4xx 工程,点击编译按钮,将编译 bspstm32f4xx 工程,编译完成后,会在 Debug 目录生成 bspiotpi.bin 文件:

avatar

烧写镜像

使用 MS-RTOS AutoTester 烧写镜像,请参考《IoT Pi 快速入门》完成 bspiotpi.binmqtt-client-example.bin 镜像烧写,注意不同的镜像需要烧写到不同的地址,如下表所示:

镜像烧写地址
bspiotpi.bin0x08000000
mqtt-client-example.bin0x08040000

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,如下图所示:

avatar

工程编译

在 IDE 中编译 SDK 和例程:

avatar

烧写镜像

参考《ESP8266 SDDC 设备开发》,配置 FLASH 编程目标,完成镜像烧写,如下图所示:

avatar

验证功能

IoT Pi 连接 EdgerOS

按下 IoT Pi 开发板的 RESET 按键,MS-RTOS 操作系统启动后,将自动运行 0x08040000 地址处的 mqtt-client-example demo 程序,该程序周期性发布主题名为 MS-RTOS/sample/a,内容为 message number 累加常数 的消息,如下图所示:

avatar

ESP8266 连接 EdgerOS

使用 PuTTY 作为串口调试工具,新建一个 Serial 类型的 Session,按如下图所示配置串口,注意 Speed 为 74880,点击 Save 按钮保存 Session 方便以后直接打开,最后点击 Open 按钮:

avatar

按下 ESP8266 开发板的 RESET 按键,将看到 MQTT 程序运行起来,并收到主题为 MS-RTOS/sample/a 的消息,如下图所示:

avatar

代码解析

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();
}
文档内容是否对您有所帮助?
有帮助
没帮助