【转】Arduino官方WIFI模块----开发记录

发布时间:2012-12-24 08:59    发布者:coocox
关键词: Arduino , WIFI , Cookie , Cox
WIFI开发计划

CooCox Cedar@Wuhan


说明:
本来计划开发SparkFun的WiFly模块,后来发现Arduino官网出了WIFI模块,就优先开发Arduino WIFI Shield。

官方的WIFI Shield资料如下:
*:原理图
*:PCB文件
*:WIFI库
*:固件代码(用于实现IP栈)
以上所有资料均可从Arduino官网下载,需要说明的是WIFI Shield的核心芯片是HDG104,进入HD官网下载资料时,

需要注册账户,注册后,不知为何,始终无法登陆,所有没有下载到任何手册。如果您有HDG104的手册,可以分享
一下,非常感谢。

因为换WIFI模块,需要重新熟悉代码和资料,现制定一份初步计划
计划如下:


  阶段1:  
学习Socket编程

难点:开发环境的搭建,熟悉网络概念


阶段
2:
用Socket实现HTTP例子(客户端和服务器)

难点:HTTP协议分析和HTML代码编写


阶段
3:
分析官方WIFI库代码

难点:理解代码的分层模型,从代码中分析WIFI模块的协议


阶段
4:
移植到Cookie

难点:Cox接口入门

                       
阶段
5:
总结

总结心得体会,分享资料,链接





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)可以编译通过,但未测试


目录结构


使用时只需要重点关注红色字体标注的文件(夹)

Socket
│  
├─client
│  │  client.c                                   ------> Socket 客户端源码
│  │  
│  ├─client
│  │  │  client.ncb
│  │  │  client.sln
│  │  │  client.vcproj                           ------> Client VC工程文件
│  │  │  client.vcproj.RENJUN-PC.RENJUN.user
│  │  │  
│  │  └─Debug
│  │          BuildLog.htm
│  │          client.exe                         ------> VC工程生成的客户端可执行文件,在本
│  │                                                     机上可以直接双击运行。如需在其它电脑运行,
│  │                                                     请参考“发布”章节中的步骤和注意事项
│  │          client.exe.embed.manifest          ------> exe文件的依赖关系,详细信息,参考“发布”章节
│  │          client.exe.embed.manifest.res
│  │          client.exe.intermediate.manifest
│  │          client.ilk
│  │          client.obj
│  │          client.pdb
│  │          mt.dep
│  │          vc90.idb
│  │          vc90.pdb
│  │         
│  └─dist                                        ------> 需要在其它没装VS2008的电脑上运行exe文件时,
│      │                                                 直接将dist文件夹复制过去就ok,
│      │                                                 详细信息,参见“发布”章节
│      │  client.exe
│      │  client.exe.embed.manifest
│      │  
│      └─Microsoft.VC90.DebugCRT
│              Microsoft.VC90.DebugCRT.manifest
│              msvcm90d.dll
│              msvcp90d.dll
│              msvcr90d.dll
│              
└─server
    │  server.c                                  ------> Socket 服务器源码
    │  
    ├─dist                                       ------> 需要在其它没装VS2008的电脑上运行exe文件时,
    │  │                                                 直接将dist文件夹复制过去就ok
    │  │                                                 详细信息,参见“发布”章节
    │  │  server.exe
    │  │  server.exe.embed.manifest
    │  │  
    │  └─Microsoft.VC90.DebugCRT
    │          Microsoft.VC90.DebugCRT.manifest
    │          msvcm90d.dll
    │          msvcp90d.dll
    │          msvcr90d.dll
    │         
    └─server
        │  server.ncb
        │  server.sln
        │  server.vcproj                           ------> Server VC工程文件
        │  server.vcproj.RENJUN-PC.RENJUN.user
        │  
        └─Debug
                BuildLog.htm
                mt.dep
                server.exe                         ------> VC工程生成的客户端可执行文件,在本机上
                                                           可以直接双击运行。如需在其它电脑运行,
                                                           请参考“发布”章节中的步骤和注意事项
                server.exe.embed.manifest          ------> exe文件的依赖关系,详细信息,参考“发布”章节
                server.exe.embed.manifest.res
                server.exe.intermediate.manifest
                server.ilk
                server.obj
                server.pdb
                vc90.idb
                vc90.pdb







使用方法


A:体验
1:双击打开server 目录下exe文件夹下exe文件,启动聊天服务器
2:双击打开client 目录下exe文件夹下exe文件,启动聊天客户端
3:启动客户端后,输入服务器主机IP地址,便可开始聊天
4:根据提示进行发送和接收信息,在发送状态时,如果不想发送数据,可以按回车,取消发送

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结构,在此重点关注部分(红色字体)。可以得到如下信息:
  • 该exe为win32可执行文件
  • 依赖Microsoft.VC90.DebugCRT目录下的dll文件
  • dll版本号为:9.0.21022.8

知道缺少了哪些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文件夹下目录组织结构如下

dist
|-->client.exe
|-->client.exe.embed.manifest  
|---Microsoft.VC90.DebugCRT
      |---> Microsoft.VC90.DebugCRT.manifest
      |---> msvcm90d.dll
      |---> msvcp90d.dll
      |---> msvcr90d.dll




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     // use Sleep function
#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
=====================
"
        "

"
        "\r\n"
        "\r\n"
        "\r\n"
};

int main(void)
{
    WSADATA wsaData;
    int iResult = 0;
    int iSendResult = 0;
    printf(WelcomeInfo);
    // Initialize Winsock
    iResult = WSAStartup(MAKEWORD(2,2), &wsaData);
    if (iResult != 0)
    {
        printf("WSAStartup failed with error: %d\n", iResult);
        return 1;
    }
    printf("Initial WinSock OK\r\n");

    // Create a SOCKET
    ListenSocket = socket(AF_INET, SOCK_STREAM, 0);
    if (ListenSocket == INVALID_SOCKET)
    {
        printf("socket failed with error: %d\n", WSAGetLastError());
        WSACleanup();
        return 1;
    }
    printf("Create Socket OK\r\n");

    // Setup the TCP listening socket
    ServerCfg.sin_family = AF_INET;
    ServerCfg.sin_port = htons(DEFAULT_PORT);
    ServerCfg.sin_addr.s_addr = htonl(INADDR_ANY);

    iResult = bind(ListenSocket,(struct sockaddr *)&ServerCfg,
            sizeof(struct sockaddr));
    if(iResult == SOCKET_ERROR)
    {
        printf("bind failed with error: %d\n", WSAGetLastError());
        closesocket(ListenSocket);
        WSACleanup();
        return 1;
    }   
    printf("Bind to PORT OK\r\n");

    // Begin listen PORT
    iResult = listen(ListenSocket, 5);
    if (iResult == SOCKET_ERROR)
    {
        printf("listen failed with error: %d\n", WSAGetLastError());
        closesocket(ListenSocket);
        WSACleanup();
        return 1;
    }
    printf("Start Listen PORT\r\n");
    printf("Waitting Client ...\r\n");

    // Accept a client socket
    ClientSocket = accept(ListenSocket, NULL, NULL);
    if (ClientSocket == INVALID_SOCKET)
    {
        printf("accept failed with error: %d\n", WSAGetLastError());
        closesocket(ListenSocket);
        WSACleanup();
        return 1;
    }
    printf("Client Connect Now\r\n");

    // No longer need server socket
    closesocket(ListenSocket);

    // Get Request

    // Receive Full response message

    printf("Receive Web bowser request\r\n");
    memset(RecvBuf, '\0', DEFAULT_BUFLEN);
    iResult = recv(ClientSocket, RecvBuf, DEFAULT_BUFLEN, 0);
    if (iResult < 0)
    {
        printf("receive failed with error: %d\r\n", WSAGetLastError());
    }
    else if(iResult > 0)
    {
        puts(RecvBuf);
    }
    else
    {
        printf("Connect Closed!\r\n");
    }

    // Send html to client
    //printf("Send HTTP Respond:\r\n%s\r\n",RespondInfo);
    iResult = send(ClientSocket, RespondInfo, strlen(RespondInfo), 0);
    if (iResult == SOCKET_ERROR)
    {
        printf("send failed with error: %d\r\n", WSAGetLastError());
    }
    printf("Send OK\r\n");
    //Wait 1S to allow boswer to receive information
    Sleep(1000);
    // shutdown the connection since no more data will be sent
    closesocket(ClientSocket);
    WSACleanup();
    printf("WebServer closed! Press Enter to exit\r\n");
    getch();
}


阶段总结





WIFI模块本身做不了什么,只有和TCP/IP协议栈,HTTP协议结合起来,才能发挥更大的作用。
从分层模型上来看,WIFI本身仅仅充当物理层和数据链路层的作用,因为各层的接口特性,上层只需要关注下层提供的接口,无需关注内部实现,所以在保证接口不变的前提下,可以直接替换内部模块。一般开发时,先在PC上调试好应用层代码,调试ok后,直接将底层代码替换成WIFI驱动,这样就可快速完成模块。
所以下面的帖子会先介绍PC上常用的软件和对应的设置方法,然后用理论加实践的方式介绍TCP/IP socket编程入门,HTTP协议分析和模拟客户端,服务器。








本文地址:https://www.eechina.com/thread-103327-1-1.html     【打印本页】

本站部分文章为转载或网友发布,目的在于传递和分享信息,并不代表本网赞同其观点和对其真实性负责;文章版权归原作者及原出处所有,如涉及作品内容、版权和其它问题,我们将根据著作权人的要求,第一时间更正或删除。
coocox 发表于 2012-12-24 09:05:17
                                                                       精简版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);
}
jlddzeec 发表于 2018-10-23 09:50:12
学习学习!
您需要登录后才可以发表评论 登录 | 立即注册

厂商推荐

相关视频

关于我们  -  服务条款  -  使用指南  -  站点地图  -  友情链接  -  联系我们
电子工程网 © 版权所有   京ICP备16069177号 | 京公网安备11010502021702
快速回复 返回顶部 返回列表