(Arduino)ESP32モジュールを用いたI2C通信LCD表示プログラム(STEP2)

#プログラム動作内容
今回はLCD表示プログラム第2弾ということで、ESP32にて自宅にインターネット接続しているWiFiルータに接続し、ネットから取得した現在時刻をLCDに1秒毎表示させるプログラムを作成する。
WiFiの接続or解除についてはトグルスイッチををON/OFFすることによってSWの外部割り込みを発生させて切り替えるようにする。
LCDに表示する現在時刻については、タイマー割り込みを使用してネットから1秒毎にデータを取得するようにする。

#割り込み処理とは
プログラムの動作中で、ある一定の条件が満たされればそのタイミングから一時的に処理を実行させたいプログラム処理に移行して実行する処理のことをいう。
その割り込み処理が終了すれば割り込み処理発生する直前に処理していたプログラム処理に戻る。
以下に今回使用するタイマー割り込みとスイッチによる外部割り込みについて説明する。

<b>①タイマー割り込み</b>
「タイマー割り込み」とは、指定した一定時間間隔で割り込み処理を実行させる処理になります。
タイマー関数としては、Arduinoであれば「MsTimer」がお手軽に使用できるのですが、ESP32だと使用できないため別のタイマー関数を使用します。
ESP32でタイマーを使用するには以下の内容をプログラム内に入れ込む必要がある。
なお、タイマー内で実行させたい処理があればonTimer()内の最後へ、メインにて実行させたい場合はif文内の最後へ追記して使用ください。
また、下記のプログラム例はタイマー起動のみで停止が入っていないが、停止させたい場合は「timerAlarmDisable(timer);」を追記ください。リスタートさせたい場合は再度「timerAlarmEnable(timer);」を追記ください。

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

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

  interruptCounter++; //割り込みカウント
  xSemaphoreGiveFromISR(timerSemaphore, NULL);  //割り込みフラグをリセット
  /* タイマー内で処理させたい処理があればここ以降に処理プログラムを追加 */

}

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

  .
  .
   /*  タイマー割り込み設定  */
  timerSemaphore = xSemaphoreCreateBinary();  //割り込みハンドラが呼ばれた際の通信のために利用するセマフォ(バイナリセマフォ)の作成
  timer = timerBegin(0, 80, true);  //タイマー番号0(0〜3)、分周比80(ESP32のクロック周波数は8MHz)、タイマー増減(true:増加,false:減少)
  timerAttachInterrupt(timer, &onTimer, true);  //タイマー変数指定:timer、タイマー割り込みする処理関:onTimer、割り込みタイプ(true:エッジ,false:レベル)
  timerAlarmWrite(timer, 1000000, true);  //タイマー変数指定:timer、タイマー1秒割り込み(8000000(クロック周波数) / 80(分周比) = 1000000Hz(=1カウントで1μs))、自動再起動有無(true:有,false:無)
  timerAlarmEnable(timer);  //タイマー起動
  .
  .

}

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

  .
  .
  if(interruptCounter > 0) {  //タイマー割り込みされれば
    
    interruptCounter --; //割り込みカウントリセット
    /* 実行したい処理をここ以降に処理プログラムを追加 */

  }
  .
  .

```

<b>②スイッチでの外部割り込み</b>
「スイッチでの外部割り込み」とは、外部入力が指定した変化をした場合に割り込み処理を実行させるものです。
こちらはタイマー割り込みと比べると比較的簡単にプログラムを構築することができます。
外部割り込みに使用する関数は以下の2種類となります。
・外部割り込み許可：attachInterrrupt(interrupt, function, mode);
・外部割り込み停止：detachInterrupt(interrupt);
　interrupt:割り込みに使用する入力ピン番号
　function:割り込み発生時に呼び出す関数
　mode:割り込みを発生させる時の入力ピンの変化指定(LOW:入力ピンがLOWの時、CHANGE:入力ピンの状態が変化した時,RISING:入力ピンがLOWからHIGHになった時,FALLING:入力ピンがHIGHからLOWになった時)
こちらを使用したプログラム例を下記に示すので参考ください。
動作：ピン16に接続されたLEDをピン39に接続されたスイッチ入力の状態変化に合わせて点滅させる場合

```C:SW_Sample
int pin = 16; //LED
int sw = 39; //SW
volatile int state = LOW; //初期状態消灯

void setup() {
  pinMode(pin, OUTPUT); //LEDを出力ピンに指定
  pinMode(sw, INPUT); //SWを入力ピンに指定
  attachInterrupt(sw, blink, CHANGE); //SW入力が状態変化すればblink関数の処理を実行
}

void loop() {
  digitalWrite(pin, state); //LEDをstate変数の状態に応じて点滅
}

void blink() {
  state = !state; //LED点灯状態を変更
}
```

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

| 名称 | 使用部品 | データのやり取り | ESP32への接続ピン |
|:---:|:-------:|:-----------:|:---------------:|
| I2C接続LCDモジュール | AE-AQM1602A(KIT) | I2C通信 | SDA:21,SCL:22 |
| トグルスイッチ | 小型３Ｐトグルスイッチ(１回路２接点) | 入力(プルダウン接続) | 39 |

<img width="895" alt="スクリーンショット 2020-03-12 0.49.29.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/595774/38a63232-1721-343b-ddeb-60596c2730dd.png">

#作成プログラムについて
実際に動作した状態を以下に示す。
<b>・トグルSW ON時(WiFi接続)</b>
<img width="620" alt="スクリーンショット 2020-03-12 1.03.31.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/595774/018fe24f-e97e-df3d-6dc2-8d273643458b.png">
<img width="619" alt="スクリーンショット 2020-03-12 1.05.25.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/595774/ba30c7f1-77b5-ccc3-5293-08e8702873a4.png">
<b>・トグルSW OFF時(WiFi接続解除)</b>
<img width="581" alt="スクリーンショット 2020-03-12 1.11.35.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/595774/93d238b5-0926-66b7-f5b1-8af3b7286320.png">

以下は今回使用したプログラムなので一部編集が必要ですがコピーして使用してみて下さい。
なお、[前回記事](https://qiita.com/pontaman007/items/4b85cf9406a7bf995e83)のプログラムをベースとして作成しており、変更箇所についてはプログラム内で明示している。

```C:01_LCD_I2C_TEST2
/*  作成日；2020/3/2
    ファイル名；01_LCD_I2C_TEST2
    プログラム内容；①WiFiからNTP通信によって取得した現在時刻をLCDへ表示する(i秒ごと)
    　　　　　　　　②トグルスイッチの状態に応じてWiFi接続を切り替える
    使用部品；ESP32、LCD、WiFiルータ、トグルスイッチ、47kΩ抵抗
    ESP32へのピン接続；(21:LCD_SDA)、(22:LCD_SCL)、(39:トグルスイッチ)
 */

#include <Wire.h>  //I2C通信ライブラリ

/****************************追加内容(下)****************************/
#include <WiFi.h> //WiFiモジュールライブラリ
/****************************追加内容(上)****************************/

#define LCD_ADRS 0x3E //I2Cアドレス
String overflow = "Over flow of characters";  //文字数が16byte以上の場合に表示させる文字

/****************************追加内容(下)****************************/
const uint16_t Tog_SW = 39;  //トグルスイッチ
bool Tog_Flag = false;  //トグルスイッチ割り込みフラグ
bool Wifi_Flag = false; //WiFi接続フラグ

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

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(Tog_SW, INPUT); //トグルスイッチ(WiFi接続)
/****************************追加内容(上)****************************/

  Wire.begin(); //I2C通信開始(ESP32をマスター)
  init_LCD(); //LCD初期化
  lcdwriteData("Booting now..."); //LCDへ起動中の表示
  
  /*  ここに起動時に確認する項目を記載  */
  delay(5000);
  /*  ここに起動時に確認する項目を記載  */

/****************************追加内容(下)****************************/
  attachInterrupt(Tog_SW, wifiFlag, CHANGE);  //トグルスイッチの切り替えごとにWiFi接続外部割り込み有効

  /*  タイマー割り込み設定  */
  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))
  /*  タイマー割り込み設定  */

  wifiConnect();  //WiFi接続処理
/****************************追加内容(上)****************************/
  
}
/*  初期設定  */


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

/****************************追加内容(下)****************************/
  while(Tog_Flag){  //トグルスイッチが操作されれば
    
    detachInterrupt(Tog_SW);  //外部割り込み禁止
    wifiConnect();  //WiFi接続処理
    Tog_Flag = !Tog_Flag; //トグルスイッチ割り込みフラグ反転
    attachInterrupt(Tog_SW, wifiFlag, CHANGE);  //外部割り込み再開

  }
  
  if(interruptCounter > 0) {  //タイマー割り込みされれば
    
    interruptCounter --; //割り込みカウントリセット
    dateDisplay();  //ネットから取得した現在時刻を表示

  }
/****************************追加内容(上)****************************/
  
}
/*  メイン本文  */

/****************************追加内容(下)****************************/
/*  現在時刻表示処理  */
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接続トグルスイッチ外部割り込み処理  */


/*  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アドレスの表示
    
    //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フラグ切り替え
    
    timerAlarmEnable(timer);  //タイマー起動

  }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); //タイマー停止
      /*  タイマー割り込み停止操作  */
      
      Wifi_Flag = !Wifi_Flag; //WiFiフラグ切り替え
      
    }
    
  }
  
}
/*  WiFi接続処理  */
/****************************追加内容(上)****************************/

/*  データ書き込み(複数byte) */
void lcdwriteData(String t_data){
  
  int m_count = t_data.length();  //送信データの文字数をカウント
  
  if(m_count>16){ //データ数が16バイトを超えたら
    lcdwriteCommand(0x01);  //LCD液晶文字クリア([Clear Display])
    //全文字を表示
    for(int i=0;i<23;i++){
      if(i==16){  //17文字目に到達したら
        lcdwriteCommand(0x40+0x80); //2行目へカーソル移動
      }
      Wire.beginTransmission(LCD_ADRS); //LCDをI2Cスレーブとして通信開始([slave address])
      Wire.write(0x40); //最終データ、LCDへの表示データ([control byte] = 8bit目 0:最終,1:連続、7bit目 0:命令,1:表示データ)
      Wire.write(overflow[i]);  //1文字をキューに格納
      Wire.endTransmission(); //キューに格納されたデータを送信してI2C通信停止

    }
  }else{
  //全文字を表示
    for(int i=0;i<m_count;i++){
      if(i==16){
        lcdwriteCommand(0x40+0x80); //2行目へカーソル移動
      }
      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通信停止

    }
  }
}
/*  データ書き込み(複数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待つ
  
}
/*  コマンド書き込み(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初期化  */
```

以下から各ブロックの説明をしていきます。
※前回作成のプログラムからの変更点について説明する

①ライブラリ定義、グローバル変数設定等

```C:01_LCD_I2C_TEST2(抜粋)
#include <WiFi.h> //WiFiモジュールライブラリ

const uint16_t Tog_SW = 39;  //トグルスイッチ
bool Tog_Flag = false;  //トグルスイッチ割り込みフラグ
bool Wifi_Flag = false; //WiFi接続フラグ

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

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

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

「#include ＜WiFi.h＞ 」：WiFi通信を実施する各関数を使用する場合に必要な定義となるので記載が必要
「const uint16_t Tog_SW = 39」：トグルスイッチの接続ピン番号を指定
「bool Tog_Flag = false」：トグルスイッチ割り込みフラグの初期化(WiFi接続スイッチ)
「bool Wifi_Flag = false」：WiFi接続フラグの初期化

WiFi関係設定関数
「WiFiServer server(80)」：サーバーのインスタンスを生成(PORT:80)
「const char* ssid     = "自宅ルータのSSID"」：接続するルータのSSIDを入力<b>（変更ください)</b>
「const char* password = "自宅ルータのPASS"」：接続するルータのPASSを入力<b>（変更ください)</b>
「IPAddress ip」：IPアドレスを格納するインスタンスを生成
「String ip_string = ""」：IPアドレスをString型に変換した値を格納する変数を定義

時刻取得の各種変数
「struct tm timeInfo」：時刻を格納するオブジェクトを生成
「String date_string = ""」：日付をString型に変換した値を格納する変数を定義
「String time_string = ""」：時刻をString型に変換した値を格納する変数を定義

タイマー割り込み設定関数
「volatile int interruptCounter」：タイマー割り込みが発生したかどうかを判断するカウンター変数を定義
「hw_timer_t * timer = NULL」：タイマー変数を初期化
「volatile SemaphoreHandle_t timerSemaphore」：タイマー割り込みが発生したかどうかを表すセマフォを生成

②タイマー割り込み処理

```C:01_LCD_I2C_TEST2(抜粋)
/*  タイマー割り込み処理  */
void IRAM_ATTR onTimer() {

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

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

③初期設定

```C:01_LCD_I2C_TEST2(抜粋)
/*  初期設定  */
void setup() {

  //ピン入出力設定
  pinMode(Tog_SW, INPUT); //トグルスイッチ(WiFi接続)

  attachInterrupt(Tog_SW, wifiFlag, CHANGE);  //トグルスイッチの切り替えごとにWiFi接続外部割り込み有効

  /*  タイマー割り込み設定  */
  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))
  /*  タイマー割り込み設定  */

  wifiConnect();  //WiFi接続処理

}
/*  初期設定  */
```

「pinMode(Tog_SW, INPUT)」：WiFi接続するトグルスイッチ(39ピン接続)を入力ピンとして指定
「attachInterrupt(Tog_SW, wifiFlag, CHANGE)」:トグルスイッチのON/OFFタイミングでWiFi接続外部割り込み処理有効

タイマー割り込み設定
「timerSemaphore = xSemaphoreCreateBinary()」:割り込みハンドラが呼ばれた際の通信のために利用するセマフォ(バイナリセマフォ)の作成　※タイマーフラグみたいなもの
「timer = timerBegin(0, 80, true)」:タイマー番号0(0〜3)、分周比80、タイマー増加カウント(true:増加,false:減少)
「timerAttachInterrupt(timer, &onTimer, true)」:タイマー変数指定 timer、タイマー割り込みする処理関数 onTimer、割り込みタイプ エッジ(true:エッジ,false:レベル)
「timerAlarmWrite(timer, 1000000, true)」:タイマー変数指定 timer、タイマー1秒割り込み(80000000(クロック周波数) / 80(分周比) = 1000000Hz(=1カウントで1μs)、自動再起動有

「wifiConnect()」:WiFi接続処理時に実行する関数処理

④メイン(ループ)

```C:01_LCD_I2C_TEST2(抜粋)
/*  メイン本文  */
void loop() {

  while(Tog_Flag){  //トグルスイッチが操作されれば
    
    detachInterrupt(Tog_SW);  //外部割り込み禁止
    wifiConnect();  //WiFi接続処理
    Tog_Flag = !Tog_Flag; //トグルスイッチ割り込みフラグ反転
    attachInterrupt(Tog_SW, wifiFlag, CHANGE);  //外部割り込み再開

  }
  
  if(interruptCounter > 0) {  //タイマー割り込みされれば
    
    interruptCounter --; //割り込みカウントリセット
    dateDisplay();  //ネットから取得した現在時刻を表示

  }
  
}
/*  メイン本文  */
```

「detachInterrupt(Tog_SW)」:トグルスイッチによる外部割り込みを禁止
「wifiConnect()」:WiFi接続処理時に実行する関数処理
「Tog_Flag = !Tog_Flag」:トグルスイッチ割り込みフラグ反転
「attachInterrupt(Tog_SW, wifiFlag, CHANGE)」:トグルスイッチによる外部割り込みを再開

「interruptCounter --」:タイマー割り込みカウントリセット
「dateDisplay()」:ネットから取得した現在時刻をLCDに表示させる関数

⑤現在時刻表示関数
構成は、「現在時刻取得→時刻をString型へ変換→LCD1行目に日付,2行目に時刻を表示」となっている。

```C:01_LCD_I2C_TEST2(抜粋)
/*  現在時刻表示処理  */
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接続トグルスイッチ外部割り込み処理

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

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

⑦WiFi接続処理関数

```C:01_LCD_I2C_TEST2(抜粋)
/*  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フラグ切り替え
    
    timerAlarmEnable(timer);  //タイマー起動

  }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); //タイマー停止
      /*  タイマー割り込み停止操作  */
      
      Wifi_Flag = !Wifi_Flag; //WiFiフラグ切り替え
      
    }
    
  }
  
}
/*  WiFi接続処理  */
```

「while (WiFi.status() != WL_CONNECTED)」:WiFi接続完了するまで待機
「ip = WiFi.localIP()」:WiFi接続完了したESP32のローカルIPアドレスを取得
「ip_string = "IP:" + String(ip[0]) + "." + String(ip[1]) + "." + String(ip[2]) + "." + String(ip[3])」:IPアドレスをString型に変換
「configTime(9 * 3600L, 0, "ntp.nict.jp", "time.google.com", "ntp.jst.mfeed.ad.jp")」:WiFi接続したルータ経由でインターネットから現在時刻を取得するためのNTPを設定
「server.begin()」:サーバー起動
「Wifi_Flag = true」:WiFi接続フラグを有効
「timerAlarmEnable(timer)」:タイマー割り込み開始
「server.stop()」:サーバー停止
「WiFi.disconnect()」:WiFi接続を切断
「interruptCounter --」:タイマー割り込みカウンタをリセット
「timerAlarmDisable(timer)」:タイマー割り込みを停止

#次回記事予定
赤外線リモコンから受信したデータをシリアルモニタへ表示させるプログラムの作成

#参考文献
・https://garretlab.web.fc2.com/arduino/esp32/reference/