65.9K
CodeProject 正在变化。 阅读更多。
Home

Arduino 半智能烘干机控制

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.83/5 (14投票s)

2014 年 3 月 10 日

CPOL

4分钟阅读

viewsIcon

33848

downloadIcon

337

如何控制浴室里的烘干机

引言

我一直关注着我们浴室的相对湿度。最近我们买了一个空气干燥器,但我对这个设备的开关周期非常不满意。虽然我将其设置为 70 或 80% 相对湿度,但它开启的时间非常短,并且(有时)我并没有看到这么高的湿度。我已经在 433MHz 频段内驱动了一个无线传感器网络,并将三个传感器的读数填充到一个数据库中。我添加了一个使用 Arduino Uno R3 和 DHT22 传感器以及 433MHz 发射器的传感器,这样我也可以跟踪浴室的温度和湿度值。

通常,控制湿度不能简单地通过观察当前的相对湿度百分比,然后打开或关闭干燥器或风扇来实现。相对湿度百分比会随着温度变化而变化,并且取决于外部湿度。例如,如果外部相对湿度为 75% 且房间未密封,则房间内将无法达到 50% 的相对湿度 (RH)。

我决定用比仅在特定湿度水平下开启/关闭更好的逻辑,通过 Arduino 控制空气干燥器。

背景

当浴室开始淋浴时,湿度会迅速增加。我希望 Arduino 能够识别出这种峰值变化,并将干燥器打开到最长时间。

由于 Arduino 已经配备了 433MHz 发射器,并且有一些电源开关也受 433MHz 控制,并且我想避免与高压接触,所以我决定使用同一个发射器来控制电源。

所以我们有 Arduino、DHT22(DHT11 太差了)和一个 433 MHz 接收器。 为了获得一些视觉反馈,我添加了一个 LCD,但这对于该功能来说不是必需的。

Using the Code

该代码使用两个缓冲区来存储最近的湿度测量值。仅保留最新的值。每次推送新值时,都会删除最旧的值。我使用一个“短时”缓冲区,该缓冲区每分钟更新一次。第二个缓冲区是长时缓冲区(内存),每次达到第一个缓冲区的容量时都会填充。 所以假设我在第一个缓冲区中存储五个值,那么第二个缓冲区将在每第五次更新时更新。

如果最新值高于最后一个值的阈值并且值正在增加,我将设置一个状态标志,该标志表示干燥器的供电。 目前未使用第二个缓冲区,但在湿度低于正常长时间值时,可用于关闭干燥器。

我实现了一个基类(使用循环缓冲区的代码)并在我的新类中init了这两个缓冲区类。

ByteBuffer.h(类似'stack'的缓冲区)

#include <stdlib.h>

#ifndef BYTEBUFFER_H
#define BYTEBUFFER_H

#define byte unsigned char

class ByteBuffer
{
public:
    ByteBuffer();

    // This method initializes the datastore of the buffer to a certain sizem the buffer should NOT be used before this call is made
    void init(unsigned int buf_size);

    // This method resets the buffer into an original state (with no data)
    void clear();

    // This releases resources for this buffer, after this has been called the buffer should NOT be used
    void deAllocate();

    // Returns how much space is left in the buffer for more data
    int getSize();

    // Returns the maximum capacity of the buffer
    int getCapacity();

    // This method returns the byte that is located at index in the buffer but doesn't modify the buffer like the get methods (doesn't remove the retured byte from the buffer)
    byte peek(unsigned int index);

    //
    // Put methods, either a regular put in back or put in front
    //
    int putInFront(byte in);
    int put(byte in);

    int push(byte in);    //HGO
    byte getMedian();    //HGO
    byte getMax();    //HGO
    byte getMin();    //HGO
    byte getState();    //HGO
    void setTreshold(byte t);    //HGO
    byte getTreshold();    //HGO
    int getDirection();
    int getStateDuration();

    int putIntInFront(int in);
    int putInt(int in);

    int putLongInFront(long in);
    int putLong(long in);

    int putFloatInFront(float in);
    int putFloat(float in);

    //
    // Get methods, either a regular get from front or from back
    //
    byte get();
    byte getFromBack();

    int getInt();
    int getIntFromBack();

    long getLong();
    long getLongFromBack();

    float getFloat();
    float getFloatFromBack();

    void setMaxDuration(int);

protected:
    byte* data;

    unsigned int capacity;
    unsigned int position;
    unsigned int length;
    byte state;
    byte treshold;

    int stateDuration;  //start if state=1 for
    int maxDuration;    //max duration (ie 5)
    int direction;
};

#endif // BYTEBUFFER_H 

这个类被使用了两次,一次用于 NewBuffer 中的长时存储,一次用于短时存储

#ifndef NEWBUFFER_H
#define NEWBUFFER_H

#include "ByteBuffer.h"


class newBuffer
{
    public:
        newBuffer();
        ~newBuffer();
        void init(int s);
        ByteBuffer bytebuffer1;
        ByteBuffer bytebuffer2;

        byte peek1(unsigned int index);
        byte peek2(unsigned int index);
        int getSize1();
        int getSize2();
           byte getMedian1();    //HGO
        byte getMax1();    //HGO
        byte getMin1();    //HGO
        byte getState1();    //HGO
        byte getTreshold1();    //HGO

           byte getMedian2();    //HGO
        byte getMax2();    //HGO
        byte getMin2();    //HGO
        byte getState2();    //HGO
        void setTreshold(byte t);    //HGO
        byte getTreshold2();    //HGO

        int getDirection1();
        int getStateDuration1();
        int getDirection2();
        int getStateDuration2();
        void setMaxDuration1(int);
        void setMaxDuration2(int);

        int put(byte in);
        int push(byte);

    protected:
        int iCount;
        byte treshold;
        int capacity;

        int maxDuration;    //max duration (ie 5)
    private:
};

#endif // NEWBUFFER_H

     

您会看到有用于从短时缓冲区和长时缓冲区获取值的 getter。

当新值被推送到堆栈时,主要工作就完成了

int newBuffer::push(byte in){
    //static byte lastByte;
    iCount++;
    bytebuffer1.push(in);
    if(iCount==capacity){
        bytebuffer2.push(in);
        iCount=0;
    }
    return 0;
}  

您会看到第二个(长时间)缓冲区仅每 x 次获得一次推送。

Arduino 草图

...
void setup(){
  Serial.begin(SERIAL_BAUD);
  Serial.println("ThermoHygroTransmitter version 4 (power save 2)");
  sensor.setup(DHT_DATA_PIN);
    
  sleepTime=10000; //10 seconds 
  
  pinMode(lcd_backlight, OUTPUT);

  lcd.begin(16, 2);
  // Print a message to the LCD.
  lcd.print("Temp/Feuchte"); //12 chars
//  lcdOFF();  
  lcdON();
  // Initialize the send buffer that we will use to send data
  //ByteBuffer send_buffer;
  send_buffer.init(5);
  send_buffer.setTreshold(5);
  send_buffer.setMaxDuration1(10);

  //init RCswitch instance
  mySwitch = RCSwitch();
  // Transmitter is connected to Arduino Pin #3  
  // shared with SensorTransmitter code 
  mySwitch.enableTransmit(TX_DATA_PIN);
  // Optional set protocol (default is 1, will work for most outlets)
  mySwitch.setProtocol(1);
  
  // Optional set number of transmission repetitions.
  //mySwitch.setRepeatTransmit(2); //stops working!
  
  //initialy switch power outlet OFF
  switchOnOff(false);
}

void loop(){
  //do stuff
    animateLCD();
    Serial.println("...waked up");
    float humidity = sensor.getHumidity();
    float temperature = sensor.getTemperature();
    if(strcmp(sensor.getStatusString(),"OK")==0){
      bValidData=true;
      lastTemp=(int)temperature;
      lastHumi=(int)humidity;     
      showLCD(lastTemp, lastHumi, send_buffer.getState1());
    }
    else{
      bValidData=false;
    }
    Serial.print("sendCount="); Serial.println(sendCount);
    if(sendCount >= sendInterval){
      sendCount=0;
     // Displays temperatures from -10 degrees Celsius to +20,
     // and humidity from 10% REL to 40% REL, with increments of 2
     if(bValidData) {

        send_buffer.push((byte)lastHumi);
        state=send_buffer.getState1();
        //send switch command
        if(state==0)
          switchOnOff(false);
        else
          switchOnOff(true);
          
        int x=0;
        Serial.println("============================");
        for(x=0; x<send_buffer.getSize1(); x++){
          Serial.print("["); 
          Serial.print(send_buffer.peek1(x));
          Serial.print("]"); 
        }
        Serial.println("\n============================");

       // Temperatures are passed at 10 times the real value,
       // to avoid using floating point math.
       ///transmitter.sendTempHumi(i * 10, i + 20);
       //HACK: send temp with ending in 5 (which is .5 degree clesius if state==1
       // if state==0 send temp with ending in 0
       if(state==1)
         transmitter.sendTempHumi(lastTemp * 10 + 5, lastHumi);
       else
         transmitter.sendTempHumi(lastTemp * 10, lastHumi);
       
       Serial.println("Transmit done");
       blinkLEDfast();
       // Wait two seconds before sending next.
       bValidData=false;
       showLCD(lastTemp, lastHumi, state);
       
     }//validData
   }//send_loop
   //delay(1000);
  sendCount++;
  blinkLED();

  Serial.println("About to sleep...");
  delay(200);
  sleep.pwrDownMode(); //set sleep mode
  sleep.sleepDelay(sleepTime); //sleep for: sleepTime
}

这是新测量的相对湿度值的推送以及 state 属性的检查,以打开/关闭插座

        send_buffer.push((byte)lastHumi);
        state=send_buffer.getState1();
        //send switch command
        if(state==0)
          switchOnOff(false);
        else
          switchOnOff(true);

当推送一个新值时,我们做一些计算并使用 state 变量启动/继续干燥风扇的供电

int ByteBuffer::push(byte in){
    if(length < capacity){
        // save data byte at end of buffer
        data[(position+length) % capacity] = in;
        // increment the length
        length++;
//        return 1;
    }
    else{
        //move all one down
        unsigned int i=0;
        for(i=0; i<length-1; i++)
            data[i]=data[i+1];
        data[length-1]=in;

    }
    //check if to switch FAN on and keep for nexxt 5 iteratrions
    if( ((getMax()-getMin())>treshold) && (getDirection()>0) ){
        //set state=1 only if stateDuration is less then maxDuration
        if(stateDuration<maxDuration){
            state=1;
        }
    }
    if(state==1 || stateDuration>0)
        stateDuration++;    //increment stateDuration
    if(stateDuration>maxDuration){
        if(state==1){
            state=0;
        }
        stateDuration=0;
    }
     return 0;
}

上面的代码首先将新值推送到堆栈上。 如果堆栈相对于最大值数量已满,则会删除最旧的值。

然后,我们检查所有值的最大值和最小值是否大于阈值,以及该值是否显示增加。 如果是这样,我们开始设置 state 变量。 state 变量用于控制干燥风扇的供电,并且将保持设置状态 maxDuration 推送周期。 因此,我们不会根据这些值直接打开/关闭风扇,而是实现后续时间。 由于我们每分钟推送一个新值,并且 maxDusration9,因此我们的后续时间为 9 分钟。

由于我将所有值记录(发送)到 php/mysql Web 服务器,因此我可以控制草图的功能

如您所见,该状态为插座供电了三次,每次都达到了湿度增加的阈值。 早上我们有三个人淋浴。 ;-)

目前,我正在使用 netio 板 (Pollin.de) 接收无线传感器的温度和湿度,并将数据转发到可以生成上述图形的 Web 服务器。

该 netio 板通过以太网连接,并通过一个简单的 433MHz 接收器接收无线传感器

致谢

待办事项

  • 清理 ByteBuffernewBuffer 中未使用的代码
© . All rights reserved.