Ⅴ. 定期自動計測とデータ記録(ESP-WROOM-02)

 ここまで次のようなテーマで個別の機能について検討し、スケッチの作成を進めてきました。
  ・espRTC.ino:NTPによる時刻自動補正とバッテリーバックアップを備えた時計の製作
  ・espI2C.ino:I2Cによる温湿度・気圧・照度センサーの組み込みと計測
  ・espSD.ino:SDカードの増設とテキストデータの入出力、およびアナログセンサーの増設
 今回はこれらをすべてまとめて、指定条件のもとで自動的に計測・記録を行うシステムに仕上げてみましょう。

 ※スケッチのダウンロードは右側の[Download]ボタンをクリックしてください。    

1.今回の開発テーマ

 次のような新たな仕組みを追加して自動計測システムを完成させます。
  ①タイマー割り込み処理
    設定した時間間隔で計測・記録処理をキックする仕組み。
    NTPによる時刻自動補正を定期的にキックする仕組み。
  ②一連の動作の連係処理
    計測指示~計測結果取得~SDカードへの記録など一連の計測関連処理を統合する。
  ③測定処理条件の整理
    測定開始時刻、測定間隔、時刻補正条件など外部からの設定・変更を要するファクターの整理。

2.ソフト開発のポイント

 今回の最終的なスケッチはかなり大きなものになります。しかし、多くは今までに作成したスケッチの組み合わせなので、新たなコードの量はわずかです。その新たなコードの大半はタイマー割り込み処理に関するものです。

(1)タイマー割り込みの利用

 では、なぜタイマー割り込みを利用するのでしょうか。
 『Ⅱ.システムクロックの製作』では、NTPサービスを利用して時刻調整をするために、loop処理中で調整時刻が到来しているかを常時監視していて、到来すれば時刻合わせを行っていました。一定間隔で計測する仕組みを同じ方法で行うとすれば、時刻調整と測定処理が重なった場合の計測モレなどが起きないような処置が必要になります。またこのシステムは最終的に、ネットからの照会に対して計測データを配信する機能をもたせる予定なので、常時監視でMCUを占有してしまう方法ではムリがあります。
 これらはタイマー割り込みを利用することで解決できます。指定時刻に自動的に割り込みが起きるので、一連のタイミング制御が不要になりコードを簡素化できます。またWi-Fi通信中でも測定モレや時刻調整抜けが起きる心配がなくなります。ただし、時刻合わせや計測の間隔はすべてタイマー任せになるので、タイマーの精度に影響されることになります。これを補正する仕組みも考えられますが、ここではふれないことにします。


(2)利用上の注意

 タイマー割り込みを利用するには、標準ライブラリーのTickerを使用します。
 Arduino IDEを起動して[ファイル]-[スケッチ例]で表示される一覧から[Ticker]を探すと、簡単なサンプルスケッチを表示できるので参考にしてください。
 さて、新規スケッチの作成準備ができたら、[ファイル]-[スケッチ]-[ライブラリをインクルード]で一覧を表示させて[Ticker]を選択してください。先頭行に"#include "が入力されて、Tickerを使える準備が整いました(わざわざこの手順を踏まなくても、直接#include文をキー入力しても同じです)。
 サンプルスケッチを眺めると、Tickerの利用は実にシンプルであることが分かります。Tickerのインスタンスを宣言しておき(例えば「Ticker ticker;」のように)、初期化処理部分で何秒ごとにどんなメソッド(コールバック関数)を起動するかを指示するだけです。例えば以下のように指示します。
    ticker.attach(10, doAnything);
 こうすると、10秒ごとにメソッドdoAnything()が起動されます。ここで気をつけなければならないのは、doAnythingの中ではネットワーク通信、シリアル通信、ファイル読み書きなどのブロッキングI/O処理を行ってはならない点です。実はうっかりこれをやってしまいました。コールバック関数内にWi-Fiを利用して時刻合わせのNTPサービスを呼び出す処理を書いてしまい、誤動作が発生して右往左往。ネットで次のページを見つけて参考にさせていただきました。
      SG Labs 「ESP-WROOM-02で定期的割り込み処理」
 なおこれに関連して、英文ページですが、次の文書を見つけました。これはTickerだけでなく、ESP-WROOM-02に内蔵されているESP8266利用上の要点が簡潔に解説されているので、一読をお勧めします。
      ESP8266 Arduino Core: Documentation for ESP8266 Arduino Core.

(3)タイマー割り込み部のコード

 タイマー割り込み関連のコードは次のとおりです。
 〔ポイント説明〕
   ・1行目: 割り込みを利用するためにTickerのヘッダーファイルをインクルードします。
   ・4~5行: 計測用と時刻調整用の2つのTickerのインスタンスを作成します。
   ・6行目: 時刻調整の割り込みが発生した場合、コールバック関数内ではNTPサーバーを呼べないので
        (ブロッキングI/Oはダメなので)、この変数をフラグとして利用します。
   ・9行目: 初期化処理で、開始時刻(分)が指定されていればシステムクロックの分の値が一致するまで
        待ちます。これで指定時刻から測定を開始できるわけです
   ・23行目: 初期化処理内で初回の時刻合わせを実行させます。
   ・26行目: 初期化処理内で初回の計測・編集・記録処理を実行させます。
   ・29~30行: 初期化処理内で計測用と時刻調整用の2つのタイマー割り込み条件を指定します。
   ・34~41行: フラグbReadyTickerが立っていれば時刻調整を行ってフラグを下ろします。
          このフラグは、時刻調整割り込み時にtrueにセットされます。
          ブロッキングI/O処理やそれを含むメソッドの実行はここに書きます。
   ・48行目: 計測処理のタイマー割り込みで呼ばれるコールバック関数です。
   ・51~53行: 計測処理を実行し、結果を編集してSDカードのファイルに記録します。
   ・61行目: 時刻調整のタイマー割り込みで呼ばれるコールバック関数です。
   ・63行目: フラッグbReadyTickerをセットするだけで、実行はloop()内に委ねます。
   ・69行目: 時刻調整メソッドです。
   ・72~73行: NTPサーバーからJSTの時刻を取得します。
   ・75行目: システムクロックの時刻を更新します(時刻を合わせます)。
#include <Ticker.h>

// Timer interruption
Ticker ticker1;               // For measurement
Ticker ticker2;               // For time adjustment
bool  bReadyTicker = false;

void setup() {
    // Wait until the specified time and start up.
    if (strStartTime != "") {
        Serial.print("Just wait until next time(minuets) =  "); Serial.println(strStartTime);
        while (1) {
            getDateTime(&dtClock);
            sTime = editTime(dtClock);
            if (sTime.substring(3, 5) == strStartTime)
                break;
            delay(100);
        }
    }
    Serial.println("It is just in time!!");

    // Adjust time.
    adjustTime();

    // Do 1'st measurement.
    kickRoutineWork();

    // Timer: interrupt time and event setting.
    ticker1.attach(iIntervalTime, kickRoutineWork);
    ticker2.attach(iNtpInterval, kickTimeAdjust);
}

void loop() {
    if (bReadyTicker) {
    /*
     * [Timer interrupt process]
     *    Match measurement timing at time correction.
     */
        adjustTime();
        bReadyTicker = false;
    }
}

/*
 * Timer interrupt event handler1
 *    <Start measurement>
 */
void  kickRoutineWork()
{
    // Measurment & write file
    doMeasurement();
    String buf =editMeasuredResult(rstMeasured);
    writeMeasurementResult(DATA_FILE, buf);
    Serial.println(buf);
}

/*
 * Timer interrupt event handler2
 *    <Start time adgustment>
 */
void kickTimeAdjust()
{
    bReadyTicker = true;
}

/*
 * Adjust the clock time.
 */
void adjustTime()
{
    while(1) {
        if (timeClient.update()) {
            String strJST = timeClient.getFormattedTime();
            Serial.print("JST time = "); Serial.println(strJST);
            setTime(strJST);
            return;
        }
        delay(50);
    }
}


(4)外部設定条件の検討

 計測開始のタイミングや計測間隔、時刻合わせの間隔など、コード内で記述している定数類には外部から指定できた方が便利なものがあります。次のフィールドはSDファイルに設定したパラメータファイルから読み込むよう、最終回までには具体化したいと思います。
// WiFi connection
const char* ssid = "your_ssid";
const char* password = "your_password";

// Time adjust
const char* sNtpUrl = "ntp.nict.jp";
int  iNtpOffset = 32400;      // UTC + 9h (3600sec * 9h)sec.
int  iNtpInterval = 180;      // Time adjust interval(sec.)

// Measurement conditions
String strStartTime = "00";   // "00"~"59" (min.)
int   iIntervalTime = 10;     // Measurement time interval(sec.)


3.スケッチの実行

(1)実行結果と評価

 下図は、動作開始時刻に"00"分を指定して起動したシリアルモニターの表示です。
 先のコードで見たように、まずSDカード装置が有効であることを確認してWi-Fiに接続します。その後、指定された00分まで待ち続け、時刻が到来するとその旨を表示して初回の時刻調整と計測を行っています。1秒の時間差で調整が行われ、このズレを含んだまま指定された10秒間隔の測定を行って結果を表示しています。
 指定された時刻調整間隔の3分が経過すると、再度時刻調整を行っている様子が分かります。次の計測から1秒のズレが補正されていますが、なぜこうなったかは定かでありません。
 このように、所定の計測条件にしたがって計測してSDカードに記録する「自動計測システム」が完成しました。



(2)スケッチ全景

 かなり大きなコードになったので、今回新たに追加ないし修正した部分をハイライト表示しています。
/*
 * File:      espMeasure.ino
 * Function:  Measurement using timer interrupt.
 * Date:      2017/01/24
 * Author:    Marchan
 * 
 * Hardware   MCU:  ESP-WROOM-02(ESP8266)
 *            RTC:  DS1307 I2C Real time clock module.
 *            Combined humidity and pressure sensor: BME280
 *            Digital illuminance sensor: BH1750
 *            SD card device: Hirose DM3AT series.
 *            Soil moisture sensor: YL-38 &YL-69
 *
 * Remarks:   Measurement and calibration codes of BME 280
 *            uses SWITCH SCIENCE's sample sketch "BME280_I2C.zip".
 *                https://trac.switch-science.com/wiki/BME280
 */
#include <Wire.h>
#include <ESP8266WiFi.h>
#include <NTPClient.h>
#include <WiFiUdp.h>
#include <SPI.h>
#include <SdFat.h>
#include <Ticker.h>

// I2C Address
#define DS1307_ADDRESS 0x68   // Realtime clock
#define BME280_ADDRESS 0x76   // Humidity, Pressure and Temperature sensor
#define BH1750_ADDRESS 0x23   // Illuminance sensor

// SD card drive & File name
#define SDCARD_DRIVE   16     // SD card chip select number
#define ANALOG_PIN     A0     // TOUT pin
#define DATA_FILE      "logfile.txt"

bool bAtFirst = true;

// SD card control
SdFat SD;
bool bSD_Enabled;

// WiFi connection
const char* ssid = "your_ssid";
const char* password = "your_password";

// Time adjust
WiFiUDP ntpUDP;
NTPClient timeClient(ntpUDP);
const char* sNtpUrl = "ntp.nict.jp";
int  iNtpOffset = 32400;      // UTC + 9h (3600sec * 9h)sec.
int  iNtpInterval = 180;      // Time adjust interval(sec.)

// Timer interruption
Ticker ticker1;               // For measurement
Ticker ticker2;               // For time adjustment
bool  bReadyTicker = false;

// Measurement conditions
String strStartTime = "00";   // "00"~"59" (min.)
int   iIntervalTime = 10;   // Measurement time interval(sec.)

// DS1307 Clock data
struct ClockData {
    byte year;
    byte month;
    byte day;
    byte week;
    byte hour;
    byte minute;
    byte sec;
    byte ctrl;
};
ClockData dtClock;

//BME280 Global variables
unsigned long int hum_raw, temp_raw, pres_raw;
signed long int t_fine;

//BME280 Calibration variables
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;
 
//Measured result
struct MeasuredResult {
    double  temperature;      // BME280
    double  pressure;         // BME280
    double  humidity;         // BME280
    int     illuminance;      // BH1750
    int     soil_moisture;    // YL-38
    String  datetime;
};
MeasuredResult rstMeasured;

/*****************************************************************************
 *                          Predetermined Sequence                           *
 *****************************************************************************/
void setup() {
    Serial.begin(115200);

    // Prepare I2C protocol.
    Wire.begin();
    delay(50);

    getDateTime(&dtClock);
    String sTime = editTime(dtClock);
    Serial.print("\r\nStart "); Serial.println(sTime);

    // Prepare SD card unit.
    if (SD.begin(SDCARD_DRIVE))
        bSD_Enabled = true;
    else {
        bSD_Enabled = false;
        Serial.println("SD Drive does'nt work!");
        return;
    }
    Serial.println("SD enabled!");

    // Prepare measurment
    prepareBME280();      // Humidity, Pressure and Temperature sensor
    prepareBH1750(BH1750_ADDRESS);  // Illuminance sensor

    // Prepare WiFi system.
    WiFi.begin(ssid, password);
    while (WiFi.status() != WL_CONNECTED) {
      delay(500);
      Serial.print(".");
    }
    Serial.println("\r\nWiFi connected!");

    // Recreate NTPClient object.
    timeClient = NTPClient(ntpUDP, sNtpUrl, iNtpOffset);

    // Wait until the specified time and start up.
    if (strStartTime != "") {
        Serial.print("Just wait until next time(minuets) =  "); Serial.println(strStartTime);
        while (1) {
            getDateTime(&dtClock);
            sTime = editTime(dtClock);
            if (sTime.substring(3, 5) == strStartTime)
                break;
            delay(100);
        }
    }
    Serial.println("It is just in time!!");

    // Adjust time.
    adjustTime();

    // Do 1'st measurement.
    kickRoutineWork();

    // Set dateTime callback function to provide timestamp.
    SdFile::dateTimeCallback(dateTime);

    // Timer: interrupt time and event setting.
    ticker1.attach(iIntervalTime, kickRoutineWork);
    ticker2.attach(iNtpInterval, kickTimeAdjust);
}

void loop() {
    if (!bSD_Enabled) {
        Serial.println("Can't work, the SD drive is disabled!");
        delay(60000);
        return;
    }

    // Check date input ('hh/mm/dd/w') from serial buffer.
    if (Serial.available() >= 8) {
        String date = Serial.readString();
        setDate(date);
    }

    if (bReadyTicker) {
    /*
     * [Timer interrupt process]
     *    Match measurement timing at time correction.
     */
        adjustTime();
        bReadyTicker = false;
    }
}

/****************************< Interrupt handler >****************************/
/*
 * Timer interrupt event handler1
 *    <Start measurement>
 */
void  kickRoutineWork()
{
    // Measurment & write file
    doMeasurement();
    String buf =editMeasuredResult(rstMeasured);
    writeMeasurementResult(DATA_FILE, buf);
    Serial.println(buf);
}

/*
 * Timer interrupt event handler1
 *    <Start time adgustment>
 */
void kickTimeAdjust()
{
    bReadyTicker = true;
}
/*****************************************************************************/

/*
 * Adjust the clock time.
 */
void adjustTime()
{
    while(1) {
        if (timeClient.update()) {
            String strJST = timeClient.getFormattedTime();
            Serial.print("JST time = "); Serial.println(strJST);
            setTime(strJST);
            return;
        }
        delay(50);
    }
}

/*
 * Measure and  calibrate BME280, also illuminance
 *    Result stored into rstMeasured structure.
 */
void doMeasurement()
{
    // Read BME280 measured result
    readData();
    // Calibration
    long temp_cal = calibration_T(temp_raw);
    long press_cal = calibration_P(pres_raw);
    long hum_cal = calibration_H(hum_raw);

    // Measurement illuminance
    int val1 = measureIlluminance(BH1750_ADDRESS);

    // Measurement soi_moisture
    int val2 = measureSoilMoisture(ANALOG_PIN);

    // Get date and time data from DS1307
    getDateTime(&dtClock);
    
    //Store into measured resut structure
    rstMeasured.temperature = (double)temp_cal / 100.0;
    rstMeasured.pressure = (double)press_cal / 100.0;
    rstMeasured.humidity = (double)hum_cal / 1024.0;
    rstMeasured.illuminance = val1;    
    rstMeasured.soil_moisture = val2;
    rstMeasured.datetime = editDateTime(dtClock);
}

/*
 * Edit measured result data.
 *    Argument: (struct)Measured result.
 *    Return:   Edited result.
 */
String editMeasuredResult(MeasuredResult data)
{
    char wbuf[20];
    String strWork = "";
    printNum(wbuf, data.temperature, 4, 1);
    strWork.concat(wbuf);
    strWork.concat(",");
    printNum(wbuf, data.pressure, 7, 1);
    strWork.concat(wbuf);
    strWork.concat(",");
    printNum(wbuf, data.humidity, 5, 1);
    strWork.concat(wbuf);
    strWork.concat(",");
    printNum(wbuf, data.illuminance, 6);
    strWork.concat(wbuf);
    strWork.concat(",");
    printNum(wbuf, data.soil_moisture, 3);
    strWork.concat(wbuf);
    strWork.concat(", ");
    strWork.concat(data.datetime);
    return strWork;
}

/*
 * Write measurment result to SD.
 *    Argument: (String)File name, String result.
 */
void writeMeasurementResult(String fname, String data)
{
    char buf[40];
    File df = SD.open(fname, FILE_WRITE);
    if (df)
    {
        df.println(data);
        df.close();
    }
}

/*
 * Read text line from SD card.
 *    Argument: File* handle, char* buffer, int Maximum text length.
 *    Return:   Size of read data.
 */
int readln(File* df, char* buf, int len)
{
    *buf = '\0';
    int pos = 0;    
    
    while (df->available()) {
        char ch = df->read();
        *(buf + pos) = ch;
        if (ch == 0x0a) {
            *(buf + pos + 1) = '\0';
            return pos;
        }
        pos++;
        if (pos >= len) {
            Serial.println("Overflowed!!");
            *buf = '\0';
            return 0;
        }
    }
}

/*
 * Print numeric (double, float)
 *    Argument: char* buffer, double data, int edit-length, int decimal.
 */
char *printNum(char *buf, double field, int len, int decimal)
{
    *buf ='\0';
    char bufw[40];
    dtostrf(field, len, decimal, bufw);
    strcpy(buf, bufw);
    return buf;
}

/*
 * Print numeric (int, long)
 *    Argument: char* buffer, int data, int edit-length.
 */
char *printNum(char *buf, int field, int len)
{
    *buf = '\0';
    char bufw[40];
    sprintf(bufw, "%d", field);
    int lenw = strlen(bufw);
    int i;
    for (i = 0; i < len - lenw; i++) {
        *(buf + i) = ' ';
    }
    *(buf + i) = '\0';
    strcat(buf, bufw);
    return buf;
}

/* ======================== DS1307 Clock Control ============================*/
/*
 * Read data from DS1307 Register 
 */
void getDateTime(ClockData *dt)
{
    int iValue = 0;
  
    Wire.beginTransmission(DS1307_ADDRESS);
    Wire.write(iValue);
    Wire.endTransmission();
    Wire.requestFrom(DS1307_ADDRESS, 7);
    dt->sec = Wire.read();
    dt->minute = Wire.read();
    dt->hour = Wire.read();
    dt->week = Wire.read();
    dt->day = Wire.read();
    dt->month = Wire.read();
    dt->year = Wire.read();
}

/*
 * Tell day of week from code.
 */
String tellDayOfWeek(byte num)
{
    static String week[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
  
    if (num >= 1 && num <= 7)
      return week[num-1];
    else
      return "";
}

/*
 * Set DS1307 time register.
 *    Argument: (String)Time string 'hh:mm:ss'.
 */
void setTime(String sTime)
{
    byte bValue = 0x00;   //Top Address
    String buf = sTime;
    buf.replace(":", "");  buf.trim();
    if (buf.length() == 6) {
        byte hour = (buf.charAt(0) << 4) + (buf.charAt(1) & 0x0f);
        byte minute = (buf.charAt(2) << 4) + (buf.charAt(3) & 0x0f);
        byte sec = (buf.charAt(4) << 4) + (buf.charAt(5) & 0x0f);
        Wire.beginTransmission(DS1307_ADDRESS);
        Wire.write(bValue);
        Wire.write(sec);
        Wire.write(minute);
        Wire.write(hour);
        Wire.endTransmission();
    }
}

/*
 * Set DS1307 date register.
 *    Argument: (String)Time string 'yy/mm/dd/w'.
 */
void setDate(String sDate)
{
    byte bValue = 0x03;   //Top Address
    String buf = sDate;
    buf.replace("/", "");  buf.trim();
    if (buf.length() >= 6) {
        byte year = (buf.charAt(0) << 4) + (buf.charAt(1) & 0x0f);
        byte month = (buf.charAt(2) << 4) + (buf.charAt(3) & 0x0f);
        byte day = (buf.charAt(4) << 4) + (buf.charAt(5) & 0x0f);
        byte week;
        if (buf.length() == 7) {
            week = (buf.charAt(6) & 0x07);
          if (week < 0x01 || week > 0x07)
            week = 0x01;
        }
        Wire.beginTransmission(DS1307_ADDRESS);
        Wire.write(bValue);
        Wire.write(week);
        Wire.write(day);
        Wire.write(month);
        Wire.write(year);
        Wire.endTransmission();
    }
}

/*
 * Edit DS1307 date and time.
 */
String editDateTime(ClockData dt)
{
    return editDate(dt) + " " + editTime(dt);
}

/*
 * Edit time register.
 */
String editTime(ClockData dt)
{
    String buf = "";
    char wbuf[12];
    sprintf(wbuf, "%02x:%02x:%02x", (int)dt.hour, (int)dt.minute, (int)dt.sec);
    buf.concat(wbuf);
    return buf;
}

/*
 * Edit date register.
 */
String editDate(ClockData dt)
{
    String buf = "20";
    char wbuf[12];
    sprintf(wbuf, "%02x/%02x/%02x", (int)dt.year, (int)dt.month, (int)dt.day);
    buf.concat(wbuf);
    return buf;
}

/*
 * Callback function to provide file timestamp.
 */
void dateTime(uint16_t* date, uint16_t* time)
{
    uint16_t year;
    uint8_t month, day, hour, minute, second;
    
    getDateTime(&dtClock);
    year = 2000 + ((int)(dtClock.year >> 4)) * 10 + (int)(dtClock.year & 0x0f);
    month = ((int)(dtClock.month >> 4)) * 10 + (int)(dtClock.month & 0x0f);
    day = ((int)(dtClock.day >> 4)) * 10 + (int)(dtClock.day & 0x0f);
    hour = ((int)(dtClock.hour >> 4)) * 10 + (int)(dtClock.hour & 0x0f);
    minute = ((int)(dtClock.minute >> 4)) * 10 + (int)(dtClock.minute & 0x0f);
    second = ((int)(dtClock.sec >> 4)) * 10 + (int)(dtClock.sec & 0x0f);
    *date = FAT_DATE(year, month, day);
    *time = FAT_TIME(hour, minute, second);
}

/* ====================== BME280 T-P-H Measurement ==========================*/
/*
 * Initiate BME280 and get calibration variables.
 */
void prepareBME280()
{
    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;

    writeReg(0xF2,ctrl_hum_reg);
    writeReg(0xF4,ctrl_meas_reg);
    writeReg(0xF5,config_reg);
    readTrim();
}

/*
 * Read calibration variables into structure.
 */
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];   
}

/*
 * Write data into BME280 register.
 *    Argument: (uint8_t)Register address, (uint8_t)data.
 */
void writeReg(uint8_t reg_address, uint8_t data)
{
    Wire.beginTransmission(BME280_ADDRESS);
    Wire.write(reg_address);
    Wire.write(data);
    Wire.endTransmission();    
}

/*
 * Read data from BME280 register.
 */
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];
}

/*
 * Calibration [Temperature]
 */
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; 
}

/*
 * Calibration [Pressure]
 */
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;
}

/*
 * Calibration [Humidity]
 */
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);   
}

/* ========================= BH1750 Illuminance =============================*/
/*
 * Initiate BH1750
 */
void prepareBH1750(int i2cAddress)
{
    Wire.beginTransmission(i2cAddress);  
    Wire.write(0x10); 
    Wire.endTransmission();
    delay(180);
}

/*
 * Measure illuminance
 */
int measureIlluminance(int i2cAddress)
{
    uint16_t val = 0;
    byte buf[2]; 
    if (measureIlluminance(i2cAddress, buf) == 2){
        val = ((buf[0] << 8) | buf[1]) / 1.2;   // Calculate
        return (int)val;
    }
    return 0;
}  
int measureIlluminance(int i2cAddress, byte *buff)
 {
    int i = 0;
    *buff = 0x00;
    Wire.beginTransmission(i2cAddress); 
    Wire.requestFrom(i2cAddress, 2);  
    while (Wire.available())  
    { 
        *(buff + i) = Wire.read();
        i++;
    }
    Wire.endTransmission();  
    return i; 
 }

/* ========================= YL-38 Soil moisture ============================*/
/*
 * Measure soil moisture
 *    Return: moisture value(0~100)
 */
int measureSoilMoisture(int pin_no)
{
    int val = 1023 - analogRead(pin_no);
    return (int)map(val, 56, 545, 0, 100);
}
 次回は、ブラウザからの要求を受けて計測したり応答するWi-Fiサーバーを構築します。お楽しみに!

 
Copyright (C) 2011-2024 Marchan, All rights reserved.