温度・湿度・気圧のロガー的な簡易グラフ表示を作ってみた。 BME280, OLED ( SSD1351 ), ESPr Developer使用

ESP8266 ( ESP-WROOM-02 )
※この記事を書いた当初のESPr Developer(ESP-WROOM-02)のFLASHサイズは4MBでした。
しかし、現在のESPr Developerおよび、ESP-WROOM-02 のFLASHサイズは2MBのものが流通しています。
その場合、東雲フォントなどの1MB以上の大きいサイズのファイルは SPIFFS へアップロードできませんのでご注意ください。
FLASHサイズの調べ方は以下の記事を参照してください。
ESP-WROOM-02 ( ESP8266 ) チップ・メモリ・MACアドレス情報確認方法
(2018/06/22)

 

OLED_SSD1351ライブラリはbeta ver 1.53では動作しませんでした。
大変失礼しました。
1.40を使用してください。
1.40は こちら にあります。
再インストールする場合は、古いライブラリのフォルダごと削除してください。

こんばんは。

ついにオリンピックが始まりました。
眠れない夜が続いてしまいます。電子工作やコーディングなんてやっている暇が無くなりそうです。

さて、オリンピックとは関係ないのですが、前回の記事に引き続き、スイッチサイエンスさんのBME280搭載、温湿度・気圧センサーモジュールを使って、フルカラーの有機ELディスプレイ( OLED ) SSD1351 に簡易グラフを表示させて、リアルタイムロガーを作ってみました。
こんな感じになりましたので動画をご覧ください。
1分過ぎあたりからドライヤーで熱風を当ててます。

いかがでしょうか?
グラフは2分毎にプロットしていきます。トータルで4分の過去のデータを見ることができます。
ドライヤーを当てると、温度が上がり、湿度が下がることがリアルタイムに分かると思います。
気圧は安定しているので、変化が殆どありませんが・・・。
これは過去4分間までの簡易ロガーですが、プログラムの数値を変えると描画するプロット間隔を変えることが出来て、過去60分や過去24時間表示なども可能です。
ただ、今回はESPr Developer ( ESP-WROOM-02 ( ESP8266 )) のSRAMに一時的に記憶しているだけなので、SDカードやフラッシュにログを書き込んでいるわけではありません。電源を抜いたらデータは消失してしまうのでご了承ください。

ちなみに、上にあるものは参考用に市販の温度湿度計を置いています。
これは薄くて軽くて持ち運び便利な、私のお気に入りのEMPEXの温度・湿度計です。

EMPEX (エンペックス) デジカード(デジタル温・湿度計) TD-8173
エンペックス(Empex)
¥2,980(2024/11/23 07:39時点)

次回以降でmicroSDカードにログを記録したり、スマホに送信したりと挑戦してみようと思っています。

スポンサーリンク

1.準備するもの

ESPr Developer ( ESP-WROOM-02 開発ボード) スイッチサイエンス製

ESP8266 を日本の電波法に対応させたモジュール ESP-WROOM-02 を、さらに電源レギュレーターやUSBシリアルインターフェイスを一まとめにしたモジュールです。
とても使いやすいので、おすすめです。

Amazon.co.jp

ESP-WROOM-02開発ボード
スイッチサイエンス(Switch Science)

ESPr Developer(ピンソケット実装済)
スイッチサイエンス(Switch Science)

このボードの使い方や組み立て方はこちらのページをご覧ください。
以下のボードもGPIOピンは同じなので使えると思います。
(※ESPr One はAmazonで販売されなくなったようです。以下のリンクはが販売元です。
ESPr® One(Arduino Uno同一形状 ESP-WROOM-02開発ボード)

BME280搭載 温湿度・気圧センサモジュール ( スイッチサイエンス製 )

前回の記事で紹介した、I2C通信、SPI通信両対応の温度・湿度・気圧の3種センサー入りの極小モジュールです。

Amazon.co.jp

スイッチサイエンス BME280温度/湿度/気圧センサモジュール SSCI-022361
スイッチサイエンス
¥2,649(2024/11/23 00:46時点)

Adafruit OLED Breakout Board – 16-bit Color 1.5″ w/microSD holder

以前の記事で紹介した、SSD1351ディスプレイドライバ制御で、カラーを16bitで指定できる、microSDホルダー付きの有機EL ( OLED ) ディスプレイです。
フルカラーでSDカード付きとなると価格はかなり高いですが、ドット単位で色を指定できるので、グラフ表示などはとても自由度が効くディスプレイだと思います。
何しろ、色がクッキリしてとても鮮やかです。
※1.27インチのものと1.5インチのものがありますが、1.5インチのものですので間違えないようにしてください。

スイッチサイエンスさんウェブショップにあります。
https://www.switch-science.com/catalog/1754/

●その他、ブレッドボード、ジャンパーワイヤー、USBケーブル、パソコン等

2.接続する

全てSPI通信で制御します。
SCLKピン、MOSIピン、MISOピンは3つのボートとも共通線ですが、CSピンはBME280側とOLED ( SSD1351 )側と別系統です。
OLEDにはRSTピンやDCピンが追加になります。

また、今回のモジュールのSPI接続ではプルアップ抵抗は必要ありません。

あと、もう一つ。
BME280 のGND線と OLED ( SSD1351 )のGND線は極力別系統にした方がセンサーの誤差を多少回避できると思います。本当は3.3V線も別系統の方が良いのですが・・・。

GPIO #16  —- OLED  OC ( OLED CS )

GPIO #15  —- BME280  CSB ( BME CS )

GPIO #14 —- BME280  SCK ( SCLK )
—- OLED  CL ( SCLK )

GPIO #13 —- BME280  SDI ( from MOSI )
—- OLED  SI ( from MOSI )

GPIO #12 —- BME280  SDO ( from MISO )
—- OLED  SO ( from MISO )

GPIO # 5  —- OLED  R ( RESET )

GPIO # 4  —- OLED  DC

3.Arduino IDE を設定しておく

Adruino IDE でプログラムを組みます。
2016/8/7現在でver 1.6.10 で動作してます。
これに Arduino core for ESP8266 Wi-Fi chip の ESP8266 ボードをインストールしておきます。
その方法は以下のページを参照してください。
Arduino IDE に Staging(Stable)版ESP8266 ボードをインストールする方法

4.Arduino IDE にライブラリをインストールする

今回インクルードするライブラリはすべて私の自作ライブラリで、以下の4つになります。
自作ですので、すべてベータバージョンです。

BME280_SPI.h   (Beta ver 1.0)
UTF8toSJIS.h   (Beta ver 1.3)
OLED_SSD1351.h   (Beta ver 1.40)
ShinonomeFONTread.h   (Beta ver 1.31)

BME280_SPI.h

BME280_SPI.h は今回初登場の温度・湿度・気圧センサー BME280 のSPI通信用ライブラリです。
前回の記事で紹介したサンプルスケッチの様な動作が簡単に可能になります。
このライブラリはGitHubのこちらのページにあります。
Web上でライブラリを公開するにあたっては、ライセンス表示が無いと配布できないらしいので、MITライセンスとしました。ライセンス表示と著作者表示してあれば、再配布、改変等はOKです。
GitHub からZIPファイルをダウンロードして、IDEにインストールしてください。
ZIPファイルからIDEへのインストール方法は以下のページを参照してください。

GitHubにある ZIP形式ライブラリ のインストール方法

UTF8toSJIS.h

UTF8toSJIS.h は文字コードのUTF-8 の文字列をShift_JISコードに変換する独自のライブラリです。
このライブラリはGitHubのこちらのページにあります。
このライブラリを使うには ”Utf8Sjis.tbl” という変換テーブルファイルが必要です。
これの詳細については以下のページを参照してください。
UTF-8 文字列から Shift_JIS へ変換する WROOM(ESP8266)用 Arduino IDE ライブラリを作ってみました

 OLED_SSD1351.h

Adafruit製 有機EL ( OLED ) SSD1351 をSPI通信で使うための独自ライブラリです。

OLED_SSD1351ライブラリはbeta ver 1.53では動作しませんでした。
大変失礼しました。
1.40を使用してください。
1.40は こちら にあります。
再インストールする場合は、古いライブラリのフォルダごと削除してください。

これの詳細については以下のページも合わせて参照してください。
Adafruit 16bit color OLED ( SSD1351 )のライブラリを自作し、日本語漢字フォントを表示させてみました

ShinonomeFONTread.h

文字列のShift_JISコードから、フリーの16×16ドットの東雲フォントのビットマップに変換する独自ライブラリです。
のライブラリはGitHubのこちらのページにあります。
以下のフォントファイルが必要です。
shnm8x16r.bdf (半角東雲フォント)
shnmk16.bdf (全角東雲フォント)
このライブラリについては以下のページも合わせて参照してください。
OLED ( 有機EL ) SSD1306 に16×16ドットのフリーの日本語漢字、東雲フォントを表示させてみました

5.SPIFFSファイルシステムアップローダーでフォントファイル等をアップロードする

ESPr Developer ( ESP-WROOM-02 ( ESP8266 )) のフラッシュにSPIFFSファイルシステムアップローダーでフォントファイルや Utf8Sjis.tbl ファイルをアップロードします。
アップロードするファイルは以下の3つです。

Utf8Sjis.tbl (UTF-8コードをShift_JISコードに変換するテーブルファイル)
shnm8x16r.bdf (半角東雲フォント)
shnmk16.bdf (全角東雲フォント)

SPIFFSファイルシステムはArduino IDEのESP8266ボード用プラグインです。
そのインストール方法やアップロード方法は以下のページを参照してください。

Arduino IDE に ESP8266 SPIFFS ファイルシステムアップローダーをインストールする方法

ここで注意していただきたいのは、アップロードするファイルのバイト数の合計が1MB以上になると、Arduino IDE のESP8266ボード設定を4M ( 3M SPIFFS )にします。
それをシリアルUSBポートでアップロードすることが出来ない場合があります。
ESPr Developer ( ESP-WROOM-02開発ボード )で下図の様な表示( Rev.2 )のあるボードはおそらくシリアルUSBでアップロード可能だと思います。
最近購入したばかりの場合はこの表示があると思いますが、古いものは表示が無いと思います。


表示の無いボードはシリアルUSBでアップロードできないと思いますので、その場合はOTAでWi-Fi経由でアップロードするしかありません。
この情報は以下のページのコメント投稿で隣人さんからいただきました。

OLED ( 有機EL ) SSD1306 に16×16ドットのフリーの日本語漢字、東雲フォントを表示させてみました

隣人さん、貴重な情報ありがとうございました。

シリアルUSBでアップロードできないばあいのOTAアップロード方法は以下のページを参照してください。
4M ( 3M SPIFFS ) をシリアルポートでアップロードできない場合のトラブルシューティング

6.スケッチを入力

では、以下のサンプルスケッチをArduino IDEに入力してみてください。
ライブラリを使用することによって、かなりシンプルになりました。
では、このスケッチについて解説していきます。
【ソースコード】 (※無保証 ※PCの場合、ダブルクリックすればコード全体を選択できます)

#include <UTF8toSJIS.h>
#include <OLED_SSD1351.h>
#include <ShinonomeFONTread.h>
#include <BME280_SPI.h>

const uint8_t sclk = 14;
const uint8_t mosi =13; //Master Output Slave Input ESP8266=Master,BME280=slave 
const uint8_t miso =12; //Master Input Slave Output
const uint8_t cs_bme = 15; //CS pin
const uint8_t cs_OLED = 16;
const uint8_t DCpin =  4;
const uint8_t RSTpin =  5;

UTF8toSJIS u8ts;
ShinonomeFONTread SFR;
OLED_SSD1351 ssd1351;
BME280_SPI bme280spi(sclk, mosi, miso, cs_bme);

const char* UTF8SJIS_file = "/Utf8Sjis.tbl"; //UTF8 Shift_JIS 変換テーブルファイル名を記載しておく
const char* ZenkakuFontFile = "/shnmk16.bdf"; //全角フォントファイル名を定義
const char* HalfFontFile = "/shnm8x16r.bdf"; //半角フォントファイル名を定義

uint8_t get_sjis[3][16];
uint16_t sj_length[8]; //Shift_JISコードの長さ
uint8_t font_buf[16][16];
uint8_t SnnmDotOut[16][16];

uint16_t ppp[128], ttt[128], hhh[128];

uint32_t M_time1, M_time2;

uint32_t Graph_plot_interval = 2000; //2秒毎にグラフプロット。Log全体は4分。2900にすると60分loggerとなる。

//*********************セットアップ********************************
void setup() {
  Serial.begin(115200);
  
  uint8_t t_sb = 5; //stanby 1000ms
  uint8_t filter = 0; //filter O = off
  uint8_t osrs_t = 4; //OverSampling Temperature x4
  uint8_t osrs_p = 4; //OverSampling Pressure x4
  uint8_t osrs_h = 4; //OverSampling Humidity x4
  uint8_t Mode = 3; //Normal mode
 
  bme280spi.BME280_SPI_Init(t_sb, filter, osrs_t, osrs_p, osrs_h, Mode);
  delay(1000);

  ssd1351.SSD1351_Init(sclk, mosi, cs_OLED, DCpin, RSTpin);
  delay(300);
  ssd1351.SSD1351_BlackOut(); //黒画面出力

  for(int i=0; i<16; i++) { //初期化しておく
    for(int j=0; j<16; j++){
      font_buf[i][j] = 0;
      SnnmDotOut[i][j] = 0;
    }
  }
  
  Serial.println();

  uint8_t Red, Green, Blue; //Red(Max=31),Green(Max=63),Blue(Max=31)

  Red = 3; Green = 8; Blue = 4;

  ssd1351.SSD1351_RectFill(0, 48, 127, 111, Red, Green, Blue);
  Red = 31; Green = 63; Blue = 31;
  ssd1351.SSD1351_RectLine(0, 48, 127, 111, Red, Green, Blue);

  Red = 15; Green = 30; Blue = 15;
  uint16_t Len; //Shift_JISコードのバイトの長さ
  char index_chr[3];
  sprintf(index_chr,"%02d",round((Graph_plot_interval*126)/60000)); //グラフプロット間隔からログ全体時間の計算
  u8ts.UTF8_to_SJIS_str_cnv(UTF8SJIS_file, String(index_chr) + "分前      現在", get_sjis[0], &Len);
  SFR.SjisToShinonome16FontRead_ALL(ZenkakuFontFile, HalfFontFile, 0, 0, get_sjis[0], Len, font_buf);
  ssd1351.SSD1351_8x16_DisplayOut_1col_LtoR(0, 112, Red, Green, Blue, Len, font_buf);
  
  for(int i=0; i<128; i++){
    ppp[i] = 127; ttt[i] = 127; hhh[i] = 127;
  }

  M_time1 = 2000; //ディスプレイに即表示させるためにこの値を代入
  M_time2 = 60000; //ディスプレイに即表示させるためにこの値を代入
}
//******************メインループ************************************
void loop() {
  double temperature = 0.0, pressure = 0.0, humidity = 0.0;
  uint8_t T_Red = 0, T_Green = 63, T_Blue = 0; //気温表示色。Red(Max=31),Green(Max=63),Blue(Max=31)
  uint8_t H_Red = 0, H_Green = 0, H_Blue = 31; //湿度表示色。Red(Max=31),Green(Max=63),Blue(Max=31)
  uint8_t P_Red = 31, P_Green = 10, P_Blue = 10; //気圧表示色。Red(Max=31),Green(Max=63),Blue(Max=31)
  String oled_str;
  uint16_t Len; //Shift_JISコードのバイトの長さ
  
  if(millis()-M_time1 > 2000){
    temperature = bme280spi.Read_Temperature();
    pressure = bme280spi.Read_Pressure();
    humidity = bme280spi.Read_Humidity();
    
    Serial.println("-----------------------");
    Serial.print("Temperature = "); Serial.print(temperature); Serial.println(" *C");
    Serial.print("Humidity = "); Serial.print(humidity); Serial.println(" %");
    Serial.print("Pressure = "); Serial.print(pressure); Serial.println(" hPa");
  
    oled_str = "気温= "+String(temperature)+" ℃ ";
    u8ts.UTF8_to_SJIS_str_cnv(UTF8SJIS_file, oled_str, get_sjis[0], &Len);
    SFR.SjisToShinonome16FontRead_ALL(ZenkakuFontFile, HalfFontFile, 0, 0, get_sjis[0], Len, font_buf);
    ssd1351.SSD1351_8x16_DisplayOut_1col_LtoR(0, 0, T_Red, T_Green, T_Blue, Len, font_buf);
  
    oled_str = "湿度= "+String(humidity)+" %";
    u8ts.UTF8_to_SJIS_str_cnv(UTF8SJIS_file, oled_str, get_sjis[1], &Len);
    SFR.SjisToShinonome16FontRead_ALL(ZenkakuFontFile, HalfFontFile, 0, 0, get_sjis[1], Len, font_buf);
    ssd1351.SSD1351_8x16_DisplayOut_1col_LtoR(0, 16, H_Red, H_Green, H_Blue, Len, font_buf);
  
    oled_str = "気圧= "+String(pressure)+"hPa";
    u8ts.UTF8_to_SJIS_str_cnv(UTF8SJIS_file, oled_str, get_sjis[2], &Len);
    SFR.SjisToShinonome16FontRead_ALL(ZenkakuFontFile, HalfFontFile, 0, 0, get_sjis[2], Len, font_buf);
    ssd1351.SSD1351_8x16_DisplayOut_1col_LtoR(0, 32, P_Red, P_Green, P_Blue, Len, font_buf);

    if(millis()-M_time2 > Graph_plot_interval){
      for(int i=126; i>0; i--){
        ttt[i] = ttt[i-1];
        hhh[i] = hhh[i-1];
        ppp[i] = ppp[i-1];
      }
      ttt[0] = round(temperature);
      hhh[0] = round(humidity*0.64);
      ppp[0] = round(pressure)-956;
    
      for(int i=0; i<126; i++){
        for(int j=0; j<62; j++){
          if(ttt[i] == j){
            ssd1351.SSD1351_1pixel_DisplayOut((127-1)-i, (111-1)-ttt[i], T_Red, T_Green, T_Blue);
          }else if(hhh[i] == j){
            ssd1351.SSD1351_1pixel_DisplayOut((127-1)-i, (111-1)-hhh[i], H_Red, H_Green, H_Blue);
          }else if(ppp[i] == j){
            ssd1351.SSD1351_1pixel_DisplayOut((127-1)-i, (111-1)-ppp[i], P_Red, P_Green, P_Blue);
          }else{
            ssd1351.SSD1351_1pixel_DisplayOut((127-1)-i, (111-1)-j, 3, 8, 4);
          }
        }
      }
      M_time2 = millis();
    }
    M_time1 = millis();
  }
}

●6-12行目:
SPI通信用に ESP-WROOM-02 ( ESP8266 ) のGPIOを定義します。

●14-17行:
ライブラリのクラス名を定義してます。名前は何でも良いです。

●19-21行:
SPIFFSファイルシステムでアップロードしたファイル名を指定してます。ファイル名の前に必ずスラッシュを入れてください。

●23-26行:
OLED ディスプレイに文字を表示するための配列定義です。

●28行:
温度、湿度、気圧データをグラフに表示するための配列定義です。OLEDは横が128ピクセルです。

●32行:
グラフをプロットするインターバル間隔をミリ秒(ms)単位で設定します。現段階では2秒としてますが、横が128ドットで、両端は枠線なので126ドットが実際のプロットする数だとすると、
2秒×126 = 252秒(約4分)
となりますので、約4分のロガー表示ということになります。
ここを好きなように設定することによって、24時間ロガーにすることもできます。

●38-43行:
スイッチサイエンスさんのBME280ボードの使い方ページを参考にしながら、変数を設定してます。
オーバーサンプリングは1よりも4の方が精度が良いですが、処理速度は遅くなります。でも、これくらいなら問題ないです。

●45行:
BME280 をSPI通信で初期化するライブラリクラス関数です。

●48行:
OLED (有機EL)SSD1351ドライバを初期化するライブラリのクラスです。

●50行:
OLED を一旦黒色画面で塗りつぶします。

●52-57行:
OLEDに表示するドットの配列をゼロで初期化しておかないと、ノイズが出るので、ここでゼロで初期化しておきます。

●65行:
OLED に塗りつぶし四角形を描くクラスです。Beta ver1.3から追加しました。
このOLED は128×128ドットですので、四角形の左端上座標と右下座標を指定して四角形を描きます。

●67行:
OLED に四角形の枠だけを描画するライブラリクラスです。これもBeta ver1.3から追加しました。

●72行:
ディスプレイの左下に表示されているように、32行のグラフプロットインターバル秒数から表示できる最大秒数を計算して、文字列に変換してます。

●73-75行:
一列分の文字列(全角8文字分)をOLED ( SSD1351 ) に表示させるライブラリクラスです。

●77-79行:
グラフプロット用の配列をOLEDの画面一番下になるように初期化してます。

●87-89行:
各データの表示色を決めています。

●94-96行:
BME280_SPIライブラリのクラスで、温度・湿度・気圧データを読み込んでいます。

●98-101行:
BME280から取得したデータをArduino IDEのシリアルモニターに表示させてます。

●103-116行:
OLED ( SSD1351 )ディスプレイの上部に気温・湿度・気圧データを文字列で表示させてます。

●118-126行:
グラフプロットインターバルで設定した時間毎にBME280から取得した気温・湿度・気圧値をOLEDの1ピクセル(ドット)の位置に変換して、過去のドットデータを1つずつ置き換えています。
この配列をそのまま表示させると、グラフが左へ移動する感じになります。

●128-140行:
OLED ( SSD1351 )ディスプレイに1ピクセル(ドット)ずつ指定して表示させてます。このライブラリクラスもBeta ver1.3から新たに追加しました。
画面の一番右が現在の気温・湿度・気圧データで、過去のディスプレイ表示を消去するために、気温・湿度・気圧プロット以外はバックグラウンドカラーで塗りつぶしてます。

スケッチのザッとした解説は以上です。

7.コンパイル書き込み、実行させる

では、コンパイル書き込みおよび実行させてみましょう。
シリアルモニターを開くとこのように表示されます。

最初の文字化けデータはESP-WROOM-02側から送信してくる起動メッセージで、76.8kbpsの速度で送られてきますので、ウィンドウ右下のシリアル通信速度を115200bpsにしていると文字化けします。これは無視してOKです。
その後はESP-WROOM-02側から115200bpsで送られてくるので、問題なく表示されます。
起動し始めはセンサーが安定しないので精度は低いです。

OLEDの画面では先に紹介した動画のように表示されればOKです。
下の写真は測定開始後4分以上経過していて、途中でドライヤーを当てて、温度を上げて湿度を低下させたグラフです。

気圧はこちらの地点では安定しているので一直線水平ですね。
台風が来るときにこのデータをみていると面白そうです。気圧もかなり上下するのではないでしょうか・・・。
ちなみに、最初に述べましたが、このロガーはSRAMに記憶しているだけなので、電源を抜いたらデータは消えます。

今回は以上です。
今後はこれをOLED についているmicroSDカードに記録させて、スマホにデータを送信させるということもやってみたいと思います。
なかなかこの Adafruit製 OLED ( SSD1351 )は高価ですが、使い勝手が良く、今後の使用用途の幅が広がりますね。

ではまた・・・。

Amazon.co.jp 当ブログのおすすめ

スイッチサイエンス ESPr Developer 32 Type-C SSCI-063647
スイッチサイエンス
¥2,420(2024/11/23 17:23時点)
ZEROPLUS ロジックアナライザ LAP-C(16032)
ZEROPLUS
¥19,358(2024/11/22 22:35時点)
Excelでわかるディープラーニング超入門
技術評論社
¥2,068(2024/11/23 14:03時点)

コメント

タイトルとURLをコピーしました