当前位置:首页 > 公众号精选 > 嵌入式微处理器
[导读]项目有些久远,前年的机器人上需要的功能,当时是需要将STM32上的数据上传到服务器,比如机器人的速度,行驶距离,是否在拍照等等。


项目有些久远,前年的机器人上需要的功能,当时是需要将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);//低电平  g
et_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服务器时会自动填充。


每连接一次WiFi都会将WiFi信息保存在模块内部,每次上电都会自动扫描暴露的WiFi,直接连接,就像手机的WIFI连接,目前最大支持五个WiFi信息的保存,超过5个会剔除最早的WiFi信息,通过短按D5(GPIO14)可进入配网模式。

/*** 按键短按回调*/
LOCAL void ICACHE_FLASH_ATTR key1ShortPress(void) {  
start_smartconfig(smartconfig_cd); 
INFO("start_smartconfig\n");
}
/*** 按键长按回调*/
LOCAL void ICACHE_FLASH_ATTR key1LongPress(void) {  
start_smartconfig(smartconfig_cd); 
INFO("start_smartconfig\n");
}
/*** 按键初始化*/
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灯。
/*** 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\n"); 
break; 
case SC_STATUS_FIND_CHANNEL: 
INFO("SC_STATUS_FIND_CHANNEL\n"); 
break; 
case SC_STATUS_GETTING_SSID_PSWD: 
INFO("SC_STATUS_GETTING_SSID_PSWD\n"); 
sc_type *type = pdata; 
if (*type == SC_TYPE_ESPTOUCH) { 
INFO("SC_TYPE:SC_TYPE_ESPTOUCH\n"); 
} else { 
INFO("SC_TYPE:SC_TYPE_AIRKISS\n"); 
} 
break; 
case SC_STATUS_LINK: 
INFO("SC_STATUS_LINK\n"); 
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\n"); 
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\n", 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!\r\n"); 
os_printf("----- WiFi连接成功,打开绿灯---\r\n"); 
GPIO_OUTPUT_SET(GPIO_ID_PIN(12), 1); 
MQTT_Connect(&mqttClient);
} 
/*** WIFI断开回调*/
void wifi_disconnect_cb(void){ 
INFO("wifi disconnect!\r\n"); 
os_printf("----- WiFi断开,关闭绿灯---\r\n"); 
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\r\n"); 
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\r\n"); 
os_printf("CD MQTT_InitClient++++++++++++++++++++++\n"); 
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\r\n"); 
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\r\n");
} 
/*** MQTT发布消息回调*/
void mqttPublishedCb(uint32_t *args) { 
MQTT_Client* client = (MQTT_Client*) args; 
INFO("MQTT: Published\r\n");
}

串口透传:
当模块的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 \r\n", 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\n"); 
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!\n"); 
system_upgrade_reboot(); 
} else { 
INFO("OTA Failed!\n"); 
}
}


其他问题: 连接的服务器地址,端口号等信息需要写在代码里烧录进模块,这些信息在在mqtt_config.h文件中定义。



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


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


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

免责声明:本文内容由21ic获得授权后发布,版权归原作者所有,本平台仅提供信息存储服务。文章仅代表作者个人观点,不代表本平台立场,如有问题,请联系我们,谢谢!

嵌入式ARM

扫描二维码,关注更多精彩内容

本站声明: 本文章由作者或相关机构授权发布,目的在于传递更多信息,并不代表本站赞同其观点,本站亦不保证或承诺内容真实性等。需要转载请联系该专栏作者,如若文章内容侵犯您的权益,请及时联系本站删除。
换一批
延伸阅读

9月2日消息,不造车的华为或将催生出更大的独角兽公司,随着阿维塔和赛力斯的入局,华为引望愈发显得引人瞩目。

关键字: 阿维塔 塞力斯 华为

加利福尼亚州圣克拉拉县2024年8月30日 /美通社/ -- 数字化转型技术解决方案公司Trianz今天宣布,该公司与Amazon Web Services (AWS)签订了...

关键字: AWS AN BSP 数字化

伦敦2024年8月29日 /美通社/ -- 英国汽车技术公司SODA.Auto推出其旗舰产品SODA V,这是全球首款涵盖汽车工程师从创意到认证的所有需求的工具,可用于创建软件定义汽车。 SODA V工具的开发耗时1.5...

关键字: 汽车 人工智能 智能驱动 BSP

北京2024年8月28日 /美通社/ -- 越来越多用户希望企业业务能7×24不间断运行,同时企业却面临越来越多业务中断的风险,如企业系统复杂性的增加,频繁的功能更新和发布等。如何确保业务连续性,提升韧性,成...

关键字: 亚马逊 解密 控制平面 BSP

8月30日消息,据媒体报道,腾讯和网易近期正在缩减他们对日本游戏市场的投资。

关键字: 腾讯 编码器 CPU

8月28日消息,今天上午,2024中国国际大数据产业博览会开幕式在贵阳举行,华为董事、质量流程IT总裁陶景文发表了演讲。

关键字: 华为 12nm EDA 半导体

8月28日消息,在2024中国国际大数据产业博览会上,华为常务董事、华为云CEO张平安发表演讲称,数字世界的话语权最终是由生态的繁荣决定的。

关键字: 华为 12nm 手机 卫星通信

要点: 有效应对环境变化,经营业绩稳中有升 落实提质增效举措,毛利润率延续升势 战略布局成效显著,战新业务引领增长 以科技创新为引领,提升企业核心竞争力 坚持高质量发展策略,塑强核心竞争优势...

关键字: 通信 BSP 电信运营商 数字经济

北京2024年8月27日 /美通社/ -- 8月21日,由中央广播电视总台与中国电影电视技术学会联合牵头组建的NVI技术创新联盟在BIRTV2024超高清全产业链发展研讨会上宣布正式成立。 活动现场 NVI技术创新联...

关键字: VI 传输协议 音频 BSP

北京2024年8月27日 /美通社/ -- 在8月23日举办的2024年长三角生态绿色一体化发展示范区联合招商会上,软通动力信息技术(集团)股份有限公司(以下简称"软通动力")与长三角投资(上海)有限...

关键字: BSP 信息技术
关闭
关闭