#include <Wire.h>  //I2C用のライブラリ
////////////////////////////////////////////////////////////////////////////////
//ライブラリをインクルードします
////////////////////////////////////////////////////////////////////////////////
#include <M5Core2.h>    //M5Core2のライブラリをインクルード
#include <BleSerial.h>  //ブルートゥースのライブラリをインクルード
#include <FS.h>         //内部データーベースのライブラリをインクルード
#include <SD.h>
#include <SPIFFS.h>             //SPIFFSライブラリをインクルード
#include <WiFi.h>               // WiFiライブラリをインクルード
#include "logo.h"               //画像ファイルです
#include <HTTPClient.h>         //HTTP通信用ライブラリ
#include <Wire.h>               //I2C用のライブラリ
#include "M5_ENV.h"             //環境センサー用のライブラリ
#include "Adafruit_Sensor.h"    //環境センサー用のライブラリ
#include <Adafruit_BMP280.h>    //環境センサー用のライブラリ
#include <SensirionI2CSht4x.h>  //環境センサー用のライブラリ
#include <esp32FOTA.hpp>        //オンラインアップデート用のライブラリ
#include <AXP192.h>             //電源管理チップ用のライブラリ
#include <RTClib.h>             //RTC用のライブラリ
#define LGFX_AUTODETECT         // 自動認識
#define LGFX_USE_V1             // LovyanGFX v1.0.0を有効に
#include <LovyanGFX.hpp>        // LovyanGFXのヘッダを準備
#include <LGFX_AUTODETECT.hpp>  // クラス"LGFX"を用意する
#include <math.h>               //数学系のライブラリ





////////////////////////////////////////////////////////////////////////////////
//RTC関係の部分
////////////////////////////////////////////////////////////////////////////////
#define JST 3600 * 9                                                        // グリニッジ標準時と日本標準時の差
const char* week[7] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };  // 曜日を定義
RTC_DateTypeDef RTC_DateStruct;                                             //RTC関連の設定
RTC_TimeTypeDef RTC_TimeStruct;                                             //RTC関連の設定
struct tm timeinfo;                                                         //RTC関連の設定





////////////////////////////////////////////////////////////////////////////////
//グローバル変数置き場
////////////////////////////////////////////////////////////////////////////////
int system_status = 1;    //プログラムステータスを1に設定します
int page_num = 0;         //ページ番号
int page_transition = 1;  //ページの遷移
//プログラムステータス説明
//0:通常稼働中です
//1:初期設定のための最初の通信を行います
//2:WiFiを設定します
//3:サーバーからUIDを取得
//4:WiFi接続エラー
//10:リセット画面
//21:オンラインアップデート開始
//21:オンラインアップデート中
//30:オンラインモード
int system_status_temp = 0;  //プログラムステータスをの控え
int resetbutton = 0;         //リセットボタンの変数

// SDカードのピン設定
#define SD_CS_PIN 4
const size_t bufferSize = JSON_OBJECT_SIZE(9);


BleSerial ble;                //ブルートゥースに使う変数を定義
DHT12 dht12;                  //環境センサー用の何か
Adafruit_BMP280 bme;          //環境センサー用の何か
AXP192 power;                 //電源管理チップ
RTC_TimeTypeDef currentTime;  //RTC時刻
RTC_DateTypeDef currentDate;  //RTC日付

int a;  //Core0のループ検知防止用
TaskHandle_t thp[1];

String model = "bamboocompost-m5core2";                             //モデルを指定
int Version = 1;                                                    //バージョンを定義
String host = "ecohero.reach-world.net";                            //サーバーのアドレスをしています
esp32FOTA esp32FOTA(model, Version, false, true);                   //バージョンとシステムタイプ
String manifest_url_string = "https://" + host + "/M5API/OTA.php";  //アップデート用のURLを生成
const char* manifest_url = manifest_url_string.c_str();             //アップデート用のURL

int env_sensor_ver = 1;
String uuid = "0";    //変数を定義
float tmp = 0;        //温度の変数
float hum = 0;        //湿度の変数
float pressure = 0;   //気圧の変数
float BTVoltage = 0;  //電圧の変数
float BTLevel = 0;    //バッテリーの容量

static LGFX lcd;                  // LGFXのインスタンスを作成
static LGFX_Sprite sprite(&lcd);  // スプライトを使う場合はLGFX_Spriteのインスタンスを作成


//ユーザー設定
int online = 1;
String DisplayImgUrl;
int LCDbrightness = 100;
int LED = 1;
int BTprotection = 1;

String SystemBackColor = "BLACK";
String SystemTextColor = "WHITE";
String SystemFront = "&fonts::efontJA_24";
String ClockBackColor = "BLACK";
String ClockTextColor = "CYAN";
String ClockFont = "&fonts::Font7";

//ANA関係
const int CH1_PIN = 35;         // CH1のピン番号
const int CH2_PIN = 36;         // CH`2のピン番号
const float MAX_VOLTAGE = 3.3;  // 3.3Vを電源とした場合
const float ANALOG_MAX = 4095;  // ESP32の場合
float value1 = 0;               // CH1のアナログ値(0-4095)
float value2 = 0;               // CH1のアナログ値(0-4095)
float voltage1 = 0;             // CH1の電圧(0-3.3V)
float realVoltage1 = 0;         // CH1の真の電圧(分圧器の倍率を掛けたもの)
float alpha = 1.0;              // 倍率(分圧抵抗)


////////////////////////////////////////////////////////////////////////////////
//最初の処理
////////////////////////////////////////////////////////////////////////////////
void setup() {
  M5.begin(true, true, true, true);  //m5を使うためのもの
  lcd.init();
  lcd.setRotation(1);                                 // 画面の向きを調整（必要に応じて）
  M5.Rtc.GetTime(&currentTime);                       //RTC時刻
  M5.Rtc.GetDate(&currentDate);                       //RTC日付
  Serial.begin(115200);                               //シリアル通信を設定
  Wire.begin();                                       //通信系の何か
  SPIFFS.begin();                                     //内部データを利用するための処理
  M5.Axp.ScreenBreath(100);                           // 画面の明るさを100
  M5.Lcd.setRotation(1);                              // 画面の向きをちょうせい
  M5.Lcd.fillScreen(WHITE);                           // 背景色
  M5.Lcd.setSwapBytes(false);                         // 色がおかしい場合に変更する
  M5.Lcd.startWrite();                                //画面表示開始
  M5.Lcd.pushImage(0, 0, imgWidth, imgHeight, logo);  //起動ロゴを表示
  M5.Lcd.setTextSize(1);                              // テキストサイズ倍率(整数で指定)
  M5.Lcd.println("V:" + String(Version));             //バージョンを表示
  StandAlone();
  esp32FOTA.printConfig();
  sensor_detect();
  Serial.println("起動しました");  //起動時にシリアルに送信(開発用)
  delay(1000);                     //少し待機
  M5.Lcd.endWrite();               //描画を一度終了します
  if (online == 1) {
    M5.Lcd.println("OnlineMode");
    xTaskCreatePinnedToCore(DataUpload, "DataUpload", 8192, NULL, 3, &thp[0], 0);  //サブのCPUコアで別のタスクを処理させるためのやつ
    Serial.println("WiFiコンフィグ探知");                                          //内部メモリを確認しシリアルに送信(開発用)
    WiFi.mode(WIFI_STA);                                                           //WiFiの動作モードを設定
    esp32FOTA.setManifestURL(manifest_url);                                        //オンラインアップデート機能
    if (SPIFFS.exists("/wifi_credentials.txt")) {                                  //WiFIの設定が済まされているかどうか
      Serial.println("wifi_credentials.txtが存在します");                          //内部メモリを確認しシリアルに送信(開発用)
      system_status = 3;                                                           //WiFiの設定が存在する場合、settingを0に設定
      connectToWiFi();                                                             //WiFIに接続
    }                                                                              //if文終了
    if (SPIFFS.exists("/uuid.txt")) {                                              //WiFIの設定が済まされているかどうか
      Serial.println("uuid.txtが存在します");                                      //内部メモリを確認しシリアルに送信(開発用)
      configTime(JST, 0, "ntp.nict.jp", "ntp.jst.mfeed.ad.jp");                    // NTPサーバとローカルのタイムゾーンを設定
      if (WiFi.status() != WL_CONNECTED) {                                         //WiFi接続を確認
        Serial.println("WiFi接続失敗");                                            //WiFiを確認しシリアルに送信(開発用)
        system_status = 4;                                                         //システムステータスを4に移行
      } else {                                                                     //if文「または」
        setNTP2RTC();                                                              //RTCをセットアップ
        readUUID();                                                                //UUIDを内部データベースから読み込み
        system_status = 0;
      }                                                 //if文終了
    }                                                   //if文終了
    Serial.println("コンフィグ探知終了");               //内部メモリを確認しシリアルに送信(開発用)
    Serial.println("Status:" + system_status);          //ステータスを通知(開発用)
    if (system_status == 1) {                           //初回起動に動作させるもの
      M5.Lcd.pushImage(0, 0, imgWidth, imgHeight, QR);  //設定用のQRコードを表示
      Serial.println("ロゴ表示完了");                   //ロゴ表示完了をシリアルに送信(開発用)
      M5.Lcd.endWrite();                                //描画を一度終了します
      ble.begin("BambooCompost");                       //ブルートゥースのデバイス名を定義しています
      Serial.println("ブルートゥースを開始しました");   //ブルートゥース接続開始をシリアルに送信(開発用)
    }                                                   //if文終了
  } else {
    if (!SD.begin(SD_CS_PIN)) {
      Serial.println("SD initialization failed!");
      return;
    }
    // data.txtファイルを開く
    File dataFile = SD.open("/data.txt", FILE_APPEND);
    if (!dataFile) {
      Serial.println("Failed to open data.txt");
      return;
    }

    dataFile.close();
    xTaskCreatePinnedToCore(SaveSD, "SaveSD", 8192, NULL, 3, &thp[0], 0);  //サブのCPUコアで別のタスクを処理させるためのやつ
  }
}  //void setup終了

void sensor_detect() {
  // I2Cデバイス(0x44)の検知
  Wire.beginTransmission(0x44);
  byte error = Wire.endTransmission();

  // 結果に応じてenv_sensor_verを設定
  if (error == 0) {
    env_sensor_ver = 4;
  }

  // 結果をシリアルモニタに出力
  Serial.print("Env sensor version: ");
  Serial.println(env_sensor_ver);
}

void StandAlone() {  //AボタンBボタンの処理
  if (!SD.begin(TFCARD_CS_PIN, SPI, 40000000)) {
    Serial.println("SDカードを挿入してください");
  } else {
    Serial.println("SDカードが挿入されています");
    online = 0;
    system_status = 30;
    M5.Lcd.println("StandAloneMode");
  }
}
////////////////////////////////////////////////////////////////////////////////
// 画像表示
////////////////////////////////////////////////////////////////////////////////



////////////////////////////////////////////////////////////////////////////////
//メインのループです
////////////////////////////////////////////////////////////////////////////////
void loop() {   // メインのループ
  M5.update();  //マイコンの状態を更新
  //初回設定用のコード
  if (system_status == 1) {               //プログラムステータス1の時に実行(void BTSettingの部分を実行します)
    BTSetting();                          //ブルートゥースで初回コネクト
  }                                       //if文終了
  if (system_status == 2) {               //プログラムステータス2の時に実行
    receiveWiFiSettings();                //WiFiのせってい(void receiveWiFiSettingsの部分を実行します)
  }                                       //if文終了
  if (system_status == 3) {               //プログラムステータス3の時に実行
    if (WiFi.status() == WL_CONNECTED) {  //WiFi接続を確認
      getUUID();                          //UUIDをサーバーから取得し、時刻を調整します
    } else {
      Serial.println("WiFi接続失敗");  //WiFiを確認しシリアルに送信(開発用)
      system_status = 4;
    }
  }                                                          //if文終了
  if (system_status == 4) {                                  //プログラムステータス2の時に実行
    M5.Lcd.pushImage(0, 0, imgWidth, imgHeight, wifierror);  //設定用のQRコードを表示
    M5.Lcd.endWrite();                                       //描画を一度終了します
    ble.begin("BambooCompost");                              //ブルートゥースのデバイス名を定義しています
    system_status = 1;
  }                           //if文終了
  if (system_status == 21) {  //プログラムステータス2の時に実行
    ota();
  }  //if文終了

  if (system_status == 30) {  //プログラムステータス2の時に実行
    printSensor();            //センサーの値を取得
  }                           //if文終了

  if (system_status == 0) {      //プログラムステータス0の時に実行
    PageButton();                //ページボタンの処理
    if (page_transition == 1) {  //ページ遷移モードを有効だったばあい
      if (page_num == 0) {
        M5.Lcd.fillScreen(BLACK);  //背景
        if (DisplayImgUrl.indexOf("http") != -1) {
        } else {
          M5.Lcd.pushImage(0, 0, imgWidth, imgHeight, eye);  //目の画像を表示
          M5.Lcd.endWrite();                                 //描画を終了
        }
        page_transition = 0;  //ページ遷移モードを無効に
      }                       //if文終了
      if (page_num == 1) {    //1ページ目の処理
        printLocalTime();     //時間を表示
        page_transition = 1;  //ページ遷移モードを有効に
      }                       //if文終了
      if (page_num == 2) {    //2ページ目の処理
        printSensor();        //センサーの値を取得
        page_transition = 1;  // ページ遷移モードを有効に
        delay(500);           // 待つ
      }                       //if文終了
      if (page_num == 3) {    //センサーの値を表示
        printSystemData();
        page_transition = 0;  //ページ遷移モードを無効に
      }                       //if文終了
                              //      if (page_num == 4) {    //センサーの値を表示
                              //        system_status = 21;   //ページ遷移モードを無効に
                              //      }                       //if文終了
    }                         //if文終了
  }                           //if文終了
  ResetButton();              //リセットボタンの処理
}  //void loop終了

//サブCPU(Core0)で実行するプログラム(オンライン時)
void DataUpload(void* args) {
  while (1) {
    delay(10000);  //サブで実行するプログラムを書く
    a++;
    if (system_status == 0) {
      uploadData();
      getSetting();
      setSetting();
    }
    Serial.println(system_status);
  }
}

void SaveSD(void* args) {
  while (1) {
    a++;
    if (system_status == 30) {
      StaticJsonDocument<bufferSize> doc;
      doc["tmp"] = tmp;
      doc["hum"] = hum;
      doc["pressure"] = pressure;
      doc["SystemVersion"] = Version;
      doc["BordModel"] = model;
      doc["BTLevel"] = BTLevel;
      doc["BTVoltage"] = BTVoltage;
      doc["SoilSensor"] = realVoltage1;

      // JSONを文字列に変換
      String output;
      serializeJson(doc, output);

      // SDカードにデータを書き込む
      File dataFile = SD.open("/data.txt", FILE_APPEND);
      if (dataFile) {
        dataFile.println(output);
        dataFile.close();
        Serial.println("SDカードにセーブしました");
      } else {
        Serial.println("Failed to open data.txt");
      }
      delay(60000);  //サブで実行するプログラムを書く
    }
  }
}
////////////////////////////////////////////////////////////////////////////////
//サーバー関係
////////////////////////////////////////////////////////////////////////////////
void uploadData() {
  if (WiFi.status() == WL_CONNECTED) {
    getSensor();
    HTTPClient http;
    // サーバーにPOSTするJSONデータを作成
    StaticJsonDocument<200> doc;
    doc["tmp"] = tmp;
    doc["hum"] = hum;
    doc["pressure"] = pressure;
    doc["uuid"] = uuid;
    doc["SystemVersion"] = Version;
    doc["BordModel"] = model;
    doc["BTLevel"] = BTLevel;
    doc["BTVoltage"] = BTVoltage;
    doc["SoilSensor"] = realVoltage1;
    String jsonPayload;
    serializeJson(doc, jsonPayload);
    http.begin("https://" + host + "/M5API/postData.php");
    http.addHeader("Content-Type", "application/json");
    int httpResponseCode = http.POST(jsonPayload);
    if (httpResponseCode > 0) {
      Serial.print("HTTP Response code: ");
      Serial.println(httpResponseCode);
      String response = http.getString();
      Serial.println(response);
    } else {
      Serial.print("HTTP Request failed. Error code: ");
      Serial.println(httpResponseCode);
    }
    http.end();
  }
}


void getSetting() {
  HTTPClient http;
  http.begin("https://" + host + "/M5API/getSetting.php");

  int httpCode = http.GET();
  if (httpCode > 0) {
    // 成功したらJSONデータを取得する
    String payload = http.getString();
    Serial.println(payload);

    // JSONデータをパースして変数に代入する
    // 以下はArduinoJsonライブラリを使用した例
    DynamicJsonDocument doc(1024);
    deserializeJson(doc, payload);

    // DisplayImgUrlの取得
    DisplayImgUrl = doc["DisplayImgUrl"].as<String>();
    Serial.println("DisplayImgUrl: " + DisplayImgUrl);

    // LCDbrightnessの取得
    LCDbrightness = doc["LCDbrightness"].as<int>();
    Serial.println("LCDbrightness: " + String(LCDbrightness));

    // LEDの取得
    LED = doc["LED"].as<int>();
    Serial.println("LED: " + String(LED));

    // LEDの取得
    BTprotection = doc["BTprotection"].as<int>();
    Serial.println("BTprotection: " + String(BTprotection));

    SystemBackColor = doc["SystemBackColor"].as<String>();
    Serial.println("SystemBackColor: " + SystemBackColor);

    SystemTextColor = doc["SystemTextColor"].as<String>();
    Serial.println("SystemTextColor: " + SystemTextColor);

    SystemFront = doc["SystemFront"].as<String>();
    Serial.println("SystemFront: " + SystemFront);

    ClockBackColor = doc["ClockBackColor"].as<String>();
    Serial.println("ClockBackColor: " + ClockBackColor);

    ClockTextColor = doc["ClockTextColor"].as<String>();
    Serial.println("ClockTextColor: " + ClockTextColor);

    ClockFont = doc["ClockFont"].as<String>();
    Serial.println("ClockFont: " + ClockFont);

  } else {
    Serial.println("Failed to connect to API");
  }

  http.end();
}  //void getSetting終了

void setSetting() {
  Serial.println("設定を反映しました");
  M5.Axp.ScreenBreath(LCDbrightness);  // 画面の明るさを設定
  lcd.setBrightness(LCDbrightness);    // 画面の明るさを設定
  if (BTprotection == 0) {
    setBatteryCharge(true);
  }
}


//バッテリーチャージの設定falseで充電しない
void setBatteryCharge(bool enabled) {
  Wire.beginTransmission(0x75);
  Wire.write(0x00);
  Wire.write(enabled ? 0x35 : 0x25);
  Wire.endTransmission();
}

void btProtect() {
  if (BTLevel > 80) {
    setBatteryCharge(false);
    Serial.println("バッテリーの充電を停止");
  }
  if (BTLevel < 30) {
    setBatteryCharge(true);
    Serial.println("バッテリーの充電を開始");
  }
}

////////////////////////////////////////////////////////////////////////////////
//センサー関係
////////////////////////////////////////////////////////////////////////////////
void getSensor() {                                                          //センサーの値を取得する
  while (!bme.begin(0x76)) {                                                //繰り返し
    M5.Lcd.println("Could not find a valid BMP280 sensor, check wiring!");  //センサーでエラーが起きたときにLCDに表示
  }                                                                         //繰り返し終了
  if (env_sensor_ver == 1) {
    tmp = dht12.readTemperature();  //温度を取得
    hum = dht12.readHumidity();
  }                                   //湿度を取得
  pressure = bme.readPressure();      //気圧を取得
  BTVoltage = power.GetBatVoltage();  //バッテリー電圧を取得
  BTLevel = power.GetBatteryLevel();  //バッテリー残量を取得
  value1 = analogRead(CH1_PIN);
  value2 = analogRead(CH2_PIN);
  voltage1 = value1 * MAX_VOLTAGE / ANALOG_MAX;
  realVoltage1 = alpha * voltage1;
}  //void getSensor終了





////////////////////////////////////////////////////////////////////////////////
//ボタン関係
////////////////////////////////////////////////////////////////////////////////
//ページ遷移ボタン
void PageButton() {                           //AボタンBボタンの処理
  if (M5.BtnA.isPressed()) {                  //ボタンの感知
    Serial.println("Bボタンが押されました");  //Bボタンのプッシュをシリアルで通知(開発用)
    delay(250);                               //待機
    if (page_num > 0) {                       //値が0を下回らないように
      page_num = page_num - 1;                //ボタンが押されていない場合は値が減少します
      page_transition = 1;                    //ページ遷移モードが有効化されます
      Serial.println(page_num);               //ページナンバーをシリアルで通知(開発用)
    }                                         //if文終了
  }                                           //if文終了
  if (M5.BtnB.isPressed()) {                  //ボタンの感知
    Serial.println("Aボタンが押されました");  //Aボタンのプッシュをシリアルで通知(開発用)
    delay(250);                               //待機
    if (page_num < 4) {                       //値が一定をを上回らないように
      page_num += 1;                          //ボタンが押されている場合は値が増加します
      page_transition = 1;                    //ページ遷移モードが有効化されます
      Serial.println(page_num);               //ページナンバーをシリアルで通知(開発用)
    }                                         //if文終了
  }                                           //if文終了
}  //void PageButton終了


//リセットの処理
void ResetButton() {
  if (M5.BtnC.isPressed()) {                             //ボタンの感知
    delay(10);                                           //ウェイト
    resetbutton += 1;                                    //ボタンが押されている場合は値が増加します
  } else {                                               //if文の「または~」
    if (resetbutton > 0) {                               //値が0を下回らないように
      resetbutton -= 1;                                  //ボタンが押されていない場合は値が減少します
    }                                                    //if文終了
  }                                                      //if文終了
  if (resetbutton == 300 && system_status != 10) {       //リセットボタンの値が一定数増えたら実行
    system_status_temp = system_status;                  //システムステータスを控える
    M5.Lcd.pushImage(0, 0, imgWidth, imgHeight, reset);  //警告画面を表示
    M5.Lcd.endWrite();                                   //描画を一度終了します
    system_status = 10;                                  //システムステータスを10に
    Serial.println("システムステータスを10にしました");  //ステータスを通知
  }                                                      //if文終了
  if (system_status == 10) {                             //システムステータスが10の時に実行
    if (resetbutton > 800) {                             //リセットボタンの値が一定数増えたら実行
      M5.Lcd.setTextSize(2);                             // 文字サイズを設定
      M5.Lcd.setCursor(0, 0);                            //カーソルの設定
      M5.Lcd.fillScreen(TFT_BLACK);                      //黒で塗りつぶす
      M5.Lcd.println("Resetting");                       //文字列を表示
      M5.Lcd.println("It may take some time");           //文字列を表示
      M5.Lcd.println("The system will restart");         //文字列を表示
      SPIFFS.format();                                   //内部データを削除
      ESP.restart();                                     //再起動
    }                                                    //if文終了
    if (resetbutton < 290) {                             //リセットボタンの値が一定数増えたら実行
      system_status = system_status_temp;                //控えたステータスを復元
      page_transition = 1;                               //ページ遷移モードを有効に
      Serial.println("リセットモードを解除しました");    //シリアルで解除を通知
      Serial.println(system_status);                     //シリアルでステータスを通知
    }                                                    //if文終了
  }                                                      //if文終了
}  //void ResetButton終了





////////////////////////////////////////////////////////////////////////////////
//WiFi関係
////////////////////////////////////////////////////////////////////////////////
//WiFiに接続
void connectToWiFi() {
  String ssid;      //ローカル変数を定義
  String password;  //ローカル変数を定義
  // WiFi認証情報を読み取る
  readWiFiCredentials(ssid, password);                              //内部データからWiFiの情報を取りに行きます
  Serial.println("SSID: " + ssid);                                  //シリアルでSSIDを送信(開発用)
  Serial.println("Password: " + password);                          //シリアルでパスワードを送信(開発用)
  Serial.println("SSID Length: " + String(ssid.length()));          //シリアルでSSIDの長さを送信(開発用)
  Serial.println("Password Length: " + String(password.length()));  //シリアルでパスワードの長さを送信(開発用)
  // WiFiに接続する
  WiFi.begin(ssid, password);                               //WiFiをスタート
  Serial.println("Connecting to WiFi...");                  //シリアルで接続開始をつうち(開発用)
  int attempts = 0;                                         //ローカル変数を定義
  while (WiFi.status() != WL_CONNECTED && attempts < 10) {  //WiFi再接続の処理
    delay(1000);                                            //1秒待機
    Serial.println("Connecting to WiFi...");                //シリアルで接続開始をつうち(開発用)
    attempts++;                                             //WiFi再接続の処理
  }                                                         //while文終了
  if (WiFi.status() == WL_CONNECTED) {                      //WiFiがつながっているときのしょり
    Serial.println("Connected to WiFi");                    //シリアルで接続をつうち(開発用)
    Serial.println(WiFi.localIP());                         //シリアルでIPをつうち(開発用)
    Serial.println(WiFi.dnsIP());                           //シリアルでDNSサーバーのIPをつうち(開発用)
  } else {                                                  //if文「または」
    Serial.println("Failed to connect to WiFi");            //シリアルで接続失敗をつうち(開発用)
  }                                                         //if文終了
  delay(10);                                                //0.01秒待つ
}  //void connectToWiFi終了


//WiFiの情報を内部データベースから取得
void readWiFiCredentials(String& ssid, String& password) {
  File file = SPIFFS.open("/wifi_credentials.txt", "r");           //WiFiの設定ファイルを開く
  if (file) {                                                      // ファイルチェック
    while (file.available()) {                                     // ファイルチェック
      String line = file.readStringUntil('\n');                    // 行から前後の余分な空白を取り除く
      line.trim();                                                 // 行から前後の余分な空白を取り除く
      int separatorIndex = line.indexOf('=');                      // 変数定義
      if (separatorIndex != -1) {                                  //WiFi設定取得関係の処理
        String key = line.substring(0, separatorIndex);            //WiFi設定取得関係の処理
        String value = line.substring(separatorIndex + 1);         //WiFi設定取得関係の処理
        if (key.equals("SSID")) {                                  //WiFi設定取得関係の処理
          ssid = value;                                            //WiFi設定取得関係の処理
        } else if (key.equals("Password")) {                       //WiFi設定取得関係の処理
          password = value;                                        //WiFi設定取得関係の処理
        }                                                          //if文終了
      }                                                            //if文終了
    }                                                              //while文終了
    file.close();                                                  //ファイルを閉じる
    Serial.println("WiFi credentials read successfully.");         //シリアルで正しく設定されたことを通知
  } else {                                                         //if文「または」
    Serial.println("Error reading WiFi credentials from SPIFFS");  //設定でエラーが起きたことをシリアルで通知
  }                                                                //if文終了
}  //void readWiFiCredentials文終了


//UUIDを読み込みます
void readUUID() {
  File file = SPIFFS.open("/uuid.txt", "r");            //uuid.txtを開く
  if (!file) {                                          //ファイルチェック
    Serial.println("uuid.txt ファイルが開けません！");  //シリアルで通知(開発用)
    return;                                             //リターン
  }                                                     //if文終了
  uuid = file.readString();                             // ファイルの中身を読み取り、変数に代入
  file.close();                                         //ファイルを閉じる
  Serial.println("読み取ったUUID: " + uuid);            // 読み取ったUUIDをシリアルモニタに表示
}  //void readUUID終了





////////////////////////////////////////////////////////////////////////////////
// 初期設定関連
////////////////////////////////////////////////////////////////////////////////
//ブルートゥースで最初のコネクトを行います
void BTSetting() {
  if (ble.available()) {                              //初回ブルートゥースコネクトの処理
    String receivedData = ble.readStringUntil('\n');  //受け取ったデータの処理
    receivedData.trim();                              //受け取ったデータの処理
    if (receivedData.equals("connect")) {             //接続先から送信された文字列が「connect」な場合に処理を実行します
      receivedData.trim();                            //受け取ったデータの処理
      char* macAddress = getMAC();                    //WiFiのMacアドレスを取得します(char* getMACを実行します)
      ble.println(macAddress);                        //接続先の危機に自身のMacアドレスを送信します
      system_status = 2;                              //プログラムステータスを2に移行
      Serial.println("コネクト");                     //ブルートゥース接続をシリアルに送信(開発用)
    }                                                 //if文終了
  }                                                   //if文終了
}  //void BTSetting終了


// WiFiの設定情報を受信
void receiveWiFiSettings() {
  if (ble.available()) {
    Serial.println("WiFiをレシーブ");                                //ブルートゥース接続をシリアルに送信(開発用)
    String wifiSettings = ble.readStringUntil('\n');                 //受け取ったデータの処理
    wifiSettings.trim();                                             //受け取ったデータの処理
    int separatorIndex = wifiSettings.indexOf(',');                  //WiFiのパスワードとSSID(WiFiの名前)を受信
    if (separatorIndex != -1) {                                      //チェック
      String ssid = wifiSettings.substring(0, separatorIndex);       //パスワードとSSIDを分割
      String password = wifiSettings.substring(separatorIndex + 1);  //パスワードとSSIDを分割
      saveWiFiCredentials(ssid, password);                           //内部データーベースに設定内容を保存します(void saveWiFiCredentialsの部分を実行)
      system_status = 3;                                             //プログラムステータスを3に移行
    }                                                                //if文終了
  }                                                                  //if文終了
}  //void receiveWiFiSettings文終了


//内部データにWiFiのデータを書き込む
void saveWiFiCredentials(const String& ssid, const String& password) {  // SPIFFSにファイルを書き込み
  SPIFFS.remove("/wifi_credentials.txt");                               //wifi_credentials.txtを削除
  File file = SPIFFS.open("/wifi_credentials.txt", "w");                //wifi_credentials.txtというテキストに書き込みます
  if (file) {                                                           // ファイルチェック
    file.println("SSID=" + ssid);                                       // SPIFFSにファイルを書き込み
    file.println("Password=" + password);                               // SPIFFSにファイルを書き込み
    file.close();                                                       //ファイルを閉じる
    Serial.println("WiFi credentials saved successfully.");             //書き込めたことをシリアルに送信(開発用)
    ESP.restart();                                                      //再起動
  } else {                                                              //if文の「または~」
    Serial.println("Error saving WiFi credentials to SPIFFS");          //エラーが出たときにシリアルに送信(開発用)
  }                                                                     //if文終了
}  //void saveWiFiCredentials終了


//WiFiのMacアドレスを取得します
char* getMAC() {                                                                                                                 //Macアドレス取得
  uint8_t baseMac[6];                                                                                                            //WiFiのMacアドレスを取得します
  esp_read_mac(baseMac, ESP_MAC_WIFI_STA);                                                                                       //WiFiのMacアドレスを取得します
  static char baseMacChr[18] = { 0 };                                                                                            // 静的な変数として宣言
  sprintf(baseMacChr, "%02X:%02X:%02X:%02X:%02X:%02X", baseMac[0], baseMac[1], baseMac[2], baseMac[3], baseMac[4], baseMac[5]);  //WiFiのMacアドレスを取得します
  Serial.println(baseMacChr);                                                                                                    //WiFiのMacアドレスをシリアルに送信(開発用)
  return baseMacChr;                                                                                                             // baseMacChr のアドレスを返す
}  //char* getMAC終了


//UUIDを取得
void getUUID() {
  HTTPClient http;                                      //httpクライアントを定義
  http.begin("https://" + host + "/M5API/getuid.php");  //サーバーのアドレス
  int httpCode = http.GET();                            //サーバーに接続
  // HTTP応答の処理
  if (httpCode > 0) {                                      //内容があるか確認
    String payload = http.getString();                     //変数にGETの内容代入
    Serial.println("HTTP GET successful");                 //シリアルで通信成功を通知(開発用)
    File file = SPIFFS.open("/uuid.txt", "w");             // SPIFFSに書き込み
    if (file) {                                            //ファイルチェック
      file.print(payload);                                 //ファイルに書き込み
      file.close();                                        //ファイルを閉じる
      Serial.println("Data written to SPIFFS");            //シリアルで書き込み成功を通知(開発用)
      ESP.restart();                                       //再起動
    } else {                                               //if文「または」
      Serial.println("Failed to open file for writing");   //失敗をシリアルで通知
    }                                                      //if文終了
  } else {                                                 //if文「または」
    Serial.print("HTTP GET failed with error code: ");     //シリアルでエラーを通知(開発用)
    Serial.println("HTTP GET failed");                     //シリアルでエラーを通知(開発用)
    Serial.println(http.errorToString(httpCode).c_str());  //シリアルでエラーを通知(開発用)
  }                                                        //if文終了
  http.end();                                              //httpを終了する
}  //void getUUID終了





////////////////////////////////////////////////////////////////////////////////
// 取得した時刻をRTCに設定
////////////////////////////////////////////////////////////////////////////////
//NTPに接続
void getTimeFromNTP() {
  configTime(JST, 0, "ntp.nict.jp", "ntp.jst.mfeed.ad.jp");  //NTPサーバーの設定
  while (!getLocalTime(&timeinfo)) {                         //繰り返し
    delay(1000);                                             //一秒待つ
  }
}


//RTCに書き込み
void setNTP2RTC() {
  getTimeFromNTP();         //RTC関係
  getLocalTime(&timeinfo);  //RTC関係
  // RTCを読み込む
  M5.Rtc.GetTime(&RTC_TimeStruct);  //RTC関係
  M5.Rtc.GetDate(&RTC_DateStruct);  //RTC関係
  // 時刻を更新する
  RTC_DateStruct.Year = timeinfo.tm_year + 1900;  //RTC関係
  RTC_DateStruct.Month = timeinfo.tm_mon + 1;     //RTC関係
  RTC_DateStruct.Date = timeinfo.tm_mday;         //RTC関係
  RTC_DateStruct.WeekDay = timeinfo.tm_wday;      //RTC関係
  M5.Rtc.SetDate(&RTC_DateStruct);                //RTC関係
  RTC_TimeStruct.Hours = timeinfo.tm_hour;        //RTC関係
  RTC_TimeStruct.Minutes = timeinfo.tm_min;       //RTC関係
  RTC_TimeStruct.Seconds = timeinfo.tm_sec;       //RTC関係
  M5.Rtc.SetTime(&RTC_TimeStruct);                //RTC関係
  Serial.println("RTCを設定しました");            //シリアルでRTC設定完了を通知(開発用)
}





////////////////////////////////////////////////////////////////////////////////
// 表示系
////////////////////////////////////////////////////////////////////////////////
//ローカルタイムを表示
void printLocalTime() {
  Serial.println("RTCを表示しました");  // シリアルでRTC設定完了を通知(開発用)
  M5.Rtc.GetDate(&RTC_DateStruct);      // 現在日時を取得
  M5.Rtc.GetTime(&RTC_TimeStruct);      // 現在時刻を取得
  lcd.setCursor(0, 0);                  // 文字列の書き出し位置
  lcd.setTextSize(1, 1);
  lcd.setFont(&fonts::Font7);
  lcd.fillScreen(BLACK);
  lcd.setTextColor(CYAN, BLACK);
  lcd.printf("%04d-%02d-%02d(%s) ",
             RTC_DateStruct.Year, RTC_DateStruct.Month, RTC_DateStruct.Date,
             week[RTC_DateStruct.WeekDay]);  // 日付を表示
  lcd.printf("%02d:%02d:%02d",
             RTC_TimeStruct.Hours, RTC_TimeStruct.Minutes, RTC_TimeStruct.Seconds);  // 現在時刻を表示
}


void printSensor() {
  getSensor();  //センサーの値を取得
  lcd.fillScreen(TFT_BLACK);
  lcd.setFont(&fonts::Font0);
  lcd.setCursor(0, 0);  // カーソルを移動
  lcd.setTextSize(3, 3);
  lcd.printf("Temp: %2.1f  \r\nHumi: %2.0f%%  \r\nPressure:%2.0fPa  \r\nBattery: %2.2fV  \r\nBattery: %3.0f%% \r\nSoilSensor: %1.2fV \r\n", tmp, hum, pressure, BTVoltage, BTLevel, realVoltage1);  // テキストを描画
}


void printSystemData() {
  lcd.setFont(&fonts::lgfxJapanMincho_24);
  lcd.fillScreen(TFT_BLACK);
  lcd.setCursor(0, 0);  // カーソルを移動
  lcd.setTextSize(1, 1);
  lcd.println("Ver:" + Version);                          //バージョンを表示
  lcd.println("Model:" + model);                          //バージョンを表示
  lcd.println("UUID:" + uuid);                            //UUIDを表示
  lcd.println("LCDbrightness:" + String(LCDbrightness));  //バージョンを表示
}


////////////////////////////////////////////////////////////////////////////////
// オンラインアップデート機能
////////////////////////////////////////////////////////////////////////////////
void getCert() {
  SPIFFS.remove("/root_ca.pem");                                     //root_ca.pemを削除
  HTTPClient http;                                                   //httpクライアントを定義
  http.begin("https://" + host + "/M5API/getCert.php?url=" + host);  //サーバーのアドレス
  int httpCode = http.GET();                                         //サーバーに接続
  if (httpCode > 0) {                                                //内容があるか確認
    String payload = http.getString();                               //変数にGETの内容代入
    Serial.println("HTTP GET successful");                           //シリアルで通信成功を通知(開発用)
    File file = SPIFFS.open("/root_ca.pem", "w");                    // SPIFFSに書き込み
    if (file) {                                                      //ファイルチェック
      file.print(payload);                                           //ファイルに書き込み
      file.close();                                                  //ファイルを閉じる
      Serial.println("Data written to SPIFFS");                      //シリアルで書き込み成功を通知(開発用)
      Serial.println(payload);                                       //シリアルで書き込み成功を通知(開発用)
    } else {                                                         //if文「または」
      Serial.println("Failed to open file for writing");             //失敗をシリアルで通知
    }                                                                //if文終了
  } else {                                                           //if文「または」
    Serial.print("HTTP GET failed with error code: ");               //シリアルでエラーを通知(開発用)
    Serial.println("HTTP GET failed");                               //シリアルでエラーを通知(開発用)
    Serial.println(http.errorToString(httpCode).c_str());            //シリアルでエラーを通知(開発用)
  }                                                                  //if文終了
  http.end();                                                        //httpを終了する
}

void ota() {

  bool updatedNeeded = esp32FOTA.execHTTPcheck();

  if (updatedNeeded) {
    esp32FOTA.execOTA();
  }

  delay(2000);
}
