macsbug

Just another WordPress.com site

ESP8266+BME280+IR LED+Air Con+ThigSpeak

leave a comment »

ESP8266+BME280+赤外線LEDでエアコンを自動化しThigSpeakに表示しました。    2015.12.01

この装置は、エアコンを買い替える事無く高機能化する事ができます。


寒くなるこのごろ、なんとかエアコンを自動化できないか。
我が家のエアコンは、温度設定くらいはできますが、室温をモニターしながらの動作が出来ないとか、
おはようボタンは 「これから6時間後に入る」で希望の時間を設定出来ず、使い物になりません。
そこで、BME280センサーで温度を検知し、赤外線LEDでエアコンを操作。
又、温度変化とエアコンの「暖房」「停止」状態を ThingSpeakでモニターできるようにしました。
日時(NTP)機能もあり、必要ならば希望の時間にエアコンをオンやオフにする事も可能です。
機能:日時(NTP)、温度、湿度、気圧の表示、エアコンの自動化、ThingSpeakでグラフ表示。


温度設定は、20〜21度(+-0.5℃)。この間でエアコンの「暖房」と「停止」を自動操作します。
結果:グラフに示すように、エアコンのオン・オフに合わせ1度の範囲で自動操作ができています。


準備:
以下の記事からハードを構成(ESP8266+BME280+OLED+NTP+赤外線LED+ThingSpeak)しました。
BME280    :ESP8266+BME280+OLED+BOX
OLED        :NTPcube ( ESP8266 + OLED )
NTP           :ESP8266 + NTPCliant  で時刻を取得し OLED に表示する
赤外線LED:赤外線LEDリモコンの制作Arduinoでリモコン作成(@eiKatou Blog氏に感謝)
_              リモコンデータを取得をする。私の場合は、RAR-3M1 HITACHI 日立 エアコンリモコン です。
ThignSpeak:ESP と BME280 と ThingSpeak で IOT


問題点:
1. ESP8266の容量不足:リモコンデータは、かなりメモリーを使用します。
_  2つの変数を使用していますが3つ使用すると動作せず多数のリモコンボタン機能は実現できません。
_  ESP8266は、Arduinoに比べ容量が多いと聞いていましたが条件によってはそうではありませんでした。
_  Arduino UNOでは、このような変数は1つしか使用できず、2つ以上ではメモリーオーバーになる。
_  スケッチの記述を工夫するか、SD CARD を使用すれば可能かも知れません。
2. エアコンの状態を直接モニターしていない為、誤動作を検知できない。
_  赤外線LEDの出力が何らかの原因で閉ざされた場合、信号が届かずエアコンの状態を把握できない。
_  エアコンのオン・オフ状態:スケッチでは最初にエアコンをオフし「オフ」と定義している。
_  何らかの原因で、エアコンのオン・オフの状態が変わるとスケッチは、誤解する。
3. 誤動作に対するスケッチの工夫が必要。たとえば、温度変化から状態をどうするかの判断をする。
4. 赤外線LEDの指向性は狭く、方向によってはエアコンで受信できない時があります。
5. 通常のリモコンと今回の装置によるエアコンの消費電力の差は調べていません。


感想:
1. リモコンは便利とはいえ、自動機能が無いとか、おはようボタンは6時間後とか不便です。
_  希望は自動的に23度に一定にして欲しいとか、朝の5時にオンして欲しいのです。
_  リモコンやスマホでコントロールできるとかは面倒で、環境をセンスして自動的にやって欲しい感じ。
2. エアコンの種類:年代やタイプによって機能が少ない物がある事と早々に交換する訳には行かない。
_  2015年最新式、デロンギの MD HEATER(75,000円)に示す ±0.5℃も可能です。
3. 現在のエアコンを買い替えずに高機能化できます。
4. エアコンの自動化を体験すると、実に気が楽になります。
5. 我が家の老朽化したアルミサッシの影響か外気温にもよりますが、30分後に1度下がっていますね。
6. 機能のグレードアップ:温度の設定をボタンで設定できるようにするとか、時間になったら動かすとか、
_  省エネ動作とか、しばらく使用してみて次の案を考えたいと思います。


スケッチ:


// NTP, TEMPERATURE, HUMIDITY, PRESSURE, AIRCON  // 2015.11.30
#include <ESP8266WiFi.h>                         // WiFi
#include <WiFiUdp.h>                             // WiFi
#include <Wire.h>                                //
#include <SPI.h>                                 //
#include <BME280_MOD_1022.h>                     // BME280
#include <Adafruit_GFX.h>                        // OLED
#include <ESP_Adafruit_SSD1306.h>                // OLED
#include <Time.h>                                //
#define OLED_RESET 4                             // OLED
Adafruit_SSD1306 display(OLED_RESET);            // OLED
extern "C" {                                     //
  #include "user_interface.h"                    //
}                                                //
char ssid[] = "xxxx";                            // your network SSID
char pass[] = "xxxx";                            // your network password
const char thingSpeakurl[] = "api.thingspeak.com";// THINGSPEAK URL
const char thingSpeakAPIKey[] = "xxxx";          // THINGSPEAK API
unsigned int localPort = 2390;                   // local port to listen
IPAddress timeServer(129, 6, 15, 28);            // time.nist.gov NTP server
const int NTP_PACKET_SIZE = 48;                  // NTP time : first 48 bytes
byte buff[ NTP_PACKET_SIZE];                     // incoming outgoing packets
WiFiUDP udp;                                     // UDP:send & rx packets
long t = -6.32;                                  // BME280 Temp     offset
long h = +12.64;                                 // BME280 Humidity offset
long p = +5.52;                                  // BME280 Pressure offset
int  w = 32;                                     // wifi count
int  ac = 0;                                     // aircon mode 0=off, 1=on
float tempset  = 21.0;                           // Center Temp
float lo    = tempset - 0.5;                     // lower  temp
float hi    = tempset + 0.5;                     // upper  temp
float maxt  = tempset + 1.0;                     // max    temp
float mint  = tempset - 1.0;                     // min    temp
int cc = 5;                                      // max,min temp check count
int ir = 12;                                     // air conn pin no : GPIO_12
unsigned int acon[] = {335,167,43,124,44,43,40,43,41,44,39,44,40,43,40,
  43,40,43,40,43,40,43,40,43,40,43,40,125,44,42,40,43,40,43,40,43,40,
  43,40,43,40,43,40,43,40,43,40,44,39,43,40,44,40,44,40,45,38,45,39,43,
  41,43,40,125,43,43,40,125,43,124,43,125,43,124,43,125,43,124,44,43,
  40,125,43,125,43,124,44,124,43,124,44,125,42,124,43,125,42,125,43,44,
  40,43,40,44,39,43,41,43,40,43,40,43,40,43,40,43,40,43,40,125,44,124,
  43,43,40,43,40,125,43,125,43,124,44,124,43,44,40,44,40,125,43,124,43,
  43,40,43,40,44,40,125,43,43,40,43,41,126,40,43,40,43,40,125,43,124,
  44,43,40,125,43,124,43,43,40,125,43,124,43,43,40,125,43,124,43,43,40,
  44,40,125,43,43,40,44,39,43,40,44,40,43,41,124,44,124,44,43,40,125,
  43,125,43,124,44,43,40,43,40,124,43,43,40,43,40,124,43,125,43,40,43,
  125,43,124,44,43,40,125,43,124,43,43,40,43,41,124,44,42,41,43,41,44,
  40,45,39,43,40,43,40,43,40,44,40,124,43,125,43,124,44,124,43,124,44,
  124,44,124,44,125,43,43,40,44,40,44,40,44,39,43,40,43,40,43,41,43,40,
  125,43,125,44,123,44,125,43,124,44,124,43,124,44,124,44,43,40,43,40,
  44,40,44,40,43,40,43,40,44,39,44,40,125,43,124,44,124,43,125,43,125,
  43,124,44,124,44,124,44,43,40,43,40,44,40,43,40,43,41,43,40,43,40,43,
  41,125,42,125,44,123,44,124,44,124,43,124,44,125,43,124,43,43,40,43,
  40,44,40,44,40,43,40,43,41,44,39,44,39,125,43,125,43,124,43,125,43,
  124,44,124,43,125,43,124,44,43,40,125,43,124,44,43,40,43,41,125,42,
  43,40,44,39,125,43,43,40,43,40,125,43,124,44,43,40,125,43,124,44,124,
  43,40,43,43,40,44,40,125,43,43,40,125,43,124,43,43,40,125,43,124,44,
  124,44,43,41,124,43,43,40,43,40,44,40,43,40,43,41,43,40,45,39,43,40,
  43,40,43,41,124,44,124,43,125,43,124,44,124,43,125,43,124,44,124,43,
  43,40,43,41,44,39,43,40,43,40,43,40,43,40,43,41,124,44,124,44,124,43,
  124,43,124,44,125,43,125,43,124,43};           // Air con (Heating)
unsigned int acoff[] = {336,167,43,125,43,41,42,41,43,41,42,41,42,41,
  42,41,43,41,42,41,43,41,42,41,42,41,44,124,43,41,42,41,42,41,42,41,
  43,41,43,41,42,41,43,41,42,41,43,40,43,41,43,41,42,41,42,41,43,40,43,
  42,42,41,43,125,43,41,43,124,43,125,44,124,43,125,43,126,42,125,43,
  41,43,125,42,125,43,125,42,125,43,125,43,125,43,125,43,125,43,125,43,
  41,42,41,43,41,43,41,42,41,43,41,42,41,42,41,43,41,43,41,42,125,43,
  126,42,41,42,41,42,125,43,125,43,125,43,125,43,41,42,41,43,125,43,
  125,42,41,42,41,42,41,43,125,43,41,43,41,42,125,43,41,42,41,42,125,
  43,125,42,41,42,125,43,125,42,41,42,125,43,125,43,40,42,125,43,125,
  43,41,42,41,42,125,43,41,42,41,42,41,43,41,42,41,43,125,43,125,43,
  40,43,125,43,125,42,125,43,40,43,41,42,125,43,41,42,41,43,125,43,
  125,42,41,42,125,43,125,42,41,42,125,43,125,43,40,43,41,43,125,43,
  40,43,41,42,41,42,41,42,41,43,44,40,41,43,41,43,125,43,125,44,124,
  43,125,42,125,42,125,43,124,43,125,42,41,42,41,43,44,40,41,43,41,
  42,42,42,41,43,43,40,125,43,125,44,124,44,124,43,124,43,124,43,124,
  43,124,43,40,43,41,42,41,42,41,43,41,43,41,43,40,43,41,43,125,43,
  124,44,124,43,124,43,125,43,124,44,124,44,124,43,41,43,41,43,41,42,
  41,43,40,43,41,42,41,42,41,43,125,43,124,44,124,43,124,43,125,43,
  124,44,125,43,124,44,40,43,41,42,41,43,41,43,41,43,40,43,41,43,41,
  42,125,43,125,43,124,43,124,43,124,43,124,43,125,42,125,43,40,43,
  125,43,124,44,40,43,41,42,125,43,42,42,41,42,125,43,41,42,41,42,
  126,42,125,43,40,43,124,43,124,43,124,43,40,43,41,42,41,43,41,43,
  40,43,125,43,125,43,40,43,125,43,125,43,124,43,125,43,125,43,40,43,
  41,43,41,43,41,42,41,43,41,43,40,43,43,40,41,43,41,43,125,43,124,44,
  124,43,125,43,124,44,124,43,124,44,124,43,41,42,41,42,41,43,43,40,
  41,42,41,42,42,42,41,43,125,43,124,44,124,43,124,44,124,44,
  124,43,125,43,125,43};                         // Air Con (STOP)

void setup(){
  //Serial.begin(115200);
  pinMode(ir,OUTPUT);digitalWrite(ir,LOW);       // AirCon Pin off
  //---------------------------------------------// OLED SETUP
  Wire.begin(13, 14); delay(10);                 // I2C SDA,SCL
  display.begin(SSD1306_SWITCHCAPVCC,0x78>>1);   // I2C_ADDRESS
  display.clearDisplay();                        // all pixels off 
  display.setTextSize(1);                        // font sizes 1 to 8
  display.setTextColor(WHITE);                   // WHITE,BLACK,INVERSE
  display.setCursor(0,0);                        // location x,y
  display.display();                             // plotted items
  //---------------------------------------------// NTP WIFI SETUP
  display.print("Mac ");                         //
  display.print(WiFi.macAddress());              // Mac Address
  display.print("Connecting to ");               //
  display.println(ssid);display.display();       //
  WiFi.begin(ssid, pass);                        //
  while (WiFi.status() != WL_CONNECTED){         //
    delay(500);display.print(WiFi.status());     //
    display.display(); w--;                      //
    if (w<0){display.print("fail");              //
      display.display();goto p;}                 //
  }                                              //
  display.println("ok"); display.print("IP: ");  //
  display.println(WiFi.localIP());               //
  udp.begin(localPort);                          //
  display.print("Local port: ");                 //
  display.println(udp.localPort());              //
  display.display();                             //
  //---------------------------------------------// ThingSpeak check
  display.println("Connecting to ..");display.display();//
  connectWiFi();                                 // ThingSpeak conn
  display.println("Connecting ok");display.display();
p:delay(2000);
  //---------------------------------------------// BME280 Setup
  BME280.readCompensationParams();               // read NVM
  BME280.writeOversamplingTemperature(os1x);     // 1x over sampling
  BME280.writeOversamplingHumidity(os1x);        // 1x over sampling
  BME280.writeOversamplingPressure(os1x);        // 1x over sampling
  //---------------------------------------------//
  aircon_off();                                  // aircon off
}                                                //

void loop(){                                     //
  // --------------------------------------------// NTP
  if ( w > 0 ){                                  // with wifi ok
  sendNTPpacket(timeServer);delay(1000);         // send NTP server
  int cb = udp.parsePacket();                    // packet check
  if (!cb) { dispset(1,"no packet yet");}        // no packet
  else {                                         // packet ok : UTC
    udp.read(buff, NTP_PACKET_SIZE);             // read packet buffer
    unsigned long hW = word(buff[40],buff[41]);  // hiWord
    unsigned long lW = word(buff[42],buff[43]);  // lowWord
    unsigned long secsSince1900 = hW << 16 | lW; //
    const unsigned long seventyYears=2208988800UL;// NTP  time
    unsigned long t=secsSince1900-seventyYears;  // Unix time:
    t = t + 32400;                               // JST=UTC+9(9*60*60)
    setTime(t);                                  // Time set
    dispset(2,"");                               // date,time display
    display.print(year()); display.print('/');   // year
    display.print(month());display.print('/');   // month
    display.println(day());display.display();    // day
    display.setTextSize(4);display.setCursor(0,30);// UTC time at GMT 
    if (((t % 86400L)/3600)<10){display.print('0');}
    display.print((t % 86400L)/3600);            // hr
    display.print(':');                          //
    if (((t % 3600)/60)<10){display.print('0');} //
    display.println((t % 3600)/60);              // minute
    display.display();                           //
  }                                              //
  delay(5000);}                                  // end of with wifi ok 
  //---------------------------------------------// BME280
  BME280.writeMode(smForced);   delay(50);       // chip goes back to sleep
  while (BME280.isMeasuring()){;delay(50);}      // Measurering check
  BME280.readMeasurements();                     // read out the data
  float  t_  = BME280.getTemperature() + t;      // Temp     data
  String temp=String(t_);                        // Temp     data
  String humi=String(BME280.getHumidity() + h);  // Humidity data
  String pres=String(BME280.getPressure() + p);  // Pressure data
  //---------------------------------------------// OLED
  ALL( 2, temp, humi, pres);                     // All  Display
  THP( 4, temp, 2, "    C");                     // temp Display
  //---------------------------------------------// airconn cont,with limit
  if ( ac == 0 && t_ <= lo ){dispset(4,"AC ON"); // <=low_temp,aircon on
    aircon_on(); ac = 1; delay(3000);            // aircon on, on mode
  }                                              //
  if ( ac == 1 && t_ >= hi ){dispset(4,"AC OFF");// >=hi_temp,aircon off
    aircon_off(); ac = 0; delay(3000);           // aircon off, off mode
  }                                              //
  //---------------------------------------------// max,min limit
  if ( t_ <= mint | t_ >= maxt ){ cc-- ;}        // max or min counter 
  if ( t_ <= mint && cc > 0 && ac == 1 ){        // minimum action
    dispset(4,"AC ON"); aircon_on();             // min_temp,aircon on
    ac = 0; cc = 5; delay(3000);                 // on mode,counter reset
  }                                              //
  if ( t_ >= maxt && cc < 0 && ac == 0 ){        // maxmum action
    dispset(4,"AC OFF"); aircon_off();           // max_temp,aircon off
    ac = 1; cc = 5; delay(3000);                 // off mode,counter reset
  }                                              //
  String ap = String( ac,DEC);                   // aircon mode on/off
  //---------------------------------------------// OLED
  THP( 4, humi, 2, " %");                        // humi Display
  THP( 4, pres, 0 ," hpa");                      // pres Display
  //---------------------------------------------// ThingSpeak
  if ( w > 0 ){                                  // with wifi ok
  dispset(1,"connection ThingsPeak");            //
  connectWiFi();                                 // ThingSpeak connect
  dispset(0,"connection ok        ");            //
  updateThingSpeak( "1=" + temp + "&2=" + humi   // data to ThingSpeak
                 + "&3=" + pres + "&4=" + ap );  // Field1,2,3,4
  dispset(0,"Transmit ThingsPeak");delay(2000);  //
  }                                              //
  //---------------------------------------------// OLED 
  ALL( 2, temp, humi, pres);                     // All Display
  //---------------------------------------------//
  delay(60000UL);                                // 1 min delay
}                                                // End of Loop

unsigned long sendNTPpacket(IPAddress & address){// send NTP request
  memset(buff, 0, NTP_PACKET_SIZE);              //
  buff[0]=0b11100011; buff[1]=0;   buff[ 2]=6;   //
  buff[3]=0xEC;       buff[12]=49; buff[13]=0x4E;//
  buff[14]=49;        buff[15]=52;               //
  udp.beginPacket(address, 123);                 //
  udp.write(buff, NTP_PACKET_SIZE);              //
  udp.endPacket();                               //
}                                                //

void ALL(int f,String t,String h,String p){      // All Display 
  display.clearDisplay();display.setTextSize(f); //
  display.setCursor(0,0);                        //
  display.print(t);display.println(" C");        // Temp
  display.print(h);display.println(" %");        // Humi
  display.print(p);display.println(" mb");       // Press
  display.display();delay(3000);                 //
}                                                //

void THP(int f,String d,int c,String m){         // one Display
  int a = d.indexOf(".") + c;                    //
  String d_ = d.substring(0, a);                 //
  display.clearDisplay();display.setTextSize(f); //
  display.setCursor(0,0);                        //
  display.println(d_);display.println( m );      //
  display.display();delay(3000);                 // 
}                                                //

void dispset( int f, String d){                  //
  if ( f != 0 ){                                 //
   display.clearDisplay();display.setCursor(0,0);//
  }                                              //
  display.setTextSize(f);                        //
  if (d.length() > 0 ){ display.println(d);      //
  display.display();}                            //
}                                                //

void connectWiFi() {                             // WiFi connect
  if (WiFi.status() == WL_CONNECTED) {return;}   // WiFi Status check
  WiFi.begin(ssid, pass);                        //
  while(WiFi.status()!=WL_CONNECTED){delay(500);}// WiFi Status check
}                                                //

void updateThingSpeak(String data) {             // ThingSpeak
  WiFiClient client;                             //
  if (!client.connect(thingSpeakurl,80)){return;}//         
  client.print(F("GET /update?key="));           //
  client.print(thingSpeakAPIKey);                //
  client.print(F("&"));                          //
  client.print(data);                            // Field No + Sensor Data
  client.print(F(" HTTP/1.1\r\nHost: api.thingspeak.com\r\n\r\n"));
  client.println();                              //
}                                                //

void aircon_on() {                               // aircon on
  int dataSize = sizeof(acon) / sizeof(acon[0]); //
  for (int cnt = 0; cnt < dataSize; cnt++) {     // data
    unsigned long len = acon[cnt]*10;            // 0us unit:ON/OFF time
    unsigned long us = micros();                 //
    do { digitalWrite(ir, 1 - (cnt&1));          // cnt:even=ON,odd=OFF
         delayMicroseconds(8);                   // CAL GREQ 38kHz
         digitalWrite(ir, 0);                    // ON/OFF Time Adjust
         delayMicroseconds(7);                   //
    } while (long(us + len - micros()) < 0);     // Loop at TX time
  }                                              //
}                                                //

void aircon_off() {                              // aircon off
  int dataSize = sizeof(acoff)/sizeof(acoff[0]); //
  for (int cnt = 0; cnt < dataSize; cnt++) {     // data
    unsigned long len = acoff[cnt]*10;           // 0us unit:ON/OFF time
    unsigned long us = micros();                 //
    do { digitalWrite(ir, 1 - (cnt&1));          // cnt:even=ON,odd=OFF
         delayMicroseconds(8);                   // CAL GREQ 38kHz
         digitalWrite(ir, 0);                    // ON/OFF Time Adjust
         delayMicroseconds(7);                   //
    } while (long(us + len - micros()) > 0);     // Loop at TX time
  }                                              //
}                                                //


参考:
Arduinoでリモコン作成:@eiKatou Blog
赤外線リモコンの通信フォーマット
赤外線リモコンをハック!Arduinoからエアコンを起動する
純正超え!韓国製エアコン用リモコン「UMA-ACRM02」を買ってみた
なぜエアコンのリモコンは学習できないのか?


 

Written by macsbug

11月 29, 2015 @ 12:29 am

カテゴリー: ESP8266

コメントを残す

以下に詳細を記入するか、アイコンをクリックしてログインしてください。

WordPress.com ロゴ

WordPress.com アカウントを使ってコメントしています。 ログアウト / 変更 )

Twitter 画像

Twitter アカウントを使ってコメントしています。 ログアウト / 変更 )

Facebook の写真

Facebook アカウントを使ってコメントしています。 ログアウト / 変更 )

Google+ フォト

Google+ アカウントを使ってコメントしています。 ログアウト / 変更 )

%s と連携中

%d人のブロガーが「いいね」をつけました。