项目有些久远,前年的机器人上需要的功能,当时是需要将STM32上的数据上传到服务器,比如机器人的速度,行驶距离,是否在拍照等等。便于管理者在PC或者手机上了解机器人的工作状态,同时可以远程下发指令给机器人,控制其完成相应动作。
因为所有的逻辑判断和控制都在服务器或者STM32上面,作为中间的无线模块仅仅需要上传STM32的数据并接收服务器下发的指令即可,所以这里对WiFi模块的要求不高,仅仅需要它作为透传功能即可。当时在选型的时候试过好几款WiFi模块,最终敲定了安信可的ESP8266,价格便宜,开发简单,但是搭建环境是真的不容易,深受其害。
选择好模块就该考虑使用AT指令还是使用SDK开发,AT指令固然简单,但是局限性非常大。如果使用AT指令,我那开发控制端的同事估计就要跳脚了,代码里需要写一大堆的AT指令,如果功能改变,指令代码就需要重写,烦不胜烦。
如果使用SDK开发,控制端只需发送简单的数据就行,完全不用考虑其他任何东西,ESP8266完全当做一个中转站,相对应的我的工作就会繁重,但是,我屈服了,选择使用SDK。
于是就有了下面基于NONOS 2.0的ESP8266串口透传。主要有以下几个功能:
初始化完成后会首先读取MAC地址,该地址是唯一的,每个模块都不一样,用于填充进主题中,便于服务器区分不同设备,用于多台量产设备的使用,在连接MQTT服务器时会自动填充。
- 纯串口透传,接收MCU串口数据,直接通过MQTT上传到服务器,接收服务器数据下发给MCU。
- smartconfig+airkiss配网,随意使用,场景丰富。
- 最多储存5个WIFI账号和密码,自动寻找网络连接。
- 按键配网,长按重新配网,前一次WiFi自动储存,添加配网指示灯。
- OTA空中升级(待验证)
程序的入口先进行串口初始化和按键的初始化,以及LED的初始化。串口要初始化波特率,按键初始化配网按键,用于短按配网,长按重新配网,LED只要用于判断模块是否进入配网模式以及是否配网完成。//程序入口
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);
}
初始化完成后会首先读取MAC地址,该地址是唯一的,每个模块都不一样,用于填充进主题中,便于服务器区分不同设备,用于多台量产设备的使用,在连接MQTT服务器时会自动填充。
每连接一次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); // 使能定时器
}
smartconfig_set_type();函数可选3个参数:分别是:SC_TYPE_ESPTOUCH、SC_TYPE_AIRKISS和SC_TYPE_ESPTOUCH_AIRKISS
第一个是smartconfig配网(手机APP),第二个是airkiss配网(微信公众号),最后一个两者都可以。进入该函数会调用smartconfig_start();,该函数会调用smartconfig_done()函数进行配网,配网成功后会点亮LED灯。
网络连接成功以后可以开始MQTT的初始化,初始化包涵一系列的连接初始化回调,连接成功或不成功回调,主题订阅发布回调等等。
当模块的WiFi和MQTT服务器都连接上之后,模块就开始监听串口和服务器的数据,如果串口有数据过来便转发到服务器或者进行OTA升级,如果服务器有指令下发就转发给串口。
第一个是smartconfig配网(手机APP),第二个是airkiss配网(微信公众号),最后一个两者都可以。进入该函数会调用smartconfig_start();,该函数会调用smartconfig_done()函数进行配网,配网成功后会点亮LED灯。
/**
* Smartconfig 状态处理
* @param status: 状态
* @param *pdata: AP数据
* @retval None
*/
void ICACHE_FLASH_ATTR
smartconfig_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_ATTR
MQTT_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_ATTR
MQTT_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_config.h文件中定义。/**
* 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! ");
}
}
上电后可以在串口助手看到打印的MAC地址:
按下配网按键(GPIO14接地),进入配网模式,使用APP或者微信公众号将信息发给模块便可联网,联网后自动连接MQTT服务器。
至此连接完成,后续只需要串口发数据给模块,便可在服务器收到信息,服务器下发指令,单片机串口也可以接收到数据。但是要记得订阅主题哦。该透传代码烧录完成可搭配任意MCU的串口使用。非常便捷。由于项目期较远,可能介绍的不是很详细,需要的大大们可以点击阅读原文回帖获取源码。自行查看。
责任编辑:xj
原文标题:什么?单片机还在裸奔?ESP8266纯串口透传,助力设备上云端
文章出处:【微信公众号:嵌入式ARM】欢迎添加关注!文章转载请注明出处。
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。
举报投诉
-
单片机
+关注
关注
6032文章
44514浏览量
632927 -
云端
+关注
关注
0文章
118浏览量
16845 -
ESP8266
+关注
关注
50文章
962浏览量
44825
原文标题:什么?单片机还在裸奔?ESP8266纯串口透传,助力设备上云端
文章出处:【微信号:gh_c472c2199c88,微信公众号:嵌入式微处理器】欢迎添加关注!文章转载请注明出处。
发布评论请先 登录
相关推荐
esp32和esp8266代码共用吗
本文将介绍ESP32和ESP8266两款流行的微控制器在代码共用性方面的可能性与差异性。 一、引言 随着物联网技术的飞速发展,越来越多的智能设备开始进入我们的生活。其中,ESP32和
esp8266和esp32区别是什么
以下是关于ESP8266和ESP32的主要区别: 处理器和架构 : ESP8266 :使用一个Tensilica L106 80MHz的处理器,属于Xtensa架构。 ESP32 :使
esp8266不烧录可以使用吗
,可以方便地与其他硬件设备进行连接。 2. ESP8266的硬件特性 处理器 :Tensilica L106,最高频率160MHz 内存 :64KB SRAM,1MB Flash Wi-Fi :支持802.11 b/g/n协议,最高速
esp8266wifi模块怎么连接手机
ESP8266 : 使用USB转TTL模块将ESP8266模块与电脑连接。 通过串口调试助手发送AT指令配置ESP8266,包括设置WiFi模式为STA模式(客户端模式),连接指定的W
ESP-WROOM-02云端设备控制失败是什么原因?
我使用的是ESP-WROOM-02的芯片,然后烧录了ESP8266_RTOS_SDK_V1.4.0使用的是ESP8266_IOT_PLATFORM这个DEMO,参照文档修改了里面的参数,下载了IOT
发表于 07-15 08:06
ESP8266透传模式是如何工作的?
,使用这个理论,如果我每 4 毫秒向 esp8266 发送一个值(7 字节),那么每 20 毫秒应该发送 35 个字节,如果我每 20 毫秒对 android 接收缓冲区进行采样,那么我应该在这个缓冲区上找到 35 个字节。这时透
发表于 07-15 07:48
ESP8266如果要连接云端,需要把相关的key加入APK编译吗?
如果要连接云端,需要把相关的key加入APK编译吗?利如owner key
我用的是ESP8266_NONOS_SDK,手机APK用的是1.2版本编译出来的,连接云端成功,但是点击进去会出现获取
发表于 07-15 07:03
确定ESP8266固件下载成功的方法
在物联网设备的开发过程中,确定esp8266固件是否成功下载至设备十分关键。以下是一种简单有效的确认方法:机智云物联网平台首先,确保你的ESP8266模块已经正确连接至计算机,并通过
使用Wi-Fi ESP8266方案模组接入云平台
ESP8266的模块芯片是基于无线通信协议的UARTWi-Fi透传模块芯片,支持802.11b/g/n的无线标准,并带有三种可选择的工作模式。ESP8266模块的控制是通过AT指令的形
STM32、ESP8266与MQTT连接阿里云物联网的串口通信异常解析
STM32、ESP8266与MQTT协议连接阿里云物联网平台时常见的串口通信异常介绍 在构建物联网应用时,STM32、ESP8266与MQTT协议的结合是实现设备与网络间稳定通信的关键
评论