(Arduino)ESP32モジュールを用いた複合プログラム(時刻表示,温湿度気圧,赤外線リモコン)

#プログラム内容
過去記事にて作成した以下の各プログラム要素を全て使用でき、起動画面にて各メニューを選択できるプログラムを作成する。
[・WIFiにてインターネットから取得した現在時刻をLCDに表示](https://qiita.com/pontaman007/items/0540409fada8c3762ca7)
[・温湿度気圧センサ(BME280)から取得した温湿度気圧値をLCDに表示](https://qiita.com/pontaman007/items/959c84a1251183e4931f)
[・赤外線送受信リモコン](https://qiita.com/pontaman007/items/b6ea2b9fdc38a2c3daeb)

#回路図
[過去の記事](https://qiita.com/pontaman007/items/f47ead88beacf50e9912)で回路図について作成しているので詳細はそちらを確認ください。
ピン接続は以下を参考にしてください。

![https___qiita-image-store.s3.ap-northeast-1.amazonaws.com_0_595774_5d0dd1df-e285-4fb5-7a97-0a8797aa1663.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/595774/3b363769-6528-eb1f-c124-e57c1d59bc4b.png)

| 名称 | 使用部品 | データのやり取り | ESP32への接続ピン |
|:---:|:-------:|:-----------:|:---------------:|
| I2C接続LCDモジュール | AE-AQM1602A(KIT) | I2C通信 | SDA:21,SCL:22 |
| 温湿度気圧センサモジュール | AE-BME280 | SPI通信 | SCK:14,SDO:12,SDI:13,CSB:26 |
| 赤外線受信モジュール | IRM-3638 | 入力 | OUT:17 |
| トグルスイッチ | 小型３Ｐトグルスイッチ(１回路２接点) | 入力 | 39 |
| タクトスイッチ | タクトスイッチ | 入力 | 34,35,36 |
| 赤外線LED | L-53F3BT  | 出力 | トランジスタ(ベース):16 |

#プログラム内容
基本は過去のプログラム内容をそのまま活用しているが、シリアルモニターへの出力機能等の無駄なプログラムは削除している。
以下がプログラム動作時の状態である。

<b>・電源ON時</b>
<img width="1583" alt="スクリーンショット 2020-05-20 22.05.58.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/595774/d6c49d47-830e-c8df-4465-1c2f4bb6cb69.png">
<b>・メニュー選択画面</b>
<img width="1460" alt="スクリーンショット 2020-05-20 22.53.32.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/595774/0df919fa-e3d9-280b-8238-e7ac5d294f9c.png">
<b>・01_TIME MODE</b>
　WiFI接続NG時
<img width="1413" alt="スクリーンショット 2020-05-20 23.11.39.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/595774/fea8b2f4-bc8f-5e46-c369-243871fce5d3.png">
　WiFI接続OK時
<img width="1410" alt="スクリーンショット 2020-05-20 23.26.00.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/595774/ca6c9222-c9a3-d0fd-69b0-a8a00c40bdad.png">
<b>・02_BME280 MODE</b>
<img width="1403" alt="スクリーンショット 2020-05-20 23.38.27.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/595774/ebd3a7e3-0068-c3b5-1e49-3b2683cebe06.png">
<b>・03_IR MODE</b>
<img width="1678" alt="スクリーンショット 2020-05-20 23.56.16.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/595774/ad4a7414-8c33-b940-4579-fe60ec376e51.png">

以下は今回使用したプログラムなのでコピーして使用してみて下さい。
※WiFi接続ルータとPASSは打ち替える必要有り
※過去プログラムの複合なので詳細解説は無し

```C:04_COMPOUND_PROGRAM
/*  作成日；2020/5/17
    ファイル名；04_COMPOUND PROGRAM
    プログラム内容；3モードの機能を操作
    　　　　　　　　①現在時刻表示
    　　　　　　　　②温湿度気圧測定
    　　　　　　　　③赤外線送受信
    使用部品；ESP32、LCD、WiFiルータ、トグルスイッチ、IRM-3638、タクトスイッチ×2、L-53F3BT、BME280
    ESP32へのピン接続；回路図参照
 */

/****************************各ライブラリ設定****************************/
#include <Wire.h>  //I2C通信ライブラリ(LCO用)
#include <WiFi.h> //WiFiモジュールライブラリ(ESP32用)
#include "ESP32_BME280_SPI.h" //BME280ライブラリ

//赤外線送受信関係ライブラリ
#include <Arduino.h>
#include <IRrecv.h>
#include <IRsend.h>
#include <IRremoteESP8266.h>
#include <IRac.h>
#include <IRtext.h>
#include <IRutils.h>
/****************************各ライブラリ設定****************************/

/****************************各ピンの接続箇所設定***************************/
const uint16_t kIrLed = 16; //赤外線送信ピン
const uint16_t kRecvPin = 17; //赤外線受信ピン
const uint16_t Tac_SW1 = 34;  //前ボタン,モード終了ボタン
const uint16_t Tac_SW2 = 35;  //次ボタン,赤外線受信許可変更ボタン
const uint16_t Tac_SW3 = 36;  //決定ボタン,BME280計測開始&終了ボタン,赤外線受信データ送信ボタン
const uint16_t Tog_SW = 39;  //WiFi接続トグルスイッチ
/****************************各ピンの接続箇所設定***************************/

/****************************各フラグ宣言***************************/
bool Tog_Flag = false;  //WiFi接続トグルスイッチ割り込みフラグ
bool Wifi_Flag = false; //WiFi接続フラグ
bool Mesure_Flag = false;  //BME280計測開始ボタン割り込みフラグ
bool Mode_Flag = false;  //BME280計測モード変更フラグ
bool Mode_finish = false; //BME280計測モード終了フラグ
/****************************各フラグ宣言***************************/

/****************************LCD関係****************************/
#define LCD_ADRS 0x3E //LCDのI2C用アドレス
uint8_t lcd_menu_select; //メニューセレクト判別(1:現在時刻表示,2:温湿度気圧測定,3:赤外線送受信)
bool lcd_menu_decision = false;  //メニュー決定フラグ
String lcd_bdata1; //最新のLCD表示文字格納変数(1列目)
String lcd_bdata2; //最新のLCD表示文字格納変数(2列目)
String lcd_cdata1; //表示用LCD表示文字格納変数(1列目)
String lcd_cdata2; //表示用LCD表示文字格納変数(2列目)
byte lcd_bcommand;  //最新のLCDコマンド格納変数
/****************************LCD関係****************************/

/****************************WiFi関係****************************/
WiFiServer server(80);  //PCからの応答を読み取るためのサーバ定義
const char* ssid     = "自宅ルータのSSID";  //SSID
const char* password = "自宅ルータのPASS";  //PASS
IPAddress ip; //IPアドレス格納変数
String ip_string = "";  //IPアドレスをString型に変換した値を格納する
/****************************WiFi関係****************************/

/****************************赤外線送受信関係****************************/
//各変数を設定
const uint16_t kCaptureBufferSize = 1024; //受信データ格納バッファ数(赤外線ON/OFFの時間を格納)
String recv_code = ""; //受信コード挿入変数
uint16_t recv_bits; //受信コード総ビット数挿入変数
uint16_t recv_onoff_bits; //ONOFF回数挿入変数
uint16_t recv_state_bits; //A/C受信コード挿入変数
uint64_t recv_data; //受信データ挿入変数
uint16_t rawData[1000]; //送信データ格納バッファ(AC以外)
unsigned char samsungState[500];  //送信データ格納バッファ(AC)
bool recv_pms_flag = true;  //赤外線受信許可フラグ
const uint16_t kMinUnknownSize = 12;  //不明な赤外線受信をカットするための変数

//kTimeout = 赤外線データの終了を判断するmsの値を設定(kTimeoutミリ秒間受信がなければ終了と判断する)
#if DECODE_AC //エアコンの赤外線リモコンの場合
const uint8_t kTimeout = 50;  //50ms
#else   //その他の赤外線リモコン機器の場合
const uint8_t kTimeout = 15;  //15ms
#endif

//各インスタンスを生成
IRrecv irrecv(kRecvPin, kCaptureBufferSize, kTimeout, true);  //IRrecv(赤外線受信ピン、受信データ格納バッファサイズ、データ終了判断時間、値保存機能(true推奨))
decode_results results;  //受信結果を保存するクラスのインスタンスを生成
IRsend irsend(kIrLed);  //赤外線送信クラスのインスタンスを生成
decode_type_t maker_code; //メーカーコードのインスタンスを生成
/****************************赤外線送受信関係****************************/

/****************************BME280関係****************************/
//各種SPI通信用変数設定
const uint8_t SCLK_bme280 = 14; //クロック入力
const uint8_t MOSI_bme280 =13; //マスター(ESP32):出力、スレーブ(BME280):入力
const uint8_t MISO_bme280 =12; //マスター(ESP32):入力、スレーブ(BME280):出力
const uint8_t CS_bme280 = 26; //チップ選択

//各種BME280測定モード設定用変数設定
uint8_t t_sb = 0; //測定間でのスタンバイ時間=0.5[ms] (0:0.5ms、1:62.5ms、2:125ms、3:250ms、4:500ms、5:1000ms、6:10、7:20ms)
uint8_t filter = 4; //測定値精度=16 (0:フィルターオフ、1:2、2:4、3:8、4以上:16) ※数字が大きいほどサンプリング回数が増える
uint8_t osrs_t = 2; //温度のオーバーサンプリング値=2 (0:無(ノイズ影響受ける)、1:1、2:2、3:4、4:8、5以上:16) ※オーバサンプリングをすると測定時間が伸びるがノイズ影響が減る
uint8_t osrs_p = 5; //気圧のオーバーサンプリング値=5 (0:無(ノイズ影響受ける)、1:1、2:2、3:4、4:8、5以上:16) ※オーバサンプリングをすると測定時間が伸びるがノイズ影響が減る
uint8_t osrs_h = 1; //湿度のオーバーサンプリング値=1 (0:無(ノイズ影響受ける)、1:1、2:2、3:4、4:8、5以上:16) ※オーバサンプリングをすると測定時間が伸びるがノイズ影響が減る
uint8_t Mode = 0; //計測=スリープモード(0:スリープモード(計測しない,電源ON直後)、1or2:フォースドモード(1度だけ測定)、3:ノーマルモード(繰り返し測定))
 
ESP32_BME280_SPI bme280spi(SCLK_bme280, MOSI_bme280, MISO_bme280, CS_bme280, 10000000); //10MHzにてSPIクラスのインスタンスを生成
/****************************BME280関係****************************/

/****************************外部割り込み設定****************************/
volatile unsigned long time_prev = 0; //割り込み時刻コピー変数
volatile unsigned long time_now;  //現在の割り込み時刻取得変数
unsigned long time_chat = 500;  //割り込み時刻
/****************************外部割り込み設定****************************/

/****************************現在時刻取得設定****************************/
struct tm timeInfo; //時刻を格納するオブジェクト
String date_string = "";  //日付をString型に変換した値を格納する
String time_string = "";  //時刻をString型に変換した値を格納する
/****************************現在時刻取得設定****************************/

/****************************タイマー割り込み設定****************************/
volatile int interruptCounter;  //タイマーカウンター
hw_timer_t * timer = NULL;  //タイマー変数
volatile SemaphoreHandle_t timerSemaphore;  //タイマー割り込みが発生したかどうかを表すセマフォ
/****************************タイマー割り込み設定****************************/


/****************************タタイマー割り込み処理****************************/
void IRAM_ATTR onTimer() {

  interruptCounter ++; //割り込みカウント
  xSemaphoreGiveFromISR(timerSemaphore, NULL);  //割り込みフラグをリセット

}
/****************************タイマー割り込み処理****************************/


/****************************初期設定 ****************************/
void setup() {

  //ピン入出力設定
  pinMode(Tac_SW1, INPUT); //SW1を入力
  pinMode(Tac_SW2, INPUT); //SW2を入力
  pinMode(Tac_SW3, INPUT); //SW3を入力
  pinMode(Tog_SW, INPUT); //トグルスイッチを入力

  //通信関係処理
  Serial.begin(115200); //シリアル通信レート開始(通信レート:115200bits/s)
  Wire.begin(); //I2C通信開始(ESP32をマスター)
  irrecv.enableIRIn();  //赤外線受信開始
  irsend.begin(); //赤外線送信開始
  bme280spi.ESP32_BME280_SPI_Init(t_sb, filter, osrs_t, osrs_p, osrs_h, Mode);  //各レジスタに初期設定をSPI通信にて書き込み
  #if DECODE_HASH
  irrecv.setUnknownThreshold(kMinUnknownSize);  //不明な赤外線受信データは除外する
  #endif

  //タイマー割り込み設定
  timerSemaphore = xSemaphoreCreateBinary();  //割り込みハンドラが呼ばれた際の通信のために利用するセマフォ(バイナリセマフォ)の作成
  timer = timerBegin(0, 80, true);  //タイマー番号0(0〜3)、分周比80(ESP32のクロック周波数は80MHz)、タイマー増減(true:増加,false:減少)
  timerAttachInterrupt(timer, &onTimer, true);  //タイマー変数指定:timer、タイマー割り込みする処理関:onTimer、割り込みタイプ(true:エッジ,false:レベル)
  timerAlarmWrite(timer, 1000000, true);  //タイマー1秒割り込み(80000000(クロック周波数) / 80(分周比) = 1000000Hz(=1カウントで1μs))
  
  init_LCD(); //LCD初期化
  lcdwriteData("Booting now...");
  delay(2000);
  wifiConnect();  //WiFi接続処理
  delay(2000);
  lcdwriteCommand(0x01);  //LCD液晶文字クリア([Clear Display])
  lcdwriteData("MENU SELECT");
  lcdwriteCommand(0x40+0x80); //2行目へカーソル移動
  lcdwriteData("01_TIME MODE");
  lcd_menu_select = 1; //現在時刻表示モード
  
  //attachInterrupt(Tac_SW1, mesure_change, RISING);  //タクトスイッチを押すとBME280計測開始or終了外部割り込み有効
  attachInterrupt(Tog_SW, wifiFlag, CHANGE);  //トグルスイッチの切り替えごとにWiFi接続外部割り込み有効
  
}
/****************************初期設定 ****************************/


/****************************メイン本文*****************************/
void loop() {

  //前ボタン(Tac_SW1)が押されたら
  if(digitalRead(Tac_SW1) == HIGH){
    
    //ボタンが離されるまでループ
    while(1){
      delay(100);
      if(digitalRead(Tac_SW1) == LOW){
        break;
      }
    }
    
    delay(100); //チャタリング防止

    lcdwriteCommand(0x01);  //LCD液晶文字クリア([Clear Display])
    lcdwriteData("MENU SELECT");
    lcdwriteCommand(0x40+0x80); //2行目へカーソル移動
    
    switch(lcd_menu_select){ //lcd_menuの値に応じて表示文字分け
      
      case 1: //現在表示メニューが「01_TIME MODE」の場合
      lcdwriteData("03_IR MODE");
      lcd_menu_select = 3; //メニューセレクト更新(赤外線受信モード)
      break;

      case 2: //現在表示メニューが「02_BME280 MODE」の場合
      lcdwriteData("01_TIME MODE");
      lcd_menu_select = 1; //メニューセレクト更新(現在時刻表示モード)
      break;

      case 3: //現在表示メニューが「03_IR MODE」の場合
      lcdwriteData("02_BME280 MODE");
      lcd_menu_select = 2; //メニューセレクト更新(温湿度気圧測定モード)
      break;
      
    }
    
  }

  //次ボタン(Tac_SW2)が押されたら
  if(digitalRead(Tac_SW2) == HIGH){
    
    //ボタンが離されるまでループ
    while(1){
      delay(100);
      if(digitalRead(Tac_SW2) == LOW){
        break;
      }
    }
    
    delay(100); //チャタリング防止

    lcdwriteCommand(0x01);  //LCD液晶文字クリア([Clear Display])
    lcdwriteData("MENU SELECT");
    lcdwriteCommand(0x40+0x80); //2行目へカーソル移動
    
    switch(lcd_menu_select){ //lcd_menuの値に応じて表示文字分け
      
      case 1: //現在表示メニューが「01_TIME MODE」の場合
      lcdwriteData("02_BME280 MODE");
      lcd_menu_select = 2; //メニューセレクト更新(温湿度気圧測定モード)
      break;

      case 2: //現在表示メニューが「02_BME280 MODE」の場合
      lcdwriteData("03_IR MODE");
      lcd_menu_select = 3; //メニューセレクト更新(赤外線受信モード)
      break;

      case 3: //現在表示メニューが「03_IR MODE」の場合
      lcdwriteData("01_TIME MODE");
      lcd_menu_select = 1; //メニューセレクト更新(現在時刻表示モード)
      break;
      
    }
    
  }

  //決定ボタン(Tac_SW3)が押されたら
  if(digitalRead(Tac_SW3) == HIGH){
    
    //ボタンが離されるまでループ
    while(1){
      delay(100);
      if(digitalRead(Tac_SW3) == LOW){
        break;
      }
    }
    
    delay(100); //チャタリング防止

    lcd_menu_decision = true; //メニュー決定フラグON
    
  }

  //トグルスイッチが操作されたら
  if(Tog_Flag){ 

    lcd_cdata1 = lcd_bdata1;  //LCD表示用文字を一時保存(1行目)
    lcd_cdata2 = lcd_bdata2;  //LCD表示用文字を一時保存(2行目)
    
    delay(100); //チャタリング防止
    
    detachInterrupt(Tog_SW);  //外部割り込み禁止
    wifiConnect();  //WiFi接続処理
    Tog_Flag = !Tog_Flag; //トグルスイッチ割り込みフラグ反転
    attachInterrupt(Tog_SW, wifiFlag, CHANGE);  //外部割り込み再開

    //直前に表示していた文字を再表示
    lcdwriteCommand(0x01);  //LCD液晶文字クリア([Clear Display])
    lcdwriteData(lcd_cdata1);
    lcdwriteCommand(0x40+0x80); //2行目へカーソル移動
    lcdwriteData(lcd_cdata2);
    
  }

  //決定ボタン(Tac_SW3)が押されてメニューが選択されたら
  if(lcd_menu_decision){

    switch(lcd_menu_select){ //lcd_menuの値に応じて表示文字分け
      
      case 1: //選択メニューが「01_TIME MODE」の場合
      time_mode();  //現在時刻表示
      break;

      case 2: //選択メニューが「02_BME280 MODE」の場合
      bme280_mode();  //温湿度気圧
      break;

      case 3: //選択メニューが「03_IR MODE」の場合
      ir_mode();  //赤外線送受信
      break;
        
    }   

    lcdwriteCommand(0x01);  //LCD液晶文字クリア([Clear Display])
    lcdwriteData("Finishjing...");
    lcd_menu_decision = false;  //メニュー決定リセット
  
    delay(2000);

    lcdwriteCommand(0x01);  //LCD液晶文字クリア([Clear Display])
    lcdwriteData("MENU SELECT");
    lcdwriteCommand(0x40+0x80); //2行目へカーソル移動
  
    switch(lcd_menu_select){ //lcd_menuの値に応じて表示文字分け
      case 1: //現在表示メニューが「01_TIME MODE」の場合
      lcdwriteData("01_TIME MODE");
      lcd_menu_select = 1; //メニューセレクト更新(現在時刻表示モード)
      break;

      case 2: //現在表示メニューが「02_BME280 MODE」の場合
      lcdwriteData("02_BME280 MODE");
      lcd_menu_select = 2; //メニューセレクト更新(温湿度気圧測定モード)
      break;

      case 3: //現在表示メニューが「03_IR MODE」の場合
      lcdwriteData("03_IR MODE");
      lcd_menu_select = 3; //メニューセレクト更新(赤外線受信モード)
      break;
      
    }
  
  }
  
}
/****************************メイン本文*****************************/


/*************************現在時刻表示処理*************************/
void dateDisplay() {
  
  getLocalTime(&timeInfo);//tmオブジェクトのtimeInfoに現在時刻を入れ込む
  date_string = "DATE " + String(timeInfo.tm_year + 1900) + "/" + String(timeInfo.tm_mon + 1) + "/" + String(timeInfo.tm_mday);  //日付をString型に変換
  time_string = "TIME " + String(timeInfo.tm_hour) + ":" + String(timeInfo.tm_min) + ":" + String(timeInfo.tm_sec); //時刻をString型に変換
  lcdwriteCommand(0x01);  //LCD液晶文字クリア([Clear Display])
  lcdwriteData(date_string); //日付を表示
  lcdwriteCommand(0x40+0x80); //2行目へカーソル移動
  lcdwriteData(time_string);  //時刻を表示
                
}
/*************************現在時刻表示処理*************************/


/****************WiFi接続トグルスイッチ外部割り込み処理****************/
void wifiFlag() {
  
  Tog_Flag = !Tog_Flag; //トグルスイッチ割り込みフラグ反転

}
/****************WiFi接続トグルスイッチ外部割り込み処理****************/

/****************BME280計測モード終了外部割り込み処理****************/
void bme280mode_finish() {
  
  Mode_finish = !Mode_finish; //BME280計測モード終了フラグ反転

}
/****************WiFi接続トグルスイッチ外部割り込み処理****************/


/*************************WiFi接続処理*************************/
void wifiConnect() {
  
  delay(500); //チャタリング防止
  lcdwriteCommand(0x01);  //LCD液晶文字クリア([Clear Display])

  if(digitalRead(Tog_SW) == HIGH){  //WiFi接続SWがONなら
    
    WiFi.begin(ssid, password); //Wi-Fiへの接続処理
    
    while (WiFi.status() != WL_CONNECTED) { //WiFiに接続されるまで待機
        delay(500);
    }
    
    ip = WiFi.localIP();  //ESP32のIPアドレスを取得
    ip_string = "IP:" + String(ip[0]) + "." + String(ip[1]) + "." + String(ip[2]) + "." + String(ip[3]); //ESP32のIPアドレスをString型へ変換
    
    lcdwriteData("WiFi Connect OK"); //起動OKの表示
    lcdwriteCommand(0x40+0x80); //2行目へカーソル移動(0x40 = 2行目先頭文字アドレス、0x80 = DDRAMアドレスへの移動)
    lcdwriteData(ip_string); //IPアドレスの表示
    delay(1000);
    
    //configTime(オフセット時間[s] = 標準時 + 9h * 3600、サマータイム 0:無,1:有、ntpサーバー設定)
    configTime(9 * 3600L, 0, "ntp.nict.jp", "time.google.com", "ntp.jst.mfeed.ad.jp");  //時刻取得用のNTPの設定
    server.begin(); //サーバー起動
    Wifi_Flag = true; //WiFiフラグ切り替え

  }else{  //WiFi接続SWがOFFなら

    lcdwriteData("WiFi Connect NG"); //WiFi接続NGの表示
    
    if(Wifi_Flag) { //WiFi接続SWがONからOFFになった時
      
      server.stop(); //サーバー停止
      WiFi.disconnect();  //WiFi接続停止

      /*  タイマー割り込み停止操作  */
      interruptCounter --; //割り込みカウントリセット
      timerAlarmDisable(timer); //タイマー停止
      /*  タイマー割り込み停止操作  */

      delay(1000);
      Wifi_Flag = !Wifi_Flag; //WiFiフラグ切り替え
      
    }
    
  }
  
}
/*************************WiFi接続処理*************************/


/**************************BME280測定**************************/
void bme_get(){ 
  
  double temperature = bme280spi.Read_Temperature();  //温度を取得
  double humidity = bme280spi.Read_Humidity();  //湿度を取得
  double pressure = bme280spi.Read_Pressure();  //気圧を取得

  //LCDへ各値を表示
  lcdwriteCommand(0x01);  //LCD液晶文字クリア([Clear Display])
  lcdwriteData("T=" + String(temperature, 1) + "C ");
  lcdwriteData("H=" + String(humidity, 1) + "%");
  lcdwriteCommand(0x40+0x80); //2行目へカーソル移動
  lcdwriteData("P=" + String(pressure, 1) + "hPa");
  
}
/**************************BME280測定**************************/


/*************************測定モード変更*************************/
void mesure_change(){ //タクトスイッチが押されたら

  time_now = millis(); //現在の割り込み時刻を取得
  if( time_now-time_prev > time_chat){  //前回の割り込みから500[ms]以上経過ならば

    Mesure_Flag = !Mesure_Flag; //測定フラグ反転
    Mode_Flag = !Mode_Flag; //モードフラグ反転
  }
  time_prev = time_now; //現在の割り込み時刻を前回の割り込み時刻へコピー
  
}
/*************************測定モード変更*************************/


/*************************01_TIME MODE*************************/
void time_mode(){
  
  if(digitalRead(Tog_SW) == LOW){
    
    lcdwriteCommand(0x01);  //LCD液晶文字クリア([Clear Display])
    lcdwriteData("WiFi Connect NG");
    delay(2000);
    return;
    
  }
  
  timerAlarmEnable(timer);  //タイマー起動
  
  while(digitalRead(Tog_SW) == HIGH){ //Tog_SWがOFFになるまで繰り返し
    
    if(interruptCounter > 0) {  //タイマー割り込みされれば
      
      interruptCounter --; //割り込みカウントリセット
      dateDisplay();  //ネットから取得した現在時刻を表示
      
    }
    
    if(digitalRead(Tac_SW1) == HIGH){ //モード終了ボタンが押されたら終了
      
      break;
      
    }
    
  }
  
  //タイマー割り込み停止操作
  interruptCounter --; //割り込みカウントリセット
  timerAlarmDisable(timer); //タイマー停止
  delay(100); //チャタリング防止

}
/*************************01_TIME MODE*************************/


/************************02_BME280 MODE************************/
void bme280_mode(){
  
  lcdwriteCommand(0x01);  //LCD液晶文字クリア([Clear Display])
  lcdwriteData("BME280");
  lcdwriteCommand(0x40+0x80); //2行目へカーソル移動
  lcdwriteData("Mesure Wating"); //測定待ちの表示
      
  attachInterrupt(Tac_SW3, mesure_change, RISING);  //タクトスイッチ3を押すと計測開始or終了の割り込み有効
  attachInterrupt(Tac_SW1, bme280mode_finish, RISING);  //タクトスイッチ1を押すとモード終了

  while(!Mode_finish){ //モード終了ボタンが押されたら終了

    if(Mode_Flag){  //測定モード変更スイッチが押されたら
          
      if(Mesure_Flag){  //計測開始の場合

        //LCDへ表示
        lcdwriteCommand(0x01);  //LCD液晶文字クリア([Clear Display])
        lcdwriteData("Mesure START"); //測定待ちの表示
      
        Mode = 3; //計測モードを繰り返し測定に変数指定
            
      }else{  //計測終了の場合
            
        //LCDへ表示
        lcdwriteCommand(0x01);  //LCD液晶文字クリア([Clear Display])
        lcdwriteData("Mesure STOP"); //測定待ちの表示
      
        Mode = 0; //計測モードをスリープ状態に変数指定
            
      }
          
      uint8_t ctrl_meas = (osrs_t << 5) | (osrs_p << 2) | Mode; //測定条件レジスタに書き込む内容を入力
      bme280spi.WriteRegister(0xF4, ctrl_meas); //測定モード変更レジスタ(ctrl_meas 0xF4)へモード変更を書き込み
      Mode_Flag = false;   //モード変更フラグリセット
      
    }

    while(Mesure_Flag & !Mode_Flag & !Mode_finish){ //計測開始になれば
            
      delay(5000);  //5秒毎に表示
      bme_get();  //各値を取得しシリアルモニター&LCDへ出力
              
    }
   
  }

  //各フラグ初期値設定
  Mesure_Flag = false;  //BME280計測開始ボタン割り込みフラグ初期設定
  Mode_Flag = false;  //BME280計測モード変更フラグ初期設定
  Mode_finish = false; //BME280計測モード終了フラグ初期設定
  Mode = 0; //計測モードをスリープ状態初期設定

  //外部割り込み禁止
  detachInterrupt(Tac_SW1);
  detachInterrupt(Tac_SW3);
  
}
/************************02_BME280 MODE************************/


/**************************03_IR MODE**************************/
void ir_mode(){

  lcdwriteCommand(0x01);  //LCD液晶文字クリア([Clear Display])
  lcdwriteData("IR Wating");

  while(digitalRead(Tac_SW1) == LOW){ //タクトスイッチ1を押すとモード終了
      
      if (irrecv.decode(&results) & recv_pms_flag) {  //赤外線受信および受信許可モードになれば
        
        maker_code = results.decode_type; //メーカーコード取得
        recv_code = resultToHexidecimal(&results);  //赤外線受信データを16進数で取得
        recv_bits = results.bits; //受信データの総Bit数を取得
        recv_onoff_bits = getCorrectedRawLength(&results);  //受信データのON/OFF切り替え回数を取得
    
        for(int i=1; i < recv_onoff_bits + 1; i++){ //受信ONOFFデータを格納
    
          rawData[i-1] = results.rawbuf[i] * kRawTick;
    
        }
    
        if (hasACState(maker_code)) {  //メーカーコードがACだった場合
    
          recv_state_bits = recv_bits / 8;  //赤外線受信データの分割ビット数を格納
    
          for(int i = 0; i < recv_state_bits; i++) {  //AC受信データ格納
    
            samsungState[i] = char(results.state[i]);
    
          }
    
          
        }else{  //メーカーコードがAC以外だった場合
    
          recv_data = results.value;  //データを取得
          
        }
    
        lcdwriteCommand(0x01);  //LCD液晶文字クリア([Clear Display])
        lcdwriteData("RECIEVED");
        lcdwriteCommand(0x40+0x80); //2行目へカーソル移動
        lcdwriteData(typeToString(maker_code,false)); //メーカーコードを表示
        delay(2000);
        
        recv_pms_flag = false; //赤外線受信許可フラグOFF
    
        lcdwriteCommand(0x01);  //LCD液晶文字クリア([Clear Display])
        lcdwriteData("Reception");
        lcdwriteCommand(0x40+0x80); //2行目へカーソル移動
        lcdwriteData("Prohibited");
    
      }
    
      if(digitalRead(Tac_SW3) == HIGH){ //赤外線データ送信ボタン(Tac_SW3)が押されたら
        
        delay(500); //チャタリング防止
    
        if (recv_pms_flag){
    
          lcdwriteCommand(0x01);  //LCD液晶文字クリア([Clear Display])
          lcdwriteData("Data Nothing");
          
        }else if (hasACState(maker_code)) {  //メーカーコードがACだった場合
          
          irsend.send(maker_code, samsungState, recv_state_bits); //AC送信データコード(メーカーコード、Stateデータ、Stateビット数)
          
        }else{  //メーカーコードがAC以外だった場合
    
          //SHARPのテレビ電源OFF後、1分ちょい開くと1度のデータコード送信だと電源が入らないので、常時リーダーコードを頭につけて送信する
          irsend.sendRaw(rawData,2,38); //赤外線のデータコードを38kHz帯で送信
          delay(10);  //1つの送信データは「リーダー部+8ms以上off」が必要なので10ms待つ
          irsend.send(maker_code, recv_data, recv_bits, 0); //AC以外送信データコード(メーカーコード、送信データ、送信ビット数、リピート回数)
    
          lcdwriteCommand(0x01);  //LCD液晶文字クリア([Clear Display])
          lcdwriteData("Data Send");
          
        }
      }
    
      if(digitalRead(Tac_SW2) == HIGH){ //赤外線受信許可ボタン(Tac_SW2)が押されたら
        
        delay(500); //チャタリング防止
        recv_pms_flag = true; //赤外線受信許可フラグON
    
        lcdwriteCommand(0x01);  //LCD液晶文字クリア([Clear Display])
        lcdwriteData("Reception");
        lcdwriteCommand(0x40+0x80); //2行目へカーソル移動
        lcdwriteData("Permited");
    
      }
      
  }

  //赤外線送受信モード終了
  recv_pms_flag = true;  //赤外線受信許可フラグ初期化
  
  
  delay(500); //チャタリング防止
  
}
/**************************03_IR MODE**************************/


/*******************データ書き込み(複数byte)*******************/
void lcdwriteData(String t_data){
  
  int m_count = t_data.length();  //送信データの文字数をカウント
  
  //全文字を表示
  for(int i=0;i<m_count;i++){
    
      Wire.beginTransmission(LCD_ADRS); //LCDをI2Cスレーブとして通信開始([slave address])
      Wire.write(0x40); //最終データ、LCDへの表示データ([control byte] = 8bit目 0:最終,1:連続、7bit目 0:命令,1:表示データ)
      Wire.write(t_data[i]);  //1文字をキューに格納
      Wire.endTransmission(); //キューに格納されたデータを送信してI2C通信停止
  
  }

  if(lcd_bcommand == 0x40+0x80){ //2行目のデータなら
    lcd_bdata2 = t_data;  //2行目のLCD文字を保存
  }
  else{
    lcd_bdata1 = t_data;  //1行目のLCD文字を保存
  }
  
}
/*******************データ書き込み(複数byte)*******************/


/*******************コマンド書き込み(1byte)*******************/
void lcdwriteCommand(byte t_command){ //(書き込み順 = [slave address] + [control byte] + [data byte] )
  
  Wire.beginTransmission(LCD_ADRS); //LCDをI2Cスレーブとして通信開始([slave address])
  Wire.write(0x00); //最終データ、LCDへの命令([control byte] = 8bit目 0:最終,1:連続、7bit目 0:命令,1:表示データ)
  Wire.write(t_command);  //命令文を送信([data byte])
  Wire.endTransmission(); //I2C通信停止
  delay(10);  //10ms待つ

  lcd_bcommand = t_command;  //最新コマンドを保存
  if(lcd_bcommand == 0x01){ //液晶文字クリアコマンドなら最新LCD表示文字データを初期化
    lcd_bdata1 = "";
    lcd_bdata2 = "";
  }
  
}
/*******************コマンド書き込み(1byte)*******************/


/*************************LCD初期化*************************/
void init_LCD(){
  
  delay(100); //電源ON後40ms以上待つ
  //[Function set] = 5bit目 0:4ビット,1:8ビット、4bit目 0:1行,1:2行、3bit目 フォントサイズ(0でOK)、1bit目 0:通常状態,1:以降拡張コマンド入力
  lcdwriteCommand(0x38); //8ビット、2行表示、5×8ドット、通常状態([Function set])
  delay(20); //26.3μs以上待つ(「lcdwriteCommand」で10ms待つためなくてもOK)
  lcdwriteCommand(0x39); //8ビット、2行表示、5×8ドット、以降拡張コマンド入力([Function set])
  delay(20); //26.3μs以上待つ(「lcdwriteCommand」で10ms待つためなくてもOK)
  //[Internal OSC frequency] = 4bit目 0:1/5バイアス電圧,1:1/4バイアス電圧、3〜1bit クロック周波数[kbits/s]
  lcdwriteCommand(0x14);  //1/5バイアス電圧、クロック周波数100kbit/s(標準)([Internal OSC frequency])
  delay(20); //26.3μs以上待つ(「lcdwriteCommand」で10ms待つためなくてもOK)
  //[Contrast set] = 4〜1bit コントラスト64段階調整([Power/ICONcontrol/Contrast set]と併せて設定)
  lcdwriteCommand(0x73);  //コントラスト設定([Contrast set])
  delay(20); //26.3μs以上待つ(「lcdwriteCommand」で10ms待つためなくてもOK)
  //[Power/ICONcontrol/Contrast set] = 4bit目 0:アイコン無,1:アイコン有(このLCDにはアイコンがないため0で設定)、3bit目 0:ブースターON(3.3V時),1:ブースターOFF(5V時)、
  //                                   2〜1bit コントラスト64段階調整([Contrast set]と併せて設定))
  lcdwriteCommand(0x56);  //アイコン無、ブースターON(3.3Vのため)、コントラスト設定(35)([Power/ICONcontrol/Contrast set]) 
  delay(20); //26.3μs以上待つ(「lcdwriteCommand」で10ms待つためなくてもOK)
  //[Follower control] = 4bit目 0:フォロア回路OFF,1:フォロア回路ON、3〜1bit 増幅率の設定(0〜7)
  lcdwriteCommand(0x6C);  //フォロア回路ON、増幅度6([Follower control])
  delay(500); //200ms以上待つ
  lcdwriteCommand(0x38);  //8ビット、2行表示、5×8ドット、通常状態(拡張コマンド入力終了)([Function set])
  delay(20); //26.3μs以上待つ(「lcdwriteCommand」で10ms待つためなくてもOK)
  lcdwriteCommand(0x01);  //LCD液晶文字クリア([Clear Display])
  delay(20); //26.3μs以上待つ(「lcdwriteCommand」で10ms待つためなくてもOK)
  //[Display ON/OFF] = 3bit目 0:文字表示OFF(RAM内のデータはそのまま),1:文字表示ON、2bit目 0:カーソル表示OFF,1:カーソル表示ON、
  //                   1bit目 0:ブランク表示OFF,1:ブランク表示ON
  lcdwriteCommand(0x0F);  //文字表示ON、カーソル表示ON、ブランク表示ON([Display ON/OFF])
  delay(20); //26.3μs以上待つ(「lcdwriteCommand」で10ms待つためなくてもOK)
  
}
/*************************LCD初期化*************************/
```

#次回記事予定
Xcodeを用いたiPhoneアプリ環境開発の構築