Arduinoとトワイライト(TWELITE)でセンサー情報を表示する

組み込みエンジニア
本記事はプロモーションが含まれています。

こんにちは、ENGかぴです。

Arduinoとトワイライトを使ってBME280のセンサー情報をLCDに表示するシステムを作ることができます。各種モジュールの動作を確認しながら1つ1つの基礎技術を取得することで様々な応用範囲が広がり楽しさが倍増することは間違いありません。

LCDはQAPASS1602(スターターキットに付属)を使用しています。BME280モジュールはBME280 温湿度・気圧センサモジュール(I2C/SPI タイプ)(ストロベリー・リナックス)を使用しています。

Arduino UNOを対象としトワイライトは子機と親機を準備してArduinoには親機を接続します。子機は温湿度と気圧のデータを取得できるBME280を接続します。

Arduino UNOを使って動作確認を行ったことを下記リンクにまとめています。

Arduinoで学べるマイコンのソフト開発と標準ライブラリの使い方

トライライト(TWELITE)で動作確認したことについてまとめています。

トワイライト(TWELITE)のソフト開発と無線通信でできること

Arduinoとトワイライトの組み合わせの全体構成

Arduinoとトワイライトの組み合わせの全体構成
Arduinoとトワイライトの組み合わせの全体構成

親機と子機の全体構成ですが、子機はトワイライト(TWELITE)とBME280の組み合わせで親機はArduino UNOとトワイライトとLCDの組み合わせです。

  1. 子機のトワイライトでBME280のデータを取得して親機に無線通信する。
  2. 親機のトワイライトは子機からの無線データを受信する
  3. 親機のトワイライトからArduinoにデータをシリアル通信する
  4. LCDにデータを表示する

1~4までの項目について通信の流れに沿いながら各項目で説明していきます。

広告

トワイライトの子機

トワイライトとBME280による子機の構成
トワイライトとBME280による子機の構成

トワイライトを子機としてBME280からI2C通信を使って温度・気圧・湿度の情報を取得し親機にデータを送信する方法は下記記事にまとめています。

トワイライト(TWELITE)のI2C通信を実装し無線通信する

const char APP_FOURCHAR[] = "TEMP";

pack_bytes(pkt.get_payload(), make_pair(APP_FOURCHAR, 4)); //無線通信で文字ヘッダーをpayloadにセット

変更点は、親機と子機でアプリの文字コードを一致させるために文字ヘッダーを送信していることです。親機はアプリケーションIDとチャンネルが一致する場合受信判定になりますが、どの種別のデータなのかの判断ができないため文字ヘッダで区別します。

スポンサーリンク

Arduinoとトワイライトの組み合わせによる親機

Arduinoとトワイライトの組み合わせ(親機)
Arduinoとトワイライトの組み合わせ(親機)

親機はArduinoとトワイライトを組み合わせた構成です。トワイライトは親機のソフトを書き込みます。子機が無線通信したデータをLCDに表示します。

SWを押すと温度→気圧→湿度→温度→・・と表示を切り替えて表示するようにします。Arduinoとトワイライトは電源電圧に差があるためR1とR2によってArduinoからトワイライトに向かう信号を分圧しています。

Arduinoはトワイライトの出力の3.3Vを受けてもHレベルと判定できるためArduinoのRXに直接信号を入力しています。

トワイライトの親機

トワイライトの親機は子機からデータを受信するとシリアル通信を使ってArduinoにデータを送信するようにします。

トワイライト(TWELITE)で親機を実装し子機から無線受信する

void receive() {
    //子機から無線データを受けた場合先に文字ヘッダを格納し判定する
    if (strncmp(APP_FOURCHAR, fourchars, 4)) {return; } //
    	expand_bytes(np,rx.get_payload().end(),rcvdata); //文字ヘッダー以下を格納
	SerialTxSet(); //Arduinoに受信データをシリアル通信する
}

親機と子機の間でアプリケーションの判定をするため文字ヘッダによるデータの判別を行っています。strncmp()で文字ヘッダを比較して同一であれば残りのデータを受け入れる処理を行います。

子機を2台以上設置する場合は場合はどの子機から送信されたデータであるかアドレスを区別するようにしておく必要があります。(文字ヘッダに加えてアドレス番号を送信するようにするなど工夫する)

今回は1対1の通信なので区別はしていません。受信データを受け入れた後はArduinoにデータをシリアル通信を使って送信します。

トワイライト(TWELITE)とArduino間でシリアル通信する

Arduinoにデータをシリアル通信しますが、トワイライトのバイナリ通信の電文を使って送信するようにしています。Arduino側はトワイライトのバイナリ通信の電文に合致する場合に受信データとして受け入れるようにしています。

void RxDataChk(){
 
    if( chkdat[0]== HEADER1 && chkdat[1]== HEADER2 && chkdat[2]== HEADER3 ){ //電文ヘッダーが一致するか
        //受信データを受け入れる 
        rxdata.sum = CalcSum(&rxdata.buf[0],sz); //SUMの計算
        if( rxdata.sum == rxdata.buf[sz]){//SUMのチェック
            DataCng();  //センサーの情報をセット
        }
    }else{
        //電文のヘッダーが不一致
    }
}

HEADER1(0xA5)・HEADER2(0x5A)・HEADER3(0x80)として電文ヘッダが一致することを確認してから受信データを受け取りSUMが一致すると正常なデータとしてセンサーの情報としてセットするようにしています。

広告

ArduinoによるLCDの表示

Arduinoを使ったLCDの表示方法は下記リンクにまとめています。

Arduinoの標準ライブラリでLCDに文字を表示する方法

const char disptbl[MD_MAX][COL_SZ]={
    "Temperature     ",
    "Pressure        ",
    "Humidity        "
};

void DispSet(){

    lcd.clear();
    lcd.setCursor(0, 0); //1段目の左端にカーソル
    lcd.print(disptbl[dispmd]); //1段目の文字列

    switch( dispmd ){
    case MODE_NO::MD_NO1:
        lcd.setCursor(0, 1); //2段目の左端にカーソル
        lcd.print((double)temperature/100); //2段目の文字列を表示させる
        lcd.print("[deg]");
        break;
    case MODE_NO::MD_NO2:
        lcd.setCursor(0, 1); //2段目の左端にカーソル
        lcd.print((double)pressure/10000); //2段目の文字列を表示される
        lcd.print("[hPa]");
        break;
    case MODE_NO::MD_NO3:
        lcd.setCursor(0, 1); //2段目の左端にカーソル
        lcd.print((double)humidity/1024); //2段目の文字列を表示される
        lcd.print("[%]");
        break;
    }
}

LCDの表示の1段目はボタンを押して確定した表示モードで表示を切り替えています。2段目は温度・気圧・湿度のデータが表示されるようにデータ値を換算してlcd.print()で表示するようにします。

今回の例ではLCD表示するセンサー情報の換算の頻度が多くなってしまいますが、全体の処理が少ないためLCDの表示の更新頻度(100msにしている)に対して余裕があると判断しています。

子機からのセンサー情報は設定したインターバルによるため毎回処理させる必要はなくインターバルが120秒であれば120秒に一回換算しておけば十分という考え方もできます。

動作確認

Arduinoとトワイライトによる動作確認
Arduinoとトワイライトによる動作確認

ライブラリの不具合で文字化けしてしまう場合があります。文字化けした場合はリセットする以外に復帰する方法がありませんでした。正常に表示できる場合は初期画面として電源を入れると最初に「Arduino&Twelite」とバージョン「Ver1.00」を表示します。

初期が終わると表示モード1としてTemperature(温度)が表示されます。ボタンを押すと表示モード2としてPressure(気圧)が表示されます。もう一度ボタンを押すと表示モード3としてHumidity(湿度)が表示されます。ボタンを押すと表示モード1に戻ります。

子機からデータを受信するまではすべてのデータは0になります。一度受信するとその値が表示され続けますが、子機からの受信が途絶えたと判断できるときは値をクリアするなど工夫してもよいかもしれません。(今回はそのまま表示しています。)

スポンサーリンク

ソースコード全体

以下のソースコードはコンパイルして動作確認をしております。コメントなど細かな部分で間違っていたりやライブラリの更新などにより動作しなくなったりする可能性があります。参考としてお使いいただければと思います。

親機側:Arduino:

#include <MsTimer2.h>
#include <LiquidCrystal.h>

#define LCD_RS 2
#define LCD_EN 3
#define LCD_D4 4
#define LCD_D5 5
#define LCD_D6 6
#define LCD_D7 7

#define TIME_UP 0
#define TIME_OFF -1
#define BASE_CNT 10 //10msがベースタイマとなる
#define LCD_TIM_MAX 10
#define RCV_TIM_MAX 10
#define DI_FILT_MAX 4
#define FILT_MIN 1
#define PIN_DI1 10
#define COL_SZ 16
#define RING_SZ 256
#define RXCHK_SZ 4
#define BUF_SZ 64
#define HEADER1 0xA5
#define HEADER2 0x5A
#define HEADER3 0x80

enum MODE_NO{
    MD_NO1 = 0,
    MD_NO2,
    MD_NO3,
    MD_MAX
};

struct DIFILT_TYP{
    uint8_t wp;
    uint8_t buf[DI_FILT_MAX];
    uint8_t di1;
};

struct RING_MNG{
    uint8_t wp;
    uint8_t rp;
    uint8_t buf[RING_SZ];
};

struct COM_TYP{
    uint8_t header[3];
    uint8_t sz;
    uint8_t buf[BUF_SZ];
    uint8_t sum;
};

const char disptbl[MD_MAX][COL_SZ]={
    "Temperature     ",
    "Pressure        ",
    "Humidity        "
};

// application use
LiquidCrystal lcd(LCD_RS, LCD_EN, LCD_D4,LCD_D5, LCD_D6, LCD_D7);
int16_t timdifilt = TIME_OFF;
int8_t timlcd = TIME_OFF;
int16_t timrcv = TIME_OFF;
int16_t cnt10ms;
bool btnflg1;
DIFILT_TYP difilt;
RING_MNG rxarduino;
uint8_t dispmd;

COM_TYP rxdata;  //チェック後の受信データ
uint32_t temperature;
uint32_t pressure;
uint32_t humidity;

/*** Local function prototypes */
void TimerCnt();
void mainTimer();
void DiFilter();
void DispSet();
void RxDataChk();
void SerialTxSet(uint8_t cmd);
uint8_t CalcSum(uint8_t *buf, uint8_t sz);
void DataCng();

void setup() {
    pinMode( PIN_DI1, INPUT_PULLUP );

    MsTimer2::set(1,TimerCnt); //1msごとに関数へ遷移
    MsTimer2::start();

    Serial.begin(115200);
  
    lcd.begin(16,2); //16×2を表示領域
    lcd.print("Arduino&Twelite");
    lcd.setCursor(0, 1); //2段目の左端にカーソル
    lcd.print("Ver1.00         ");
    delay(2000); //初期表示を2秒間行う
  
    timdifilt = FILT_MIN;
    for( uint8_t i=0; i < 10; i++ ){ //DIの初期値を確定するためのループ
        mainTimer();
        DiFilter();
        delay(10);
    }
    cnt = 0; //カウントが進んでいるので0でクリア
    lcd.clear(); //初期表示をクリア
    timlcd = LCD_TIM_MAX;
}

void loop() {
    mainTimer();
    DiFilter();

    if(difilt.di1 == 0){
        if(btnflg1){
            btnflg1 = false;
            if(++dispmd >= MD_MAX ){ //ボタンを押すとモードを変更する
                dispmd = MD_NO1;
            }
        }
    }else{
        btnflg1 = true;
    }

    if( timlcd == TIME_UP ){
        timlcd = LCD_TIM_MAX;
        DispSet(); //LCD表示部分をセット
    }

    while( Serial.available()){ //シリアル通信データの受信
        rxarduino.buf[rxarduino.wp] = Serial.read();

        if( ++rxarduino.wp >= RING_SZ ){ //次にデータを入れる場所を更新
            rxarduino.wp = 0;
        }
    }
    RxDataChk(); //シリアルデータの電文チェック
}
/* callback function add */
void TimerCnt(){
    ++cnt10ms;
}
/* Timer Management function add */
void mainTimer(){

    if( cnt10ms >= BASE_CNT ){ //10msごとにここに遷移する
        cnt10ms -=BASE_CNT;
        if( timdifilt > TIME_UP ){
            timdifilt--;
        }
        if( timlcd > TIME_UP ){
            timlcd--;
        }
        if( timrcv > TIME_UP ){
            timrcv--;
        }
    }
}
/* DiFilter function add */
void DiFilter(){

    if( timdifilt == TIME_UP ){
        difilt.buf[difilt.wp] = digitalRead(PIN_DI1);
 
        if( difilt.buf[0] == difilt.buf[1] &&
            difilt.buf[1] == difilt.buf[2] &&
            difilt.buf[2] == difilt.buf[3] ){ //4回一致を確認
               difilt.di1 = difilt.buf[0];
        }
        if( ++difilt.wp >= DI_FILT_MAX ){
            difilt.wp = 0;
        }
        timdifilt = FILT_MIN;
    }
}
/* DispSet function add */
void DispSet(){

    lcd.clear();
    lcd.setCursor(0, 0); //1段目の左端にカーソル
    lcd.print(disptbl[dispmd]); //1段目の文字列

    switch( dispmd ){
    case MODE_NO::MD_NO1: //温度表示
        lcd.setCursor(0, 1); //2段目の左端にカーソル
        lcd.print((double)temperature/100); //2段目の文字列を表示させる
        lcd.print("[deg]");
        break;
    case MODE_NO::MD_NO2: //気圧表示
        lcd.setCursor(0, 1); //2段目の左端にカーソル
        lcd.print((double)pressure/10000); //2段目の文字列を表示される
        lcd.print("[hPa]");
        break;
    case MODE_NO::MD_NO3: //湿度表示
        lcd.setCursor(0, 1); //2段目の左端にカーソル
        lcd.print((double)humidity/1024); //2段目の文字列を表示される
        lcd.print("[%]");
        break;
    }
}
/* ChkSum function add */
uint8_t CalcSum(uint8_t *buf, uint8_t sz=0){
    uint8_t ret = 0;

    for(uint8_t i=0; i < sz; i++ ){
        ret = ret ^ (*buf);
        buf++; 
    }
    return ret;
}
/* RX function add */
void RxDataChk(){
    int rxsz;
    uint8_t sz;
    uint8_t allsz;
    uint8_t *adrs;
    uint8_t rp = rxarduino.rp;
    uint8_t chkdat[RXCHK_SZ];

    rxsz = rxarduino.wp - rxarduino.rp; //受信データ数の算出
    if( rxsz < 0 ){
        rxsz = rxsz + RING_SZ;
    }
    if( timrcv == TIME_UP){
        timrcv = TIME_OFF;
        if(++rxarduino.rp >= RING_SZ ){
          rxarduino.rp = 0;
        }
    }
    if(rxsz >= RXCHK_SZ){
        if( timrcv == TIME_OFF ){
            timrcv = RCV_TIM_MAX;
        }
        for(uint8_t i=0; i< RXCHK_SZ; i++ ){  //ヘッダーのチェックのため一時保存
            chkdat[i] = rxarduino.buf[rp];
            if(++rp > RING_SZ ){
                rp = 0;
            }
        }
        if( chkdat[0]== HEADER1 && chkdat[1]== HEADER2 && chkdat[2]== HEADER3 ){
            sz = chkdat[3] & 0x3F;//szが63を超えないようにする(データ長がおかしい場合の対策)
            allsz = RXCHK_SZ + sz + 1; //+1はSUMで受信データすべての長さを算出
            if( rxsz >= allsz ){
                adrs = &rxdata.header[0];
                for(uint8_t i=0; i< allsz; i++ ){
                    *adrs = rxarduino.buf[rxarduino.rp];
                    adrs++;
                    if(++rxarduino.rp >= RING_SZ ){
                        rxarduino.rp = 0;
                    }
                }
                rxdata.sum = CalcSum(&rxdata.buf[0],sz);
                if( rxdata.sum == rxdata.buf[sz]){//SUMのチェック
                    timrcv = TIME_OFF;
                    //センサーの情報をセット
                    DataCng();
                }
           }  
        }else{
            if(++rxarduino.rp >= RING_SZ ){
                rxarduino.rp = 0;
            }
        }
    }
}
/* Datachg function add */
void DataCng(){

    temperature = ((uint32_t)rxdata.buf[0] << 24 ) 
                + ((uint32_t)rxdata.buf[1] << 16 )
                + ((uint32_t)rxdata.buf[2] << 8 )
                + ((uint32_t)rxdata.buf[3]);

    pressure = ((uint32_t)rxdata.buf[4] << 24 ) 
             + ((uint32_t)rxdata.buf[5] << 16 )
             + ((uint32_t)rxdata.buf[6] << 8 )
             + ((uint32_t)rxdata.buf[7]);

    humidity = ((uint32_t)rxdata.buf[8] << 24 ) 
             + ((uint32_t)rxdata.buf[9] << 16 )
             + ((uint32_t)rxdata.buf[10] << 8 )
             + ((uint32_t)rxdata.buf[11]);     
}

親機側:トワイライト(参考記事:Arduinoとトワイライトの組み合わせによる親機のリンク)

#include <TWELITE>
#include <NWK_SIMPLE>

#define CNT_MAX 1000
#define BUF_SZ 64
#define HEADER1 0xA5
#define HEADER2 0x5A
#define HEADER3 0x80

struct TXCOM_TYP{
    uint8_t header[3];
    uint8_t sz;
    uint8_t buf[BUF_SZ];
    uint8_t sum;
};

/*** Config part */
// application ID
const uint32_t APP_ID = 0x1234abcd;
const uint8_t CHANNEL = 13;
const uint8_t PIN_DI1 = mwx::PIN_DIGITAL::DIO10; 
const uint8_t PIN_DO1 = mwx::PIN_DIGITAL::DIO9;


// application use
TXCOM_TYP txdata;
uint8_t rcvdata[12];

/*** application defs */
const char APP_FOURCHAR[] = "TEMP";
uint8_t u8devid = 0;

/*** function prototype */
void receive();
void SerialTxSet();
uint8_t CalcSum(uint8_t *buf, uint8_t sz);
/*** application defs */

/*** setup procedure (run once at cold boot) */
void setup() {

    the_twelite
        << TWENET::appid(APP_ID)
	<< TWENET::channel(CHANNEL)
	<< TWENET::rx_when_idle();

    auto&& nwksmpl = the_twelite.network.use<NWK_SIMPLE>();
    nwksmpl << NWK_SIMPLE::logical_id(0x00); 

    the_twelite.begin();

}
/*** loop procedure (called every event) */
void loop() {

    while (the_twelite.receiver.available()) {
        receive();
	rcvflg = true;
    }
}
/*add function recive()*/
void receive() {
    uint8_t i;

    auto&& rx = the_twelite.receiver.read();
    char fourchars[5]{}; 
    auto&& np = expand_bytes(rx.get_payload().begin(), rx.get_payload().end()
		             , make_pair((uint8_t*)fourchars, 4));

    if (strncmp(APP_FOURCHAR, fourchars, 4)) {return; } //文字ヘッダのチェック(不一致の場合関数から抜ける)
    	expand_bytes(np,rx.get_payload().end(),rcvdata);
	SerialTxSet();
}
/* TX function add */
void SerialTxSet(){
    uint8_t *adrs;
    uint8_t allsz = 0;

    txdata.header[0] = HEADER1;
    txdata.header[1] = HEADER2;
    txdata.header[2] = HEADER3;
    txdata.sz = sizeof(rcvdata);

    for(uint8_t i=0; i < txdata.sz; i++){
      	txdata.buf[i] = rcvdata[i];
    }
    txdata.sum = CalcSum(&txdata.buf[0],txdata.sz); //チェックSUMの計算
    allsz = sizeof(txdata.header)+ sizeof(txdata.sz)+ txdata.sz; //電文のサイズを計算
    adrs =&txdata.header[0];
    for(uint8_t i = 0; i < allsz; i++ ){ //SUM以外を送信
        Serial << (*adrs);
        adrs++;
    }
    Serial.write(txdata.sum); //SUMを送信
}
/* ChkSum function add */
uint8_t CalcSum(uint8_t *buf, uint8_t sz=0){
    uint8_t ret = 0;

    for(uint8_t i=0; i < sz; i++ ){
        ret = ret ^ (*buf);
        buf++; 
    }
    return ret;
}

子機:トワイライト(参考記事:トワイライトの子機のリンク)

#include <TWELITE>
#include <NWK_SIMPLE>
#include "bme280.h"

#define UINT8_C(val) val
#define INT8_C(val) val
#define TIME_UP 0
#define TIME_OFF -1
#define ZIG_WAIT_MAX 10
#define DATASZ 12   //uint32のデータを3つ送る
#define SLEEP_MAX2 119900
#define FORCED_MODE //省エネモードを使用するとき定義する

enum  E_STATE {
    INIT = 0,
    WORK_JOB,    
    TX,          
    WAIT_TX,     
    EXIT_NORMAL, 
    EXIT_FATAL   
};

struct FILT_MEAN
{
    uint8_t md;
    uint32_t during;
};

const uint8_t PIN_DO1 = mwx::PIN_DIGITAL::DIO18; //Boot
const uint8_t PIN_AD1 = mwx::PIN_ANALOGUE::A1; //TWE-EH-SのVCC

/*** Config part */
const uint32_t APP_ID = 0x1234abcd; 
const uint8_t CHANNEL = 13; 
/*** application defs */
const char APP_FOURCHAR[] = "TEMP"; //文字ヘッダ(親機と同じにする)

// application use
bme280_dev bme280main;
bme280_data sensor_data;
bool b_senser_started = false;
E_STATE eState;
bool b_transmit = false;
int16_t timzigwait = TIME_OFF;
uint8_t u8txid = 0;
uint8_t u8devid = 0x00;	//parent
byte bme280data[DATASZ];
bool bmeinitflg = false;
MWX_APIRET txreq_stat;
uint8_t errres_cnt =0;
uint32_t u32millis_tx; // millis() at Tx 
uint16_t twevcc;

FILT_MEAN sleep;

/*** Local function prototypes */
void Bme280Init();
bool startSensorCapture();
void sleepNow();
void napNow();
MWX_APIRET transmit();
int8_t user_i2c_read(uint8_t dev_id, uint8_t reg_addr, uint8_t *reg_data, uint16_t len);
int8_t user_i2c_write(uint8_t dev_id, uint8_t reg_addr, uint8_t *reg_data, uint16_t len);
void user_delay_ms(uint32_t period);
int8_t stream_sensor_data_forced_mode(struct bme280_dev *dev);
int8_t stream_sensor_data_normal_mode(struct bme280_dev *dev);

/*** the setup procedure (called on boot) */
void setup() {

    pinMode(PIN_DO1, OUTPUT_INIT_LOW); //BOOTをLOW
    Wire.begin();
    
    txreq_stat = MWX_APIRET(false, 0);
    the_twelite
	<< TWENET::appid(APP_ID)    
	<< TWENET::channel(CHANNEL) 
	<< TWENET::rx_when_idle(false );     

    auto&& nwksmpl = the_twelite.network.use<NWK_SIMPLE>();
    nwksmpl << NWK_SIMPLE::logical_id(0xFE);   
    the_twelite.begin();     
}
/*** the loop procedure (called every event) */
void loop() {
    bool loop_more; // if set, one more loop on state machine.

    do {
	loop_more = false; // set no-loop at the initial.

        switch(eState) {
	case E_STATE::INIT:
	    eState = E_STATE::WORK_JOB;
	    loop_more = true;
	    timzigwait = ZIG_WAIT_MAX;
	    break;
	case E_STATE::WORK_JOB:
            eState = E_STATE::TX;
            loop_more = true;
	    break;
	case E_STATE::TX:
            txreq_stat = transmit();
            if (txreq_stat){
                u32millis_tx = millis();
                eState = E_STATE::WAIT_TX;
                loop_more = true;
            }else{
                eState = E_STATE::EXIT_FATAL;
                loop_more = true;
            }
            break;
        case E_STATE::WAIT_TX:
            if (the_twelite.tx_status.is_complete(txreq_stat.get_value())){

                eState = E_STATE::EXIT_NORMAL;
            }else if(millis() - u32millis_tx > 100) {
                eState = E_STATE::EXIT_FATAL;
                loop_more = true;
            }
	    break;
	case E_STATE::EXIT_NORMAL:
	    sleepNow();
	    break;
	case E_STATE::EXIT_FATAL:
            delay(100);
	    the_twelite.reset_system();
	    break;
	}
    } while (loop_more);
}
/* callback begin */
void begin(){
    Bme280Init();
    napNow();
}
/* callback wakeup */
void wakeup(){

    if (!b_senser_started) {
        #ifdef FORCED_MODE
            if( bme280_set_sensor_mode(BME280_FORCED_MODE, &bme280main)==0 && bmeinitflg ){
                b_senser_started = true;
                napNow();
            }else{
                Bme280Init();
                sleepNow();
            }
        #else
            b_senser_started = true;
            napNow();
        #endif
    }else{

        if( startSensorCapture()){
            eState = E_STATE::TX;
            b_transmit = false;
        }else{

            sleepNow();
        }
    }
}
/* Bme280 sensor iniialize */
void Bme280Init(){
    int8_t no;

    bme280main.dev_id = BME280_I2C_ADDR_PRIM;
    bme280main.intf = bme280_intf::BME280_I2C_INTF;
    bme280main.read = user_i2c_read;
    bme280main.write = user_i2c_write;
    bme280main.delay_ms = user_delay_ms;
    no = bme280_init(&bme280main);
    if(no ==0){
        bmeinitflg = true;
    }
    #ifdef FORCED_MODE
        stream_sensor_data_forced_mode(&bme280main);
    #else
        stream_sensor_data_normal_mode(&bme280main);
    #endif
}
/* sensor data read */
bool startSensorCapture(){
    bool ret = true;
    int8_t i=0;
    
    if(bme280_get_sensor_data(BME280_ALL,&sensor_data, &bme280main) == 0 ){  
        bme280data[i++] = byte(sensor_data.temperature >> 24);
        bme280data[i++] = byte(sensor_data.temperature >> 16);
        bme280data[i++] = byte(sensor_data.temperature >> 8);
        bme280data[i++] = byte(sensor_data.temperature & 0xFF);
        bme280data[i++] = byte(sensor_data.pressure >> 24);
        bme280data[i++] = byte(sensor_data.pressure >> 16);
        bme280data[i++] = byte(sensor_data.pressure >> 8);
        bme280data[i++] = byte(sensor_data.pressure & 0xFF);
        bme280data[i++] = byte(sensor_data.humidity >> 24);
        bme280data[i++] = byte(sensor_data.humidity >> 16);
        bme280data[i++] = byte(sensor_data.humidity >> 8);
        bme280data[i++] = byte(sensor_data.humidity & 0xFF);
    }
    else{
        ret = false;
    }
    return ret;
}
/* perform period sleep */
void sleepNow(){
    uint32_t u32ct = sleep.during + random(100);
    b_senser_started = false;
    b_transmit = false;
    the_twelite.sleep(u32ct);
}
/* perform short period sleep */
void napNow() {
    uint32_t u32ct = 100;
    the_twelite.sleep(u32ct, false, false, TWENET::SLEEP_WAKETIMER_SECONDARY);
}
/* transmit a packet */
MWX_APIRET transmit() {

    if (auto&& pkt = the_twelite.network.use<NWK_SIMPLE>().prepare_tx_packet()) {
        pkt << tx_addr(u8devid)
	    << tx_retry(0x1) 
	    << tx_packet_delay(0,0,2);
 
	pack_bytes(pkt.get_payload(), make_pair(APP_FOURCHAR, 4)); //文字ヘッダをセット
        
        for(int i = 0; i < DATASZ; i++){ //データをセット
            pack_bytes(pkt.get_payload(),bme280data[i]);
        }
	    return pkt.transmit();
    }
    return MWX_APIRET(false, 0);
}
/* Bme280 api use function add */
int8_t user_i2c_read(uint8_t dev_id, uint8_t reg_addr, uint8_t *reg_data, uint16_t len)
{
    int8_t rslt = 0;

    if( Wire.probe(dev_id)){
        if(auto&& wrt = Wire.get_writer(dev_id)){
            wrt << reg_addr;
        }
        if(auto&& rdr = Wire.get_reader(dev_id,(uint8_t)len)){
            for(uint16_t i = 0; i < len; i++){
                *reg_data = rdr();
                reg_data++;
            }
        }
    }else{
        rslt = BME280_E_DEV_NOT_FOUND;
        for(auto&& x : bme280data) x = 0xFF;
        bmeinitflg = false;
        Serial << "data_NG1" << mwx::crlf;
    }
    return rslt;
}
/* Bme280 api use function add */
int8_t user_i2c_write(uint8_t dev_id, uint8_t reg_addr, uint8_t *reg_data, uint16_t len)
{
    int8_t rslt = 0;

    if( Wire.probe(dev_id)){
        if(auto&& wrt = Wire.get_writer(dev_id)){
            wrt << reg_addr;
            for(uint16_t i = 0; i < len; i++){
                wrt << reg_data[i];
            }
        }
    }else{
        rslt = BME280_E_DEV_NOT_FOUND;
        bmeinitflg = false;
    }
    return rslt;
}
/* Bme280 api use function add */
void user_delay_ms(uint32_t period)
{
    delay(period);
}
/* Bme280 api use function add */
int8_t stream_sensor_data_forced_mode(struct bme280_dev *dev)
{
    int8_t rslt;
    uint8_t settings_sel;
    uint32_t req_delay;
    
    dev->settings.osr_h = BME280_OVERSAMPLING_1X;
    dev->settings.osr_p = BME280_OVERSAMPLING_1X;
    dev->settings.osr_t = BME280_OVERSAMPLING_1X;
    dev->settings.filter = BME280_FILTER_COEFF_OFF;
    settings_sel = BME280_OSR_PRESS_SEL | BME280_OSR_TEMP_SEL | BME280_OSR_HUM_SEL | BME280_FILTER_SEL;
    rslt = bme280_set_sensor_settings(settings_sel, dev);
    req_delay = bme280_cal_meas_delay(&dev->settings);
    
    //while (1) {
        rslt = bme280_set_sensor_mode(BME280_FORCED_MODE, dev);
 
        //dev->delay_ms(req_delay);
        //rslt = bme280_get_sensor_data(BME280_ALL, &sensor_data, dev);
    //}
    return rslt;
}
/* Bme280 api use function add */
int8_t stream_sensor_data_normal_mode(struct bme280_dev *dev)
{
    int8_t rslt;
    uint8_t settings_sel;
   
    dev->settings.osr_h = BME280_OVERSAMPLING_1X;
    dev->settings.osr_p = BME280_OVERSAMPLING_16X;
    dev->settings.osr_t = BME280_OVERSAMPLING_2X;
    dev->settings.filter = BME280_FILTER_COEFF_16;
    dev->settings.standby_time = BME280_STANDBY_TIME_62_5_MS;
    settings_sel = BME280_OSR_PRESS_SEL;
    settings_sel |= BME280_OSR_TEMP_SEL;
    settings_sel |= BME280_OSR_HUM_SEL;
    settings_sel |= BME280_STANDBY_SEL;
    settings_sel |= BME280_FILTER_SEL;
    rslt = bme280_set_sensor_settings(settings_sel, dev);
    rslt = bme280_set_sensor_mode(BME280_NORMAL_MODE, dev);

    //while (1) {
        dev->delay_ms(70);
	rslt = bme280_get_sensor_data(BME280_ALL, &sensor_data, dev);
   //}
    return rslt;
}

関連リンク

Arduinoのライブラリを使って動作確認を行ったことを下記リンクにまとめています。

Arduinoで学べるマイコンのソフト開発と標準ライブラリの使い方

Seeeduino XIAOで学べるソフト開発と標準ライブラリの使い方

ESP32-WROOM-32Eで学べるソフト開発と標準ライブラリの使い方

また無線モジュールであるトワイライトを使ってソフト開発したことについてまとめています。

トワイライト(TWELITE)のソフト開発と無線通信でできること

広告

最後まで、読んでいただきありがとうございました。

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