Arduino 监控客厅温度湿度

来自Jack's Lab
跳转到: 导航, 搜索

有了 DHT21/AM2301 数字温湿度传感器 就能把读取的温湿度数据,通过 Ethernet (使用 ENC28J60)上传到服务器上呈现出来

Ethernet 部分的线路连接,可以参考:Arduino Ethernet 的另一性价比选择

DH21 传感器模块,供电电压也用 3.3V,Arduino 核心板只有 1 pin 的 3.3V 电压输出,因此我们把它接到小面包板上,让 ENC28J60 和 DH21 共同使用。

DH21 的数据接到数字 I/O pin 2 上


接好的图如下:


Ether-dh21.JPG


比较早的开始提供传感器数据存储和呈现服务的提供商是 pachube (现在改名叫cosm

国内有一个模仿,叫 yeelink,我们以 Yeelink 为例,来解析数据上传的过程

他们都提供 http API,来访问、修改、存储数据,api 很简单,一看就懂 Yeelink API Cosm API

对于 yeelink,在用户中心创建了设备和传感器后,得到一个传感器的 URL: http://api.yeelink.net/v1.0/device/462/sensor/907/datapoints ,就可发一个如下 http 请求上传数据了:


POST /v1.0/device/462/sensor/907/datapoints HTTP/1.1
Host: api.yeelink.net
Accept: */*
U-ApiKey: xxxxxxxxYOUR_API_KEYxxxxxxxxx
Content-Length: 13
Content-Type: application/x-www-form-urlencoded

{"value": 30}


如下命令即发送上面的 http 请求:


curl --request POST --data-binary "{\"value\": 30}" --header "U-ApiKey: YOUR_API_KEY_HERE"    \
http://api.yeelink.net/v1.0/device/462/sensor/907/datapoints


Arduino 下使用 ethercard 库,可直接使用 httpPost() 这个函数来发数据,很方便。


撰写程序,Ethernet 直接用 ethercard 库,DH21 则用我们封装好的库 撰写自己的 Arduino 库



#include <EtherCard.h>
#include <dh21.h>
 
#define TEMP_TYPE    1
#define HUMI_TYPE    2
 
// pin 2 is connected to DH21
DH21 dh21(2);
char temp[10];
 
/*
 * ATmega1280/2560: MOSI --> 51; MISO --> 52; SCK --> 53, SS --> 8
 * Others: MOSI --> 11; MISO --> 12; SCK --> 13, SS --> 8
*/
 
// ethernet interface mac address
static byte mymac[] = {0x04,0x69,0x69,0x2b,0x30,0x31}; 
static byte myip[] = {192,168,1,253};
static byte gwip[] = {192,168,1,1};
static byte dnsip[] = {192,168,1,1};
  
// remote website name
char website[] PROGMEM = "api.yeelink.net";
static byte cloud_ip[] = {202,136,56,203};
 
char temp_urlBuf[] PROGMEM = "/v1.0/device/462/sensor/907/datapoints";
char humi_urlBuf[] PROGMEM = "/v1.0/device/462/sensor/908/datapoints";
char apiKey[] PROGMEM = "U-ApiKey: 9594675aea73335d031273a1f2ce8eb8";
 
byte Ethernet::buffer[400];   // a very small tcp/ip buffer is enough here
 
 
// 数据发送间隔时间,yeelink 的两次数据上传间隔要大于 10s
#define REQUEST_RATE 13000    // 13000ms
static long timer;
 
uint8_t flag = TEMP_TYPE;
static char buf[20];
  
// 回调函数,打印出服务器的返回字符串
static void cb_debug (byte status, word off, word len)
{
  Serial.print("\n--> server response ");
  Serial.print(millis() - timer);
  Serial.println(" ms");
  Serial.println((const char*) Ethernet::buffer + off);
}
  
void setup ()
{
  Serial.begin(9600);
  Serial.println("\nSet gateway ip, interface ip\n");
  
  if (ether.begin(sizeof Ethernet::buffer, mymac) == 0) 
    Serial.println( "\nFailed to access Ethernet controller");
  
  //config network interface
  ether.staticSetup(myip, gwip, dnsip);
 
  // resolve the server domain name
  if (!ether.dnsLookup(website))
    Serial.println("DNS failed");
  else
    memcpy(ether.hisip, cloud_ip, 4);
   
  ether.printIp("Server IP is: ", ether.hisip);
 
  // testing the gateway internection
  while (ether.clientWaitingGw())
    ether.packetLoop(ether.packetReceive());
 
  Serial.println("Gateway found");
   
  // 初始化计时变量
  timer = - REQUEST_RATE;
}
 
void loop ()
{  
  ether.packetLoop(ether.packetReceive());
  
  // 间隔时间大于 13s 时才向服务器发送数据
  if (millis() > timer + REQUEST_RATE) {
 
    // 更新计时变量
    timer = millis();
 
    Serial.println("\n--> Sending Request");
     
    // 获取传感器数值字符串
    memset(buf, 0, 20);
    get_send_string(buf, flag);
 
    // 温度和湿度数据交替发送
    switch (flag){
      case TEMP_TYPE:
        ether.httpPost (temp_urlBuf, website, apiKey, buf, cb_debug);
        Serial.println("post temp");
        flag = HUMI_TYPE;
        break;
 
      case HUMI_TYPE:
        ether.httpPost (humi_urlBuf, website, apiKey, buf, cb_debug);
        Serial.println("post humi");
        flag = TEMP_TYPE;
        break;
    }
  }
}
 
// 简单的 float to string 类型的转换函数,sprintf() 不可用
char *ftoa(char *a, double f, int precision)
{
  long p[] = {0,10,100,1000,10000,100000,1000000,10000000,100000000};
   
  char *ret = a;
  long heiltal = (long)f;
  itoa(heiltal, a, 10);
  while (*a != '\0') a++;
  *a++ = '.';
  long desimal = abs((long)((f - heiltal) * p[precision]));
  itoa(desimal, a, 10);
  return ret;
}
  
void get_send_string(char *p, uint8_t type)
{
  while((dh21.get_data() == -1) || (dh21.data_check() == -1)) delay(1000);
   
  switch (type) {
    case TEMP_TYPE:
 
     memset(temp, 0, 10);
     sprintf(p,"{\"value\":%s}", ftoa(temp, dh21.temperature(), 1));
     break;
 
    case HUMI_TYPE:
     memset(temp, 0, 10);
     sprintf(p,"{\"value\":%s}", ftoa(temp, dh21.humidity(), 1));
     break;
  }
 
  Serial.println(p);
}


数据上传成功,串口应该有类似如下的输出:

--> server response 127ms
HTTP/1.1 200 OK
Server: nginx/1.0.14
Date: Tue, 25 Sep 2012 05:08:33 GMT
Content-Type: text/html
Connection: keep-alive
X-Powered-By: PHP/5.3.10
Set-Cookie: CAKEPHP=6sjmsggjnudob61so4teq3c202; expires=Wed, 03-Oct-2012 13:08:33 GMT; path=/
P3P: CP="NOI ADM DEV PSAi COM NAV OUR OTRo STP IND DEM"
Vary: Accept-Encoding
Content-Length: 0


即表示数据上传成功


一段时间后,获得的数据曲线如下:

客厅温度曲线.jpg


这样我们就可以远程查看家里的温度、湿度了


















个人工具
名字空间

变换
操作
导航
工具箱