zcbzjx 发表于 2012-8-29 08:35:16

【第二弹】基于dht22+dsm501A+enc28J60+arduino+lcd1602+yeelink+实现家居环境监控

本帖最后由 zcbzjx 于 2012-9-3 10:13 编辑

2012年9月3日:更新dhcp租约到期问题。

上一次做了个基于18B20+enc28j60+arduino+yeelink的远程温度监控,觉得yeelink这个平台还凑合,能很好的展示和记录各个机房温度值,超过阈值还能激活email和新浪微博进行提醒,详细请看北京联合大学生物化学工程学院网络机房远程监控系统,enc28j60以平易的价格够用(10M)的带宽也基本能满足需求,性能稳定。所以这次也以这个模块和yeelink平台当然还有arduino做了个家居环境监控系统。lcd实时显示,第一排显示温度 湿度 灰尘浓度,第二排显示实时时间,这个时间自动和internet时间校准,大概间隔 TIME_SET_TIMER*5s,我程序设置的是4000*5s也就是差不多5个半小时自动校准一次。

硬件同样看图。洞洞板,估计也只有我能看明白。不过这次用eagle6.1.0做了个原理图。


代码:/*
Yeelink sensor client
by zcb
2012.08.27
*/
#include <EtherCard.h>
#include <dht.h>
#include <LiquidCrystal.h>
#include <Time.h>

#define IN
#define OUT

LiquidCrystal lcd(2, 3, 4, 5, 6, 7);

#define DHT_PIN A0
#define DSM_PIN_10 A4
#define DSM_PIN_25 A2

///////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////
////传感器个数及对应传感器ID
#define N 4
const uint16_t PostingInterval = 5000;
// 发送到服务器时间间隔,N大了可以适当缩短,
//每个传感器发送时间间隔为PostingInterval*N

const uint16_t SensorID = {
XXX,XXX,XXX,XXX}; // replace your sensor ID

const uint8_t MyMac = {
0x74,0x69,0x69,0x20,0x07,0xAA };

////////////////////////////////////////////////////////////

// for yeelink api
char APIKey[] PROGMEM = "XXXXXXXXXXXXXXXXXXXXXXX"; // replace your yeelink api key here
char DeviceID[] PROGMEM = "XXX"; // replace your device ID


// 固定的网关

char website[] PROGMEM = "api.yeelink.net";


uint32_t lastConnectionTime = 0;          // last time you connected to the server, in milliseconds

//上传数据的字符串变量及字符串长度变量
char sensorData;
uint8_t dataLength;

uint8_t Ethernet::buffer;
Stash stash;

//ASM2302
DHT sensor = DHT();

//轮流向服务器发送相应传感器变量 0 <= i <N,
uint8_t i=0;//

//time
boolean timeNeedSet = true;
uint16_t timeSetTimer = 0;
#define TIME_SET_TIMER 4000 //about TIME_SET_TIMER*5s


void setup() {
Serial.begin(9600);
lcd.begin(16, 2);

// start serial port:
// start the Ethernet connection:
if (ether.begin(sizeof Ethernet::buffer, MyMac) == 0)
    Serial.println( "Failed to access Ethernet controller");

Serial.println("Setting up DHCP");
if (!ether.dhcpSetup())
    Serial.println( "DHCP failed");

ether.printIp("My IP: ", ether.myip);
ether.printIp("Netmask: ", ether.mymask);
ether.printIp("GW IP: ", ether.gwip);
ether.printIp("DNS IP: ", ether.dnsip);
ether.dnsLookup(website);
sensor.attach(DHT_PIN);

pinMode(DSM_PIN_10, INPUT);

}

void loop() {
if (!ether.dhcpValid() && !ether.dhcpSetup())
Serial.println("DHCP failed");
ether.packetLoop(ether.packetReceive());
if( millis() < lastConnectionTime || millis() - lastConnectionTime > PostingInterval){
    Serial.println(i);
    get_Send_String(i);
    send_Data(i);
    i++;
    if (i == N) i=0;
}
else dsmStatistics();
}

void send_Data(uint8_t j) {
// Create a Post for yeelink server,
// and send request saving sessionID
Serial.println(sensorData);
Serial.println(SensorID);
Serial.println();
Stash::prepare(PSTR("POST /v1.0/device/$F/sensor/$D/datapoints HTTP/1.1" "\r\n"
    "Host: api.yeelink.net" "\r\n"
    "U-ApiKey: $F" "\r\n"
    "Content-Length: $D" "\r\n"
    "Content-Type: application/x-www-form-urlencoded" "\r\n" "\r\n"                     
    "$S" "\r\n"),
DeviceID,SensorID,APIKey,dataLength,sensorData);

if(timeNeedSet){
    setupTime();      
}
else{
    ether.tcpSend();
    displayClock();
}
lastConnectionTime = millis();
}

void switchtoEthercard(){
word len;
do{
    len = ether.packetReceive();
    ether.packetLoop(len);
}
while(len != 0);
}


#define REQEST_TIMEOUT 10000

void setupTime(){
lcd.setCursor(0,1);
lcd.print("                ");
lcd.setCursor(0,1);
lcd.print("Update time...");
uint8_t session_id;
session_id = ether.tcpSend();
uint32_t requestTimer = millis() + REQEST_TIMEOUT;
while(millis() < requestTimer){
    ether.packetLoop(ether.packetReceive());
    char bufx;
    const char* reply = ether.tcpReply(session_id);
    if(reply !=0){
      for(uint8_t I=0;I<21;I++){
      bufx=reply;      
      }
      tmElements_t time1;
      timeconvert(bufx,&time1);
      time_t second = makeTime(time1);      
      setTime(second);
      adjustTime(8*60*60);
      lcd.print("OK");
      timeNeedSet=false;
      break;
    }
}
}
void displayClock(){
// digital clock display of the time
timeSetTimer++;
if (timeSetTimer>TIME_SET_TIMER) {
    timeSetTimer=0;
    timeNeedSet=true;
}
lcd.setCursor(0,1);
lcd.print(year());
lcd.print("-");
printDigits(month());
lcd.print("-");
printDigits(day());
lcd.print(" ");
printDigits(hour());
lcd.print(":");
printDigits(minute());
}
void printDigits(int digits){
// utility function for digital clock display: prints preceding colon and leading 0
if(digits < 10)
    lcd.print("0");
lcd.print(digits);
}


//比较周数,成功返回0-6的数,错误返回7
//p代表周数,取周数前3个字母,如Mon代表周1,以此类推
//改动周几不影响返回的时间值,可以通过改动日期的日数来达到修改时间

//比较月份,成功返回0-11的数,错误返回12
//P 为月份的前三个字母,如Feb代表二月,以此类推
int monthcmp( IN char *p)
{
char *month[]={
    "Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"            };

int i;
for(i=0;i<12;i++){
    if(strcmp(p,month)==0)
      break;
}
if(i==12)
{
    return i;
}
return i;
}
//将字串格式的时间转换为结构体,返回距离1970年1月1日0:0:0的秒数,当字符串格式错误或超值时返回0
//BUF 为类似Tue May 15 14:46:02 2007格式的,p为时间结构体
void timeconvert(IN char *buf,OUT tmElements_t *p)
{
char cmonth;
int16_t td,th,tm,ts,ty;
sscanf(buf,"%d %s %d %d:%d:%d",&td,cmonth,&ty,&th,&tm,&ts);
//sscanf("2012 Aug " ,"%4d %s",&ty,cmonth);
p->Year = ty - 1970;
p->Month = monthcmp(cmonth) + 1;
p->Day = td;
p->Hour = th;
p->Minute = tm;
p->Second = ts;
}


//dsm501统计变量
uint32_t dsmStatTimes10 = 0; //DSM
uint32_t sumTimeofLow10 = 0;
uint32_t dsmStatTimes25 = 0;
uint32_t sumTimeofLow25 = 0;
uint8_t switch1025 = 0;
//递推滤波变量
#define FILT_N 10
#define FILT_I 2
uint8_t filtI={
0,0};
uint16_t filtDsm;
uint32_t filtSum = {
0,0};

void dsmStatistics(){
if(switch1025){
    dsmStatTimes10 ++;
    sumTimeofLow10 += !digitalRead(DSM_PIN_10);
    switch1025 = 0;
}
else{
    dsmStatTimes25 ++;
    sumTimeofLow25 += !digitalRead(DSM_PIN_25);
    switch1025 = 1;
}
}

uint16_t filteringDsm(uint8_t l,uint16_t _dsmData){
filtSum =filtSum- filtDsm];
filtSum =filtSum+ _dsmData;
filtDsm] = _dsmData;
filtI=filtI+1;
if (filtI >= FILT_N) filtI = 0;
return filtSum/FILT_N;
}



float dsmTemp25;

void get_Send_String(uint8_t k){
switch(k){
case 0: //DSM501
    {
      float dsmTemp0 = (sumTimeofLow10*100.0)/dsmStatTimes10;
      float dsmTemp10 = 0.1776*pow(dsmTemp0,3) - 0.24*pow(dsmTemp0,2) + 94.003*dsmTemp0;
      lcd.setCursor(12,0);
      lcd.print(dsmTemp10);
    dataLength = sprintf(sensorData,"{\"value\":%d}",filteringDsm(k,int(dsmTemp10)));
      sumTimeofLow10=0;
      dsmStatTimes10=0;
      dsmTemp0 = (sumTimeofLow25*100.0)/dsmStatTimes25;
      dsmTemp25 = 0.1776*pow(dsmTemp0,3) - 0.24*pow(dsmTemp0,2) + 94.003*dsmTemp0;
      dsmTemp25 = dsmTemp10-dsmTemp25;
      sumTimeofLow25=0;
      dsmStatTimes25=0;
      switchtoEthercard();
      break;
    }
case 1://pm25
    {
      dataLength = sprintf(sensorData,"{\"value\":%d}",filteringDsm(k,int(dsmTemp25)));
      break;
    }
case 2://Asm2302 temperature
    {
      sensor.update();
      uint16_t tempData = (sensor.getTemperatureInt() << 8) | sensor.getTemperatureFrac();
      lcd.setCursor(0,0);
      lcd.print(tempData/10);
      lcd.print(".");
      lcd.print(tempData%10);
      lcd.print("C");
      format_String(k,tempData);
      break;
    }
case 3://Asm2302 humidity
    {
      sensor.update();
      uint16_t humiData = (sensor.getHumidityInt() << 8) | sensor.getHumidityFrac();
      lcd.setCursor(6,0);
      lcd.print(humiData/10);
      lcd.print(".");
      lcd.print(humiData%10);
      lcd.print("%");
      format_String(k,humiData);
      break;
    }

}
}

void format_String(uint8_t l,uint16_t sensor_Data){
uint16_t Tc_100 = sensor_Data;
uint8_t whole, fract;
whole = Tc_100/10 ;// separate off the whole and fractional portions
fract = Tc_100 % 10;
dataLength = sprintf(sensorData,"{\"value\":%d.%d}",whole,fract);
}
同样对洞洞板电路简单说下,左边14*2排针是接的ft232Rl用转接板接的。 4个圆形排母其实是打眼固定enc28j60模块,下面2*2排针接方形usb口(和uno那个口一样);右边4*1排针接dsm501a,3*1排针接dht22(asm2302),5*2排针接enc模块,下面16*1接lcd1602,缺口是安dsm501A的,整个装在11.5cm宽5.5cm高7深的铝合金盒子中。整个arduino相当于一个nano,刷的nano 328的bootloader。

幻生幻灭 发表于 2012-8-29 08:51:25

还没看过第一弹,先占个沙发吧。看标题很有货的感觉

erjiang 发表于 2012-8-29 09:00:38

牛逼!张老师威武啊!

erjiang 发表于 2012-8-29 09:07:25

还凑合这个是什么情况...Yeelink不凑和!{:soso_e130:} 幸福与金钱无关,与内心相连,刚刚的...

zcbzjx 发表于 2012-8-29 09:40:42

还凑合是督促进步嘛,不要自满!

erjiang 发表于 2012-8-29 09:55:53

张老师多批评指导..{:soso_e183:},哪里不对咱们改改改,呵呵

zcbzjx 发表于 2012-8-29 10:43:05





































极客工坊太抠了,3M的图片都说太大。{:soso_e118:} ,放163相册了!
点击查看更多高清图片

davidce 发表于 2012-8-29 22:04:18

盒子很漂亮

弘毅 发表于 2012-8-29 23:44:05

zcbzjx 发表于 2012-8-29 10:43 static/image/common/back.gif
极客工坊太抠了,3M的图片都说太大。 ,放163相册了!
点击查看更多高清图片

额。。。压缩压缩嘛。。。。现在照片限制的是400K~~~~有没有必要。。开放高清无码大图呢{:soso_e113:} 163的高清图不错。。。我帮你全转过来,哈哈

zcbzjx 发表于 2012-8-30 06:13:13

谢谢 郝老板!

acdc0616 发表于 2012-8-30 09:35:55

高科技啊!具体怎么用,怎么操作能在讲的详细些吗。

zcbzjx 发表于 2012-8-30 10:33:11

插上usb和网线就行了啊,不用操作。

erjiang 发表于 2012-8-30 10:38:28

这个产品特点就是一个按钮也没有...

acdc0616 发表于 2012-8-30 14:22:04

本帖最后由 acdc0616 于 2012-8-30 14:24 编辑

eagle6.1.0是破解版吗?我安装完了,怎么还要选择试用的还是注册的呢,软件里好多英文。。。晕:dizzy:
在附送个eagle视频教程http://eagle.timll.com/eagle_video_1.asp

zcbzjx 发表于 2012-8-30 18:07:02

我们用免费版本就够了,现在基本掌握了用法,还可以自己做库文件了,就是不太清楚如何布局和走pcb线比较合理
页: [1] 2 3 4
查看完整版本: 【第二弹】基于dht22+dsm501A+enc28J60+arduino+lcd1602+yeelink+实现家居环境监控