WIFI開發計劃 CooCox Cedar@Wuhan 說明: 本來計劃開發SparkFun的WiFly模塊,后來發現Arduino官網出了WIFI模塊,就優先開發Arduino WIFI Shield。 官方的WIFI Shield資料如下: *:原理圖 *:PCB文件 *:WIFI庫 *:固件代碼(用于實現IP棧) 以上所有資料均可從Arduino官網下載,需要說明的是WIFI Shield的核心芯片是HDG104,進入HD官網下載資料時, 需要注冊賬戶,注冊后,不知為何,始終無法登陸,所有沒有下載到任何手冊。如果您有HDG104的手冊,可以分享 一下,非常感謝。 因為換WIFI模塊,需要重新熟悉代碼和資料,現制定一份初步計劃 計劃如下:
PS:如果有任何疑問,請跟帖或email我,3Q Email: renjun@coocox.com Socket軟件包使用說明 綜述 本Socket軟件包作為Arduino WIFI shield入門指導中的一部分,用于說明如何在windows下用TCP/IP Socket編程,如何建立鏈接,綁定端口,收發數據,深入理解這部分,更利于后期學習HTTP客戶端和服務器代碼。這里用Socket寫了一個簡單的局域網聊天工具,在不同的機器上分別運行客戶端和服務器,然后就像QQ一樣聊天。 軟硬件環境 操作系統: Win7 開發環境: VS2008 開發語言:C 注:所有代碼在上述環境中測試通過,理論上在其它環境(如VC++6.0,windows XP)可以編譯通過,但未測試 目錄結構 使用時只需要重點關注紅色字體標注的文件(夾)
使用方法 A:體驗 1:雙擊打開server 目錄下exe文件夾下exe文件,啟動聊天服務器 B:開發 1:確保您已經正確安裝VS2008 2:雙擊軟件包中的server.vcproj和client.vcproj,打開對應的工程文件 3:編譯和調試server和client代碼 發布 如果想要將生成的exe文件在其他電腦上運行,只復制exe文件過去,打開時,會出現下面的錯誤 file:///C:/Users/RENJUN/AppData/Local/youdao/ynote/images/6286383C8E6B40F6B8A017F7F6E13A14/R%5DQEAS%257BW%40)%40%5B8QENKYFPA67.jpg 這是因為缺少對應的dll,所以我們需要同時復制dll,exe和描述文件過去,別人才能正常運行 這里以打包client為例,來說明如何正確發布軟件 1:新建一個文件夾,文件夾名字隨便起,這里我們將文件夾命名為dist,該文件夾用于存放exe文件和對應的依賴文件 2:打開軟件包中Debug文件夾,找到 client .exe和 client .exe.embed.manifest文件 如沒有這些文件,進入VS2008,rebuild一下 3:打開 client.exe.embed.manifest文件,查看exe依賴的dll 在我電腦上,client.exe.embed.manifest內容如下: 文件組織方式為標準xml結構,在此重點關注
知道缺少了哪些dll,只需要將這些dll復制到exe文件夾下即可 4:進入VS2008安裝目錄,找到Microsoft.VC90.DebugCRT這個文件夾,在我的電腦上路徑如下: D:\Program Files\Microsoft Visual Studio 9.0\VC\redist\Debug_NonRedist\x86 不同電腦,路徑可能不一樣,但大致結構相同 5:將Microsoft.VC90.DebugCRT文件夾整體拷貝到第1步建立的dist文件夾中 6:現在所有的必須的文件都已經復制完畢,dist文件夾下目錄組織結構如下
7:檢查dist目錄下client.exe.embed.manifest和Microsoft.VC90.DebugCRT.manifest版本號(9.0.21022.8)是否一致 8:現在您可以將dist文件夾打包,發送給朋友了 ^_^ 軟件包下載 所有代碼可以從github網站下載: https://github.com/cedar-renjun/Socket_server_client_chat_Example Socket Http WebClient 說明:直接用Socket連接百度web服務器,然后發送HTTP Get請求來獲取百度首頁,VS2008工程在上個帖子中有鏈接,這次直接發代碼,使用時,將代碼復制到.c文件中,rebuild生成exe文件,然后點擊運行,就可以看到獲取的html網頁了 #include #include #include // Need to link with Ws2_32.lib #pragma comment (lib, "Ws2_32.lib") #define DEFAULT_BUFLEN 512 #define DEFAULT_PORT 80 WSADATA wsaData; SOCKET ConnectSocket = INVALID_SOCKET; char *HttpRequst ="GET / HTTP/1.1\r\nConnection: close\r\n\r\n"; char RecvBuf[DEFAULT_BUFLEN]; char *IP = "119.75.217.56"; struct sockaddr_in ServerCfg; char WelcomeInfo[] = { "\t===============WIFI Shield Dirver================\r\n" "\tName: HTTP WebClient Example\r\n" "\tHOST : http://www.baidu.com\r\n" "\tIP : 119.75.217.56\r\n" "\tPORT : 80\r\n" "\t=============== CooCox Team =====================\r\n" }; int main(void) { int tmp = 0; int cnt = 0; int iResult = 0; printf(WelcomeInfo); // Initialize Winsock iResult = WSAStartup(MAKEWORD(2,2), &wsaData); if (iResult != 0) { printf("WSAStartup failed with error: %d\r\n", iResult); return 1; } printf("Initial WinSock OK\r\r\n"); // Create a SOCKET for connecting to server ConnectSocket = socket(AF_INET, SOCK_STREAM, 0); if (ConnectSocket == INVALID_SOCKET) { printf("socket failed with error: %ld\r\n", WSAGetLastError()); WSACleanup(); return 1; } printf("Create Socket OK\r\n"); //Connect to Server ServerCfg.sin_family = AF_INET; ServerCfg.sin_port = htons(DEFAULT_PORT); ServerCfg.sin_addr.s_addr = inet_addr(IP); printf("Try to connect server\r\n"); iResult = connect(ConnectSocket, (struct sockaddr *)&ServerCfg, sizeof(struct sockaddr)); if (iResult == SOCKET_ERROR) { printf("Unable to connect to server!\n"); WSACleanup(); return 1; } printf("Connect to server!\r\n"); // Send Message to Server printf("Send HTTP Requst\r\n%s", HttpRequst); iResult = send( ConnectSocket, HttpRequst, strlen(HttpRequst), 0); if (iResult == SOCKET_ERROR) { printf("send failed with error: %d\r\n", WSAGetLastError()); } printf("-----------Receive Respon Message---------\r\n"); // Receive Full response message while(1) { // Important,MUST NOT comment this memset(RecvBuf, '\0', DEFAULT_BUFLEN); iResult = recv( ConnectSocket, RecvBuf, DEFAULT_BUFLEN, 0); if (iResult < 0) { printf("receive failed with error: %d\r\n", WSAGetLastError()); } else if(iResult > 0) { puts(RecvBuf); } else { printf("\r\n----------------------------------------------"); printf("\r\nReceive OK\r\n"); break; } } // clean printf("Now Release resource\r\n"); closesocket(ConnectSocket); WSACleanup(); printf("Closed! Example is over\r\n"); printf("Press Enter to exit\r\n"); getch(); //while(1); } Socket Http WebServer #include #include #include #include #include // Need to link with Ws2_32.lib #pragma comment(lib, "Ws2_32.lib") #define DEFAULT_BUFLEN 2048 #define DEFAULT_PORT 27013 char RecvBuf[DEFAULT_BUFLEN]; char SendBuf[DEFAULT_BUFLEN]; struct sockaddr_in ServerCfg; SOCKET ListenSocket = INVALID_SOCKET; SOCKET ClientSocket = INVALID_SOCKET; char WelcomeInfo[] = { "\t===============WIFI Shield Dirver================\r\n" "\tName: TCP Socket Http Webserver Example\r\n" "\tPORT : 8080\r\n" "\t=============== CooCox Team =====================\r\n" }; char RespondInfo[] = { "HTTP/1.1 200 OK\r\n" "Content-Type: text/html\r\n" "Connnection: close\r\n" "\r\n" "\r\n" "\r\n" "\r\n" " ""================= WIFI Shield Dirver=============== " "Name: TCP Socket Http Webserver Example " "PORT : 27013 " "===================" "CooCox Team===================== " " |
精簡版inet_addr 說明: 今天在移植代碼的時候,需要一個客戶端WiFiClientConnect函數,該函數的一個參數為服務器地址,第二個為端口。 其中服務器的地址可以是主機地址(如: www.baidu.com ),也可以是IP地址(如:192.168.2.71)。 官方的WIFI驅動是C++重載函數寫的,可以根據不同的輸入格式來調用對應的函數,C中沒有重載,只有手工解析,解決方法有3個: 1:分別采用2個函數,比如提供WiFiClientConnectVia 和Host WiFiClientConnectViaIP兩個函數,供用戶調用 2:像Arduino WiFi驅動接口那樣,使用一個函數,但增加一個入口參數,用于指明服務器地址類型 3:符合Arduino 官方WIFI的調用習慣,用一個WiFiClientConnect函數,用戶只需輸入HOST或IP即可,解析的問題,交給函數內部來處理如 WiFiConnect( "www.baidu.com", 80);WiFiConnect( "192.168.2.71", 80) 最終為了方便用戶,采用了最后一個方案,這樣用戶可以更加方便的使用WIFI 我們需要在內部識別出是否是有效的IP地址,如果是的話,則轉換為網絡地址,這樣就需要一個識別和轉換函數IPToNetAddr。 IPToNetAddr將傳入的IP地址解析成網絡格式,比如將“192.168.2.71”分割成4個10進制的數字,存到數組里面 在Q群問了網友,有人說inet_addr符合要求,找到對應源碼后,發現它考慮的情況太多了,支持各種進制的數字,而我們這里,只需要支持10進制就ok,網上搜了一下,貌似沒人寫過這函數,所以還是自己動手寫一個吧 要求: 1:能識別出輸入IP字符串是否有效,有效則返回0,無效則返回-1 2:輸入錯誤的IP地址,函數不損壞存放結果的數組 錯誤的IP例子: 1:子項過大:每個子項應小于255。下面的IP地址第一個子項為1921,大于255 1921.108.2.71 2:子項過多:應有4個子項,由3個.號分隔,下面的IP地址有5個子項 192.168.2.1.2 測試代碼如下: int main(void) { int retv = 0; //目標IP地址字符串 char IP_OK[] = "192.168.002.071"; char IP_ERROR_1[] = "1921.108.2.71"; char IP_ERROR_2[] = "192.168.2.1.2"; char IP_ERROR_3[] = "293.168.2.1.2"; char IP_ERROR_4[] = "193.168.2.1.300"; //存儲結果數組 uint8_t result[4] = {0, 0, 0, 0}; retv = IPToNetAddr(IP_OK, result); if(-1 == retv) { printf("Test Failure\r\n"); while(1); } retv = IPToNetAddr(IP_ERROR_1, result); if(0 == retv) { printf("Test Failure\r\n"); while(1); } retv = IPToNetAddr(IP_ERROR_2, result); if(0 == retv) { printf("Test Failure\r\n"); while(1); } retv = IPToNetAddr(IP_ERROR_3, result); if(0 == retv) { printf("Test Failure\r\n"); while(1); } retv = IPToNetAddr(IP_ERROR_3, result); if(0 == retv) { printf("Test Failure\r\n"); while(1); } printf("Test OK, Press any key to exit\r\n"); getchar(); return (0); } 完整代碼如下: CODE: #include "stdafx.h" #include #include //#include #include typedef unsigned char uint8_t; typedef signed char int8_t; typedef unsigned int uint32_t; typedef signed int int32_t; int IPToNetAddr(char * IPStr, uint8_t * NetAddr); int main(void) { int retv = 0; //目標IP地址字符串 char IP_OK[] = "192.168.002.071"; char IP_ERROR_1[] = "1921.108.2.71"; char IP_ERROR_2[] = "192.168.2.1.2"; char IP_ERROR_3[] = "293.168.2.1.2"; char IP_ERROR_4[] = "193.168.2.1.300"; //存儲結果數組 uint8_t result[4] = {0, 0, 0, 0}; retv = IPToNetAddr(IP_OK, result); if(-1 == retv) { printf("Test Failure\r\n"); while(1); } retv = IPToNetAddr(IP_ERROR_1, result); if(0 == retv) { printf("Test Failure\r\n"); while(1); } retv = IPToNetAddr(IP_ERROR_2, result); if(0 == retv) { printf("Test Failure\r\n"); while(1); } retv = IPToNetAddr(IP_ERROR_3, result); if(0 == retv) { printf("Test Failure\r\n"); while(1); } retv = IPToNetAddr(IP_ERROR_4, result); if(0 == retv) { printf("Test Failure\r\n"); while(1); } printf("Test OK, Press any key to exit\r\n"); getchar(); return (0); } int IPToNetAddr(char * IPStr, uint8_t * NetAddr) { uint32_t _IP[4] = {0, 0, 0, 0}; uint8_t cnt = 0; uint8_t idx = 0; char _str = NULL; //檢查IP和接收緩存區是否有效 if(IPStr == NULL || NetAddr == NULL) { return (-1); } while((_str = *IPStr++) != NULL) { if(_str == '.') { //清空計數器,該計數器最大值為3 cnt = 0; if(_IP[idx] > 255) { return (-1); } idx = idx + 1; } else if(_str >= '0' && _str <= '9') //檢查是否為有效字符 '0' --> '9' { if(cnt++ < 3 && idx <= 3) { _IP[idx] = (10 * _IP[idx]) + (_str - '0'); } else { return (-1); } } else { return (-1); } } if(_IP[idx] > 255) { return (-1); } //復制數據到結果緩存區 NetAddr[0] = (uint8_t)_IP[0]; NetAddr[1] = (uint8_t)_IP[1]; NetAddr[2] = (uint8_t)_IP[2]; NetAddr[3] = (uint8_t)_IP[3]; return (0); } |
學習學習! |