最近再研究 rt-thread 的通信 ,想設計出 eps8266(多個)<-> rt-thread(作為中控) <-> 服務器的通信框架,使用的開發板是 潘多拉
首先我們需要 確保使用的開發板可以聯網,其次 我們需要在rt-setting 中打開套接字抽象層
如圖
下載樣例后,把IoT_Boardrt-threadexamplesnetwork 下的tcpserver.c 和 tcpclient.c拿出來放到我們的工程中。
這兩個文件提供了tcp連接指令給我們
具體使用方法如圖。
在進行下一步前最好測試一下這兩個指令是否生效。
為了接下來能看懂,我這邊簡單介紹一些 esp8266的邏輯 ,
連接wifi -> 連接終端(即rt-thread)->首次連接發送設備id-> 循環查看是否接受到數據,如果接收到即對指令進行處理,否則發送當前設備的狀態
接下來看一下終端這邊的邏輯,終端首先要連接wifi ,之后提供 tcpserver 等待 esp8266連接,在esp8266連接上后,創建一個線程,線程名為設備id,再將線程socket資源放到一個全局結構體中,用于之后的維護。
Tcpserver.c 中沒有多少修改的,我只粘貼修改的部分
在tcpserv函數中,在receive 后修改如下
bytes_received = recv(connected, str, BUFSZ, 0);
if (bytes_received < 0)
{
LOG_E("Received error, close the connect.");
closesocket(connected);
connected = -1;
break;
}
else
{
/* 在控制終端顯示收到的數據 */
rt_thread_t tid;
LOG_D("Received threadName = %s n", str,str);
tid = rt_thread_create(str,
tcpserver_to_client, (void *)connected,
1024, RT_THREAD_PRIORITY_MAX/3, 20);
if (tid != RT_NULL)
{
sockets.tids[sockets.len]=tid;
sockets.connects[sockets.len]=connected;
sockets.len++;
rt_thread_startup(tid);
}
}
即和上面的邏輯一樣,創建通信的線程,將線程資源和socket資源放到全局結構體中維護,結構體的定義如下
接下來看通信線程,代碼如下
static void tcpserver_to_client(void conn){
int running=1;
int bytes_received,connected;
char str[20];
connected = (int)conn;
/ 客戶端連接的處理 /
while (running)
{
/ 從connected socket中接收數據,接收buffer是1024大小,但并不一定能夠收到1024大小的數據 /
bytes_received = recv(connected, str, BUFSZ, 0);
if (bytes_received < 0)
{
LOG_E("Received error, close the connect.");
closesocket(connected);
connected = -1;
return ;
}
else
{
/ 在控制終端顯示收到的數據 */
rt_mb_send(&mb, (rt_uint32_t)&str);
}
}
}
在這個線程中,通過傳遞的socket資源,接受數據,注意這邊的recv是阻塞的,如果接受到數據,就將數據發送到郵箱中,這邊為什么要使用郵箱呢,以為后續我們需要和tcpclient線程進行通信,在esp8266發送消息給我們后,我們需要將消息轉發給服務器。
在繼續講解rt-thread邏輯前我們來看一下服務器的邏輯,服務器邏輯比較簡單,就是提供sever服務,接受tcpclient的數據,并且給tcpclient發送消息,注意服務器端的recv是非阻塞的
接下來看tcpclient的邏輯,在tcpclient中,我們既需要將esp8266的數據轉發給服務器,也需要接受服務器傳的數據,所以我們在這邊的recv不能是阻塞的,那怎么將esp8266的消息轉發呢,就是用之前的郵箱來通信啦,代碼如下
while (is_running)
{
/* 從sock連接中接收最大BUFSZ - 1字節數據 /
bytes_received = recv(sock, recv_data, BUFSZ - 1, MSG_DONTWAIT);
if (bytes_received > 0)
{
char threadName[10]={0};
rt_strncpy(threadName, recv_data, 4);
/ 有接收到數據,把末端清零 /
recv_data[bytes_received] = '?';
/ 在控制終端顯示收到的數據 /
rt_kprintf("Received data = %sn", recv_data);
send_client(threadName,recv_data);
}
if(get_mailbox()==RT_TRUE){
/ 發送數據到sock連接 /
ret = send(sock, str, rt_strlen(str), 0);
if (ret < 0)
{
/ 接收失敗,關閉這個連接 /
rt_kprintf("send error, close the socket.");
goto __exit;
}
else if (ret == 0)
{
/ 打印send函數返回值為0的警告信息 */
rt_kprintf("Send warning, send function return 0.");
}
}
rt_thread_mdelay(3000);
}
在這段代碼中,recv 的flag 改成了MSG_DONTWAIT 這是revc非阻塞的標志,
比較關鍵的是recv后面的內容,在接受數據后,會取出數據的前四位(前四位為設備id),并且通過send_client 來判斷,如果前四位和之前創建的線程名一樣,就把數據發送給對應的設備send_client函數如下
void send_client(char threadName[],char recvData[]){
int i,ret;
for(i=0;i {
if(rt_strcmp(threadName, (sockets.tids[i])->name)==0){
rt_kprintf("find order to %sn",threadName);
ret = send(sockets.connects[i], recvData, rt_strlen(recvData), 0);
if (ret < 0)
{
/* 接收失敗,關閉這個連接 /
rt_kprintf("send error n");
}
else if (ret == 0)
{
/ 打印send函數返回值為0的警告信息 */
rt_kprintf("Send warning, send function return 0.n");
}
}
}
}
get_mailbox()函數是判斷郵箱中是否有東西,有的話就放到str中,并且發送給服務器get_mailbox 函數代碼如下
static int get_mailbox(){
if (rt_mb_recv(&mb, (rt_ubase_t *)&str, RT_WAITING_NO) == RT_EOK)
{
rt_kprintf("thread1: get a mail from mailbox, the content:%d, str1= %sn",str,str);
return RT_TRUE;
}
return RT_FALSE;
}
以上就是實現的流程了,其實還是比較簡單的。
-
控制器
+關注
關注
112文章
16198瀏覽量
177398 -
Socket
+關注
關注
0文章
211瀏覽量
34632 -
RT-Thread
+關注
關注
31文章
1272瀏覽量
39919 -
ESP8266
+關注
關注
50文章
962瀏覽量
44830 -
TCP通信
+關注
關注
0文章
146瀏覽量
4217
發布評論請先 登錄
相關推薦
評論