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 上
接好的图如下:
比较早的开始提供传感器数据存储和呈现服务的提供商是 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
即表示数据上传成功
一段时间后,获得的数据曲线如下:
这样我们就可以远程查看家里的温度、湿度了