1. 程式人生 > >ESP8266 Non-OS SDK開發探坑之七-簡單的TCP狀態上報客戶端

ESP8266 Non-OS SDK開發探坑之七-簡單的TCP狀態上報客戶端

繼TCP Server之後,推出TCP Client,用於完成資訊的上報,其實這兩個保留一個即可完成幾乎全部功能,這裡既然是造輪子,既然是探坑,就索性一併實現了。

至於只保留一個,如果是TCP Server,可以配備查詢命令,即可返回所需要的感測器狀態和資料。

而如果是TCP Client,在定時上報的時候服務端可以順帶返回指令,不過缺點是指令有延遲,必須等ESP8266上報的時候才能進行指令控制,實時性靠上報的頻率決定。

TCP Client比TCP Server複雜點,需要遠端伺服器的連結資訊,比如IP和Port,這裡為了能支援的更泛一些,我也加入了DNS支援的方法,所以就顯的繁瑣了。首先,TCP Client連線的遠端伺服器資訊需要提前配置,可以 用巨集進行固定燒寫,可以用上一篇的TCP Server進行配置,當然也可以用上上篇節的Web server 進行配置。這裡採用web server 的serverconfig頁面進行配置並儲存到flash(參看【】)。

要開啟TCP Client,首先要連上wifi,否則client無法連線遠端伺服器,所以開啟tcp client的命令需要在wifi連上的時候進行。

我把和wifi相關的部分行為放在了WifiManager.c檔案中。主要是wifi AP、Station引數配置模式切換,並開啟定時器對wifi狀態輪詢以便進行程式狀態控制。wifi的斷線重連 SDK有提供api

通過wifi_station_get_connect_status();函式查詢狀態,可以得到:

STATION_IDLE

STATION_CONNECTING

STATION_WRONG_PASSWORD

STATION_NO_AP_FOUND

STATION_CONNECT_FAIL

STATION_GOT_IP

這裡主要是在STATION_GOT_IP狀態下進行處理,自定義了一個回撥,如果配置了該回調函式,那麼當連上wifi的時候就執行該回調。

回撥函式:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

void ICACHE_FLASH_ATTR

WifiStaConnCB(){

if(!TCPServOn){

TCPServInit(TCP_SERV_PORT);

}

if(!TCPClientOn){

if(stFlashProtParam.Domain){

struct ip_info stationIP;

wifi_get_ip_info(STATION_IF,&stationIP);

memcpy(tcpDNSTmp.local_ip,&stationIP.ip.addr,4);

connDNSTmp.type = ESPCONN_TCP;

connDNSTmp.state = ESPCONN_NONE;

connDNSTmp.proto.tcp = &tcpDNSTmp;

    espconn_gethostbyname(&connDNSTmp,stFlashProtParam.RemoteAddr.Domain , &ipDNS, DNSFoundCB);

    os_timer_disarm(&tmDNS);

    os_timer_setfn(&tmDNS, (os_timer_func_t *)DNSRetryTimerCB, &connDNSTmp);

    os_timer_arm(&tmDNS, 5000, 0);

}else{

TCPClientInit(stFlashProtParam.RemoteAddr.IP.addr, stFlashProtParam.RemotePort);

StationTimerInit();

}

}

}

這裡面判斷了如果TCPServer或者TCP Client沒開啟就開啟,其中TCP Client判斷如果遠端伺服器是域名資訊,則啟動dns和dns狀態控制定時器。

dns解析回撥:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

LOCAL void ICACHE_FLASH_ATTR

DNSFoundCB(const char *name, ip_addr_t *ipAddr, void *arg)

{

    struct espconn *pEspConn = (struct espconn *)arg;

    if (ipAddr == NULL) {

     DNSRetryCtn++;

        TRACE("domain dns not found\r\n");

        return;

    }

    DNSFound = true;

    TRACE("domain dns found "IPSTR"\n",IP2STR(ipAddr));

TCPClientInit(ipAddr->addr, stFlashProtParam.RemotePort);

StationTimerInit();

}

回撥裡判斷是否成功獲取到IP,如果是,則開啟TCP Client,否則自增重試次數,在定時器回撥裡判斷DNS狀態,如果還未成功就重試。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

LOCAL void ICACHE_FLASH_ATTR

DNSRetryTimerCB(void *arg)

{

    struct espconn *pEspConn = arg;

    TRACE("DNS retry\n");

    if(DNSFound){

    os_timer_disarm(&tmDNS);

     return;

    }

    espconn_gethostbyname(pEspConn, (char *)stFlashProtParam.RemoteAddr.Domain, &ipDNS, DNSFoundCB);

if(DNSRetryCtn >= MAX_DNS_RETRY ){

system_os_post(TCPCOMM_TASK_PRIO, TSIG_DNS_FAILED, 0x00);

DNSRetryCtn = 0;

    os_timer_disarm(&tmDNS);

}else{

os_timer_arm(&tmDNS, 5000, 0);

}

}

當TCP Client成功連線伺服器後,開啟StationTimer定時器,用來定時傳送裝置狀態、感測器資料等。傳送訊息到任務佇列排隊傳送。

1

2

3

4

void ICACHE_FLASH_ATTR

StationTimerCB(){

system_os_post(TCPCOMM_TASK_PRIO, TSIG_REPORT,0x00);

}

而在任務佇列中將訊息收集好傳送:

1

2

3

4

5

6

7

case TSIG_REPORT:

if(!TCPClientOn) return;

LOCAL char szSendBuf[64];

os_memset(szSendBuf,0,sizeof(szSendBuf));

os_sprintf(szSendBuf,"{\"relay\":\"%s\"}",RelayStatus?"off":"on");

TCPResponse(&connClient,szSendBuf,(uint16)os_strlen(szSendBuf));

break;

相比前文的兩個server,client的收發資料回撥都不怎麼做什麼。如果需要服務端的確認訊息,可以在接收回調裡處理。

此外,由於web配置的時候講過tcp client 連線伺服器支援伺服器域名格式,那麼如果伺服器是部署在區域網內,那麼為了更方便進行配置部署,在區域網內部署一個DNS伺服器,我用的dnsmasq,然後將路由器的DNS解析指向該伺服器即可,我先是把cubieboard 上部署了dnsmasq,解析速度只需要不到1ms,但突然某天cubieboard硬體故障無法啟動了,只好把路由器刷了老毛子韌體,然後直接用路由器上的dnsmasq, 好在都支援泛域名解析,但是解析速度降到5ms左右了。

如上圖配置即可完成泛域名解析。

程式碼見:

原部落格: