esp8266.jpg

Raspberry PiからArduinoへ

ここ最近IoTがAIと共に「幻滅期」に入ったというニュースが出ていましたが、数年前に発売されたRaspberry Pi(ラズパイ)は今年で4代目が発売され、今やちょっとしたPCと同じぐらいのスペックになりつつあります。

そんなラズパイだとLinuxが普通に動くし、ちょっとした電子工作もできるので自分みたいなソフトウェア側の人間からすととても居心地のよい環境なのですが、本格的なIoTをやりたいと思うとちょっと物足りなく感じるのも正直なところです。

そんなところに「ワンコイン(500円)でインターネットにつながる機器が出た」というニュースを見て衝動買いしたのがESP-WROOM-02(ESP8266)でした。
これはArduiono(アルデュイーノ)というラズパイの先輩?にあたる電子工作の統合開発環境を使って開発ができるマイコンで、ワンコイン(実際には数百円ほど)という安さなのに、WiFi接続や各種I/Oポートも持っているというものです。
(しかも国内で電波を発信できる「技適」も取得している)

前々からArduinoは気になっていたものの、ラズパイからすると敷居が高そうで二の足を踏んでいたところに、安価に開発できると聞いて衝動買い、、したのが4年前くらいになります(それまで寝かせてたw)。

部品調達

ESP8266は既にWiFiに加えてBluetoothにも対応した後継機(ESP32)も出ており、このままだと肥やしになりかねないので、とりあえずまずはATコマンドを試してみようと部品を揃えるところから始めました。

  • PCとESP8266とシリアル通信するため、ebay.comでFT232RLを購入
  • Lチカ用にLEDは対応電圧が分からなかったので適当(2.1V, 50mA)なやつを購入
  • 単3リチウム電池4本で駆動させるため、リード線付き電池ボックスを購入
  • microUSBから電源を取る(+念のため2.1mmDCジャックの)DIPキットを購入
  • 上記電源をESP8266に流すため、3.3V0.5Aの三端子レギュレーターを購入
  • Arduino IDEで作ったソフトを書き込むモードと実行するモードの切り替え、およびESP8266本体再起動のため、切り替えスイッチとリセットスイッチを購入
  • ブレッドボードと各種パーツを接続用のジャンパーケーブルを購入

部品が揃ったら、ググりながら(笑)見よう見まねでESP8266の各端子をブレッドボード上で接続し、最後にmicroUSBを接続して電源を供給したところ、、無事Arduino IDEのシリアルコンソールに文字が出てきました!
(ただ、ファームウェアのアップデートはなぜかChip sync error: Failed to connect to ESP8266: Timed out waiting for packet headerエラーが出て失敗。。)

プログラムの書き込みと実行

次は自作プログラムをESP8266上で動かすのを試してみます。IoT界の「Hello World」はLチカ(LEDをチカチカ点滅させる)らしいので、極性に注意しながらブレッドボードに指し、こちらのプログラムを参考にさせて頂き、WiFi接続とURLアクセスでLEDが点滅するのを確認できました。

温湿度センサー(BME280)から値を取得

自作プログラムの書き込みと実行が確認できたところで、今度はBME280から値を取得してシリアルコンソールに出力するプログラムをこちらのライブラリーを参考にして書き、無事出力できることを確認できました。
ネット上だとBME280から値を取る場合はI2Cを使うことが多いみたいですが、手持ちのBME280はSPIで取得する必要があったため、前述のライブラリーを使用しています。

ディープスリープと電池駆動

ネットワーク接続と値取得が確認できたところで、いよいよこれらを組み合わせて温湿度計プログラムを作ります。ESP8266にはディープスリープ(Deep Sleep)と呼ばれるモードがあり、このモードに入るとCPUやWiFiを全て止めて次の時間まで待機する(リセットがかかる)代わりに、その間ごく微量の消費電力で駆動させることができます。

温湿度の計測は1時間に一度行い、その結果をクラウド上のInfluxDBにPOSTしてまた1時間ディープスリープする形にしました。
動作確認できたところで、最後はmicroUSB電源から電池ボックスのリード線からの電源に切り替えて変わらず動いたら成功です。

#include <ESP8266HTTPClient.h>
#include <ESP8266WiFi.h>

ADC_MODE(ADC_VCC); 

#include <SPI.h>
#include "BME280SpiSw.h" 

#define SERIAL_BAUD 115200
#define CSEL_PIN 5
#define MOSI_PIN 13
#define MISO_PIN 12
#define SCLK_PIN 14 

BME280SpiSw::Settings settings(CSEL_PIN, MOSI_PIN, MISO_PIN, SCLK_PIN);
BME280SpiSw bme(settings); 

const char WLAN_SSID[] = "wifi-ssid";
const char WLAN_PASS[] = "wifi-password"; 

const IPAddress addr(192, 168, 0, 251);
const IPAddress gwip(192, 168, 0, 1);
const IPAddress mask(255, 255, 255, 0); 

const char TSDB_HOST[] = "influxdb.example.com";
const int  TSDB_PORT   = 8086;
const char TSDB_PATH[] = "/write?db=weather";
const char TSDB_USER[] = "user";
const char TSDB_PASS[] = "password"; 

void setup() {
  Serial.begin(SERIAL_BAUD);
  Serial.println(); 

  while (!bme.begin()) {
    Serial.println("Could not find BME280 sensor!");
    delay(1000);
  } 

  WiFi.config(addr, gwip, mask, gwip);
  if (WiFi.SSID() != WLAN_SSID) {
    WiFi.persistent(true);
    WiFi.mode(WIFI_STA);
    WiFi.setAutoConnect(true);
    WiFi.begin(WLAN_SSID, WLAN_PASS);
  }

  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
  }
}

void loop() {
  int vcc = ESP.getVcc();

  float temp(NAN), hum(NAN), pres(NAN);
  BME280::TempUnit tempUnit(BME280::TempUnit_Celsius);
  BME280::PresUnit presUnit(BME280::PresUnit_hPa);
  bme.read(pres, temp, hum, tempUnit, presUnit); 

  String payload = "temperature,locate=room value=" + String(temp) + "\n";
  payload += "pressure,locate=room value=" + String(pres) + "\n";
  payload += "humidity,locate=room value=" + String(hum) + "\n";
  payload += "vcc,locate=room value=" + String(vcc) + "\n";
  Serial.println("payload=" + payload);

  if (WiFi.status() == WL_CONNECTED) {
    WiFiClient client;
    HTTPClient http;
    http.begin(client, TSDB_HOST, TSDB_PORT, TSDB_PATH);
    http.setAuthorization(TSDB_USER, TSDB_PASS);
    int ret = http.POST(payload);
    http.writeToStream(&Serial);
    http.end();
    Serial.println("ret=" + String(ret)); 

    WiFi.persistent(false);
    WiFi.disconnect();
  } 

  // enter deepsleep for 3600 seconds
  ESP.deepSleep(3600 * 1000 * 1000, WAKE_RF_DEFAULT);
  delay(1000);
}

まだまだこれから。。

「やっとできた!」と嬉々としてこの記事を書いていたものの、1時間経過してもInfluxDBにはログが記録されていなかったので、上記プログラムか回路側の方にまだ問題があるようです、、でも乾電池で駆動するのは置き場所の制約が緩和されるので、今後の電子工作のアイデアも広がりそうな可能性を感じました。

追記:WiFi.persistent()/WiFi.setAutoConnect()は動いていなさそうだったので削除して都度新規接続するようにし、ディープスリープの3600秒(1時間)を1200秒(20分)に修正ました。