0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

ESP8266纯串口透传,助力设备上云端

Q4MP_gh_c472c21 来源:嵌入式ARM 作者:嵌入式ARM 2021-02-10 17:51 次阅读
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

项目有些久远,前年的机器人上需要的功能,当时是需要将STM32上的数据上传到服务器,比如机器人的速度,行驶距离,是否在拍照等等。便于管理者在PC或者手机上了解机器人的工作状态,同时可以远程下发指令给机器人,控制其完成相应动作。 因为所有的逻辑判断和控制都在服务器或者STM32上面,作为中间的无线模块仅仅需要上传STM32的数据并接收服务器下发的指令即可,所以这里对WiFi模块的要求不高,仅仅需要它作为透传功能即可。当时在选型的时候试过好几款WiFi模块,最终敲定了安信可的ESP8266,价格便宜,开发简单,但是搭建环境是真的不容易,深受其害 选择好模块就该考虑使用AT指令还是使用SDK开发,AT指令固然简单,但是局限性非常大。如果使用AT指令,我那开发控制端的同事估计就要跳脚了,代码里需要写一大堆的AT指令,如果功能改变,指令代码就需要重写,烦不胜烦。 如果使用SDK开发,控制端只需发送简单的数据就行,完全不用考虑其他任何东西,ESP8266完全当做一个中转站,相对应的我的工作就会繁重,但是,我屈服了,选择使用SDK。 于是就有了下面基于NONOS 2.0的ESP8266串口透传。主要有以下几个功能:
  • 纯串口透传,接收MCU串口数据,直接通过MQTT上传到服务器,接收服务器数据下发给MCU。
  • smartconfig+airkiss配网,随意使用,场景丰富。
  • 最多储存5个WIFI账号和密码,自动寻找网络连接。
  • 按键配网,长按重新配网,前一次WiFi自动储存,添加配网指示灯。
  • OTA空中升级(待验证)
从程序的入口开始:

//程序入口void ICACHE_FLASH_ATTR user_init(void){        uart_init(115200, 115200);        os_delay_us(60000);        keyInit();        set_uart_cb(uart_cb);
        PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTDI_U, FUNC_GPIO12); //GPIO12初始化        GPIO_OUTPUT_SET(GPIO_ID_PIN(12), 0);//低电平
    get_mac();//获取MAC地址
        wifi_set_opmode(STATION_MODE);        //设置wifi信息存储数量,最大为5个        wifi_station_ap_number_set(2);
    mqtt_init();
        set_wifistate_cb(wifi_connect_cb, wifi_disconnect_cb);}
程序的入口先进行串口初始化和按键的初始化,以及LED的初始化。串口要初始化波特率,按键初始化配网按键,用于短按配网,长按重新配网,LED只要用于判断模块是否进入配网模式以及是否配网完成。
初始化完成后会首先读取MAC地址,该地址是唯一的,每个模块都不一样,用于填充进主题中,便于服务器区分不同设备,用于多台量产设备的使用,在连接MQTT服务器时会自动填充。

951a5030-5f65-11eb-8b86-12bb97331649.png


每连接一次WiFi都会将WiFi信息保存在模块内部,每次上电都会自动扫描暴露的WiFi,直接连接,就像手机的WIFI连接,目前最大支持五个WiFi信息的保存,超过5个会剔除最早的WiFi信息,通过短按D5(GPIO14)可进入配网模式。
/***         按键短按回调*/LOCAL void ICACHE_FLASH_ATTR key1ShortPress(void) {
        start_smartconfig(smartconfig_cd);        INFO("start_smartconfig
");}/***         按键长按回调*/LOCAL void ICACHE_FLASH_ATTR key1LongPress(void) {
        start_smartconfig(smartconfig_cd);        INFO("start_smartconfig
");}/***         按键初始化*/LOCAL void ICACHE_FLASH_ATTR keyInit(void) {
        //设置按键数量        set_key_num(1);        //长按、短按的按键回调        key_add(D5, NULL, key1ShortPress);        key_add(D5, NULL, key1LongPress);
}

由于找不到最新的代码。这里的长按我没做处理,应该是断开WiFi重新进入配网模式, 或者软复位模块,再进入start_smartconfig()函数:
/*** 开始Smartconfig配置  * @param  cd: Smartconfig状态回调* @retval None*/void ICACHE_FLASH_ATTR start_smartconfig(smartconfig_cd_t cd) {        smartconfig_flag = 1;        smartconfig_set_type(SC_TYPE_ESPTOUCH_AIRKISS); //SC_TYPE_ESPTOUCH,SC_TYPE_AIRKISS,SC_TYPE_ESPTOUCH_AIRKISS        wifi_station_disconnect();        wifi_set_opmode(STATION_MODE);        finish_cd = cd;        smartconfig_start(smartconfig_done);        os_timer_disarm(&OS_Timer_Wifichange);        // 关闭定时器
        if(connect_flag == 1){                w_disconnect();                connect_flag = 0;        }
        os_timer_disarm(&OS_Timer_SM);        // 关闭定时器        os_timer_setfn(&OS_Timer_SM, (os_timer_func_t *) sm_wait_time, NULL);// 设置定时器        os_timer_arm(&OS_Timer_SM, 1000, 1);  // 使能定时器}

9565e0cc-5f65-11eb-8b86-12bb97331649.png

smartconfig_set_type();函数可选3个参数:分别是:SC_TYPE_ESPTOUCH、SC_TYPE_AIRKISS和SC_TYPE_ESPTOUCH_AIRKISS
第一个是smartconfig配网(手机APP),第二个是airkiss配网(微信公众号),最后一个两者都可以。进入该函数会调用smartconfig_start();,该函数会调用smartconfig_done()函数进行配网,配网成功后会点亮LED灯。

/*** Smartconfig 状态处理* @param  status: 状态* @param  *pdata: AP数据* @retval None*/void ICACHE_FLASH_ATTRsmartconfig_done(sc_status status, void *pdata) {        switch (status) {        case SC_STATUS_WAIT:                INFO("SC_STATUS_WAIT
");                break;        case SC_STATUS_FIND_CHANNEL:                INFO("SC_STATUS_FIND_CHANNEL
");                break;        case SC_STATUS_GETTING_SSID_PSWD:                INFO("SC_STATUS_GETTING_SSID_PSWD
");                sc_type *type = pdata;                if (*type == SC_TYPE_ESPTOUCH) {                        INFO("SC_TYPE:SC_TYPE_ESPTOUCH
");                } else {                        INFO("SC_TYPE:SC_TYPE_AIRKISS
");                }                break;        case SC_STATUS_LINK:                INFO("SC_STATUS_LINK
");                sm_comfig_status = SM_STATUS_GETINFO;                struct station_config *sta_conf = pdata;                wifi_station_set_config(sta_conf);                wifi_station_disconnect();                wifi_station_connect();                break;        case SC_STATUS_LINK_OVER:                sm_comfig_status = SM_STATUS_FINISH;                INFO("SC_STATUS_LINK_OVER
");                if (pdata != NULL) {                        //SC_TYPE_ESPTOUCH                        uint8 phone_ip[4] = { 0 };                        os_memcpy(phone_ip, (uint8*) pdata, 4);                        INFO("Phone ip: %d.%d.%d.%d
", phone_ip[0], phone_ip[1],                                        phone_ip[2], phone_ip[3]);                } else {                        //SC_TYPE_AIRKISS - support airkiss v2.0                        airkiss_start_discover();                }                smartconfig_stop();                smartconfig_flag = 0;                connect_flag = 0;                os_timer_disarm(&OS_Timer_SM);        // 关闭定时器                finish_cd(sm_comfig_status);                os_timer_arm(&OS_Timer_Wifichange, 3000, 1);  // 使能定时器                break;        }
}
/***         WIFI连接回调*/void wifi_connect_cb(void){
        INFO("wifi connect!
");        os_printf("----- WiFi连接成功,打开绿灯---
");        GPIO_OUTPUT_SET(GPIO_ID_PIN(12), 1);        MQTT_Connect(&mqttClient);}
/***         WIFI断开回调*/void wifi_disconnect_cb(void){        INFO("wifi disconnect!
");        os_printf("----- WiFi断开,关闭绿灯---
");        GPIO_OUTPUT_SET(GPIO_ID_PIN(12), 0);        MQTT_Disconnect(&mqttClient);}

连接MQTT服务器:

网络连接成功以后可以开始MQTT的初始化,初始化包涵一系列的连接初始化回调,连接成功或不成功回调,主题订阅发布回调等等。

/***         MQTT初始化*/void ICACHE_FLASH_ATTR mqtt_init(void) {
        MQTT_InitConnection(&mqttClient, MQTT_HOST, MQTT_PORT, DEFAULT_SECURITY);        MQTT_InitClient(&mqttClient, mac_str, MQTT_USER,MQTT_PASS, MQTT_KEEPALIVE, 1);        MQTT_InitLWT(&mqttClient, lwt_topic, LWT_MESSAGE, 0, 0);        MQTT_OnConnected(&mqttClient, mqttConnectedCb);        MQTT_OnDisconnected(&mqttClient, mqttDisconnectedCb);        MQTT_OnPublished(&mqttClient, mqttPublishedCb);        MQTT_OnData(&mqttClient, mqttDataCb);}
void ICACHE_FLASH_ATTRMQTT_InitConnection(MQTT_Client *mqttClient, uint8_t* host, uint32_t port, uint8_t security){        uint32_t temp;        INFO("MQTT_InitConnection
");        os_memset(mqttClient, 0, sizeof(MQTT_Client));        temp = os_strlen(host);        mqttClient->host = (uint8_t*)os_zalloc(temp + 1);        os_strcpy(mqttClient->host, host);        mqttClient->host[temp] = 0;        mqttClient->port = port;        mqttClient->security = security;
}
void ICACHE_FLASH_ATTRMQTT_InitClient(MQTT_Client *mqttClient, uint8_t* client_id, uint8_t* client_user, uint8_t* client_pass, uint32_t keepAliveTime, uint8_t cleanSession){        uint32_t temp;        INFO("MQTT_InitClient
");        os_printf("CD MQTT_InitClient++++++++++++++++++++++
");        os_memset(&mqttClient->connect_info, 0, sizeof(mqtt_connect_info_t));
        temp = os_strlen(client_id);        mqttClient->connect_info.client_id = (uint8_t*)os_zalloc(temp + 1);        os_strcpy(mqttClient->connect_info.client_id, client_id);        mqttClient->connect_info.client_id[temp] = 0;
        if (client_user)        {                temp = os_strlen(client_user);                mqttClient->connect_info.username = (uint8_t*)os_zalloc(temp + 1);                os_strcpy(mqttClient->connect_info.username, client_user);                mqttClient->connect_info.username[temp] = 0;        }
        if (client_pass)        {                temp = os_strlen(client_pass);                mqttClient->connect_info.password = (uint8_t*)os_zalloc(temp + 1);                os_strcpy(mqttClient->connect_info.password, client_pass);                mqttClient->connect_info.password[temp] = 0;        }

        mqttClient->connect_info.keepalive = keepAliveTime;        mqttClient->connect_info.clean_session = cleanSession;
        mqttClient->mqtt_state.in_buffer = (uint8_t *)os_zalloc(MQTT_BUF_SIZE);        mqttClient->mqtt_state.in_buffer_length = MQTT_BUF_SIZE;        mqttClient->mqtt_state.out_buffer =  (uint8_t *)os_zalloc(MQTT_BUF_SIZE);        mqttClient->mqtt_state.out_buffer_length = MQTT_BUF_SIZE;        mqttClient->mqtt_state.connect_info = &mqttClient->connect_info;
        mqtt_msg_init(&mqttClient->mqtt_state.mqtt_connection, mqttClient->mqtt_state.out_buffer, mqttClient->mqtt_state.out_buffer_length);
        QUEUE_Init(&mqttClient->msgQueue, QUEUE_BUFFER_SIZE);
        system_os_task(MQTT_Task, MQTT_TASK_PRIO, mqtt_procTaskQueue, MQTT_TASK_QUEUE_SIZE);        system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)mqttClient);}

WiFi连接成功和失败会触发不同的回调函数:

/***         MQTT连接回调*/void mqttConnectedCb(uint32_t *args) {        MQTT_Client* client = (MQTT_Client*) args;
        INFO("MQTT: Connected
");        MQTT_Publish(client, birth_topic, BIRTH_MESSAGE, os_strlen(BIRTH_MESSAGE), 0,0);        MQTT_Subscribe(client,ota_topic, 0);        if(updata_status_check()){                MQTT_Publish(client, ota_topic, "updata_finish", os_strlen("updata_finish"), 0,0);        }}/**
*         MQTT断开连接回调*/void mqttDisconnectedCb(uint32_t *args) {        MQTT_Client* client = (MQTT_Client*) args;        INFO("MQTT: Disconnected
");}
/***         MQTT发布消息回调*/void mqttPublishedCb(uint32_t *args) {        MQTT_Client* client = (MQTT_Client*) args;        INFO("MQTT: Published
");}
串口透传:
当模块的WiFi和MQTT服务器都连接上之后,模块就开始监听串口和服务器的数据,如果串口有数据过来便转发到服务器或者进行OTA升级,如果服务器有指令下发就转发给串口。

/***         MQTT接收数据回调(用于OTA升级和串口透传)*/void mqttDataCb(uint32_t *args, const char* topic, uint32_t topic_len,                const char *data, uint32_t data_len) {        char *topicBuf = (char*) os_zalloc(topic_len + 1), *dataBuf =                        (char*) os_zalloc(data_len + 1);
        uint8 *pdata = (uint8*)data;        uint16 len = data_len;        uart0_tx_buffer(pdata, len);//串口输出
        MQTT_Client* client = (MQTT_Client*) args;
        os_memcpy(topicBuf, topic, topic_len);        topicBuf[topic_len] = 0;
        os_memcpy(dataBuf, data, data_len);        dataBuf[data_len] = 0;
//        INFO("Receive topic: %s, data: %s 
", topicBuf, dataBuf);
        //data = {"url"="http://yourdomain.com:9001/ota/"}        if (os_strcmp(topicBuf, ota_topic) == 0) {                char url_data[200];                if(get_josn_str(dataBuf,"url",url_data)){//            INFO("ota_start
");            ota_upgrade(url_data,ota_finished_callback);                }        }
        os_free(topicBuf);        os_free(dataBuf);


}
/***         ota升级回调*/void ICACHE_FLASH_ATTR ota_finished_callback(void * arg) {        struct upgrade_server_info *update = arg;        if (update->upgrade_flag == true) {                INFO("OTA  Success ! rebooting!
");                system_upgrade_reboot();        } else {                INFO("OTA Failed!
");        }}
其他问题:连接的服务器地址,端口号等信息需要写在代码里烧录进模块,这些信息在在mqtt_config.h文件中定义。

95c8a590-5f65-11eb-8b86-12bb97331649.png


上电后可以在串口助手看到打印的MAC地址:

96055684-5f65-11eb-8b86-12bb97331649.png


按下配网按键(GPIO14接地),进入配网模式,使用APP或者微信公众号将信息发给模块便可联网,联网后自动连接MQTT服务器。

9631973a-5f65-11eb-8b86-12bb97331649.png

96d612b0-5f65-11eb-8b86-12bb97331649.png

至此连接完成,后续只需要串口发数据给模块,便可在服务器收到信息,服务器下发指令,单片机串口也可以接收到数据。但是要记得订阅主题哦。该透传代码烧录完成可搭配任意MCU的串口使用。非常便捷。由于项目期较远,可能介绍的不是很详细,需要的大大们可以点击阅读原文回帖获取源码。自行查看。

责任编辑:xj

原文标题:什么?单片机还在裸奔?ESP8266纯串口透传,助力设备上云端

文章出处:【微信公众号:嵌入式ARM】欢迎添加关注!文章转载请注明出处。


声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 单片机
    +关注

    关注

    6074

    文章

    45341

    浏览量

    663594
  • 云端
    +关注

    关注

    0

    文章

    126

    浏览量

    17522
  • ESP8266
    +关注

    关注

    51

    文章

    966

    浏览量

    49012

原文标题:什么?单片机还在裸奔?ESP8266纯串口透传,助力设备上云端

文章出处:【微信号:gh_c472c2199c88,微信公众号:嵌入式微处理器】欢迎添加关注!文章转载请注明出处。

收藏 人收藏
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

    评论

    相关推荐
    热点推荐

    网络串口芯片CH9121方案

    在工业控制、物联网设备等场景中,大量传统设备仅具备串口通信能力,难以融入现代网络系统。英尚微电子基于此推出一款名为CH9121的网络串口
    的头像 发表于 12-04 15:47 289次阅读

    晶科鑫 | 国产26MHz晶振匹配Espressif(乐鑫) ESP8285/ESP8266芯片案例

    【应用】国产26MHz频率晶振应用于物联网WIFI物联网模块(串口转WiFi模块),Espressif(乐鑫)ESP8285/ESP8266芯片匹配测试OKESP8285其实是ESP8266
    的头像 发表于 11-21 15:37 4053次阅读
    晶科鑫 | 国产26MHz晶振匹配Espressif(乐鑫) <b class='flag-5'>ESP</b>8285/<b class='flag-5'>ESP8266</b>芯片案例

    ESP8266ESP32开发板常见的2种下载方式

    ESP32和ESP8266是性价比很高而且使用较为广泛的的Wi-Fi模块,在物联网(IoT)领域的DIY项目中可以首先考虑这2款产品。 两款芯片都属于32位处理器。ESP32是低功耗双核80MHz
    的头像 发表于 11-10 20:30 335次阅读
    <b class='flag-5'>ESP8266</b>和<b class='flag-5'>ESP</b>32开发板常见的2种下载方式

    ESP8266ESP32开发板常见的2种下载方式

    协议,完整的 TCP/IP 协议栈以及蓝牙协议栈。用户可以使用这两款模块为现有的设备添加WIFI联网功能或蓝牙(ESP32),轻松的构建独立的网络控制器和远程控制监控设备ESP8266
    发表于 10-24 18:04

    请问一下我想用ESP8685直接替换ESP8266而不用写程序,可以让ESP8685直接通过之前ESP8266的外挂的flash运行程序吗

    请问一下我想用ESP8685直接替换ESP8266而不用写程序,可以让ESP8685直接通过之前ESP8266的外挂的flash运行程序吗
    发表于 09-17 12:06

    ESP8266】进入模式后,数据间隔怎么调整,

    01.TCP客户端用ESP8266模式发送数据是10ms发送一次,(连续发送) 02.TCP服务器接收到的数据是200ms左右接收一次,数据未丢失,但是更新不及时; 03.通过TCP调试助手已
    发表于 09-15 10:16

    【RA-Eco-RA6M4开发板评测】——4.使用ESP8266获取任意城市的天意预报

    软件 1.取名RA6M4_ESP 2。选择芯片 3。继续 4。finish完成 5。设置通信串口 首先P110和P109用来打印调试 配置好参数 ESP8266选择P100和P101 6。生成代码
    发表于 07-23 19:50

    STM32+esp8266连接机智云,上传温湿度数据并控制继电器开关(平台配置、代码生成、代码移植)

    代码,并通过手机APP进行设备控制。元器件准备在开始之前,您需要准备以下硬件元器件:1.STM32开发板2.ESP8266Wi-Fi模块3.温湿度传感器(如DHT1
    的头像 发表于 07-15 18:54 748次阅读
    STM32+<b class='flag-5'>esp8266</b>连接机智云,上传温湿度数据并控制继电器开关(平台配置、代码生成、代码移植)

    ESP32用作经典蓝牙串口模块与手机进行串口通信

    本文介绍了如何把ESP32用作一个蓝牙串口设备使用,其功能和常用的HC-05/06串口蓝牙
    的头像 发表于 06-20 17:45 2106次阅读
    <b class='flag-5'>ESP</b>32用作经典蓝牙<b class='flag-5'>串口</b><b class='flag-5'>透</b><b class='flag-5'>传</b>模块与手机进行<b class='flag-5'>串口</b>通信

    单片机实例项目:ESP8266串口WIFI

    单片机实例项目:ESP8266串口WIFI,推荐下载!
    发表于 06-03 21:01

    ESP8266烧录与机智云一键配网教程

    ESP8266模块因其低成本和高性能,广泛应用于智能家居和工业自动化等领域。机智云平台提供了一键配网功能,简化了设备的联网过程。本教程将详细介绍如何使用ESP8266进行烧录,并通过机智云实现一键配
    的头像 发表于 05-24 11:04 1561次阅读
    <b class='flag-5'>ESP8266</b>烧录与机智云一键配网教程

    【RA-Eco-RA4M2开发板评测】RA4M2 + ESP8266获取城市天气预报(二)使用串口助手获取天气+破解任意类型AT指令

    :就是用串口来进行,把串口助手上发送的AT指令通过调试串口接收来发送给ESP8266模块的
    发表于 05-04 10:07

    【RA-Eco-RA4M2开发板评测】RA4M2 + ESP8266获取城市天气预报(一)

    很高兴收到瑞萨电子的RA4M2开发板,有幸成为了幸运观众,本期就来使用瑞萨的RA4M2开发板通过串口连接WIFI模块ESP8266,获取天气预报数据信息。并实时显示出来。它可以获取国内任何城市
    发表于 05-02 14:28

    零知开源——ESP8266+MPU6050 实现运动姿态检测

    零知ESP8266的I2C通信        在运动姿态检测、机器人平衡控制、VR头戴设备等应用中,MPU6050(三轴加速度计+三轴陀螺仪)是一个常见的姿态传感器。而ESP8266作为一款低功耗
    的头像 发表于 02-20 17:54 2807次阅读
    零知开源——<b class='flag-5'>ESP8266</b>+MPU6050 实现运动姿态检测

    CW32模块使用 ESP01S WIFI模块

    为现有的设备添加联网功能,也可以构建独立的网络控制器。即使在不了解其原理的情况下,只要有一定的串口知识理解,那么就可以轻松上手该模块,进而实现手机WiFi操作。功能特点:基于ESP8266芯片开发,模组集成了
    的头像 发表于 01-23 13:08 1176次阅读
    CW32模块使用 <b class='flag-5'>ESP</b>01S WIFI模块