Archive for 11月 2015
ESP8266+BME280+IR LED+Air Con+ThigSpeak
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」を買ってみた
なぜエアコンのリモコンは学習できないのか?
赤外線LEDリモコンの制作
エアコンを操作する為に 赤外線LEDでリモコンを作りました。 2015.11.29
操作は ArduinoかESP8266 で行う為に 赤外線LEDに接続する抵抗値を計算しました。
赤外線LEDのスケッチやリモコン操作については
eiKatou:Arduinoでリモコン作成を参考にしてください。@eiKatou Blog氏に感謝。
東京お気楽カメラ:WROOM02を使った赤外線リモコン その1
_ 詳細に計算されています。東京お気楽カメラ氏に感謝。:2017.09.16 追記。
結論:
赤外線LEDは OSI5LA5113A。トランジスターは 2SC2655L-Y を使用します。
トランジスターの入力抵抗は 680 Ω。:2017.09.16 変更(1.1KΩ -> 680 Ω)。
トランジスターの出力抵抗は 10 Ω。
部品:
赤外線LED :OSI5LA5113A:IFP=1000mA :資料:秋月電子通商
トランジスター:2SC2655L-Y :コレクタ電流 2A:資料:秋月電子通商
抵抗 :10Ω。680Ω。:rev. 2017.09.16(1.1K -> 680)。
抵抗値の計算:rev : 2017.09.16( 1.1K -> 675 )
R1 = (3.3-0.6)/(500/120) = 675 Ω。 R2 = (5-(1.2+0.5))/500 = 7Ω。
赤外線LED(OSI5LA113A)は、電流消費大の為 ドライバーとしてトランジスターが必要。
赤外線LEDは 500mAから1A程度流す必要があります。
Arduinoの出力電流は 最大40mA。ESP8266の出力電流は 最大10mA です。
よって ESP8266 では LEDを直接駆動できません。
赤外線LEDの条件:500mA流す事とする。電流が少ないと伝達距離が少なくなります。
100mAで15センチ程です。
トランジスターの条件:1Aか2A程度が必要。そこで2A流せる 2SC2655L-Yを選択。
入力の条件:電圧は ESP8266 3.3V 。周波数は 38KHzでトランジスタの対応可能。
計算方法:赤外線LEDとトランジスターの規格から図面に示す内容で計算しました。
◎トランジスタ入力:ベース入力 の 抵抗の計算値は 675 Ωにて 680 Ωを接続。:rev.2017.09.16
◎トランジスタ出力:LEDに接続する抵抗の計算値は 7 Ωにて 10 Ωを接続。
準備:
Arduino:
Arduinoでリモコン:Arduinoでリモコン作成:
東京お気楽カメラ :WROOM02を使った赤外線リモコン その1::2017.09.16 追記。
以上で、部屋の隅からでもエアコンをリモコン操作できました。
メモ:
計算方法はこれで良いかと思いますが 間違っていたらごめんなさい。
ALPSのリモコンは、トランジスタとLEDの所に、10 Ω が付いていました。
2016.05.25 変更:OSI5LA113A -> OSI5LA5113A
2017.09.16 変更:式は良いが 3.3V を 5V で計算していたミスが判明。
_ 2of9 氏のご指摘により誤記の原因が判明しました。
_ 2of9 さん 大変ありがとうございます。感謝致します。
NTPcube ( ESP8266 + OLED )
NTP(Network Time Protocol) サーバ から、時刻情報を取得しOLEDに表示しました。 2015.11.17
仕組み:NTPcube が 下図の NTPクライアントになります。
準備:
ESP8266 + NTPCliant で時刻を取得し OLED に表示する。
ESP8266+BME280+OLED+BOX。
基本スケッチは、Ivan Grokhotkov氏に感謝。
SSIDとPassword は各自で設定の事。
ルーターの設定:ルーターにマックアドレスの設定が必要な場合は、事前にマックアドレスを調べるか
_ 電源ON時、OLEDにマックアドレスが表示されますので確認してください。(命令:WiFi.macAddress())。
感想:
NTPが何かを知りませんでしたが、ネットの時刻情報から時計が作れ小さなケースに入れると楽しいものです。
NTPは、本来PCの時計あわせに使うようですが、家の中の環境では、GPSや電波時計より時刻情報を得やすいです。
ネットにアクセスする為に、秒単位の表示は無理で分単位にしました。アクセスは 10秒ごとです。
ネットの負荷を少なくする為に、内蔵時計を使用し時刻あわせに NTP を使用した方が良いかとも思います。
内蔵時計の時刻あわせは、通信の時間やスケッチ上の時間遅れがあり、秒単位の設定は考慮が必要と思われます。
// Udp NTP Client #include <ESP8266WiFi.h> #include <WiFiUdp.h> #include <Wire.h> #include <SPI.h> #include <BME280_MOD_1022.h> #include <Adafruit_GFX.h> #include <ESP_Adafruit_SSD1306.h> #include <Time.h> #define OLED_RESET 4 Adafruit_SSD1306 display(OLED_RESET); extern "C" { #include "user_interface.h" } char ssid[] = "xxxx"; // your network SSID (name) char pass[] = "xxxx"; // your network password unsigned int localPort = 2390; // local port to listen for UDP packets IPAddress timeServer(129, 6, 15, 28); // time.nist.gov NTP server const int NTP_PACKET_SIZE = 48; // NTP time stamp:first 48 bytes byte packetBuffer[NTP_PACKET_SIZE]; // buffer:incoming and outgoing packets WiFiUDP udp; // UDP:send & receive packets over UDP void setup(){ //Serial.begin(115200); //---------------------------------------------// Wire.begin(13, 14); delay(10); // SDA,SCL(GPIO_13,GPIO_14) display.begin(SSD1306_SWITCHCAPVCC,0x78>>1); // I2C_ADDRESS display.clearDisplay(); // all pixels off display.setTextSize(1); // printable sizes 1 to 8 display.setTextColor(WHITE); // WHITE, BLACK, INVERSE display.setCursor(0,0); // location x,y display.display(); // plotted items //---------------------------------------------// display.print("Mac ");display.println(WiFi.macAddress());display.display(); display.print("Connecting to ");display.println(ssid);display.display(); WiFi.begin(ssid, pass); while (WiFi.status() != WL_CONNECTED){ delay(500);display.print(".");display.display();} display.println("ok"); display.print("IP: ");display.println(WiFi.localIP()); udp.begin(localPort); display.print("Local port: ");display.println(udp.localPort()); display.display();dealy(2000); } void loop(){ sendNTPpacket(timeServer);delay(1000);// send NTP packet time server & Wait int cb = udp.parsePacket(); if (!cb) {display.println("no packet yet");display.display();} else { udp.read(packetBuffer, NTP_PACKET_SIZE); // read packet into buffer unsigned long highWord = word(packetBuffer[40],packetBuffer[41]); unsigned long lowWord = word(packetBuffer[42],packetBuffer[43]); unsigned long secsSince1900 = highWord << 16 | lowWord; const unsigned long seventyYears = 2208988800UL; // Convert NTP time unsigned long epoch = secsSince1900 - seventyYears;// Unix time epoch = epoch + 32400; // UTC + 9 ( 9 * 60 * 60 ) // JST setTime(epoch); // Date Time set display.clearDisplay();display.setTextSize(2);display.setCursor(0,0); // Date display.print(year());display.print('/'); display.print(month());display.print('/'); display.println(day());display.display(); // Time display.setTextSize(4); if (((epoch % 86400L)/3600) < 10){display.print('0');} display.print((epoch % 86400L)/3600);display.print(':'); // day if (((epoch % 3600)/60) < 10 ){display.print('0');} display.println((epoch % 3600)/60); // minute //display.print(':');if ((epoch % 60) < 10 ){display.print('0');} //display.println(epoch % 60); // second display.display(); } delay(10000); // wait 10 seconds before asking for the time again } unsigned long sendNTPpacket(IPAddress& address){ // send an NTP request memset(packetBuffer, 0, NTP_PACKET_SIZE); packetBuffer[0] = 0b11100011;// LI, Version, Mode packetBuffer[1] = 0; // Stratum, or type of clock packetBuffer[2] = 6; // Polling Interval packetBuffer[3] = 0xEC; // Peer Clock Precision packetBuffer[12] = 49; // 8 bytes of zero for Root:Delay & Dispersion packetBuffer[13] = 0x4E; packetBuffer[14] = 49; packetBuffer[15] = 52; udp.beginPacket(address, 123);//NTP requests are to port 123 udp.write(packetBuffer, NTP_PACKET_SIZE); udp.endPacket(); }
ESP8266+BME280+OLED+BOX
ESP8266(ESP-WROOM-02等) + I2C BME280(温度、湿度、気圧センサー) + OLED 2015.11.14
温度、湿度、気圧を測る BME280センサーのデーターを ESP8266 で OLED に表示しました。
ケースのサイズは、49 x 49 x 42 mm の手のひらに乗る大きさで、なかなかいい感じになります。
![]() |
![]() |
![]() |
|
![]() |
|
![]() |
表示は、温度+湿度+気圧 -> 温度 -> 湿度 -> 気圧 を繰り返す。
基本回路:
基板の準備:ESPの基板画像は省略(右の基板のソケット上に装着する)。
USB Chrager の出力5Vから基板中央にあるレギュレーター(NJM2845DL1-33) により 3.3V を作る。
OLEDの固定:特殊な物はPolymorphで止める。これは数分で出来るので便利。
BME280センサー:左下の4ピンに縦に接続。
プログラミング:USB接続で外付けのコネクタ経由(中央上側の4ピンコネクタ)で行う。
書き込みボタン:外付けのコネクタ経由(中央下側の3ピンコネクタ)で行う。
電源:Logitech L-LY11 Battery pack + DLG-MRD-100 USB Chrager:出力の2本(上赤黒)は5VDC。
準備:
1. ESP8266をArduino IDE で書き込める状態である事。
2. ESP-WROOM-02 等。
3. I2C BME280:ebayから購入(846円)。
4. Battery:Logitech L-LY11 Battery pack:3.7V 600mA:ロジテックの無線マウスを分解して使う。
5. USB Chrager:DLG-MRD-100:以前、千石電商で特価品のモバイル充電器を分解して使う:500円。
6. 箱:3Dプリンターにて 制作。
7. Polymorph:湯温で軟化し常温で硬化する樹脂。ebayから購入。
8. 5V to 3.3V 800mAレギュレーター:NJM2845DL1-33、1uF,10uF,47uF。
9. 他:基板、コネクター、スイッチ、等々。
スケッチ:
// ESP8266 + I2C BME280 + OLED BOX // Using the SSD1306 Text/Graphics OLED Display module // http://engineeringnotes.blogspot.jp/2013/07/using-ssd1306-textgraphics-display.html #include <Wire.h> #include <SPI.h>; #include <BME280_MOD_1022.h> // BME280 Liblary #include <Adafruit_GFX.h> // OLED Liblary #include <ESP_Adafruit_SSD1306.h> // OLED Liblary #define OLED_RESET 4 // OLED RESET Adafruit_SSD1306 display(OLED_RESET); // OLED RESET extern "C" { #include "user_interface.h" } long t = -4.07; // BME280 Temp offset long h = +7.84; // BME280 Humidity offset long p = +4.52; // BME280 Pressure offset void setup() { Serial.begin(115200); // Serial TRx Wire.begin(13, 14); delay(10); // SDA,SCL(GPIO_13,GPIO_14) //---------------------------------------------// display.begin(SSD1306_SWITCHCAPVCC,0x78>1); // I2C_ADDRESS display.clearDisplay(); // all pixels off display.setTextSize(1); // printable sizes 1 to 8 display.setTextColor(WHITE); // WHITE, BLACK, INVERSE display.setCursor(0,0); // location x,y display.display(); // plotted items //---------------------------------------------// BME280.readCompensationParams(); // read NVM comp parameters BME280.writeOversamplingTemperature(os1x); // 1x over sampling BME280.writeOversamplingHumidity(os1x); // 1x over sampling BME280.writeOversamplingPressure(os1x); // 1x over sampling } void loop() { BME280.writeMode(smForced); delay(50); // chip goes back to sleep while (BME280.isMeasuring()){;delay(50);} // Measurering check BME280.readMeasurements(); // read out the data String temp=String(BME280.getTemperature()+t); // Temp String humi=String(BME280.getHumidity()+h); // Humidity String pres=String(BME280.getPressure()+p); // Pressure //---------------------------------------------// all( temp, humi, pres ); // All Display temp_humi_Pres( temp, 2, " C"); // temp Display temp_humi_Pres( humi, 2, " %"); // humi Display temp_humi_Pres( pres, 0 ," hpa"); // pres Display } void all(String temp,String humi,String pres){ // All Display display.clearDisplay();display.setTextSize(2); display.setCursor(0,0); display.print(temp);display.println(" C"); display.print(humi);display.println(" %"); display.println(""); display.print(pres);display.println(" mb"); display.display(); delay(3000); } void temp_humi_Pres(String data,int c,String m){ // one Display int a = data.indexOf(".") + c; String data_ = data.substring(0, a); display.clearDisplay();display.setTextSize(4); display.setCursor(0,0); display.println(data_);display.println( m ); display.display(); delay(3000); }
問題点:
1. 正しい温度とは:幾つかのセンサーで温湿度計を作った物や通常の製品と比較すると誤差が大きい。
_ 製品版でも差があるので、どうしたものかという事になる。
_ 測定する場所でも差がでてくる。これでは、小数点一桁のセンサーを使用しても意味が無くなってくる。
_ 今回は乱暴かもしれないが4個の温湿度計を集めて平均値の差をオフセット値として加えた。
2. 通気:製品版を参考にセンサー用にケース後ろに通気用の穴を開けた。
3. OLEDのフォントサイズ:フォントは拡大表示ができるが奇麗さがなくなる。
_ 奇麗に出す事のでるフォントはあるが、今回は採用せず。
感想:
1. 電源不要で最も安価な温度計は、百均にある。
2. 今回は、センサーの温度、湿度、気圧 表示だけにしました。
3. データーをSingSpeakに送るには、以前の記事を参考にスケッチを加えれば動きます。
4. ESPのWiFi機能を追加すればもっと楽しい物ができますね。
5. Logitech の L-LY11 Battery pack は、角のある形が良くかなり便利でしたし容量も満足できます。
_ ラジコン用のバッテリーの場合は、形に丸みがある事や電流容量、価格が問題になってくる。
6. 完成基板が欲しい:
_ ケースも含めた完成品があると、プログラムに集中できる事とアイデアが湧いてくる。
_ ブレットボードでの試作は動作確認できてよいが、プログラム制作の集中度が低くなる訳です。
_ こうして体験すると ESP には完成品がなく電子工作する上で時間を要したり完成度が低くなる。
_ 直ぐ動く完成基板を作りたいが私にはまだその経験がありません。
7. ケースと基板:ケースに基板を固定する必要があり、設計の段階から基板を止める場所の確保が必要でした。
ESP-WROOM-02 ファームウェアアップデート
ESP-WROOM-02 に スケッチを書き込むと AT コマンドが使えなくなる。 2015.11.05
ESP-WROOM-02 用のメモとして備忘録を記載しました。
AT コマンドを使えるようにするには、マックでターミナル操作が必要となります。
ターミナル操作は面倒ですが手順をまとめ動く事を確認しました。(ESP-WROOM-02で確認)
参考:ESP8266 ファームウェアアップデート(ESP-01)
書き込み装置の用意や、ファームウエアーのダウンロードはとても面倒です。
まして、ターミナル操作となると理解できないコマンドを入力する事がさらに面倒です。
そんな私も以下の要領で、なんとかできるようになりましたので参考にしてください。
準備:以下のように配線します:PROGスイッチは、ジャンパーワイヤーのほうが便利です。
環境:Mac OSX 10.8.5 Mountain Lion にて実施。
手順: ターミナルと esptool を使用した方法。
01. ESP8266 の GPIO_0 を GND にする。スイッチでなくワイヤーでジャンパーした方が楽です。
02. esp という名前のフォルダーをデスクトップに作る。
03. USBで接続したデバイス名を Arduino IDEで調べてメモる。例:cu.usbserial-ABC
04. esptool-master.zip をダウンロード。
_ https://github.com/themadinventor/esptool :esptool.py を入手する。
05. 解凍した esptool-master フォルダーの中味を、espフォルダーに入れる。
06. esp_iot_sdk_v1.4.0_15_09_08.zip をダウンロード。
_ http://http://bbs.espressif.com/viewtopic.php?f=46&t=1124 の下にある。
07. ダウンロードしたファイル名は、esp_iot_sdk_v1-2。
08. esp_iot_sdk_v1-2 を、esp_iot_sdk_v1_2 に変更。
09. esp_iot_sdk_v1_2 > esp_iot_sdk_v1.4.0 > bin > boot_v1.4(b1).bin を、boot_v1.4b1.bin に変更。
10. esp_iot_sdk_v1_2 のフォルダーを、espフォルダーに入れる。
11. 以下、ターミナル入力
12. cd desktop/esp/
13. sudo python setup.py install
14. cd esp_iot_sdk_v1-2/esp_iot_sdk_v1.4.0/bin/
15. ESP8266 のRST端子をGNDにしてOPENする。(RESET スイッチをプッシュし離す )
16. 以下の文に、メモしたUSBデバイス名を入れてターミナル入力する。
17. esptool.py -p /dev/cu.usbserial-ABC write_flash -ff 40m -fm qio -fs 32m 0x00000 boot_v1.2.bin 0xfc000 esp_init_data_default.bin 0xfe000 blank.bin 0x01000 at/1024+1024/user1.2048.new.5.bin
_ esptool ,,, bin まで連続した文字です。
18. Connecting…
_ Erasing flash…
_ Wrote 2048 bytes at 0x00000000 in 0.2 seconds (73.3 kbit/s)…
_ Erasing flash…
_ Wrote 1024 bytes at 0x000fc000 in 0.1 seconds (73.2 kbit/s)…
_ Erasing flash…
_ Wrote 4096 bytes at 0x000fe000 in 0.4 seconds (78.9 kbit/s)…
_ Erasing flash…
_ Wrote 279552 bytes at 0x00001000 in 30.5 seconds (73.3 kbit/s)…
_ Leaving…
_ を表示して終了。
19. ESP8266 の GPIO_0 を OPEN にする。ジャンパーワイヤーを外す。
20. ESP8266 のRST端子をGNDにしてOPENする。(RESET スイッチをプッシュし離す )
21. ATコマンドで調べる。
メモ:ファイル名に – (ダッシュ)を含む場合 エラーが起きます。その為 _ (アンダーライン)に変更。
ESP8266 WiFi Scan のケースを作る
ESP8266 による WiFi Scan を ケースにおさめました。 2015.11.02
WiFi Scan は、周囲の WiFi SSID を繰り返し表示します。
38mm x 38mm x 38mm のサイズで、見やすくする為に台も作りました。
構成:
ESP8266 と OLEDデイスプレー と 3端子レギュレーター 。電源はUSBを使用。
ディスプレーは、0.96インチ OLED 128×64 を使用。
USB 5V 80mA 程度。
スケッチ:
// SSID and RSSI for each network found // WiFi.SSID(i); // WiFi.RSSI(i); // WiFi.encryptionType(i) == ENC_TYPE_NONE)?" ":"*"; #include "ESP8266WiFi.h" #include <Wire.h> #include <Adafruit_GFX.h> #include <ESP_Adafruit_SSD1306.h> #define OLED_RESET 4 Adafruit_SSD1306 display(OLED_RESET); void setup() { Serial.begin(115200); Wire.begin(0,2); // SDA(GPIO_0), SCL(GPIO_2) display.begin(SSD1306_SWITCHCAPVCC, 0x78>>1); display.clearDisplay(); // Clear the buffer. display.setTextSize(1); // font size 1 ( minnimum ) display.setTextColor(WHITE); display.clearDisplay();display.println("Connecting to");display.display(); delay(100);Serial.println("Setup done");display.display(); } void loop() { display.clearDisplay(); display.setCursor(0,0); int n = WiFi.scanNetworks(); // number of networks found if (n == 0){display.println("no networks found");} else{ int y = 9; for (int i = 0; i < n; ++i ){ y = y - 1; if ( y == 0){ delay(3000); display.clearDisplay();display.setCursor(0,0); y = 9; } display.println(WiFi.SSID(i));display.display(); // SSID delay(10); } } delay(10000); // Wait a bit before scanning again }
メモ: このサンプルは、ESP や OLED のテストに使用できます。 ESPは、受信のみのテストができます。 これは、ルーターを介さない為に ルーターの設定や接続不良、誤動作はありません。 OLEDは電源投入時に "Connecting to" を表示するか、SSIDを受信すれば、SSIDが表示 されますから OLED のテストもできる訳です。
ESP8266WiFi のサンプルを試す:WiFiScan
ESP8266 のサンプルが動く事を確かめる。 2015.11.02
その4:WiFiScan
SSID, RSSI, WiFi encryption Type が表示されている。
準備:
ハード :ESP-WROOM-02 及び 書き込み環境が用意されている事
開発環境:Arduino IDE 1.6.5:Tools -> Board に Generic ESP8266 Module の設定及び選択されている事。
スケッチ:Sandeep Mistry氏の sandeepmistry から esp8266-Arduino をダウンロードする。
_ 必要の無い物もある為、ここでは、esp8266 というフォルダーを作り Liblary をまとめる。
_ 以下の Liblary(ESP8266WiFi等々) の中味を esp8266 フォルダーに入れ Arduino のライブラリ−に入れる。
_ esp8266-Arduino > esp8266com/esp8266 > libraries > ESP8266WiFi等々
スケッチを選択する:
Liblary > esp8266 > ESP8266WiFi > WiFiScan
スケッチの変更:なし。
以上、これだけで、動きました。
ESP8266 + AE-BME280 + ThingSpeak
ESP-WROOM-02 + BME280 + ThingSpeak 2015.11.01
ESP-WROOM-02 と AE-BME280(温度、湿度、気圧センサー)(秋月電子通商)と
超小型USB SERIAL変換器を使用して、ThingSpeak へデーターを送りグラフ表示しました。
配線図:
準備:
Arduino ESP 開発環境:既にESP8266のスケッチと書き込みができる用意ができている事。
Liblary:BME280_MOD-1022:BME280のESP用ライブラリー:Ian Harris氏に感謝。
ESP-WROOM-02:ピン番号を確認してください。
AE-BME280(温度、湿度、気圧センサー):秋月電子通商:(1080円)
_ J1=J2=Jumper(4.7kΩ Pull up)、J3=Jumper(I2C)
_ I2C 接続使用ピン:Wire.begin(13, 14); // (SDA, SCL) (GPIO_13, GPIO_14)
超小型USB SERIAL 変換器:秋月電子通商(600円)
抵抗:10KΩ (4個):秋月電子通商
プッシュスイッチ :(2個):秋月電子通商
電源:3.3VDC:ブレッドボード電源等、500mA以上ある事。
その他、基板、コネクター等々。秋月電子通商とか。
メモ:
_ スケッチ入力:const char* ssid = 各自のルーターのSSIDを入力
_ スケッチ入力:const char* password = 各自のルーターの Password を入力
_ スケッチ入力:const char* thingspeak_key = 各自の API key を入力
DeepSleep Modeを使用し10分間スリープする。system_deep_sleep(10*60*1000000);
_ スリープモード(10uA)と起動:GPIO_16とRSTを接続する事。(配線図参照)
_ DeepSleep解除時にGPIO_16からLow信号が出力しRestart(RST)がかかり再起動する。
BME280:eBayには、Breakout Temperature Humidity Barometric Pressure BME280
_ Digital Sensor Module として、830円のものがある。
_
乾電池駆動:BME280のスリープ時の省電力方法の回路を再考する事。
スケッチ:
#include <ESP8266WiFi.h> #include <Wire.h> #include <SPI.h> #include <BME280_MOD_1022.h> extern "C" { #include "user_interface.h" } const char* ssid = "xxxx"; // myRouter SSID const char* password = "xxxx"; // myRouter Password const char* host = "api.thingspeak.com"; // thingspeak URL const char* thingspeak_key = "xxxxxxxxxxx"; // thingspeak Write KEY void setup() { Serial.begin(115200); Wire.begin(13, 14); delay(10); // SDA=GPIO_13,SCL=GPIO_14 BME280.readCompensationParams(); // read the NVM compensation parameters BME280.writeOversamplingTemperature(os1x); // 1x over sampling BME280.writeOversamplingHumidity(os1x); // 1x over sampling BME280.writeOversamplingPressure(os1x); // 1x over sampling Serial.println();Serial.println();Serial.print("Connecting to "); Serial.println(ssid); WiFi.begin(ssid, password); while (WiFi.status() != WL_CONNECTED) { delay(500);Serial.print(".");} Serial.println("");Serial.println("WiFi connected");Serial.println("IP address: "); Serial.println(WiFi.localIP()); } void loop() { delay(5000); Serial.print("connecting to ");Serial.println(host); WiFiClient client; // Use WiFiClient class to create TCP connections const int httpPort = 80; if (!client.connect(host,httpPort)){Serial.println("connection failed");return;} BME280.writeMode(smForced); // After taking the measurement the chip goes back to sleep while (BME280.isMeasuring()){Serial.println("Measuring...");delay(50);} Serial.println("Done!"); BME280.readMeasurements(); // read out the data String temp = String(BME280.getTemperature()); // Temp String humi = String(BME280.getHumidity()); // Humidity String pres = String(BME280.getPressure()); // Pressure String url = "/update?key="; url += thingspeak_key; // thingspeak Write KEY url += "&field1=";url += temp; // Temp url += "&field2=";url += humi; // Humidity url += "&field3=";url += pres; // Pressure Serial.print("Requesting URL: ");Serial.println(url); // This will send the request to the server client.print(String("GET ") + url + " HTTP/1.1\r\n" + "Host: " + host + "\r\n" + "Connection: close\r\n\r\n"); delay(10); while(client.available()){ // Read all the lines of the reply String line = client.readStringUntil('\r'); Serial.print(line); } Serial.println();Serial.println("closing connection. going to sleep..."); delay(1000); system_deep_sleep_set_option(0); // go to deepsleep for 10 minutes system_deep_sleep(10 * 60 * 1000000); // 10 min }
参考:
BOSCH BME280:Data Sheet