wifi無線モジュールESP-WROOM-02を使って、インタネット接続をできるようにした。
これまでは、ArduinoにEthernet シールドとwifi ルータを接続して、IoT環境を実現していたが、wifi無線モジュールESP-WROOM-02は安価でかつ小型であるため、現在の観測システムに代用できるか実験。 温湿度・気圧センサーBME280のデータを無事さくらインターネットにWifi送信できた。
Arduinoスケッチ filename: esp-wroom-02-httpget-bme280
///program name: kasasan-esp-bme280-US /// 2017-06-29 rev1.0 /// iot for hakuba #include "ESP8266.h" #include <SoftwareSerial.h> /// BME280 setting #include <Wire.h> ////////////////////////////////////////////////// /// esp8266setting #define SSID "aterm-*******" //wifi route sside #define PASSWORD "***********" //wifi router key #define HOST_NAME "***.***.***.***" // sakura server IP kasasan.sakura.ne.jp #define FILE_NAME "/logdata/*****.php" int n = 0; SoftwareSerial mySerial(2, 3); //RX, TX ESP8266 wifi(mySerial); ////////////////////////////////////////////////// /// BME280 setting #define BME280_ADDRESS 0x76 unsigned long int hum_raw,temp_raw,pres_raw; signed long int t_fine; uint16_t dig_T1; int16_t dig_T2; int16_t dig_T3; uint16_t dig_P1; int16_t dig_P2; int16_t dig_P3; int16_t dig_P4; int16_t dig_P5; int16_t dig_P6; int16_t dig_P7; int16_t dig_P8; int16_t dig_P9; int8_t dig_H1; int16_t dig_H2; int8_t dig_H3; int16_t dig_H4; int16_t dig_H5; int8_t dig_H6; /////////////////////////////////////////////////////// // 超音波距離センサモジュール HC-SR04 //https://www.switch-science.com/catalog/1606/ /*電源電圧 5V 待機電流 2mA未満 信号出力 0-5V センサ角度 15度以下 測定可能距離 2cm-450cm 分解能 0.3cm 使い方 トリガ端子を10us以上Highにしてください。 このセンサモジュールが40kHzのパルスを8回送信して受信します。 受信すると、出力端子がHighになります。 出力端子がHighになっている時間がパルスを送信してから受信するまでの時間です。 出力端子がHighになっている時間の半分を音速で割った数値が距離です。 */ //Trig端子をArduinoのD8、Echo端子をD9に接続した場合 int Trig = 8; int Echo = 9; int Duration; float Distance; int ii; ////////////////////////////////////////////////////// void setup(void) { /// bme280 setting uint8_t osrs_t = 1; //Temperature oversampling x 1 uint8_t osrs_p = 1; //Pressure oversampling x 1 uint8_t osrs_h = 1; //Humidity oversampling x 1 uint8_t mode = 3; //Normal mode uint8_t t_sb = 5; //Tstandby 1000ms uint8_t filter = 0; //Filter off uint8_t spi3w_en = 0; //3-wire SPI Disable uint8_t ctrl_meas_reg = (osrs_t << 5) | (osrs_p << 2) | mode; uint8_t config_reg = (t_sb << 5) | (filter << 2) | spi3w_en; uint8_t ctrl_hum_reg = osrs_h; Serial.begin(9600); Wire.begin(); writeReg(0xF2,ctrl_hum_reg); writeReg(0xF4,ctrl_meas_reg); writeReg(0xF5,config_reg); readTrim(); #define LED_PIN 13 /// program name Serial.println("program name: kasasan-esp-bme280-US rev1.0 2017/06/29"); /// esp8266 setting if (wifi.setOprToStationSoftAP()) { Serial.println("to station ok"); } else { Serial.println("to station error"); } if (wifi.joinAP(SSID, PASSWORD)) { Serial.println("connect success"); } else { Serial.println("connect error"); } if (wifi.disableMUX()) { Serial.println("disable mux success"); } else { Serial.println("disable mux error"); } /// ultrasonic setting pinMode(Trig,OUTPUT); pinMode(Echo,INPUT); } void loop(void) { /// ultrasonic mesurement /////////////////////////// for(ii=1 ; ii<= 3 ; ii++) { digitalWrite(Trig,LOW); delayMicroseconds(5); digitalWrite(Trig,HIGH); delayMicroseconds(20); // 20--> 50 digitalWrite(Trig,LOW); Duration = pulseIn(Echo,HIGH); if (Duration>0) { Distance = Duration/2; Distance = Distance*340*100/1000000; // ultrasonic speed is 340m/s = 34000cm/s = 0.034cm/us } delay(500); } Serial.print(Duration); Serial.print(" us "); Serial.print(Distance); Serial.println(" cm"); /// BME280 measurement ///////////////////////////// double temp_act = 0.0, press_act = 0.0,hum_act=0.0; signed long int temp_cal; unsigned long int press_cal,hum_cal; readData(); temp_cal = calibration_T(temp_raw); press_cal = calibration_P(pres_raw); hum_cal = calibration_H(hum_raw); temp_act = (double)temp_cal / 100.0; press_act = (double)press_cal / 100.0; hum_act = (double)hum_cal / 1024.0; Serial.print("TEMP : "); Serial.print(temp_act); Serial.print(" DegC PRESS : "); Serial.print(press_act); Serial.print(" hPa HUM : "); Serial.print(hum_act); Serial.println(" %"); //////// convert measdata to chara data char d_us[7]; dtostrf(Distance, 3, 1, d_us); char t_act[7]; dtostrf(temp_act, 3, 1, t_act); char h_act[7]; dtostrf(hum_act, 3, 1, h_act); char p_act[7]; dtostrf(press_act, 3, 1, p_act); delay(500); ///////////////////////////////////////////////// n++; // TCPで接続 wifi.createTCP(HOST_NAME, 80); // サーバーへ渡す情報 char sendStr[256]; //sprintf(sendStr, "GET /%s?data=%d HTTP/1.0\r\nHost: %s\r\nUser-Agent: arduino\r\n\r\n", FILE_NAME, n, HOST_NAME); <--- original //sprintf(sendStr, "GET /%s?temp=%s&cds=%s HTTP/1.0\r\nHOST:219.94.129.150\r\n\r\n", FILE_NAME, tt, cc); <---- NG //sprintf(sendStr, "GET /%s?temp=%s&cds=%s HTTP/1.1\r\nHOST:miyasan.sakura.ne.jp\r\nConnection:close\r\n\r\n", FILE_NAME, tt, cc); //?? sprintf(sendStr, "GET /%s?temp_act=%s&hum_act=%s&press_act=%s&distance=%s HTTP/1.1\r\nHOST:kasasan.sakura.ne.jp\r\n\r\n", FILE_NAME, t_act, h_act, p_act, d_us); // <--- OK wifi.send((const uint8_t*)sendStr, strlen(sendStr)); Serial.println(n); delay(60000); } ///////////// sub-routine bme280 //////////////////////////// /*-------- BME280 code ----------*/ void readTrim() { uint8_t data[32],i=0; Wire.beginTransmission(BME280_ADDRESS); Wire.write(0x88); Wire.endTransmission(); Wire.requestFrom(BME280_ADDRESS,24); while(Wire.available()){ data[i] = Wire.read(); i++; } Wire.beginTransmission(BME280_ADDRESS); Wire.write(0xA1); Wire.endTransmission(); Wire.requestFrom(BME280_ADDRESS,1); data[i] = Wire.read(); i++; Wire.beginTransmission(BME280_ADDRESS); Wire.write(0xE1); Wire.endTransmission(); Wire.requestFrom(BME280_ADDRESS,7); while(Wire.available()){ data[i] = Wire.read(); i++; } dig_T1 = (data[1] << 8) | data[0]; dig_T2 = (data[3] << 8) | data[2]; dig_T3 = (data[5] << 8) | data[4]; dig_P1 = (data[7] << 8) | data[6]; dig_P2 = (data[9] << 8) | data[8]; dig_P3 = (data[11]<< 8) | data[10]; dig_P4 = (data[13]<< 8) | data[12]; dig_P5 = (data[15]<< 8) | data[14]; dig_P6 = (data[17]<< 8) | data[16]; dig_P7 = (data[19]<< 8) | data[18]; dig_P8 = (data[21]<< 8) | data[20]; dig_P9 = (data[23]<< 8) | data[22]; dig_H1 = data[24]; dig_H2 = (data[26]<< 8) | data[25]; dig_H3 = data[27]; dig_H4 = (data[28]<< 4) | (0x0F & data[29]); dig_H5 = (data[30] << 4) | ((data[29] >> 4) & 0x0F); dig_H6 = data[31]; } void writeReg(uint8_t reg_address, uint8_t data) { Wire.beginTransmission(BME280_ADDRESS); Wire.write(reg_address); Wire.write(data); Wire.endTransmission(); } void readData() { int i = 0; uint32_t data[8]; Wire.beginTransmission(BME280_ADDRESS); Wire.write(0xF7); Wire.endTransmission(); Wire.requestFrom(BME280_ADDRESS,8); while(Wire.available()){ data[i] = Wire.read(); i++; } pres_raw = (data[0] << 12) | (data[1] << 4) | (data[2] >> 4); temp_raw = (data[3] << 12) | (data[4] << 4) | (data[5] >> 4); hum_raw = (data[6] << 8) | data[7]; } signed long int calibration_T(signed long int adc_T) { signed long int var1, var2, T; var1 = ((((adc_T >> 3) - ((signed long int)dig_T1<<1))) * ((signed long int)dig_T2)) >> 11; var2 = (((((adc_T >> 4) - ((signed long int)dig_T1)) * ((adc_T>>4) - ((signed long int)dig_T1))) >> 12) * ((signed long int)dig_T3)) >> 14; t_fine = var1 + var2; T = (t_fine * 5 + 128) >> 8; return T; } unsigned long int calibration_P(signed long int adc_P) { signed long int var1, var2; unsigned long int P; var1 = (((signed long int)t_fine)>>1) - (signed long int)64000; var2 = (((var1>>2) * (var1>>2)) >> 11) * ((signed long int)dig_P6); var2 = var2 + ((var1*((signed long int)dig_P5))<<1); var2 = (var2>>2)+(((signed long int)dig_P4)<<16); var1 = (((dig_P3 * (((var1>>2)*(var1>>2)) >> 13)) >>3) + ((((signed long int)dig_P2) * var1)>>1))>>18; var1 = ((((32768+var1))*((signed long int)dig_P1))>>15); if (var1 == 0) { return 0; } P = (((unsigned long int)(((signed long int)1048576)-adc_P)-(var2>>12)))*3125; if(P<0x80000000) { P = (P << 1) / ((unsigned long int) var1); } else { P = (P / (unsigned long int)var1) * 2; } var1 = (((signed long int)dig_P9) * ((signed long int)(((P>>3) * (P>>3))>>13)))>>12; var2 = (((signed long int)(P>>2)) * ((signed long int)dig_P8))>>13; P = (unsigned long int)((signed long int)P + ((var1 + var2 + dig_P7) >> 4)); return P; } unsigned long int calibration_H(signed long int adc_H) { signed long int v_x1; v_x1 = (t_fine - ((signed long int)76800)); v_x1 = (((((adc_H << 14) -(((signed long int)dig_H4) << 20) - (((signed long int)dig_H5) * v_x1)) + ((signed long int)16384)) >> 15) * (((((((v_x1 * ((signed long int)dig_H6)) >> 10) * (((v_x1 * ((signed long int)dig_H3)) >> 11) + ((signed long int) 32768))) >> 10) + (( signed long int)2097152)) * ((signed long int) dig_H2) + 8192) >> 14)); v_x1 = (v_x1 - (((((v_x1 >> 15) * (v_x1 >> 15)) >> 7) * ((signed long int)dig_H1)) >> 4)); v_x1 = (v_x1 < 0 ? 0 : v_x1); v_x1 = (v_x1 > 419430400 ? 419430400 : v_x1); return (unsigned long int)(v_x1 >> 12); }さくらサーバーのPHPコード
<?php /*** 設定 ***/ // filename: bme280_esp8266_log.php // 2017/06/30 作成 // 温度、気圧、湿度同時計測用 // データ保存ファイルの相対パス define('TEMP_FILE_PATH', '../testlog/bme280_esp8266.txt'); // Arduino sketchでphpのあるパスを指定 "GET (www) /logdata/bme280log/bme280.php?temp="; ... (www)は無い // php programのあるDirから、一度相対パスでlogdataに登り、 ../ --&amp;amp;amp;gt; ./bme280log/bme280.txtを指定 // 日時情報(YYYY/MM/DD HH:ii:ss形式) $strDate = date("Y/m/d H:i:s"); // 書き込む文字列 $strLog = ''; /*** 値チェック ***/ // 'temp_act'という名前の変数に格納された値を受け取る $strTempact = $_GET['temp_act']; $strHumact = $_GET['hum_act']; $strPressact = $_GET['press_act']; /*** 値を書き込み ***/ // 書き込み形式 // 日時情報(YYYY/MM/DD HH:ii:ss) + 温度値(00.0℃) + 改行コード // $strLog = $strDate . "," . $strTempVal . "," . $strCds . "," . $strNum . "\n"; $strLog = $strDate . "," . $strTempact . "," . $strHumact . "," . $strPressact . "\n"; // データを保存するテキストファイルを追記モードでオープン $fp = fopen(TEMP_FILE_PATH, "a"); // ファイルを排他ロック flock($fp, LOCK_EX); // 送信された値をテキストファイルに書き込み fwrite($fp, $strLog); // ロックを開放 flock($fp, LOCK_UN); // ファイルポインタをクローズ fclose($fp); ?>サーバデータ送信結果:
2017/07/01 08:00:25,24.5,61.3,901.4 2017/07/01 08:01:27,24.6,61.2,901.5 2017/07/01 08:02:28,24.6,61.1,901.5 2017/07/01 08:03:30,24.6,61.0,901.5 2017/07/01 08:04:31,24.6,60.9,901.5
備忘録
ESP-WROOM-02
ESP-WROOM-02 は、ESP8266 は 32 bit Tensilica MCU (マイコン; MicroController Unit) を内蔵した WiFi モジュールをPKG化したもの。日本での技適も取得済みのため安心して使用できる。
アマゾンでwaves ESP8266 WiFiモジュール(技適取得済み) ESP-WROOM-02 キットを購入・・・890円。
老眼には微細ピッチの半田付けが厳しいが、すべて必要な部品が揃っていてお得。
WiFi モジュール ESP-WROOM-02 と Arduino の接続方法
ESP-WROOM-02は、Programを書き込むことでArduinoのIDE環境で単体でWifi接続が可能だが、ArduinoのようにAnalogやI/O端子が多くないので、Wifiモジュールとして使うことにする。
Arduinoボードからシリアル通信でATコマンドを送信することでESP-WROOM-02 Wi-Fiシールドを制御できる。
接続方法(回路図、ピンアサインメント):
・ArduinoとESP-WROOM-02はSoftwareSerialでシリアルで通信させる。
・ESP-WROOM-02は3.3V動作のため、5Vから3.3Vのレギュレータが必要。
そこで秋月で下記のキャパシタとセットを購入(100円):
低損失三端子レギュレーター 3.3V/500mA TA48033S : Toshiba
回路イメージ:
注意:
・3.3Vはレギュレータから供給
・ESP-WROOM-02とRXDには、Arduinoのpin3(TX)から2KΩと1kΩでレベルシフトを作って入力 <UNO TX(SoftwareSreial ピン2) - 1kΩ - ESP/RXD - 2kΩ - GND>
SoftwareSerial を使用
ESP-WROOM-02/TXD <-> Arduino/2番ピン (SoftwareSerial で RX に設定)
ESP-WROOM-02/RXD <-> Arduino/3番ピン (SoftwareSerial で TX に設定)
公式ページに記載されているように、Arduino には HardwareSerial の 0 番ピン (RX) と 1 番ピン (TX) が備わっています。この HardwareSerial を利用して以下のように接続しても ESP-WROOM-02 と通信できます。
HardwareSerial
ESP-WROOM-02/TXD <-> Arduino/0番ピン (Hardwareserial RX)
ESP-WROOM-02/RXD <-> Arduino/1番ピン (Hardwareserial TX)
しかしながら、この HardwareSerial は Arduino IDE から Arduino に USB ケーブルを介してシリアル通信でプログラムを書き込むときにも使用されるため、書き込みエラーが発生する原因になります。
接続(AT動作)の確認
Arduinoのシリアルモニタにて、ATコマンドを入力
「AT+GMRコマンド」でもう一度ファームのバージョンを確認し、動作すればOK
以降は下記URLを参考にWifi 接続を可能にした。
【Arduino】ESP-WROOM-02( ESP8266 )を使ってWifiで無線通信する
初期設定
ArduinoとESP-WROOM-02間の通信はソフトウエアシリアル通信を使います。ESP-WROOM-02が初期状態では115200bpsで通信するのに対して、Arduinoのソフトウエアシリアルの最高速度は9600bpsです。
このままでは安定した通信ができないため、ESP-WROOM-02の通信速度を9600bpsまで下げる必要があります。まずは次のスケッチを作成してArduinoに転送して下さい。
#include <SoftwareSerial.h> SoftwareSerial mySerial(2, 3); // RX, TX void setup() { // Open serial communications and wait for port to open: Serial.begin(115200); while (!Serial) { ; // wait for serial port to connect. Needed for native USB port only } Serial.println("Goodnight moon!"); // set the data rate for the SoftwareSerial port mySerial.begin(115200); mySerial.println("Hello, world?"); } void loop() { // run over and over if (mySerial.available()) { Serial.write(mySerial.read()); } if (Serial.available()) { mySerial.write(Serial.read()); } }転送できたら、右上のシリアルモニタボタンをクリックして、シリアルモニタを開いて下さい。
シリアルモニタウインドウの右下の改行コードを「CRおよびLF」、転送レートを「112500」に変更します。
すると、シリアルモニタに次のように表示されると思います。表示されない場合はArduinoのリセットボタンを押してみて下さい。
Goodnight moon! Hello, world?これに続けて次のコマンドを入力して、送信ボタンを押します。
AT+UART_DEF=9600,8,1,0,0シリアルモニタの画面上にOKが表示されれば、設定は完了です。
ESP8266のライブラリをインストール
まずはESP8266を動かすためのライブラリをインストールしましょう。
ライブラリを↓からダウンロードしてください。
ダウンロードしたライブラリをインストールします。
Arduinoのメニューから「スケッチ」→「ライブラリをインクルード」→「.ZIP形式のライブラリをインストール」を選択し、ダウンロードしたITEADLIB_Arduino_WeeESP8266-masterをインストールします。
上記ライブラリは、本家で提供されているライブラリを修正し、ソフトウエアシリアルを使えるようにしたものです。
無線通信プログラムの作成
ではいよいよ、データを送信するためのプログラムの作成を作りましょう。次のプログラムを入力して下さい。
#include "ESP8266.h" #include <SoftwareSerial.h> #define SSID "Buffalo-G-XXXX" #define PASSWORD "xxxxxxxxxxxxx" #define HOST_NAME "192.168.xx.xx" #define FILE_NAME "test.php" int n = 0; SoftwareSerial mySerial(2, 3); //RX, TX ESP8266 wifi(mySerial); /** * 初期設定 */ void setup(void) { Serial.begin(9600); if (wifi.setOprToStationSoftAP()) { Serial.println("to station ok"); } else { Serial.println("to station error"); } if (wifi.joinAP(SSID, PASSWORD)) { Serial.println("connect success"); } else { Serial.println("connect error"); } if (wifi.disableMUX()) { Serial.println("disable mux success"); } else { Serial.println("disable mux error"); } } void loop(void) { n++; // TCPで接続 wifi.createTCP(HOST_NAME, 80); // サーバーへ渡す情報 char sendStr[128]; sprintf(sendStr, "GET /%s?data=%d HTTP/1.0\r\nHost: %s\r\nUser-Agent: arduino\r\n\r\n", FILE_NAME, n, HOST_NAME); wifi.send((const uint8_t*)sendStr, strlen(sendStr)); Serial.println(n); delay(100); }
SSIDとPASSWORDの欄にはお使いのルータのSSIDとパスワードを入力して下さい。HOST_NAMEの欄には、次のステップで作成するPHPファイルを置くサーバのホスト名を入力します
サーバー側のプログラムを作る
最後にArduinoから送信されたデータをサーバで受け取るプログラムを作りましょう。
※参考:ESP-WROOM-02単体で開発す場合の環境設定と、ファームのリセット・更新方法
ESP-WROOM-02開発ボードをArduino IDEで開発する方法
第34回 Arduinoマイコンとしても使える小型WifiモジュールESP-WROOM-02を使ってみる(Arduino利用編)
なお、USBシリアル変換デバイスがなくても、Arduinoのボードを使えばファームやプログラムが書き込み可能 (参考URL http://mechanic.pilotz.jp/2016/02/writing-to-esp-wroom-02-using-arduino-as-ftdi/)
*参考 BME280/BMP280について 同じ実験をしている方のBlog記事
URL:http://okiraku-camera.tokyo/blog/?p=5291
GY-BMP280
6本足のピンポストをハンダ付けしてブレッドボードにさしてみた。中央上よりの銀色の四角いデバイスがBMP280で、小さな穴が開いているのが分かる。このモジュールについての資料がなかなか見つからなかったので、等価と思われる回路図も起こしてみた。
実は、湿度も測れるBME280が載ったモジュールと勘違いしてポチっとしてしまったのだけど、I2Cを使ったArduinoやWROOM-02への接続や測定のためのスケッチはBMEもBMPもほとんど同じだった。
秋月電子のAE-BME280 (上図) には、SPIとI2Cのどちらを使うのかを選択したり、I2C使用時のアドレスを設定するためのジャンパエリアがあったのだけど、GY-BMP280にはそんな気の利いたものはなく、BMP280自体のピンは以下のように結線されているようだ。
- SDO : 10kΩでプルダウン (I2Cアドレス 0x76に固定)
- CSB : 10kΩでプルアップ (I2C接続モードを指定。I2C時は通信には使わない)
- SDI : 10kΩでプルアップ (I2CのSDAライン)
- SCK : 10kΩでプルアップ (I2CのSCLライン)
- VIO (VDDIO)はデジタルインタフェース専用の電源、VDDはそれ以外のアナログ部分も使う電源とのことで、シビアな測定に使う用途では分けて与えるのだろう。
※ BMP280のデータシートによれば、I2C接続を用いるときにはCSBはプルアップではなく、VDDIOに直結すべきとのこと。そうしないと、起動時にSPIになってしまうかもね、と書いてある。たしかに、AE-BME280ではプルアップ抵抗は入っていなかった。