WiFi-Duino使用教程(1)环境准备

简介

WiFi-Duino是和Arduino系列编程软件兼容的硬件开发板,如果您之前还没接触过Arduino,或者使用的是较老版本的Arduino(Arduino UNOArduino Diecimila等),那么请仔细阅读本教程。如果您是Arduino老手,那么请直接阅读接下来的教程,有任何疑问请联系我

WiFi-Duino采用的是和Arduino Leonardo以及Arduino Yun相同的相同的ATmega32U4主控,与之前较老的Arduino UNOArduino Diecimila相比有诸多变化,鉴于大部分国内Arduino爱好者使用的还是Arduino Diecimila及其变种,这里列举一些不同点:

  • SRAM从2K增加到2.5K,可以改善运行时内存不足的情况。
  • 硬件引脚有一些改变(主要是 SPI 和 TWI),请参见WiFi-Duino官方页面硬件章节
  • 主控运行程序的同时管理和USB的连接。
    之前的Arduino大多使用USB转串口芯片完成电脑和主控连接,完成下载程序或者数据交流的功能,ATmega32U4集成了USB接口,硬件上直接和USB相连,不再需要USB转串口芯片。这么做增加了使用的灵活性,例如可以模拟键盘鼠标等USB设备,同时也带来了一些其他使用上的变化,请仔细阅读本教程中的注意事项。

软件安装

Wifi-Duino使用Arduino系列编程软件进行编程,请先前往下载页面下载Arduino编程软件,WiFi-Duino函数库。

WiFi-Duino函数库的安装方法有两种:

  • 打开Arduino软件,执行Sketch > Import Library > Add Library…,选择下载的WiFi-Duino函数库压缩包即可。

  • 如果您更喜欢手动安装,请将下载得到的WiFi-Duino函数库解压后,将wiFiDuino文件夹复制到libraries目录下,在Windows下libraries文件夹的路径是:

    My Documents\Arduino\libraries\

    Mac下libraries文件夹的路径是:

    Documents/Arduino/libraries/

    Linux下libraries文件夹的路径是:

    Home/Username/sketchbook/libraries/

安装完毕之后,打开Arduino(如已经打开请重启Arduino),执行File > Examples > wiFiDuino 即可查看官方示范。

驱动安装

  • MAC OSX第一次连接WiFi-Duino时,系统可能会出现”键盘设置助理”(中文)或者”keyboard setup assistant”(英文),直接关闭即可。
  • Linux大部分Linux发行版无需安装驱动即可正常使用。
  • Windows第一次第一次连接WiFi-Duino时,请取消自动安装驱动,然后执行以下步骤(以Win7为例):
    • 开始菜单 > 控制面板 > 硬件 > 设备管理器。
    • 找到 其他设备 > 未知设备 , 右键打开属性对话框。
    • 执行 驱动程序 > 更新驱动程序(P)…。
    • 执行 浏览计算机以查找驱动程序软件(P)。
    • 在接下来的对话框中,浏览到Arduino的安装目录的drivers子目录下,继续安装即可。

注意事项

  • 复位的时候,串口会重新枚举,串口会从硬件管理器中消失再重新出现。Arduino Diecimila在复位时,USB转串口芯片会保持和系统的连接,而在WiFi-Duino中,因为USB连接由ATmega32U4管理,当ATmega32U4复位时,USB连接也会重新建立。
  • 当建立串口连接(例如打开串口监视器),WiFi-Duino本身不会复位。Arduino Diecimila在建立串口连接时,电脑会发出复位信号,并使得Arduino中的程序重新运行,而在WiFi-Duino中,这并不会发生。这意味着可能在你打开串口监视器时,看不到在此之前WiFi-Duino发送的数据,为了让程序等待电脑打开串口监视器之后再运行,您可以使用以下代码:
...Serial.begin(9600);   // 等待串口打开,否则什么都不做:   while (!Serial) ;...
  • 增加了模拟USB设备(键盘,鼠标等)的功能。使用新的处理器的一个好处就是,可以使用您的WiFi-Duino模拟键盘,鼠标等USB设备,具体的使用请参考Mouse Keyboard library。需要注意的是在模拟USB设备的时候,WiFi-Duino无法进行程序下载,您需要设置合适的唤醒程序(例如一个按键),停止模拟之后再进行程序下载。
  • 板载串口端口的处理和Leonardo和Yun相同, 库函数中的Serial代表的是USB连接模拟的串口端,而不是板上标注TX和RX的串口,如果您想使用该硬件串口,请使用Serial1, 当使用板载WiFi功能时,该串口将被占用,如果需要连接其他串口设备,建议使用SoftwareSerial
  • 数字引脚12的处理当使用板载WiFi功能时,数字引脚12将被占用,请勿使用该端口进行输入或者输出的操作。

当你完成了软件,库函数以及驱动的安装之后,请连接WiFi-Duino,并打开File > examples > wifiDuino > LED_OnBoard,接下来的教程:WiFi-Duino使用教程(2):用浏览器控制板载LED,Hello WiFi-Duino!,会指导您走进物联网开发的第一步。

WiFi-Duino使用教程(2):用浏览器控制板载LED,Hello WiFi-Duino!

简介

这段时间博客的停更,大部分时间在处理WiFi-Duino的配套库函数wifiDuino,现在大部分的API已经确定下来,说好的教程也将陆续发布,这篇教程将会展示:

  • 如何使用WiFi-Duino新建一个热点,并在WiFi-Duino上运行一个简单的Http服务器,向浏览器输出消息。
  • 如何使用WiFi-Duino连接到周围的热点,并在收到Http请求时,输出在flash中储存的网页,并根据不同的路径响应控制板载LED的亮灭。

Hello World!

下面的程序使用WiFi-Duino新建一个名为WiFi-Duino的热点,并且建立了服务器等待Http请求:

/*
 * Hello World!
 * "Hello World!" from WiFi-Duino board
 * Author: Winterland
 * Doc: http://www.winterland.com/wifiduino
 */
#include <wifiDuino.h>
/* 板载LED引脚 */
#define led 13
/* 定义数组储存接收到的Http请求 */
char request[32] = "";
void setup()
{
/* 初始化LED为亮 */
    pinMode( led, OUTPUT );
    digitalWrite( led, HIGH );
/* 启动WiFi-Duino,如果未启动则需等待30s */
    wifiDuino.begin();
/* 设置WiFi-Duino为热点模式 */
    wifiDuino.setMode( WIFI_MODE_AP );
/*
 * 设置WiFi-Duino的热点参数
 * 热点名称:WiFi-Duino  加密方式:自动  热点密码:122334345
 */
    wifiDuino.setWiFiConfig( "WiFi-Duino", "auto", "122334345" );
/* 写入网络配置,如果和之前的配置不同则需等待30s */
    wifiDuino.writeConfig();
/* 在555端口启动服务器 */
    wifiDuino.startServer( "555" );
/* 关闭LED,表示可以访问 */
    digitalWrite( led, LOW );
}


void loop()
{
/* 等待Http请求 */
    if ( wifiDuino.waitHttpRequest( request, 32, 500 ) )
    {
/* 判断请求路径 */
        if ( !strcmp( request, "/" ) )
        {
/* 路径正确,显示Hello World! */
            wifiDuino.sendHttpMessage( "Hello World!" );
        }else
/* 路径错误,显示404消息 */
            wifiDuino.sendHttpMessage( "404 Not Found!" );
    }
}

wiFiDuino的API十分简洁,不过需要注意以下几点:

  • wifiDuino.begin()会判断WiFi-Duino是否已经启动完毕,如果已经启动完毕,则跳过,如果没有,则会等待30s(wifi模块启动)。

  • wifiDuino.writeConfig()会判断要写入的网络配置和当前WiFi-Duino的网络配置是否相同,如果相同,则跳过,如果不同,则会写入配置并等待30s(wifi模块重启)。

  • 综上两点,第一次运行程序会等待最长60s的时间,所以在程序中使用板载LED用来显示服务器是否启动,服务器启动之后LED会熄灭,在正常情况下(配置参数已写入),wifi模块启动之后服务器就会立即启动。

  • AP模式下加密方式设置为auto,则会使用wpa2方式加密,请保证设置的密码大于或等于8位0。

  • AP模式下,服务器地址为固定值:192.168.1.254,程序中在555端口启动服务器,因此使用浏览器访问地址为:

    http://192.168.1.254:555

浏览器控制板载LED

在上面的例子中,我们使用sendHttpMessage发送Http文本,sendHttpMessage的设计目的是为了发送短消息,函数参数也是储存在内存中的字符数组:

static void wifiDuino.sendHttpMessage(char* p);

如果需要输出网页的话,请使用sendHttpPage函数:

static void sendHttpPage(PGM_P webpage); 

函数接受一个PROGMEM指针,也就是储存在atmega32u4芯片的flash区的指针,一般单片机的flash容量远远大于ram,所以可以用来储存一些复杂的网页。程序代码如下:

/*
 * LED_OnBoard  Control the LED on WiFi-Duino board
 * Author: Winterland
 * Doc: http://www.winterland.com/wifiduino
 */
#include <wifiDuino.h>
/* 板载LED */
#define led 13
/* 定义数组储存接收到的Http请求 */
char request[32] = "";
/* 在flash空间中储存网页 */
char webPage[] PROGMEM = "<html>"  "<head><title>"    "multipackets Test"  "</title></head>"  "<body>"    "<a href='/ledon'>LED ON</a><br>"    "<a href='/ledoff'>LED OFF</a><br>"  "</body>"   "</html>";
void setup()
{
/* 初始化LED */
    pinMode( led, OUTPUT );
    Serial.begin( 9600 );
    wifiDuino.begin();
/* 设置WiFi-Duino为网卡模式 */
    wifiDuino.setMode( WIFI_MODE_ADAPTER );
/* 设置您的WiFi环境配置 */
    wifiDuino.setWiFiConfig( "您所在的WiFi名称", "auto", "您所在的WiFi密码" );
/* 关闭DHCP,以免找不到服务器地址 */
    wifiDuino.setDHCP( DHCP_DISABLE );
/* 手动设置ip配置 */
    wifiDuino.setIpConfig( "192.168.1.254", "255.255.255.0", "192.168.1.1" );  wifiDuino.setDnsConfig( "192.168.1.254", "8.8.8.8" );
    wifiDuino.writeConfig();
/* 在555端口启动服务器 */
    wifiDuino.startServer( "555" );
/* 在接收到Http请求之前闪烁LED */
    while ( true )
    {
        digitalWrite( led, HIGH );
        delay( 500 );
        digitalWrite( led, LOW );
        if ( wifiDuino.waitHttpRequest( request, 32, 500 ) )
        {
            wifiDuino.sendHttpPage( webPage );
            break;
        }
    }
}


void loop()
{
    if ( wifiDuino.waitHttpRequest( request, 32, 500 ) )
    {
        if ( !strcmp( request, "/ledon" ) )
        {
/* 访问路径为"/ledon",打开LED */
            digitalWrite( led, HIGH );
        }
/* 访问路径为"/ledoff",关闭LED */
        if ( !strcmp( request, "/ledoff" ) )
        {
            digitalWrite( led, LOW );
        }
/* 输出网页 */
        wifiDuino.sendHttpPage( webPage );
    }
}

这个例子使用到了WiFi-Duino的网卡模式,WiFi-Duino在接受了环境WiFi配置之后会连接到路由器上,连接到相同路由器的用户都可以访问到WiFi-Duino上的服务器。用浏览器打开:

http://192.168.1.254:555

会显示一个含有两个链接的网页,用鼠标点击试试吧,在网卡模式下需要注意以下几点:

  • WiFi-Duino处于网卡模式时,ip可以设置为自动或者手动分配(setDHCP),如果您希望在WiFi-Duino上建立服务器接收请求的话,建议手动分配一个固定ip,因为此时服务器地址就是网卡所在ip地址,如果设置为自动分配,则无法确定用户访问地址(如果有路由器管理权限的话可以登录路由器后台查看当前ip分配情况,但也很麻烦)。
  • 如果无法访问,请检查路由器是否开启AP隔离,AP隔离时局域网内无法互相访问。

下一篇教程WiFi-Duino使用教程(3):控制板载LED升级!在世界任何一个有网络的地方控制板载LED,将会讲述如何使用WiFi-Duino连接到物联网平台Yeelink,实现互联网远程遥控。

WiFi-Duino使用教程(3):控制板载LED升级!在世界任何一个有网络的地方控制板载

上一篇教程WiFi-Duino使用教程(2):用浏览器控制板载LED,Hello WiFi-Duino!展示了如何使用WiFi-Duino新建一个热点,或者连接到周围的路由器上,并新建一个server来响应http请求,这篇教程将会展示:

  • 如何使用yeelink新建一个数据节点,并获得数据节点的访问地址。
  • 如何使用WiFi-Duino连接到周围的热点,并通过网络查询储存在yeelink上的数据节点。

Yeelink是一个面向硬件的云数据平台,用户可以在联网的环境下上传和下载数据,例如开关控制信号,传感器数值等,这里我们仍然使用板载LED为例子,不同的时由于使用了云服务,在任何联网的地方都可以实现控制。

首先去Yeelink的网站申请一个帐号,然后再用户中心里可以看到Yeelink的后台管理页面,添加一个新设备,并在管理设备页面中添加一个传感器,由于是控制LED,类型选择开关,添加成功后你将获得如下的状态URL:

打开这个URL,你会得到如下的JSON格式字符串:

{"timestamp":"1970-01-01T07:00:00","value":0}

其中value就对应着云端服务器中改传感器节点的值,改变这个值的方法有很多,你可以直接在设备管理页面上点击那个开关按钮,或者下载Yeelink的手机APP,登录之后同样可以管理设备,访问这个URL就会返回传感器节点的值。

所以接下来我们在WiFi-Duino中的程序中,简单的polling一下这个URL返回的字符串,然后截取相应的0或者1即可控制LED开关。不过在Yeelink的官方文档中,推荐在发送的HTTP请求中,附上ApiKey等Header域,我们也遵守这个原则,ApiKey可以在用户中心的账户设置页面中找到。

下面是WiFi-Duino的代码,请确保WiFi-Duino工作在适配器模式并且连接上因特网:

#include <wifiDuino.h>
#define LED 13
/* 你的传感器节点的地址 */
#define URL "/v1.0/device/4283/sensor/10701/datapoints"
void setup()
{
    pinMode( LED, OUTPUT );
    Serial.begin( 9600 );
    wifiDuino.begin();
    wifiDuino.setMode( WIFI_MODE_ADAPTER );
    wifiDuino.setWiFiConfig( "你的wifi名称", "auto", "你的wifi密码" );
    wifiDuino.setDHCP( DHCP_ENABLE );
    wifiDuino.writeConfig();
    wifiDuino.startClient( "api.yeelink.net", "80" );
}


void loop()
{
    char respond[256] = "";
    wifiDuino.sendHttpRequest( URL, "api.yeelink.net", HTTP_GET, false );
/* 每2秒钟查询一次传感器状态 */
    if ( wifiDuino.waitRawData( respond, 256, 2000 ) )
    {
        Serial.println( respond );
        char* state;
        if ( state = strstr( respond, "value" ) )
        {
            state += 7;
            if ( *state == '0' )
            {
                digitalWrite( LED, LOW );
            }else
                digitalWrite( LED, HIGH );
        }
    }
}

OK,是不是很简单,打开Yeelink的APP试试看吧。

wifiDuino函数库手册(一):WiFi模块设置手册 | Winter's Land

这篇文章中将详细解释wifiDuino中和硬件交互的函数接口,包括切换热点/网卡模式,更改WiFi配置等:

//wait for WiFi-Duino started and enter AT command mode.

基本操作

wifiDuino.waitHttpRequest

static void begin();   

检查WiFi-Duino是否启动,如果已经启动则设置WiFi-Duino进入配置模式并立即返回,如果还在启动中则等待30s再检查。

/*Set working mode, available options are:
    WIFI_MODE_AP: Create a WiFi access point with WiFi configurations.
    WIFI_MODE_ADAPTER: Connect to an environment WiFi access point with WiFi configurations.
*/

wifiDuino.setMode

static void setMode(char mode);
//mode 工作模式
//选项:WIFI_MODE_AP 热点模式,WIFI_MODE_ADAPTER,适配器模式

设置WiFi-Duino工作模式。热点模式下WiFi-Duino会新建热点供其他设备连接,适合无路由器时的遥控设备,适配器模式下WiFi-Duino会连接周围的路由器,适合局域网控制或者连接远程云服务。

wifiDuino.setWiFiConfig

static void setWiFiConfig(char* SSID, char* encrypt, char* password);
//SSID WiFI名称,encrypt WiFI 加密方式,password WiFi密码
//encrypt选项:    
//none:         无密码
//wep_open:     wep加密,开放认证
//wep:          wep加密,加密认证
//wpa_tkip:     wpa tkip 加密
//wpa_aes:      wpa aes 加密
//wpa2_tkip:    wpa2 tkip 加密
//wpa2_aes:     wpa2 aes 加密
//wpawpa2_tkip: wpa/wpa2 tkip 加密
//wpawpa2_aes:  wpa/wpa2 aes 加密
//auto:         自动选择,默认。

设置WiFi-Duino的WiFi参数,当处于热点模式时,该参数为WiFi-Duino建立热点的参数,当处于适配器模式时,该参数为WiFi-Duino连接的路由器WiFi参数,对于encrypt选项建议选择auto。

wifiDuino.writeConfig

static void writeConfig();  

写入网络配置参数,如果发现设置参数需要重启,则自动重启WiFi模块(等待30s)。

适配器模式操作

wifiDuino.setDHCP

static void setDHCP(char dhcp);
//dhcp DHCP选项:DHCP_ENABLE DHCP开启,DHCP_DISABLE DHCP关闭 

设置IP地址分配方式,对此不了解的同学请参考动态主机设置协议。如果关闭了DHCP设置,请使用下面的函数手动配置IP等参数。

wifiDuino.setIpConfig wifiDuino.setDnsConfig

static void setIpConfig(char* ip, char* mask, char* gateway);
//ip IP地址,mask 子网掩码,gateway 网关。

static void setDnsConfig(char* dns1, char* dns2);
//dns1 DNS地址1,dns2 DNS地址2

在DHCP关闭时手动设置网络参数。请参考您的路由器设置进行配置。

当处于热点模式时,WiFi-Duino和路由器一样有自己的网络参数:

网关:192.168.1.1
IP地址:192.168.1.254
子网掩码:255.255.255.0
支持DNS分配客户端IP地址。

wifiDuino函数库手册(二):处理HTTP请求和应答 | Winter's Land

阅读这篇文章需要对HTTP协议有基本的了解,不了解的同学可以查看之前写的一篇文章:HTTP协议简介,这篇文章中将详细解释wifiDuino为方便物联网开发设计的函数及其使用:

服务器模式相关函数:

wifiDuino.startServer

static void startServer(char *port);
//port 服务器监听端口号

在指定端口启动服务器。

wifiDuino.waitHttpRequest

static bool waitHttpRequest(char* url, uint8_t url_len, uint16_t timeout); 
//url 保存请求路径的指针,url_len 允许的请求路径长度,timeout 等待时间(ms)  

等待HTTP GET请求,函数接到请求之后会将请求的路径写入url对应内存区域,并会立即返回ture,超过timeout时间仍未收到请求则返回false,注意:请求超过了定义的url_len,仍会返回true。剩余部分将从串口缓存中清除。举例:

...
char urlBuffer[128] = "";
while( wifiDuino.waitHttpRequest( urlBuffer, 128, 1000 ) ){
    //处理请求
}
...

wifiDuino.sendHttpPage

static void sendHttpPage(PGM_P webpage); 
//webpage flash区域指针        

发送保存在flash区域的字符串内容(网页),并添加适当的header(Content-Type: text/html),函数使用循环输出保存在flash区域的字符串,由于避免了使用内存。因此适合发送一些较大的网页(内嵌javascript脚本,css等)举例:

...
char webPage[] PROGMEM =
"<html>"
    //网页内容
"</html>";
...
    wifiDuino.sendHttpPage(webPage);
...    

wifiDuino.sendHttpMessage

static void sendHttpMessage(char *message); 
//message 发送的消息

发送一个字符串(在内存中的),并添加适当的header(Content-Type: text/plain),由于字符串是储存在内存中,适合发送短消息,例如配合网页中的javascript实现ajax,举例:

...
char msg[] = "Hello WiFi-Duino!"
wifiDuino.sendHttpMessage(nsg);
...

客户端相关函数

wifiDuino.startClient

static void startClient(char *remoteDomain, char *remotePort);
//remoteDomain 目标服务器域名,port 目标服务器端口号

建立与目标服务器指定端口的TCP连接。

wifiDuino.sendHttpRequest

static void sendHttpRequest(char *url, char* host, char* method ="GET", char* customFields = NULL, char* body = NULL);
//url 保存请求的指针,host 请求目标域名,method 请求方法(默认为GET)
//customFields 请求header中的自定义项目(默认为空),body 请求的消息体(默认为空)

发送HTTP请求,这个函数在wifiDuino中被特别设计成一个较为通用的接口,可以发送不同类型的HTTP请求以满足不同的服务器的需求,同时省去字符串拼接的麻烦。举例:

...
wifiDuino.sendHttpRequest("/", "www.baidu.com");  
//向www.baidu.com发出GET请求。
...
...
char host[] = "api.yeelink.net";
char apiKey[] = "U-ApiKey:af3c800cb5e89af8ec88f8f4e828f0f3";
wifiDuino.sendHttpRequest("/v1.0/device/4283/sensor/7758/datapoints", host, apiKey);
//向api.yeelink.net/v1.0/device/4283/sensor/7758/datapoints 发出GET请求,并在header中附加apiKey认证信息。
...

wifiDuino.waitHttpRespond

static bool waitHttpRespond(char* respond, uint16_t len, uint16_t timeout);
//respond 保存响应的指针,len 允许的响应路径长度,timeout 等待时间(ms)  

等待HTTP响应,函数接到响应之后会自动识别内容的header,将内容body的写入respond对应的内存区域,并会立即返回ture,超过timeout时间仍未收到请求则返回false,注意:响应超过了定义的len,仍会返回true,此时respond中只保存了响应的一部分,剩余部分将从串口缓存中清除。举例:

...
char contentBuffer[512] = "";
if( wifiDuino.waitHttpRespond( contentBuffer, 512, 5000 ) ){
    //处理响应内容
}
...

TCP操作相关函数

wifiDuino.sendRawData

static void sendRawData(char *data);
//data 发送的数据(内存中)

向底层TCP连接直接发送内存中的字符串数据,类似Arduino中的print。

wifiDuino.sendRawDataPGM

static void sendRawDataPGM(PGM_P data);
//data 发送的数据(flash中)

向底层TCP连接直接发送flash中的字符串数据。

wifiDuino.waitRawData

static void waitRawData(char* data, uint16_t len, uint16_t timeout);
//respond 保存数据的指针,len 允许的响应路径长度,timeout 等待时间(ms)  

等待TCP连接接收数据并存入内存,并会立即返回ture,超过timeout时间仍未收到请求则返回false,注意:数据超过了定义的len,仍会返回true,并且剩余部分将保留在串口缓存中。举例:

...
char contentBuffer[512] = "";
while( wifiDuino.waitRawData( contentBuffer, 512, 5000 ) ){
    //处理响应内容
    ...
    //所有消息接收完毕循环终止
}
...