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


#define LGFX_AUTODETECT  // 自動認識
#define LGFX_USE_V1      // LovyanGFX v1.0.0を有効に
////////////////////////////////////////////////////////////////////////////////
//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;         //リセットボタンの変数

String ssid;
int ip;

WebServer server(80);
IPAddress apIP(192, 168, 1, 100);
DNSServer dnsServer;

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


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

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

String model = "MierunCore2";  //モデルを指定
String WiFiName = model;
int Version = 2;                                                    //バージョンを定義
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のインスタンスを作成（クラスLGFXを使ってlcdコマンドでいろいろできるようにする）
static LGFX_Sprite canvas(&lcd);  // LGFX_Spriteのcanvasインスタンスを作成


//ユーザー設定
int online = 1;
String DisplayImgUrl;
int LCDbrightness = 100;
int LED = 1;
int BTprotection = 1;
int UpdateSpeed = 1500;  //ミリ秒

int SystemBackColor = BLACK;
int 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.pushImage(0, 0, imgWidth, imgHeight, logo);  //起動ロゴを表示
  M5.Lcd.setTextSize(1);                              // テキストサイズ倍率(整数で指定)
  lcd.println("V:" + String(Version));                //バージョンを表示
  StandAlone();
  esp32FOTA.printConfig();
  sensor_detect();
  Serial.println("起動しました");  //起動時にシリアルに送信(開発用)
  canvas.setPsram(true);
  canvas.createSprite(lcd.width(), lcd.height());
  int FalseWiFi = 0;
  FalseWiFi = readFromEEPROM(1);
  if (online == 1) {
    lcd.println("OnlineMode");
    Serial.println("WiFiコンフィグ探知");                  //内部メモリを確認しシリアルに送信(開発用)
    esp32FOTA.setManifestURL(manifest_url);                //オンラインアップデート機能
    if (SPIFFS.exists("/wifi_credentials.txt")) {          //WiFIの設定が済まされているかどうか
      Serial.println("wifi_credentials.txtが存在します");  //内部メモリを確認しシリアルに送信(開発用)
      system_status = 3;                                   //WiFiの設定が存在する場合、settingを0に設定
      if (FalseWiFi == 0) {
        connectToWiFi();  //WiFIに接続
      }
    }                                          //if文終了
    if (SPIFFS.exists("/uuid.txt")) {          //WiFIの設定が済まされているかどうか
      Serial.println("uuid.txtが存在します");  //内部メモリを確認しシリアルに送信(開発用)
      if (WiFi.status() != WL_CONNECTED) {     //WiFi接続を確認
        Serial.println("WiFi接続失敗");        //WiFiを確認しシリアルに送信(開発用)
        WiFiError();
      } else {                                                     //if文「または」
        configTime(JST, 0, "ntp.nict.jp", "ntp.jst.mfeed.ad.jp");  // NTPサーバとローカルのタイムゾーンを設定
        setNTP2RTC();                                              //RTCをセットアップ
        readUUID();                                                //UUIDを内部データベースから読み込み
        system_status = 0;
        xTaskCreatePinnedToCore(getSensor, "getSensor", 8192, NULL, 1, &thp[0], 1);
        xTaskCreatePinnedToCore(WebServer, "WebServer", 8192, NULL, 2, &thp[1], 0);
        xTaskCreatePinnedToCore(DataUpload, "DataUpload", 8192, NULL, 3, &thp[2], 1);  //サブのCPUコアで別のタスクを処理させるためのやつ
      }                                                                                //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(WiFiName.c_str());                                                     //ブルートゥースのデバイス名を定義しています
      Serial.println("ブルートゥースを開始しました");                                  //ブルートゥース接続開始をシリアルに送信(開発用)
      APStart();
    }  //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();
    APStart();
    xTaskCreatePinnedToCore(getSensor, "getSensor", 8192, NULL, 1, &thp[1], 0);
    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;
    sht4x.begin(Wire, SHT40_I2C_ADDR_44);
  }
  dht.begin(&Wire, DHT12_I2C_ADDR, 32, 33, 400000U);
  bmp.begin(&Wire, BMP280_I2C_ADDR, 32, 33, 400000U);
  bmp.setSampling(BMP280::MODE_NORMAL,     /* Operating Mode. */
                  BMP280::SAMPLING_X2,     /* Temp. oversampling */
                  BMP280::SAMPLING_X16,    /* Pressure oversampling */
                  BMP280::FILTER_X16,      /* Filtering. */
                  BMP280::STANDBY_MS_500); /* Standby time. */


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

void WiFiError() {
  int FalseWiFi = 0;
  FalseWiFi = readFromEEPROM(1);
  if (FalseWiFi == 1) {
    saveToEEPROM(0, 1);
    system_status = 4;  //システムステータスを4に移行
    APStart();
  } else {
    Serial.println("再起動");  //WiFiを確認しシリアルに送信(開発用)
    saveToEEPROM(1, 1);
    ESP.restart();
  }
}

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

void APStart() {
  Serial.println("APスタート");  //WiFiを確認しシリアルに送信(開発用)
  WiFi.mode(WIFI_AP);
  WiFi.softAP(WiFiName.c_str());
  delay(200);  // Important! This delay is necessary
  WiFi.softAPConfig(apIP, apIP, IPAddress(255, 255, 255, 0));
  xTaskCreatePinnedToCore(getSensor, "getSensor", 8192, NULL, 1, &thp[1], 0);
  xTaskCreatePinnedToCore(WebServer, "WebServer", 8192, NULL, 2, &thp[1], 0);
}

void saveToEEPROM(int value, int address) {
  EEPROM.begin(512);           // EEPROMを初期化
  EEPROM.put(address, value);  // 変数を指定されたアドレスに保存
  EEPROM.commit();             // 変更を書き込む
  EEPROM.end();                // EEPROMを閉じる
}

int readFromEEPROM(int address) {
  int value;
  EEPROM.begin(512);           // EEPROMを初期化
  EEPROM.get(address, value);  // 指定されたアドレスから変数を読み込む
  EEPROM.end();                // EEPROMを閉じる
  return value;
}


////////////////////////////////////////////////////////////////////////////////
// 画像表示
////////////////////////////////////////////////////////////////////////////////



////////////////////////////////////////////////////////////////////////////////
//メインのループです
////////////////////////////////////////////////////////////////////////////////
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を確認しシリアルに送信(開発用)
      WiFiError();
    }
  }                                                          //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) {    //2ページ目の処理
        TaskMgr();            //センサーの値を取得
        page_transition = 1;  // ページ遷移モードを有効に
        delay(500);           // 待つ
      }                       //if文終了
    }                         //if文終了
  }                           //if文終了
  ResetButton();              //リセットボタンの処理
}  //void loop終了

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

void SaveSD(void* args) {
  while (1) {
    a++;
    delay(10000);
    if (system_status == 30) {
      M5.Rtc.GetDate(&RTC_DateStruct);  // 現在日時を取得
      M5.Rtc.GetTime(&RTC_TimeStruct);  // 現在時刻を取得
      int Date = RTC_DateStruct.Year + RTC_DateStruct.Month + RTC_DateStruct.Date;
      int Time = RTC_TimeStruct.Hours + RTC_TimeStruct.Minutes + RTC_TimeStruct.Seconds;  // 現在時刻を表示
      StaticJsonDocument<bufferSize> doc;
      doc["BordModel"] = model;
      doc["SystemVersion"] = Version;
      doc["tmp"] = tmp;
      doc["hum"] = hum;
      doc["pressure"] = pressure;
      doc["SoilSensor"] = realVoltage1;
      doc["BTLevel"] = BTLevel;
      doc["BTVoltage"] = BTVoltage;
      doc["Date"] = Date;
      doc["Time"] = Time;

      // 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");
      }
    }
  }
}


void handleRoot() {
  String html;
  html = R"(
    <!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>みえるん</title>
    <style>
        body {
            font-family: Arial, sans-serif;
            margin: 0;
            padding: 0;
        }
        header {
            background-color: #333;
            color: #fff;
            padding: 20px;
            text-align: center;
        }
        footer {
            background-color: #333;
            color: #fff;
            padding: 10px;
            text-align: center;
            position: fixed;
            bottom: 0;
            width: 100%;
        }
        #sensorData {
            background-color: #fff;
            border-radius: 5px;
            padding: 20px;
            box-shadow: 0 2px 5px rgba(0,0,0,0.1);
            margin: 20px;
        }
        #systemInfo {
            background-color: #fff;
            border-radius: 5px;
            padding: 20px;
            box-shadow: 0 2px 5px rgba(0,0,0,0.1);
            margin: 20px;
        }
    </style>
</head>
<body>
    <header>
        <h1>みえるん</h1>
    </header>

    <div id="sensorData">
        <h2>センサーデータ</h2>
        <p id="sensorValues">Loading...</p>
    </div>
    <div id="systemInfo">
        <h2>システム情報</h2>
        <p id="systemInfoValue">Loading...</p>
    </div>

    <footer>
        <p>&copy; 2024 EcoHeros</p>
    </footer>

    <script>
        // APIからデータを取得して表示する関数
        function fetchSensorData() {
            fetch('./api/sensor')
                .then(response => response.json())
                .then(data => {
                    // 取得したデータを表示する
                    const { BordModel, SystemVersion, uuid, tmp, hum, pressure, SoilSensor, BTLevel, BTVoltage, Date, Time} = data;
                    const sensorValuesElement = document.getElementById('sensorValues');
                    sensorValuesElement.innerHTML = `温度: ${tmp}°C<br>湿度: ${hum}%<br>気圧: ${pressure} hPa<br>土壌水分量: ${SoilSensor}V`;
                    const systemInfoValueElement = document.getElementById('systemInfoValue');
                    systemInfoValueElement.innerHTML = `ボードモデル: ${BordModel} <br>ファームウェアバージョン: ${SystemVersion} <br>UUID: ${uuid} <br>バッテリー残容量: ${BTLevel}% <br>バッテリー電圧: ${BTVoltage}V <br>日付: ${Date} <br>時間: ${Time}`;
                })
                .catch(error => {
                    // エラーが発生した場合にエラーメッセージを表示する
                    const sensorValuesElement = document.getElementById('sensorValues');
                    sensorValuesElement.innerHTML = 'Failed to fetch sensor data.';
                    console.error('Error fetching sensor data:', error);
                });
        }

        // 初期読み込み時にデータを取得して表示する
        fetchSensorData();

        // 10秒ごとにデータを更新する
        setInterval(fetchSensorData, 10000);
    </script>
</body>
</html>
)";
  server.send(200, "text/html", html);
}


void WiFiSetting() {
  String html;
  html = R"(
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>WiFi設定</title>
    <style>
        body {
            font-family: Arial, sans-serif;
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }
        h1 {
            text-align: center;
            margin-top: 20px;
        }
        .description {
            text-align: center;
            margin-bottom: 20px;
        }
        form {
            width: 300px;
            margin: 0 auto;
        }
        label {
            display: block;
            margin-bottom: 5px;
        }
        input[type="text"],
        input[type="password"] {
            width: 100%;
            padding: 8px;
            margin-bottom: 10px;
            border: 1px solid #ccc;
            border-radius: 4px;
            box-sizing: border-box;
        }
        button {
            width: 100%;
            padding: 10px;
            background-color: #4CAF50;
            color: white;
            border: none;
            border-radius: 4px;
            cursor: pointer;
        }
        button:hover {
            background-color: #45a049;
        }
        #loadingIndicator {
            text-align: center;
            display: none;
            margin-top: 20px;
        }
    </style>
</head>
<body>
<h1>みえるん WiFi設定</h1>
<div class="description">
    <p>デバイスをネットワークに接続するために、ご自宅のWiFiを設定してください。</p>
    <p>設定した情報は内部データベースにのみ保存されます。</p>
</div>
<form id="wifiForm">
    <div>
        <label for="ssid">SSID:</label>
        <input type="text" id="ssid" name="ssid" required>
    </div>
    <div>
        <label for="password">パスワード:</label>
        <input type="password" id="password" name="password" required>
    </div>
    <div>
        <button type="submit">設定を保存</button>
    </div>
</form>

<div id="loadingIndicator">
    <p>設定を保存中...</p>
    <p>「みえるん」は再起動されます</p>
</div>

<div id="macAddress" style="text-align: center; margin-top: 20px;"></div>

<script>
    document.getElementById('ssid').addEventListener('blur', function(event) {
        var ssidInput = event.target;
        var ssidValue = ssidInput.value;

        // 特定の文字が含まれる場合に警告文を表示
        if (ssidValue.match(/-a|-A|a-|A-|_a|_A|a_|A_|-A-|_A_|_a_|-a-|5G|5g/)) {
            var warningMessage = "注意:WiFiが5GHzを利用している可能性があります。このデバイスは2.4GHzのみ対応しています。";
            var warningElement = document.createElement('p');
            warningElement.textContent = warningMessage;
            warningElement.className = 'warning-message'; // 警告メッセージにクラス名を追加
            warningElement.style.color = 'orange'; // オレンジ色に設定

            // 既に警告文が表示されている場合は削除
            var existingWarning = ssidInput.parentNode.querySelector('.warning-message');
            if (existingWarning) {
                existingWarning.remove();
            }

            ssidInput.parentNode.insertBefore(warningElement, ssidInput.nextSibling);
        } else {
            // 特定の文字が含まれない場合は警告文を削除
            var existingWarning = ssidInput.parentNode.querySelector('.warning-message');
            if (existingWarning) {
                existingWarning.remove();
            }
        }
    });

    document.getElementById('wifiForm').addEventListener('submit', function(event) {
        event.preventDefault();
        submitWiFiSettings();
    });

    function submitWiFiSettings() {
        var ssid = document.getElementById('ssid').value;
        var password = document.getElementById('password').value;

        var data = {
            ssid: ssid,
            password: password
        };

        var jsonData = JSON.stringify(data);

        var xhr = new XMLHttpRequest();
        xhr.open('POST', './api/WiFiSetting', true);
        xhr.setRequestHeader('Content-type', 'application/json');

        document.getElementById('loadingIndicator').style.display = 'block';

        xhr.onload = function () {
            if (xhr.status == 200) {
                fetchMacAddress();
            } else {
                console.error('Error:', xhr.statusText);
            }
            document.getElementById('loadingIndicator').style.display = 'none';
        };

        xhr.send(jsonData);
    }

    function fetchMacAddress() {
        fetch('./api/sensor')
            .then(response => response.json())
            .then(data => {
                var macAddress = data.MAC;
                document.getElementById('macAddress').textContent = 'MACアドレス: ' + macAddress;
            })
            .catch(error => console.error('Error:', error));
    }

    window.onload = function () {
        fetchMacAddress();
    };
</script>


</body>
</html>
)";
  server.send(200, "text/html", html);
}

void WiFiSettingAPI() {
  // POSTされたデータを取得
  String body = server.arg("plain");

  // JSONデータを解析
  DynamicJsonDocument doc(1024);
  DeserializationError error = deserializeJson(doc, body);

  // エラーチェック
  if (error) {
    server.send(400, "application/json", "Failed to parse JSON");
    return;
  }

  // JSONからssidとpasswordを取得
  String ssid = doc["ssid"];
  String password = doc["password"];
  saveWiFiCredentials(ssid, password);

  // 成功応答を返す
  server.send(200, "application/json", "{\"message\":\"WiFi settings updated\"}");
}


void sensorapi() {
  M5.Rtc.GetDate(&RTC_DateStruct);  // 現在日時を取得
  M5.Rtc.GetTime(&RTC_TimeStruct);  // 現在時刻を取得
  String Date = String(RTC_DateStruct.Year) + "/" + String(RTC_DateStruct.Month) + "/" + String(RTC_DateStruct.Date);
  String Time = String(RTC_TimeStruct.Hours) + "/" + String(RTC_TimeStruct.Minutes) + "/" + String(RTC_TimeStruct.Seconds);  // 現在時刻を表示
  StaticJsonDocument<200> doc;
  doc["BordModel"] = model;
  doc["SystemVersion"] = Version;
  doc["uuid"] = uuid;
  doc["MAC"] = getMAC();
  doc["tmp"] = tmp;
  doc["hum"] = hum;
  doc["pressure"] = pressure;
  doc["SoilSensor"] = realVoltage1;
  doc["BTLevel"] = BTLevel;
  doc["BTVoltage"] = BTVoltage;
  doc["Date"] = Date;
  doc["Time"] = Time;
  String jsonPayload;
  serializeJson(doc, jsonPayload);
  server.send(200, "application/json", jsonPayload);
}

void WebServer(void* args) {
  if (system_status == 0) {
    Serial.print("mDNS server instancing");
    if (!MDNS.begin(WiFiName)) {
      Serial.println("Error setting up MDNS responder!");
      while (1) {
        delay(100);
      }
    }
    MDNS.addService("http", "tcp", 80);
  }
  if (system_status == 1 || system_status == 2 || system_status == 3 || system_status == 4) {
    dnsServer.setErrorReplyCode(DNSReplyCode::NoError);
    dnsServer.start(53, "*", apIP);
    server.on("/", WiFiSetting);
    server.onNotFound(WiFiSetting);
  } else {
    server.on("/", handleRoot);
    server.on("/WiFiSetting", WiFiSetting);
    server.onNotFound(handleRoot);
  }
  server.sendHeader("Access-Control-Allow-Origin", "*");
  server.sendHeader("Access-Control-Max-Age", "10000");
  server.sendHeader("Content-Length", "0");
  server.on("/api/sensor", sensorapi);
  server.on("/api/WiFiSetting", HTTP_POST, WiFiSettingAPI);
  server.begin();
  Serial.println("Webサーバー開始");
  while (1) {
    if (system_status == 1 || system_status == 2 || system_status == 3 || system_status == 4) {
      dnsServer.processNextRequest();
    }
    server.handleClient();
    a++;
    delay(1);
  }
}


////////////////////////////////////////////////////////////////////////////////
//センサー関係
////////////////////////////////////////////////////////////////////////////////
void getSensor(void* args) {  //センサーの値を取得する
  while (1) {
    bmp.update();
    if (env_sensor_ver == 1) {
      dht.update();
      tmp = dht.cTemp;     //温度を取得
      hum = dht.humidity;  //湿度を取得
    }
    if (env_sensor_ver == 4) {
      sht4x.measureHighPrecision(tmp, hum);
    }
    pressure = bmp.pressure / 100;  //気圧を取得

    BTVoltage = power.GetBatVoltage();  //バッテリー電圧を取得
    BTLevel = power.GetBatteryLevel();  //バッテリー残量を取得
    value1 = analogRead(CH1_PIN);
    value2 = analogRead(CH2_PIN);
    voltage1 = value1 * MAX_VOLTAGE / ANALOG_MAX;
    realVoltage1 = alpha * voltage1;
    a++;
    delay(1000);
  }
}  //void getSensor終了

////////////////////////////////////////////////////////////////////////////////
//サーバー関係
////////////////////////////////////////////////////////////////////////////////
void uploadData() {
  if (WiFi.status() == WL_CONNECTED) {
    HTTPClient http;
    // サーバーにPOSTするJSONデータを作成
    StaticJsonDocument<200> doc;
    doc["BordModel"] = model;
    doc["SystemVersion"] = Version;
    doc["uuid"] = uuid;
    doc["tmp"] = tmp;
    doc["hum"] = hum;
    doc["pressure"] = pressure;
    doc["SoilSensor"] = realVoltage1;
    doc["BTLevel"] = BTLevel;
    doc["BTVoltage"] = BTVoltage;
    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?uuid=" + uuid);

  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));

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

    // 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<int>();
    Serial.println("SystemBackColor: " + String(SystemBackColor));

    SystemTextColor = doc["SystemTextColor"].as<int>();
    Serial.println("SystemTextColor: " + String(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 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 < 5) {                       //値が一定をを上回らないように
      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);                      //黒で塗りつぶす
      lcd.println("Resetting");                          //文字列を表示
      lcd.println("It may take some time");              //文字列を表示
      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() {
  ssid;             //ローカル変数を定義
  String password;  //ローカル変数を定義
  // WiFi認証情報を読み取る
  WiFi.mode(WIFI_STA);                                              //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をスタート
  WiFi.setHostname(WiFiName.c_str());
  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をつうち(開発用)
    lcd.println(WiFi.localIP());
    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) {                                      //チェック
      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);      // 現在時刻を取得

  canvas.setCursor(0, 0);  // 文字列の書き出し位置
  canvas.setTextSize(1, 1);
  canvas.setFont(&fonts::Font7);
  canvas.fillScreen(ClockBackColor.toInt());
  canvas.setTextColor(CYAN, BLACK);
  canvas.printf("%04d-%02d-%02d(%s) ",
                RTC_DateStruct.Year, RTC_DateStruct.Month, RTC_DateStruct.Date,
                week[RTC_DateStruct.WeekDay]);  // 日付を表示
  canvas.printf("%02d:%02d:%02d",
                RTC_TimeStruct.Hours, RTC_TimeStruct.Minutes, RTC_TimeStruct.Seconds);  // 現在時刻を表示
  canvas.pushSprite(0, 0);
}


void printSensor() {
  canvas.fillScreen(TFT_BLACK);
  canvas.setTextColor(CYAN, BLACK);
  canvas.setFont(&fonts::Font0);
  canvas.setCursor(0, 0);  // カーソルを移動
  canvas.setTextSize(3, 3);
  canvas.printf("Temp: %2.1f  \r\nHumi: %2.0f%%  \r\nPressure:%2.0fhPa  \r\nBattery: %2.2fV  \r\nBattery: %3.0f%% \r\nSoilSensor: %1.2fV \r\n", tmp, hum, pressure, BTVoltage, BTLevel, realVoltage1);  // テキストを描画
  canvas.pushSprite(0, 0);
}

void TaskMgr() {
  uint32_t freeRAM = ESP.getFreeHeap();
  uint32_t totalRAM = ESP.getHeapSize();
  float ramUsage = ((float)(totalRAM - freeRAM) / totalRAM) * 100;

  // CPU使用率の計算
  uint32_t startMillis = millis();
  delay(100);  // CPU使用率を測定するためのディレイ
  uint32_t endMillis = millis();
  float cpuUsage = ((float)(endMillis - startMillis) / 100) * 100;
  uint32_t totalMemory = ESP.getFreeHeap();

  // Output total memory size in MB
  float totalMemoryMB = (float)totalMemory / 1024 / 1024;

  // Get free memory size
  uint32_t freeMemory = ESP.getFreeHeap();

  // Output free memory size in MB
  float freeMemoryMB = (float)freeMemory / 1024 / 1024;

  // 結果の表示
  canvas.fillScreen(TFT_BLACK);
  canvas.setTextColor(CYAN, BLACK);
  canvas.setCursor(0, 0);
  canvas.setTextColor(TFT_WHITE);
  canvas.setTextSize(2);
  canvas.printf("RAM Usage: %.2f%%\n", ramUsage);
  canvas.printf("RAM FreeMemory: %.2f%MB\n", freeMemoryMB);
  canvas.printf("CPU Usage: %.2f%%\n", cpuUsage);
  canvas.pushSprite(0, 0);
}


void printSystemData() {
  lcd.setFont(&fonts::lgfxJapanMincho_24);
  lcd.fillScreen(TFT_BLACK);
  lcd.setCursor(0, 0);  // カーソルを移動
  lcd.setTextSize(1, 1);
  lcd.println("Model:" + model);                           //バージョンを表示
  lcd.println("SystemVer:" + String(Version));             //バージョンを表示
  lcd.println("UUID:" + uuid);                             //UUIDを表示
  lcd.println("EnvSensor-Ver:" + String(env_sensor_ver));  //UUIDを表示
  lcd.println("LCDbrightness:" + String(LCDbrightness));
  lcd.println("UpdateSpeed:" + String(UpdateSpeed));  //バージョンを表示
  lcd.printf("IP:");
  lcd.println(WiFi.localIP());
  lcd.println("WiFi:" + ssid);
  lcd.printf("MAC:");
  char* macAddress = getMAC();  //WiFiのMacアドレスを取得します(char* getMACを実行します)
  lcd.println(macAddress);      //接続先の危機に自身のMacアドレスを送信します
}

////////////////////////////////////////////////////////////////////////////////
// オンラインアップデート機能
////////////////////////////////////////////////////////////////////////////////
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);
}
