ESP WROOM 02(ESP8266)で、気温、湿度、気圧センサーのBME280とSSD1306 OLEDを同時に使用しました。
SSD1306はI2C接続のものとSPI接続のものがありますが、ESP8266のSPIは使ったことがないので、とりあえずI2C接続のものを使用しました。
I2Cは信号線をプルアップして使うので、I2CとSPIで信号線を共有するのは難しそうです。ESP8266はIOで使えるピンが少ないので、SPIとI2Cを同時使用するにはちゃんと検証する必要がありそうです。
ESP8266のI2Cの使い方もよくわかっていないので、ライブラリ等ありものを使わせていただいてとりあえず動作させるのを優先しました。
ブレッドボード配線図
運用時(USB-UART変換モジュールなし)
I2Cの信号線はBME280モジュールでもSSD1306モジュールでもオンボードでプルアップされているので、理屈としてはそれぞれの抵抗を並列した値になります。
10kΩ // 4.7kΩ ≒ 3.2kΩ
Lの時に (3.3V / 3.2kΩ) ≒ 1mA 程度流れます。SCLとSDAがあるので無視できない消費量ですね。
Arduinoのスケッチ
BME280のライブラリは「
SparkFun BME280 Arduino Library」を使用していましたが、問題がありました。
I2CのピンがデフォルトでハードウェアI2Cのものを使うようになっていて、自由にピンを割り当てられるソフトウェアI2Cを使うのが少しめんどうです。
スケッチ例に「Example9_SoftwareI2C.ino」があって、Arduino IDEのライブラリマネージャから「SoftwareWire by Testato」をインストールして使うようになってます。
他にI2CのピンをアサインできるBME280のライブラリを探してみると「
Arduino-ESP8266-BME280」がありました。(Ambientさんでした)
exampleもAmbientに送るようになっているのでそのまま使わせていただくことにしました。
SSD1306のライブラリは
u8g2を使いました。
参考
「
HiLetogo 0.9" I2C OLED 128X32をu8g2で使ってみる。」
「
u8g2のFull BufferとPage Buffer」
BME280、SSD1306のどちらのライブラリもArduinoのI2C制御クラスのWireオブジェクトをクラス内で初期化(begin())していて同時に使用するのは怖いんですが、ArduinoのI2C制御クラスのWireはSingletonになっているとおもうので(←確証なしです)、SCLとSDAに使うPINを合わせておけばだいじょうぶかな~と半分やけくそで使いました。
結果としては動作しているように見えます。
WiFiでAmbientにデータを送信するようにして、DeepSleepをかけました。
<Ambient_ESP8266_BME280_u8g2.ino>
#include <ESP8266WiFi.h>
#include <Wire.h>
#include "BME280.h"
#include "Ambient.h"
#include "U8g2lib.h"
#include "etc/config.h"
extern "C" {
#include "user_interface.h"
}
#define UART_TRACE (1)
#define PIN_CHECK (1)
#define USE_OLED (1)
#define TITLE_STR1 ("ESP8266 BME280 Thermo")
#define TITLE_STR2 ("2018.08.13")
#define LED 4
#define SDA 14
#define SCL 13
#define PERIOD 60
/*
const char* ssid = "ssid";
const char* password = "password";
unsigned int channelId = 100;
const char* writeKey = "...writeKey...";
*/
WiFiClient client;
Ambient ambient;
BME280 bme280;
#if (USE_OLED)
U8G2_SSD1306_128X32_UNIVISION_F_SW_I2C u8g2(U8G2_R0, /* clock=*/ SCL, /* data=*/ SDA, /* reset=*/ U8X8_PIN_NONE);
#endif
float calcDiscomfort(float temperature, float humidity) {
return 0.81 * temperature + 0.01 * humidity * (0.99 * temperature - 14.3) + 46.3;
}
void setup()
{
int t = millis();
//wifi_set_sleep_type(LIGHT_SLEEP_T);
struct rst_info* p_reset_info = ESP.getResetInfoPtr();
#if (UART_TRACE)
Serial.begin(115200);
delay(10);
Serial.println("");
Serial.println("");
Serial.println(TITLE_STR1);
Serial.println(TITLE_STR2);
Serial.print(ESP.getResetReason());
Serial.print("\t");
Serial.println(p_reset_info->reason);
Serial.print("Start");
#endif
#if (PIN_CHECK)
pinMode(LED, OUTPUT);
digitalWrite(LED, HIGH);
#endif
// BME280
bme280.begin(SDA, SCL);
// 電源投入直後の値は不安定なので、読み捨てる
bme280.readTemperature();
bme280.readHumidity();
bme280.readPressure();
double temperature = 0.0;
double humidity = 0.0;
double pressure = 0.0;
float discomfort = 0.0;
temperature = bme280.readTemperature();
humidity = bme280.readHumidity();
pressure = bme280.readPressure();
discomfort = calcDiscomfort(temperature, humidity);
#if (UART_TRACE)
Serial.print("temp: ");
Serial.print(temperature);
Serial.print(" DegC, humid: ");
Serial.print(humidity);
Serial.print(" %, pressure: ");
Serial.print(pressure);
Serial.print(" hPa ");
Serial.print(discomfort);
Serial.println("");
#endif
#if (USE_OLED)
u8g2.begin();
if (p_reset_info->reason != REASON_DEEP_SLEEP_AWAKE) {
u8g2.clearBuffer();
u8g2.setFont(u8g2_font_7x14_tf);
u8g2.drawStr(0, 16, TITLE_STR1);
u8g2.drawStr(0, 32, TITLE_STR2);
u8g2.sendBuffer();
delay(3000);
}
char strBuffer1[16];
char strBuffer2[16];
u8g2.clearBuffer();
u8g2.setFont(u8g2_font_logisoso20_tf);
sprintf(strBuffer2, "%s%cC", dtostrf(temperature, 2, 1, strBuffer1), 0xb0);
u8g2.drawStr(0, 32, strBuffer2);
u8g2.setFont( u8g2_font_helvR14_tr);
sprintf(strBuffer2, "%s%%", dtostrf(humidity, 2, 1, strBuffer1));
u8g2.drawStr(78, 16, strBuffer2);
u8g2.drawStr(78, 32, dtostrf(discomfort, 2, 1, strBuffer1));
u8g2.sendBuffer();
#endif
// WiFi
WiFi.begin(ssid, password);
int i = 0;
while (WiFi.status() != WL_CONNECTED) {
delay(500);
#if (PIN_CHECK)
digitalWrite(LED, i++ % 2);
#endif
#if (UART_TRACE)
Serial.print(".");
#endif
}
#if (PIN_CHECK)
digitalWrite(LED, LOW);
#endif
#if (UART_TRACE)
Serial.println("WiFi connected");
Serial.print("IP address: ");
Serial.print(WiFi.localIP());
Serial.println("");
#endif
ambient.begin(channelId, writeKey, &client);
ambient.set(1, temperature);
ambient.set(2, humidity);
ambient.set(3, pressure);
ambient.set(4, discomfort);
ambient.send();
t = millis() - t;
t = (t < PERIOD * 1000) ? (PERIOD * 1000 - t) : 1;
ESP.deepSleep(t * 1000, RF_DEFAULT);
delay(1000);
}
void loop()
{
}
スケッチ冒頭の
struct rst_info* p_reset_info = ESP.getResetInfoPtr();
でResetの理由を保持するreset_infoを取得して、起動時にタイトル画面をOLEDに表示する処理を、Sleep復帰時には
if (p_reset_info->reason != REASON_DEEP_SLEEP_AWAKE) {
...
}
としてバイパスするようにしています。
ESP.getResetReason()
で、整数値ではなくString型でResetの理由も取得できます。
Sleep中もOLEDに電源を供給しておけば表示され続けますが、Sleepからの復帰時にOLEDが初期化されるので一瞬OLEDの表示が消えます。
電池ももったいないし、OLEDは動作確認用として、長期運用する場合は外しておくつもりです。
もっと細かい周期で環境を測定するためには、例えばArduino Nanoを使ってUSB経由でロギングするものを作ればいい?
Ambientのグラフ
電源電流
電源に0.1ΩのRを直列に入れOWON B35(DC/mV)で測定(1mV→10mA換算)。1秒周期
OLED、LEDあり
Avg
1.2414
mV
Min
0.59
mV
Max
12.83
mV
WakeUp時に80mA~130mA程度、
Sleep時に6mA程度
OLED、LEDをブレッドボードから抜いた
Avg
0.614133333
mV
Min
0
mV
Max
12.35
mV
WakeUp時に70mA~125mA程度、Sleep時に0mA程度
OLED、LEDがあると常時6mA程度消費します。
I2C信号
ch1:SDA ch2:SCL
IC2クロックは多少揺れがあり、100kHzより少し遅いぐらいです。←Standard Modeぐらい。
と、なんとか不快指数計をWiFi対応させたところで、猛暑のピークは過ぎたようです(@@;