「えだまめ」しているラズパイ

30年ぶりに半田ごて握ってラズパイ勉強中。

BME680で温湿度・気圧計の作成(5) スケッチの作成

◯やりたいこと

(4)で確定した回路図をもとにスケッチを作成する。


(現在は実際に電池稼働での実験に入っています。)

◯やったこと

・スケッチの作成

現時点で動いているスケッチは以下の通りとなっています。

/**
*
*    WiFi 温湿度・気圧計 WR02_BME680_OLED v0.0.5b
*
*      CPU    : WROOM-02(ESP8266) シンプル版
*      Pin Function
*        IO0  : SCL
*        IO1  : TX (OLED Power)
*        IO2  : SDA
*        IO3  : RX (SW)
*        IO4  : -
*        IO5  : -
*        IO12 : -
*        IO13 : -
*        IO14 : -
*        IO15 : -
*        IO16 : Reset出力(OnBord)
*        TOUT : -
*      Board
*        ESP8266 Community : Generic ESP8266 Module
*        URL : http://arduino.esp8266.com/stable/package_esp8266com_index.json
*
*/
// Standerd Library	(IDE v1.8.19)
#include              <Arduino.h>
#include              <time.h>
#include              <Wire.h>

// Board Library (ESP8266 Boards v2.7.2)
#include              <ESP8266WiFi.h>
#include              <WiFiClient.h>

// Install Library
#include              <Adafruit_GFX.h>     // v1.11.3
#include              <Adafruit_SSD1306.h> // v2.5.7
#include              <Adafruit_Sensor.h>
#include              <Adafruit_BME680.h>  // v2.0.2

// Global Value
/* WiFi */
#define CONN_SSID     "YOUR_SSID"
#define CONN_PASS     "YOUR_PASSWORD"

/* My ID,IP,MAC */
String  MY_NAME =     "ESP093";

IPAddress ip          (YOUR_IP);
IPAddress gateway     (YOUR_GATEWAY);
IPAddress dnServer    (YOUR_DNSSERVER);
IPAddress netmask     (YOUR_NETMASK);

/* UART */
#define TX            1         // D1
#define RX            3         // D3

/* I2C */
#define I2C_SDA       2         // D2
#define I2C_SCK       0         // D0
#define I2C_CLK       50000     // fSCL=50KHz

/* WakeUp Interval */
#define wup_itvl      10        // [分]

/* SW */
#define SW_RES        RX
#define SW_on         HIGH

/* OLED */
#define OLED_ADR      0x3C      // I2C ADDRESS
#define OLED_RESET    -1        // Reset pin # (or -1 if sharing Arduino reset pin)
#define SCREEN_WIDTH  128       // OLED display width, in pixels
#define SCREEN_HEIGHT 64        // OLED display height,in pixels
#define OLED_PWR      TX        // OLED 電源制御ピン
#define OLED_off      LOW
#define OLED_on       HIGH

Adafruit_SSD1306      display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

/* NTP DATA */
#define JST           (3600 * 9)
#define DAYLIGHTOFFSET_JST (0)
#define NTP_SERVER1   "ntp.nict.jp"         // NTP1
#define NTP_SERVER2   "ntp.jst.mfeed.ad.jp" // NTP2
static const char *pszWDay[]  = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
static const char *pszMonth[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};

/* BME680 */
#define SEALEVELPRESSURE_HPA (1013.25)
#define BME_ADR       0x76
Adafruit_BME680       bme;      // I2C接続:引数なし

/* Flag */
boolean flg_wifi =    false;    // True: WiFi on
boolean flg_boot =    false;    // True: Manual boot

/* Value */
float   tmp, hum, prs, gas;

///// ハードウエア 初期設定
void setup() {
  /* Serial (TXピンをGPIOとして利用) */
 Serial.begin(115200,SERIAL_8N1,SERIAL_RX_ONLY);

  /* Manual boot Check */
  pinMode(SW_RES, INPUT);
  if(digitalRead(SW_RES)==SW_on) flg_boot = true;
  
  /* Sleep Mode */
  wifi_set_sleep_type(MODEM_SLEEP_T);
  
  /* I2C */
  Wire.begin(I2C_SDA, I2C_SCK); // I2C pins
  Wire.setClock(I2C_CLK);       // I2C clock(fSCL)
  delay(100);

  /* OLED */
  digitalWrite(OLED_PWR, OLED_off);
  pinMode(OLED_PWR, OUTPUT);
  dsp_start();

  /* WiFi */
  WiFi.config(ip, gateway, netmask, gateway);
  if (!wifi_connect()) wifi_end();
  flg_wifi = true;

  /* NPT */
  configTzTime("JST-9", NTP_SERVER1, NTP_SERVER2);
  if (!ntp_connect()) wifi_end();

  /* BME680 */
  unsigned long timeout = millis();
  while(millis() - timeout < 1000) {
    if (bme.begin(BME_ADR)) {
      delay(150);
      break;
    }
    delay(150);
  }
  if (!bme.begin(BME_ADR)) wifi_end();
}

///// メインルーチン
void loop() {
  // 時刻表示
  dsp_datetime();
  
  // 温湿度・気圧測定(BME680)
  unsigned long timeout = millis();
  while(millis() - timeout < 1000) {
    if (bme.performReading()) {
      tmp = bme.temperature;
      hum = bme.humidity;
      prs = bme.pressure / 100.0;
      gas = bme.gas_resistance / 1000.0;
      break;
    }
    else delay(250);
  }

  // DATA 表示
  dsp_data();

  // SQL DATA送信
  /* ~ ここにWiFiでデータを送信するスケッチを書く 〜 */

  // 測定結果表示
  wifi_stop();
  if (flg_boot) delay(8000);

  // 計測終了
  wifi_end();
}

///// Display Control /////
/* OLED 起動処理 */
void dsp_start() {
  // Manual Boot Check
  if (!flg_boot) return;
  
  // OLED 
  digitalWrite(OLED_PWR, OLED_on);
  delay(500);
  
  // 初期化
  display.begin(SSD1306_SWITCHCAPVCC, OLED_ADR);

  // 初期表示
  display.clearDisplay();
  display.setTextColor(WHITE);
  display.setTextSize(1);
  display.setCursor( 10, 20);
  display.print("Sensor Startup..");
  display.display();
  display.clearDisplay();
}

/* OLED 時刻表示 */
void dsp_datetime() {
  // Manual Boot Check
  if (!flg_boot) return;
  
  // NPT 現在時刻の取得
  time_t timeNow   = time(NULL);
  struct tm* tmNow = localtime(&timeNow);

  // DATE 文字列作成
  char fmtDate[11];
  sprintf( fmtDate, "%02d/%02d(%s)",
           tmNow->tm_mon + 1,
           tmNow->tm_mday,
           pszWDay[tmNow->tm_wday] );
  // TIME 文字列作成
  char fmtTime[6];
  sprintf( fmtTime, "%02d:%02d",
           tmNow->tm_hour,
           tmNow->tm_min );
  // 表示
  display.clearDisplay();
  //display.fillRect(  0,  0,128, 16,BLACK);
  display.setTextSize(1);
  display.setCursor(  4, 4);
  display.println(fmtDate);
  display.setTextSize(2);
  display.setCursor( 68, 0);
  display.println(fmtTime);
  display.display();
}

/* OLED 測定結果表示 */
void dsp_data() {
  // Manual Boot Check
  if (!flg_boot) return;
  
   // 変数初期化
  char buf1[9], buf2[21];

  // 測定結果表示
  display.setTextColor(WHITE);
  display.setTextSize(1);
  /* Temp 表示 */
  display.setCursor(  0, 19);
  dtostrf          (tmp,4,1,buf1);
  sprintf          (buf2, "Tmp: %sC", buf1);
  display.print    (buf2);
  /* Humi 表示 */
  display.setCursor(  0, 28);
  dtostrf          (hum,4,1,buf1);
  sprintf          (buf2, "Hum: %s", buf1);
  display.print    (buf2);
  display.print    ('%');
  /* Press 表示 */
  display.setCursor(  0, 37);
  dtostrf          (prs,4,0,buf1);
  sprintf          (buf2, "Prs: %sHp", buf1);
  display.print    (buf2);
  /* GAS 表示 */
  display.setCursor(  0, 46);
  dtostrf          (gas,4,1,buf1);
  sprintf          (buf2, "Gas: %s", buf1);
  display.print    (buf2);
  
  /* MY_NAME 表示 */
  display.setCursor( 70, 54);
  display.print    ("   "+ MY_NAME);
  
  display.display();
}

/* OLED 終了処理 */
void dsp_end() {
  // Manual Boot Check
  digitalWrite(OLED_PWR, OLED_off);
}

///// WiFi Control /////
/* WiFi接続開始処理 -> True:WiFi接続完了, False:WiFi接続失敗 */
boolean wifi_connect() {
  // WiFi 接続サイクルスタート(接続->確認->再接続) 3トライ
  for(int i = 0; (i < 2); i++) {
    // WiFi接続 接続が記憶されていない場合接続情報を記憶させ次回自動接続にする。
    if (WiFi.SSID() != CONN_SSID) {  
      WiFi.persistent(true);
      WiFi.mode(WIFI_STA);
      WiFi.setAutoConnect(true);
      WiFi.begin(CONN_SSID, CONN_PASS); 
    }

    // 5秒間 0.5秒毎に接続確認
    unsigned long timeout = millis();
    while(millis() - timeout < 10000) {
      // 接続できた場合 Trueリターン
      if(WiFi.status() == WL_CONNECTED) return true;
      // 未接続は 500ms待ちしてループ
      delay(500);
    }
  }
  // WiFi 接続不可 エラーリターン
  return false;
}

/* WiFi stop処理 */
void wifi_stop() {
  if (WiFi.status() == WL_CONNECTED) {
    WiFi.persistent(false);
    WiFi.disconnect(); // ステーションモード維持,ssid・passwordをクリアしない
    delay(10);
  }
}

/* WiFi stop,終了処理 */
void wifi_end() {
  // 現在時刻取得
  time_t tmUnix    = time(NULL);           // unix
  struct tm* tmNow = localtime(&tmUnix);   // unix

  // WiFi切断
  wifi_stop();

  // 現在から起動までの分を計算
  int minSlp = 
    (wup_itvl<2) ? 1:(wup_itvl-(tmNow->tm_min % wup_itvl));

  // 30秒過ぎは起動を30秒早める
  int secSlp = 0;
  if (tmNow->tm_sec>30) {
    minSlp = minSlp-1; 
    secSlp = 30;
  }

  // 諸機能停止
  dsp_end();

  ESP.deepSleep((minSlp*62+ secSlp)*1000*1000);
  delay(500);
}

///// NTP Control /////
/* NTP 同期処理 */
boolean ntp_connect() {
  // NTP サーバー同期確認 (max 5sec)
  unsigned long timeout = millis();
  while (millis() - timeout < 10000) {
    // 現在時刻読込
    time_t timeNow   = time(NULL);
    struct tm* tmNow = localtime(&timeNow);

    // NPT同期完了 & 起動時間は正常リターン
    if(tmNow->tm_year>=100) {
      if (flg_boot) return true;
      if (wup_itvl==0) return true;
      if ((tmNow->tm_min % wup_itvl)==0) return true;
      return false;
    }

    // 同期未了は0.25秒おきに再確認
    delay(250);
  }
  // 接続できなかったので エラーリターン
  return false;
}

(2023.06.24 v0.0.5b改訂 マニュアルブート時のみ8秒の表示待ちを行う)
(2023.08.05 マニュアルブート判断文のバグ修正)

まあ基本的に起動して測定して終了するという一本道スケッチです。
主な注意点を箇条書きしますと、
・インストールが必要なモジュールは // Board Library, // Install Libraryにまとめてあります。
・起動してすぐ手動起動SWを読込みマニュアルブートかをチェック、そうであればflg_bootを立てます。
WiFiSSIDやパスワード、ipアドレスなどは適宜自分の環境に合わせた設定が必要です。
・測定値を格納する変数はグローバル変数にしていて各関数で引数の引渡しを不要にしています。
WiFiでデータを送信する部分は別に作成が必要です。ロギングが不要であればなくても構いません。
wifi_end()で次の正10分迄の時間を計算し、その時間deepsleepします。
・起動時にNTPに接続して正10分の確認をし、違っていればwifi_end()を呼びます。
・手動起動の場合は無条件で起動し測定を実施します。
・シリアルのTXをGPIOピンとして利用するためRXオンリーモードで起動しています。(参考
ESP-01 and ESP-01S How program and use the Pins and Leds)

てなところでしょうか。
スケッチは独学で自分がわかりやすい(忘れない!)ように書いているため一般的なフォーマットで記述されていないと思います。少々見にくい点はご容赦ください。