29-ESP8266 SDK开发基础入门篇--编写TCP 客户端程序(Lwip RAW模式,非RTOS版,精简入门)
https://www.cnblogs.com/yangfengwu/p/11456667.html
由于上一节的源码长时间以后会自动断开,所以再做这一版非RTOS版的,咱直接用lua源码里面别人写的(站在巨人的肩膀上)
咱再细细的看下lua怎么实现的源码
首先确定lua都调用了什么函数
https://www.cnblogs.com/yangfengwu/p/7533302.html lua写的TCP 客户端
用到的部分程序
Client = net.createConnection(net.TCP, 0)
Client:connect(8080,"192.168.1.103") Client:on("receive", function(Client, data) uart.write(0,data) end) Client:on("connection", function(sck, c) ClientConnectedFlage = 1 TcpConnect = Client print("Link OK") tmr.stop(1) Client:on("disconnection", function(sck, c) ClientConnectedFlage = 0 TcpConnect = nil tmr.start(1) end) end)
https://nodemcu.readthedocs.io/en/master/modules/net/ 用到的函数都在net里面
注意:lua源码里面为了使用户操作起来便捷,所以封装的很灵活(代码更复杂些,咱只提取咱用到的部分)
源码实际处理部分
就是返回的那个结构体变量,后面的话肯定就是调用结构体变量里面的成员变量
大家知道lwip提供了两种实现网络通信的方式
第一种是 :RAW模式 就是不需要操作系统参与,所有的都是靠回调函数实现
第二种是:跑任务的
其实lua源码里面是使用的RAW模式的
大家可以自行百度LWIP RAW实现TCP客户端,然后呢,大家大可不必深究lua的每一句代码了,现在应该提炼人家实现的思想,把人家提供的源码当做自己的参考
烂程序员关心的是代码,好程序员关心的是数据结构和它们之间的关系!
实际上实现连接很简单
#include "esp_common.h" #include "gpio.h" #include "uart.h" #include "esp_timer.h" #include "hw_timer.h" #include "pwm.h" #include "data_dispose.h" #include "espconn.h" #include "esp_wifi.h" #include "lwip/api.h" #include "crc.h" #include "smart_config.h" #include "lwip/err.h" #include "lwip/ip_addr.h" #include "lwip/dns.h" #include "lwip/igmp.h" #include "lwip/tcp.h" LOCAL os_timer_t public_timer;//定时器 u32 public_timer_cnt=0;//累加 u32 public_timer_state=0;//状态 u32 public_timer_out=0;//超时 u32 public_timer_cnt1=0;//累加 extern u8 Usart1ReadBuff[Usart1ReadLen];//接收数据的数组 extern u32 Usart1ReadCnt;//串口1接收到的数据个数 extern u32 Usart1ReadCntCopy;//串口1接收到的数据个数拷贝 extern u8 Usart1ReadFlage;//串口1接收到一条完整数据 #define SSID "Learn8266" //无线名称 #define PWD "11223344" //密码 struct softap_config soft_ap_Config;//AP模式配置 //连接的路由器信息 #define APssid "qqqqq" #define APpassword "11223344" struct station_config stationConf; //TCP客户端 struct tcp_pcb *TcpClient; ip_addr_t addr;//创建连接的服务器地址用 ResolveData ResolveDataTest;//解析数据IEEE754 //uint32 pin_info_list[1][3]={PERIPHS_IO_MUX_GPIO5_U,FUNC_GPIO5,5};//配置GPIO5作为PWM输出 //int duty[1]={0};//高电平时间是0us /****************************************************************************** * FunctionName : user_rf_cal_sector_set * Description : SDK just reversed 4 sectors, used for rf init data and paramters. * We add this function to force users to set rf cal sector, since * we don't know which sector is free in user's application. * sector map for last several sectors : ABCCC * A : rf cal * B : rf init data * C : sdk parameters * Parameters : none * Returns : rf cal sector *******************************************************************************/ uint32 user_rf_cal_sector_set(void) { flash_size_map size_map = system_get_flash_size_map(); uint32 rf_cal_sec = 0; switch (size_map) { case FLASH_SIZE_4M_MAP_256_256: rf_cal_sec = 128 - 5; break; case FLASH_SIZE_8M_MAP_512_512: rf_cal_sec = 256 - 5; break; case FLASH_SIZE_16M_MAP_512_512: case FLASH_SIZE_16M_MAP_1024_1024: rf_cal_sec = 512 - 5; break; case FLASH_SIZE_32M_MAP_512_512: case FLASH_SIZE_32M_MAP_1024_1024: rf_cal_sec = 1024 - 5; break; default: rf_cal_sec = 0; break; } return rf_cal_sec; } //串口调用此函数就说明接收到了一条完整的数据,就可以去处理了 void UartReadCallback()//定义一个函数 { } //网络接收到数据 void TcpClientRecv(void *arg, char *pdata, unsigned short len) { while(len--) { uart0_write_char(*(pdata++));//发送到串口 } } //断开了连接 void TcpClientDisCon(void *arg) { dbg_printf("\nTcpClientDisCon\n"); } //连接上服务器 err_t TcpConnected(void *arg, struct tcp_pcb *tpcb, err_t err) { if (TcpClient != tpcb || err != ERR_OK){ TcpClient = NULL; dbg_printf("\nConnection aborted\n"); } dbg_printf("\nTcpConnected\n"); return ERR_OK; } //连接出错 void TcpConnectErr(void *arg, sint8 err) { dbg_printf("\nTcpConnectErr=%d\n",err); } //监听WiFi状态改变 void wifi_event_monitor_handle_event_cb(System_Event_t *evt) { switch (evt->event_id) { case EVENT_STAMODE_CONNECTED://连接上路由器 dbg_printf("\n\tSTAMODE_CONNECTED\n"); dbg_printf("\tConnected to SSID %s, Channel %d\n", evt->event_info.connected.ssid, evt->event_info.connected.channel); break; case EVENT_STAMODE_DISCONNECTED://和路由器断开 dbg_printf("\n\tSTAMODE_DISCONNECTED\n"); dbg_printf("\tDisconnect from SSID %s, reason %d\n", evt->event_info.disconnected.ssid, evt->event_info.disconnected.reason); break; case EVENT_STAMODE_AUTHMODE_CHANGE://这个是 啥.. dbg_printf("\n\tSTAMODE_AUTHMODE_CHANGE\n"); dbg_printf("\tAuthmode: %u -> %u\n", evt->event_info.auth_change.old_mode, evt->event_info.auth_change.new_mode); break; case EVENT_STAMODE_GOT_IP://连接上路由器,并获取了IP dbg_printf("\n\tGOT_IP\n"); dbg_printf("\tIP:" IPSTR ",Mask:" IPSTR ",GW:" IPSTR "\n", IP2STR(&evt->event_info.got_ip.ip), IP2STR(&evt->event_info.got_ip.mask), IP2STR(&evt->event_info.got_ip.gw)); if(public_timer_state == 0)//正常运行下连接的路由器 { TcpClient = tcp_new();//创建TCP,咱需要这个 IP4_ADDR(&addr,192,168,1,93);//设置连接的IP地址 tcp_connect(TcpClient, &addr,60000, TcpConnected);//连接 } break; case EVENT_STAMODE_DHCP_TIMEOUT://连接上路由器,但是路由器给WIFI模块分配IP等信息超时了 dbg_printf("\n\tSTAMODE_DHCP_TIMEOUT\n"); break; case EVENT_SOFTAPMODE_STACONNECTED://AP模式下,有设备连接WIFI模块的无线 dbg_printf("\n\tSOFTAPMODE_STACONNECTED\n"); dbg_printf("\tStation: " MACSTR "join, AID = %d\n", MAC2STR(evt->event_info.sta_connected.mac), evt->event_info.sta_connected.aid); break; case EVENT_SOFTAPMODE_STADISCONNECTED://AP模式下,有设备断开和WIFI模块的无线连接 dbg_printf("\n\tSOFTAPMODE_STADISCONNECTED\n"); dbg_printf("\tstation: " MACSTR "leave, AID = %d\n", MAC2STR(evt->event_info.sta_disconnected.mac), evt->event_info.sta_disconnected.aid); break; case EVENT_SOFTAPMODE_PROBEREQRECVED://这是啥??,,,信号强度改变了 dbg_printf("\n\tSOFTAPMODE_PROBEREQRECVED\n"); dbg_printf("Station PROBEREQ: " MACSTR " RSSI = %d\n", MAC2STR(evt->event_info.ap_probereqrecved.mac), evt->event_info.ap_probereqrecved.rssi); break; default://其它错误 dbg_printf("\n\tswitch/case default\n"); break; } } //所有需要定时操作的函数在此函数中执行 LOCAL void ICACHE_FLASH_ATTR public_timer_callback(void) { if(GPIO_INPUT_GET(0) == 0)//按键按下 { public_timer_cnt++; if(public_timer_cnt>=300 && public_timer_state==0)//3S { dbg_printf("\nstartsmart\n"); public_timer_state=1; wifi_station_disconnect(); wifi_set_opmode(STATION_MODE); smartconfig_set_type(SC_TYPE_ESPTOUCH_AIRKISS);//SmartConfig + AirKiss xTaskCreate(smartconfig_task, "smartconfig_task", 256, NULL, 2, NULL); } } else { if(public_timer_state!=1 && public_timer_cnt>0 && public_timer_cnt<300)//短按复位 { dbg_printf("\nsystem_restart\n"); system_restart();//复位 } public_timer_cnt=0; } switch(public_timer_state) { case 0:break; case 1: public_timer_out++; public_timer_cnt1++; if(public_timer_out>=6000)//60S { dbg_printf("\nsmartconfig_timeout\n"); system_restart();//复位 } if(public_timer_cnt1>10)//LED快闪 { public_timer_cnt1=0; GPIO_OUTPUT_SET(2, 1-GPIO_INPUT_GET(2));//LED快闪 } break; default:break; } } /****************************************************************************** * FunctionName : user_init * Description : entry of user application, init user function here * Parameters : none * Returns : none *******************************************************************************/ void user_init(void) { GPIO_OUTPUT_SET(5, 1); GPIO_OUTPUT_SET(2, 0);//让两个灯初始的状态一样,GOIO2是反接的,0的时候是亮 GPIO_OUTPUT_SET(0, 1); uart_init_new(); printf("SDK version:%s\n", system_get_sdk_version()); wifi_set_opmode(STATIONAP_MODE);//配置WiFi的模式STATION + AP AP--连接WIFI自身的无线实现通信 STATION--wifi连接路由器,手机或者电脑也连接路由器,实现通信 soft_ap_Config.ssid_len = strlen(SSID);//热点名称长度,与你实际的名称长度一致就好 memcpy(soft_ap_Config.ssid,SSID,soft_ap_Config.ssid_len);//实际热点名称设置,可以根据你的需要来 memcpy(soft_ap_Config.password,PWD,strlen(PWD));//热点密码设置 soft_ap_Config.authmode = AUTH_WPA2_PSK;//加密模式 soft_ap_Config.channel = 1;//信道,共支持1~13个信道 soft_ap_Config.max_connection = 4;//最大连接数量,最大支持四个,默认四个 wifi_softap_set_config_current(&soft_ap_Config);//设置 Wi-Fi SoftAP 接口配置,不保存到 Flash // wifi_softap_set_config(&soft_ap_Config);//设置 Wi-Fi SoftAP 接口配置,保存到 Flash UartCallbackRegister(UartReadCallback);//把 UartReadCallback 函数地址传过去,在串口里面调用 os_timer_disarm(&public_timer); os_timer_setfn(&public_timer, (os_timer_func_t *)public_timer_callback, NULL); os_timer_arm(&public_timer, 10, 1);//10ms //设置连接的路由器,如果想直接测试不想配网,请去掉屏蔽 stationConf.bssid_set = 0; memcpy(stationConf.ssid,APssid,strlen(APssid));//路由器名称 memcpy(stationConf.password,APpassword,strlen(APpassword));//路由器密码 wifi_station_disconnect(); wifi_station_set_config(&stationConf); wifi_set_event_handler_cb(wifi_event_monitor_handle_event_cb);//监听WiFi状态改变 }
下载进去测试下
我先整体说完哈,最后说一下细节问题
现在呢加几个回调函数
lua里面封装了以后 ,所有的信息都记录在 ud那个结构体里面
tcp_arg()的第二个参数是ud
其实下面的每一个回调函数都有个参数,上面设置以后,传递给回调函数的参数就是ud了,lua做法是直接判断和控制ud的成员变量
可以随意看个回调
其实所有的链接信息都保存在ud里面,做这个程序的人需要 获取 和 操作 这个变量,所以需要把ud传给每一个回调函数.
咱呢不需要
所以
直接上源码吧!(实现的也是透传)
#include "esp_common.h" #include "gpio.h" #include "uart.h" #include "esp_timer.h" #include "hw_timer.h" #include "pwm.h" #include "data_dispose.h" #include "espconn.h" #include "esp_wifi.h" #include "lwip/api.h" #include "crc.h" #include "smart_config.h" #include "lwip/err.h" #include "lwip/ip_addr.h" #include "lwip/dns.h" #include "lwip/igmp.h" #include "lwip/tcp.h" LOCAL os_timer_t public_timer;//定时器 u32 public_timer_cnt=0;//累加 u32 public_timer_state=0;//状态 u32 public_timer_out=0;//超时 u32 public_timer_cnt1=0;//累加 extern u8 Usart1ReadBuff[Usart1ReadLen];//接收数据的数组 extern u32 Usart1ReadCnt;//串口1接收到的数据个数 extern u32 Usart1ReadCntCopy;//串口1接收到的数据个数拷贝 extern u8 Usart1ReadFlage;//串口1接收到一条完整数据 #define SSID "Learn8266" //无线名称 #define PWD "11223344" //密码 struct softap_config soft_ap_Config;//AP模式配置 //连接的路由器信息 #define APssid "qqqqq" #define APpassword "11223344" struct station_config stationConf; //TCP客户端 struct tcp_pcb *TcpClient; ip_addr_t addr;//创建连接的服务器地址用 #define TcpReadLen 2048 unsigned char TcpRead[TcpReadLen]={0};//接收数据缓存的数组,最大接收1024字节 ResolveData ResolveDataTest;//解析数据IEEE754 //uint32 pin_info_list[1][3]={PERIPHS_IO_MUX_GPIO5_U,FUNC_GPIO5,5};//配置GPIO5作为PWM输出 //int duty[1]={0};//高电平时间是0us /****************************************************************************** * FunctionName : user_rf_cal_sector_set * Description : SDK just reversed 4 sectors, used for rf init data and paramters. * We add this function to force users to set rf cal sector, since * we don't know which sector is free in user's application. * sector map for last several sectors : ABCCC * A : rf cal * B : rf init data * C : sdk parameters * Parameters : none * Returns : rf cal sector *******************************************************************************/ uint32 user_rf_cal_sector_set(void) { flash_size_map size_map = system_get_flash_size_map(); uint32 rf_cal_sec = 0; switch (size_map) { case FLASH_SIZE_4M_MAP_256_256: rf_cal_sec = 128 - 5; break; case FLASH_SIZE_8M_MAP_512_512: rf_cal_sec = 256 - 5; break; case FLASH_SIZE_16M_MAP_512_512: case FLASH_SIZE_16M_MAP_1024_1024: rf_cal_sec = 512 - 5; break; case FLASH_SIZE_32M_MAP_512_512: case FLASH_SIZE_32M_MAP_1024_1024: rf_cal_sec = 1024 - 5; break; default: rf_cal_sec = 0; break; } return rf_cal_sec; } //串口调用此函数就说明接收到了一条完整的数据,就可以去处理了 void UartReadCallback() { tcp_write(TcpClient, Usart1ReadBuff, Usart1ReadCntCopy, TCP_WRITE_FLAG_COPY);//存放到发送对列(默认延时一会组合起来所有数据再发送) tcp_output(TcpClient);//立即发送 } //网络接收到数据 err_t TcpClientRecv(void *arg, struct tcp_pcb *tpcb,struct pbuf *p, err_t err) { int len = p->len; int cnt = 0; tcp_recved(tpcb,len);//获取数据 if(len > TcpReadLen){//接收的数据大于了数组大小 len= TcpReadLen; } memcpy(TcpRead,p->payload,len);//拷贝数据到数组 while(len--) { uart0_write_char(TcpRead[cnt]);//串口输出 cnt++; } pbuf_free(p); return ERR_OK; } //连接上服务器 err_t TcpConnected(void *arg, struct tcp_pcb *tpcb, err_t err) { if (TcpClient != tpcb || err != ERR_OK){ TcpClient = NULL; dbg_printf("\nConnection aborted\n"); } dbg_printf("\nTcpConnected\n"); return ERR_OK; } //成功发送数据 err_t TcpClientSendOk(void *arg, struct tcp_pcb *tpcb,u16_t len) { return ERR_OK; } //客户端有错误 void TcpClientErr(void *arg, err_t err) { } //监听WiFi状态改变 void wifi_event_monitor_handle_event_cb(System_Event_t *evt) { switch (evt->event_id) { case EVENT_STAMODE_CONNECTED://连接上路由器 dbg_printf("\n\tSTAMODE_CONNECTED\n"); dbg_printf("\tConnected to SSID %s, Channel %d\n", evt->event_info.connected.ssid, evt->event_info.connected.channel); break; case EVENT_STAMODE_DISCONNECTED://和路由器断开 dbg_printf("\n\tSTAMODE_DISCONNECTED\n"); dbg_printf("\tDisconnect from SSID %s, reason %d\n", evt->event_info.disconnected.ssid, evt->event_info.disconnected.reason); break; case EVENT_STAMODE_AUTHMODE_CHANGE://这个是 啥.. dbg_printf("\n\tSTAMODE_AUTHMODE_CHANGE\n"); dbg_printf("\tAuthmode: %u -> %u\n", evt->event_info.auth_change.old_mode, evt->event_info.auth_change.new_mode); break; case EVENT_STAMODE_GOT_IP://连接上路由器,并获取了IP dbg_printf("\n\tGOT_IP\n"); dbg_printf("\tIP:" IPSTR ",Mask:" IPSTR ",GW:" IPSTR "\n", IP2STR(&evt->event_info.got_ip.ip), IP2STR(&evt->event_info.got_ip.mask), IP2STR(&evt->event_info.got_ip.gw)); if(public_timer_state == 0)//正常运行下连接的路由器 { TcpClient = tcp_new();//创建TCP,咱需要这个 IP4_ADDR(&addr,192,168,1,93);//设置连接的IP地址 tcp_err(TcpClient, TcpClientErr);//有错误回调函数 tcp_recv(TcpClient, TcpClientRecv);//接收到数据回调函数 tcp_sent(TcpClient, TcpClientSendOk);//成功发送了数据回调函数 tcp_connect(TcpClient, &addr,60000, TcpConnected);//连接 } break; case EVENT_STAMODE_DHCP_TIMEOUT://连接上路由器,但是路由器给WIFI模块分配IP等信息超时了 dbg_printf("\n\tSTAMODE_DHCP_TIMEOUT\n"); break; case EVENT_SOFTAPMODE_STACONNECTED://AP模式下,有设备连接WIFI模块的无线 dbg_printf("\n\tSOFTAPMODE_STACONNECTED\n"); dbg_printf("\tStation: " MACSTR "join, AID = %d\n", MAC2STR(evt->event_info.sta_connected.mac), evt->event_info.sta_connected.aid); break; case EVENT_SOFTAPMODE_STADISCONNECTED://AP模式下,有设备断开和WIFI模块的无线连接 dbg_printf("\n\tSOFTAPMODE_STADISCONNECTED\n"); dbg_printf("\tstation: " MACSTR "leave, AID = %d\n", MAC2STR(evt->event_info.sta_disconnected.mac), evt->event_info.sta_disconnected.aid); break; case EVENT_SOFTAPMODE_PROBEREQRECVED://这是啥??,,,信号强度改变了 dbg_printf("\n\tSOFTAPMODE_PROBEREQRECVED\n"); dbg_printf("Station PROBEREQ: " MACSTR " RSSI = %d\n", MAC2STR(evt->event_info.ap_probereqrecved.mac), evt->event_info.ap_probereqrecved.rssi); break; default://其它错误 dbg_printf("\n\tswitch/case default\n"); break; } } //所有需要定时操作的函数在此函数中执行 LOCAL void ICACHE_FLASH_ATTR public_timer_callback(void) { if(GPIO_INPUT_GET(0) == 0)//按键按下 { public_timer_cnt++; if(public_timer_cnt>=300 && public_timer_state==0)//3S { dbg_printf("\nstartsmart\n"); public_timer_state=1; wifi_station_disconnect(); wifi_set_opmode(STATION_MODE); smartconfig_set_type(SC_TYPE_ESPTOUCH_AIRKISS);//SmartConfig + AirKiss xTaskCreate(smartconfig_task, "smartconfig_task", 256, NULL, 2, NULL); } } else { if(public_timer_state!=1 && public_timer_cnt>0 && public_timer_cnt<300)//短按复位 { dbg_printf("\nsystem_restart\n"); system_restart();//复位 } public_timer_cnt=0; } switch(public_timer_state) { case 0:break; case 1: public_timer_out++; public_timer_cnt1++; if(public_timer_out>=6000)//60S { dbg_printf("\nsmartconfig_timeout\n"); system_restart();//复位 } if(public_timer_cnt1>10)//LED快闪 { public_timer_cnt1=0; GPIO_OUTPUT_SET(2, 1-GPIO_INPUT_GET(2));//LED快闪 } break; default:break; } } /****************************************************************************** * FunctionName : user_init * Description : entry of user application, init user function here * Parameters : none * Returns : none *******************************************************************************/ void user_init(void) { GPIO_OUTPUT_SET(5, 1); GPIO_OUTPUT_SET(2, 0);//让两个灯初始的状态一样,GOIO2是反接的,0的时候是亮 GPIO_OUTPUT_SET(0, 1); uart_init_new(); printf("SDK version:%s\n", system_get_sdk_version()); wifi_set_opmode(STATIONAP_MODE);//配置WiFi的模式STATION + AP AP--连接WIFI自身的无线实现通信 STATION--wifi连接路由器,手机或者电脑也连接路由器,实现通信 soft_ap_Config.ssid_len = strlen(SSID);//热点名称长度,与你实际的名称长度一致就好 memcpy(soft_ap_Config.ssid,SSID,soft_ap_Config.ssid_len);//实际热点名称设置,可以根据你的需要来 memcpy(soft_ap_Config.password,PWD,strlen(PWD));//热点密码设置 soft_ap_Config.authmode = AUTH_WPA2_PSK;//加密模式 soft_ap_Config.channel = 1;//信道,共支持1~13个信道 soft_ap_Config.max_connection = 4;//最大连接数量,最大支持四个,默认四个 wifi_softap_set_config_current(&soft_ap_Config);//设置 Wi-Fi SoftAP 接口配置,不保存到 Flash // wifi_softap_set_config(&soft_ap_Config);//设置 Wi-Fi SoftAP 接口配置,保存到 Flash UartCallbackRegister(UartReadCallback);//把 UartReadCallback 函数地址传过去,在串口里面调用 os_timer_disarm(&public_timer); os_timer_setfn(&public_timer, (os_timer_func_t *)public_timer_callback, NULL); os_timer_arm(&public_timer, 10, 1);//10ms //设置连接的路由器,如果想直接测试不想配网,请去掉屏蔽 stationConf.bssid_set = 0; memcpy(stationConf.ssid,APssid,strlen(APssid));//路由器名称 memcpy(stationConf.password,APpassword,strlen(APpassword));//路由器密码 wifi_station_disconnect(); wifi_station_set_config(&stationConf); wifi_set_event_handler_cb(wifi_event_monitor_handle_event_cb);//监听WiFi状态改变 }
测试:
注:
这些回调函数的参数需要根据它内部规定的写,我只说一个
所以
其它的就是固定形式了,就是要那样做....
这节只当做个入门,只是简简单单的,下节接着优化,需要做上自动重连.
其实感觉挺有成就感的