ESP-WROOM-32でSDカードの読み書きをする

最終修正日:2022/2/3

2022/2/2:カードリーダモジュールを変更してみました。改善!

2022/1/17:ファイルシステムの問題点があるみたいですが判らない

2021/6/18:SDカードロギング方法を修正しました

SPIFFSの項でESP32の4MBあるフラッシュ領域の一部を読書領域として使う事確認済みなのですが、書込頻度を考えるとSDカードに対しての読書を考えておく必要があると考えました。
Espressif System社オリジナルのモジュールにもMicroSDスロットを搭載したものがありますが、日本国内での入手性はよいとはいえません。ただ、サードパーティ製は容易に入手できるようです。
ここでは、標準的な開発モジュールにSDカードモジュールを接続することを検討しました。これであればTFでなくSDでも使えるものを用意できます。
標準的な開発ボードはUSBの5Vをレギュレータ( AMS1117 3.3V)で3.3Vに落として使うのですが、容量が800mA程度あるようです。ESP-WROOM-32の安定動作には500mAぐらい必要だという先人の書き込みがあり、SDカードモジュールを3.3Vから供給して駆動は十分とはいえないかもしれません。
SDカードモジュールも5Vから落として使うようにしておくほうが外部5V供給を想定すると意味がありそうです。
※USB2.0のポートだと3.3V800mAを要求したら電圧ドロップするかと思います。その意味でUSB3.0のポートを使用する意味も出てきます。実行時は2.0A以上供給できるアダプタ接続が無難ですね。

ESP32はモジュール内部でフラッシュメモリとSPI接続しているはずなので、SPI接続のSDカードはバスを共有することになります。


サンプルスケッチ】

/*
 * 2021/01/21 T.Wanibe
 * ESP32でSD(TF)カードの動作確認をします。オリジナルのコードはSD_MMC.hを使用する事になっています。
 * コンパイルは通るのですが、採用しているTFカードがMMC規格では認識出来ません。エラーが出ます。
 * SD_MMC.hをSD.hに変更するだけで実行可能になりました。
 * CATALEX MicroSD Card ModuleとESP32DevCを接続します。
 * 1    CS-----------IO5
 * 2    SCK----------IO18
 * 3    MOSI---------IO17
 * 4    MISO---------IO19
 * 5    VCC----------5V
 * 6    GND----------GND
 * ※CATALEXはモジュール内にレギュレータを搭載しており、VCCには5Vを供給する必要がある。
 *  SDカード自体は3.3VI/Oだがモジュール内で対応してくれる
 * このスケッチは32GBのカードをちゃんと認識しました。
 * 最大1310720バイトのフラッシュメモリのうち、スケッチが667238バイト(50%)を使っています。
 * 最大327680バイトのRAMのうち、グローバル変数が38976バイト(11%)を使っていて、ローカル変数で288704バイト使うことができます。
 */
#include "arduino_secrets.h"                                            //機密データを「Secret」タブ/arduino_secrets.hに入力してください
#include "FS.h"
#include "SD.h"
#include "SPI.h"
#include <time.h> 
#include <WiFi.h>
//
char    ssid[16]        = SECRET_SSID;                                  //ネットワークSSID(名前)
char    pass[16]        = SECRET_PASS;   
long    timezone        = 9;                                            //
byte    daysavetime     = 1;
//--------------------------
void listDir(fs::FS &fs, const char * dirname, uint8_t levels){
        Serial.printf("Listing directory: %s\n", dirname);
        //
        File root = fs.open(dirname);
        if(!root){
                Serial.println(F("Failed to open directory"));
                return;
        }
        if(!root.isDirectory()){
                Serial.println(F("Not a directory"));
                return;
        }
        //
        File file = root.openNextFile();
        while(file){
                if(file.isDirectory()){
                        Serial.print(F("  DIR : "));
                        Serial.print (file.name());
                        time_t t= file.getLastWrite();
                        struct tm * tmstruct = localtime(&t);
                        Serial.printf("  LAST WRITE: %d-%02d-%02d %02d:%02d:%02d\n",(tmstruct->tm_year)+1900,( tmstruct->tm_mon)+1, tmstruct->tm_mday,tmstruct->tm_hour , tmstruct->tm_min, tmstruct->tm_sec);
                        if(levels){
                                listDir(fs, file.name(), levels -1);
                        }
                } else {
                        Serial.print(F("  FILE: "));
                        Serial.print(file.name());
                        Serial.print(F("  SIZE: "));
                        Serial.print(file.size());
                        time_t t= file.getLastWrite();
                        struct tm * tmstruct = localtime(&t);
                        Serial.printf("  LAST WRITE: %d-%02d-%02d %02d:%02d:%02d\n",(tmstruct->tm_year)+1900,( tmstruct->tm_mon)+1, tmstruct->tm_mday,tmstruct->tm_hour , tmstruct->tm_min, tmstruct->tm_sec);
                }
                file = root.openNextFile();
        }
}
//--------------------------
void createDir(fs::FS &fs, const char * path){
        Serial.printf("Creating Dir: %s\n", path);
        if(fs.mkdir(path)){
                Serial.println(F("Dir created"));
        } else {
                Serial.println(F("mkdir failed"));
        }
}
//--------------------------
void removeDir(fs::FS &fs, const char * path){
        Serial.printf("Removing Dir: %s\n", path);
        if(fs.rmdir(path)){
                Serial.println(F("Dir removed"));
        } else {
                Serial.println(F("rmdir failed"));
        }
}
//--------------------------
void readFile(fs::FS &fs, const char * path){
        Serial.printf("Reading file: %s\n", path);
        //
        File file = fs.open(path);
        if(!file){
                Serial.println(F("Failed to open file for reading"));
                return;
        }
        //
        Serial.print(F("Read from file: "));
        while(file.available()){
                Serial.write(file.read());
        }
        file.close();
}
//--------------------------
void writeFile(fs::FS &fs, const char * path, const char * message){
        Serial.printf("Writing file: %s\n", path);
        //
        File file = fs.open(path, FILE_WRITE);
        if(!file){
                Serial.println(F("Failed to open file for writing"));
                return;
        }
        if(file.print(message)){
                Serial.println(F("File written"));
        } else {
                Serial.println(F("Write failed"));
        }
        file.close();
}
//--------------------------
void appendFile(fs::FS &fs, const char * path, const char * message){
        Serial.printf("Appending to file: %s\n", path);
        //
        File file = fs.open(path, FILE_APPEND);
        if(!file){
                Serial.println(F("Failed to open file for appending"));
                return;
        }
        if(file.print(message)){
                Serial.println(F("Message appended"));
        } else {
                Serial.println(F("Append failed"));
        }
        file.close();
}
//--------------------------
void renameFile(fs::FS &fs, const char * path1, const char * path2){
        Serial.printf("Renaming file %s to %s\n", path1, path2);
        if (fs.rename(path1, path2)) {
                Serial.println(F("File renamed"));
        } else {
                Serial.println(F("Rename failed"));
        }
}
//--------------------------
void deleteFile(fs::FS &fs, const char * path){
        Serial.printf("Deleting file: %s\n", path);
        if(fs.remove(path)){
                Serial.println(F("File deleted"));
        } else {
                Serial.println(F("Delete failed"));
        }
}
//--------------------------
void setup(){
        Serial.begin(115200);
        // We start by connecting to a WiFi network
        Serial.print(F("\n\nConnecting to "));Serial.println(ssid);
        WiFi.begin(ssid, pass);
        while (WiFi.status() != WL_CONNECTED) {
                delay(500);
                Serial.print(".");
        }
        Serial.println(F("WiFi connected"));
        Serial.print(F("IP address: "));Serial.println(WiFi.localIP());
        Serial.println(F("Contacting Time Server"));
	configTime(3600*timezone, daysavetime*3600, "time.nist.gov", "0.pool.ntp.org", "1.pool.ntp.org");
	struct tm tmstruct ;
        delay(2000);
        tmstruct.tm_year = 0;
        getLocalTime(&tmstruct, 5000);
	Serial.printf("\nNow is : %d-%02d-%02d %02d:%02d:%02d\n",(tmstruct.tm_year)+1900,( tmstruct.tm_mon)+1, tmstruct.tm_mday,tmstruct.tm_hour , tmstruct.tm_min, tmstruct.tm_sec);
        //
        if(!SD.begin()){
                Serial.println(F("Card Mount Failed"));
                return;
        }
        uint8_t cardType = SD.cardType();
        if(cardType == CARD_NONE){
                Serial.println(F("No SD card attached"));
                return;
        }
        //
        Serial.print(F("SD Card Type: "));
        if(cardType == CARD_MMC){
                Serial.println(F("MMC"));
        } else if(cardType == CARD_SD){
                Serial.println(F("SDSC"));
        } else if(cardType == CARD_SDHC){
                Serial.println(F("SDHC"));
        } else {
                Serial.println(F("UNKNOWN"));
        }
        //
        uint64_t cardSize = SD.cardSize() / (1024 * 1024);
        Serial.printf("SD Card Size: %lluMB\n", cardSize);
        //
        listDir(SD, "/", 0);
        removeDir(SD, "/mydir");
        createDir(SD, "/mydir");
        deleteFile(SD, "/hello.txt");
        writeFile(SD, "/hello.txt", "Hello ");
        appendFile(SD, "/hello.txt", "World!\n");
        listDir(SD, "/", 0);
}
//--------------------------
void loop(){
        ;
}

SDカードにデータ記述するように検討したところ、WDTによるリスタートの発生を確認出来る事を見いだしました。
リスタート後、SDカードに新規ファイルを起動時のタイムスタンプをファイル名として起動します。
計測データを1秒毎に記録してゆきます。
もしWDTが働いたら、リスタート後に新たなファイルを作成して、計測値を新たなファイルに記録してゆくので、SDカードの中身を確認したタイミングでWDTの発生確認が出来ます。

SDカードは32GBのものが利用できますので、空き容量を意識するようなことは無いと思いますし、1つのログデータは2GBまでは追記できるはずです。※やったこと無いけど!

/*
 * 2021/1/21 T.Wanibe
 * 11.0更新:
 *      TFカードに計測データを記録するように修正
 *      SDカードモジュールはCATALEX MicroSD Card ModuleでESP32DevCと接続します。
 * 1    CS-----------IO5
 * 2    SCK----------IO18
 * 3    MOSI---------IO17
 * 4    MISO---------IO19
 * 5    VCC----------5V
 * 6    GND----------GND
 *      32GBのSDHCカードが使えることを確認しました。
 * 10.6更新:
 *      WDT機能を追加
 *      計測データの移動平均化
 *      isnan()を使うように修正しました。
 * CCS811の計測値が安定しないためSparkFunのドライバに変更してみる
 * 照度センサ(TSL2561) 空気品質センサ(CCS811)を追加
 * I2Cアドレス管理
 * TSL2561:     0x39
 * CCS811:      0x5A
 * OLCD1306:    0x3C
 * BME280:      0x76
 * SPIFFSをサポートし,WebPageからJavaScriptの読込先を外部に依存しないように変更してみた
 * ChartJSに馴れてきたこともあり、もう少しグラフ表示を凝ったモノにして見る。
 * 24時間更新に関し、更新した次のデータ枠をNULLにするようにした。更新位置を明確にするため
 * /setup 頁を作成 EEPROMに必要な情報を書き込むように変更
 * 『Indoor environment monitor』としてプロジェクトを更新
 * Chart.jsを作り込み強化
 * フラッシュの使用量が多少増えているが、まだまだ大丈夫そう
 * 1日分のデータバッファをサイクリックに使うことにした。そのため初期値をキチンと定義することにした。
 * データが無い場合はNULLにすることでグラフプロットが無くなることが判っているので対応した。
 * RootのHTML表示スタイルを更新した。
 * 時刻表示を追加して見ました。起動時にNTPサーバにアクセスし、時刻取得。ESP32内部のRTCに登録してOLCDに時刻表示する
 * ESP32のGPIOの一部のpinにはArduinoに用意されているanalogWrite()がありません。DACが存在するからかもしれません。
 * その代わりなのかLED_PWMという機能が用意されているようです。https://garretlab.web.fc2.com/arduino/lab/ledc/
 * この機能が利用できるPinは16個のようです。今回PO3(GPIO4)、PO4(GPIO2)が対象pinのようですので、改造してみました。
 * PWMによるLEDの明るさ調整ですが、単純に直線的な輝度制御は難しいですね
 * LEDの配置等を変更
 * OTAを維持したまま、モードをST+APにして普段はPCとの間でトランシーバモード、一方、ファーム更新はSTモードで実現出来ないか確認する
 * Adafruit_BME280.hがESP32でうまく動作しないため、ライブラリを変更し、BME280I2C.hを採用した
 * WebServer  on()の扱いをリファレンスで確認する必要があります。
 * https://garretlab.web.fc2.com/arduino/esp32/reference/libraries/WebServer/WebServer_on.html
 * この関数の為、WiznetのWebServer関数との整合性がとれない。 ESP32とSTM32の共存は難しそう
 * この記述はESP8266/32のみ
 * ※HTMLの記述にてString型を駆使して記述するが、その手法も多岐にわたる。好みの方法を確立しておく必要がある
 * 最大1310720バイトのフラッシュメモリのうち、スケッチが928898バイト(70%)を使っています。
 * 最大327680バイトのRAMのうち、グローバル変数が45176バイト(13%)を使っていて、ローカル変数で282504バイト使うことができます。
 * ブラウザからの転送が20%程度で止まってしまいます。シリアルによる書込は問題ないです。
 */
#include <WiFi.h>
#include <time.h>
#include <WiFiClient.h>
#include <WebServer.h>
#include <ESPmDNS.h>
#include <Update.h>
#include "SPIFFS.h"
#include "FS.h"
#include "SD.h"
#include "SPI.h"
#include "Adafruit_GFX.h"
#include "Adafruit_SSD1306.h"
#include <BME280I2C.h>
#include "SparkFunTSL2561.h"
#include "SparkFunCCS811.h"
//#include <Adafruit_CCS811.h>
#include "arduino_secrets.h"                                            //機密データを「Secret」タブ/arduino_secrets.hに入力してください
#include <EEPROM.h>
#include "esp_system.h"                                                 //MACマックアドレスを読み込む
//#define COMSCANDEC 0xC8
//#define COMSCANINC 0xC0
#define COMSCANDEC      0xC0
#define COMSCANINC      0xC8
//#define SETCOMPINS    0xA0
#define SETCOMPINS      0xA1
//#define SETSEGMENTREMAP 0xA1
#define SETSEGMENTREMAP 0xA0
#define HTTPport        80
#define SCREEN_WIDTH    128                                             // OLED display width, in pixels
#define SCREEN_HEIGHT   64                                              // OLED display height, in pixels
#define OLED_RESET      -1                                              // Reset pin # (or -1 if sharing Arduino reset pin)
#define NUMFLAKES       10                                              // アニメーション例の雪片の数
#define LOGO_HEIGHT     16
#define LOGO_WIDTH      16
#define OLCDAres        0x3C                                            //OLCD1306:     0x3C か 0x3D
#define TSLAres         0x39                                            //TSL2561:      0x39 0x29 0x49
#define CCSAres         0x5A                                            //CCS811:       0x5A 0x5B
#define BMEAres         0x76                                            //BME280:       0x76 0x77
#define LED             13                                              //ボード上のLEDは使えないのでGPIO13にLEDを接続
#define AIport          A6
#define PO1             17
#define PO2             16
#define PO3             4
#define PO4             2
#define PI1             39
#define PI2             34
#define PI3             35
#define PI4             33
#define LEDC_CHANNEL_0  0                                               // use first channel of 16 channels (started from zero)
#define LEDC_CHANNEL_1  1                                               // use first channel of 16 channels (started from zero)
const char*     host            = "esp32";
char            ssid[16]        = SECRET_SSID;                          //ネットワークSSID(名前)
char            pass[16]        = SECRET_PASS;                          //ネットワークパスワード(WPAの使用、またはWEPのキーとして使用)
String          myID            = "admin";
String          myPASS          = "password";
//variabls for blinking an LED with Millis
unsigned long   previousMillis  = 0;                                    //LEDが最後に更新されたときに保存されます
const long      interval        = 1000;                                 //点滅する間隔(ミリ秒)
int             ledState        = LOW;                                  //LEDの設定に使用されるledState
unsigned        status;
//Station用
byte            mac[]           = {0x24,0x0A,0xC4,0x61,0x1E,0x7C};      //Espressif Inc この企業は60個ほど保有している模様
byte            apMac[]         = {0x24,0x0A,0xC4,0x61,0x1E,0x7D};      //mac+1になっている模様 実際には本体に問い合わせ
byte            ip[]            = {192, 168, 0, 33};
byte            gateway[]       = {192, 168, 0, 1};
byte            netmask[]       = {255, 255, 255, 0};
//SoftAP用
byte            apIp[]          = {192, 168, 250, 33};
byte            apGateway[]     = {192, 168, 250, 1};
byte            apNetmask[]     = {255, 255, 255, 0};
char            apSsid[16]      = "ESP_AP";                             //このSSIDが公開される。
char            apPassword[16]  = "password";                           //
char            buf1[128];
char            buf2[128];
char            buf3[128];
char            buf4[128];
char            buf5[128];
char            buf6[128];
String          buf11           = "ABC";
String          buf22           = "DEF";
float           gDataBuf[7];                                            //現在は3要素いずれ7要素
float           gDayBuf[24][7];                                         //24時間分3要素データ
float           gSMABuf[10][7];                                         //1分ごとの要素データを確保し移動平均を求める
float           gSMAAve[7];
float           Vtmp(NAN),Vprs(NAN);                                    // hum(NAN)は無効
float           Vhum            = NAN;
float           Vco2            = NAN;
float           Vdin            = NAN;
float           Vspm            = 0.0;
double          Vlux            = NAN;
int             brightness      = 0;
int             fadeAmount      = 25;
boolean         TSLgain         = 0;                                    // Gain setting, 0 = X1, 1 = X16;
unsigned int    TSLms;                                                  // Integration ("shutter") time in milliseconds
//const char*     ntpServer     ="ntp.jst.mfeed.ad.jp";                 //日本のNTPサーバー選択
char            ntpServer[32]   ="192.168.0.199";                       //LocalTimeServer
float           timeZone        = 9.0;                                  //日本のTimeZone GMT+9
                                                                        //EEPROMには90と入力 -12の場合は -120 +1:30の場合は15
const long      gmtOffset_sec   = long(timeZone * 3600.0);              //9時間の時差を入れる
const int       daylightOffset_sec = 0;                                 //夏時間はないのでゼロ
bool            fCcsRun         = true;
struct tm       timeinfo;
WebServer       WS(HTTPport);
//I2C(SDA、SCLピン)に接続されたSSD1306ディスプレイの宣言
Adafruit_SSD1306 OLCD(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
BME280I2C       bmp;                                                    // Default : forced mode, standby time = 1000 ms
                                                                        // Oversampling = pressure ×1, temperature ×1, humidity ×1, filter off,
SFE_TSL2561     light;
//Adafruit_CCS811 ccs;
CCS811          mySensor(CCSAres);
struct st_esp {                                                         // EEPROMで利用する構造体を宣言
        char id;
        char DT[47];
        char ssid[16];
        char pass[16];
        char apSsid[16];
        char apPass[16];
        char timeZoneVal;
        char ntpServer[32];
};
const byte      ID = 0xA2;
const int       wdtTimeout      = 5;                                    //timeOut 5秒
hw_timer_t      *timer          = NULL;
char    f_name[128];
File    logFile;
//--------------WDT timeUpCall
void IRAM_ATTR resetModule() {
  ets_printf("reboot\n");
  esp_restart();
}
//-------------- Style
String style =
"<style>\n"
        "\t#file-input,input{width:100%;height:50px;border-radius:4px;margin:10px auto;font-size:15px}\n"
        "\tinput{background:#f1f1f1;border:0;padding:0 15px}body{background:#17A1A5;font-family:sans-serif;font-size:14px;color:#777}\n"
        "\t#file-input{padding:0;border:1px solid #ddd;line-height:44px;text-align:left;display:block;cursor:pointer}\n"
        "\t#bar,#prgbar{background-color:#f1f1f1;border-radius:10px}#bar{background-color:#17A1A5;width:0%;height:10px}\n"
        "\tform{background:#fff;max-width:500px;margin:150px auto;padding:30px;border-radius:5px;text-align:center}\n"
        "\t.btn{background:#17A1A5;color:#fff;cursor:pointer}\n"
"</style>\n";
//-------------- Login page
String loginIndex = 
"<form name=loginForm>\n"
        "\t<h1>ESP32 Login</h1>\n"
        "\t<input name=userid placeholder='User ID'>\n"
        "\t<input name=pwd placeholder=Password type=Password>\n"
"<input type=submit onclick=check(this.form) class=btn value=Login>\n</form>\n"
"<script>\n"
        "\tfunction check(form) {\n"
                "\t\tif(form.userid.value== '" + myID + "' && form.pwd.value== '" + myPASS +"' )\n"
                        "\t\t\t{window.open('/serverIndex')}\n"
                "\t\telse\n"
                        "\t\t\t{alert('Error Password or Username')}\n"
        "\t}\n"
"</script>\n" + style;
//-------------- Server Index Page
String serverIndex = 
"<script src='https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js'></script>\n"
"<form method='POST' action='#' enctype='multipart/form-data' id='upload_form'>\n"
        "\t<input type='file' name='update' id='file' onchange='sub(this)' style=display:none>\n"
        "\t<label id='file-input' for='file'>   Choose file...</label>\n"
        "\t<input type='submit' class=btn value='Update'>\n"
        "\t<br><br>\n"
        "\t<div id='prg'></div>\n"
        "\t<br><div id='prgbar'><div id='bar'></div></div><br>\n"
"</form>\n"
"<script>\n"
        "\tfunction sub(obj){\n"
                "\t\tvar fileName = obj.value.split('\\\\');\n"
                "\t\tdocument.getElementById('file-input').innerHTML = '   '+ fileName[fileName.length-1];\n"
        "\t};\n"
        "\t$('form').submit(function(e){\n"
                "\t\te.preventDefault();\n"
                "\t\tvar form = $('#upload_form')[0];\n"
                "\t\tvar data = new FormData(form);\n"
                "\t\t$.ajax({\n"
                        "\t\t\turl: '/update',\n"
                        "\t\t\ttype: 'POST',\n"
                        "\t\t\tdata: data,\n"
                        "\t\t\tcontentType: false,\n"
                        "\t\t\tprocessData:false,\n"
                        "\t\t\txhr: function() {\n"
                                "\t\t\t\tvar xhr = new window.XMLHttpRequest();\n"
                                "\t\t\t\txhr.upload.addEventListener('progress', function(evt) {\n"
                                        "\t\t\t\t\tif (evt.lengthComputable) {\n"
                                                "\t\t\t\t\t\tvar per = evt.loaded / evt.total;\n"
                                                "\t\t\t\t\t\t$('#prg').html('progress: ' + Math.round(per*100) + '%');\n"
                                                "\t\t\t\t\t\t$('#bar').css('width',Math.round(per*100) + '%');\n"
                                        "\t\t\t\t\t}\n"
                                "\t\t\t\t}, false);\n"
                                "\t\t\t\treturn xhr;\n"
                        "\t\t\t},\n"
                        "\t\t\tsuccess:function(d, s) {\n"
                                "\t\t\t\tconsole.log('success!')\n"
                        "\t\t\t},\n"
                        "\t\t\terror: function (a, b, c) {\n"
                        "\t\t\t}\n"
                "\t\t});\n"
        "\t});\n"
"</script>\n" + style;
//------------ fileNotFound
String fileNotFound = 
"<html><head>\n"
        "\t<title>ESP32 : 404 Not Found</title>\n"
"</head><body>\n"
        "\t<center><h1>ESP32 404 : Not Found</h1></center>\n"
        "\t<p>The requested URL was not found on this server.</p>\n"
"</body></html>\n";
//------------
void handleGetData() {
        gDataBuf[0]     =       Vprs;
        gDataBuf[1]     =       Vtmp;
        gDataBuf[2]     =       Vhum;
        gDataBuf[3]     =       Vdin;
        gDataBuf[4]     =       Vco2;
        gDataBuf[5]     =       float(Vlux);
        gDataBuf[6]     =       analogRead(AIport);
        WS.send(200, "text/html", SendHTML(gDataBuf));         //Send ADC value only to client ajax request
}
//------------
void handleNotFound(){
        if (!handleFileRead(WS.uri())) {
                //  ファイルが見つかりません
                Serial.println(F("404 not found"));
                WS.send(404, "text/html",fileNotFound);
        }
}
//------------SPIFSS のファイルをクライアントに転送する
bool handleFileRead(String path) {
        Serial.println("handleFileRead: trying to read " + path);
        // パス指定されたファイルがあればクライアントに送信する
        if (path.endsWith("/")) path += "index.html";
                String contentType = getContentType(path);
                if (SPIFFS.exists(path)) {
                        Serial.println("handleFileRead: sending " + path);
                        File file = SPIFFS.open(path, "r");
                        WS.streamFile(file, contentType);
                        file.close();
                        Serial.println("handleFileRead: sent " + path);
                        return true;
        }else {
                Serial.println(F("handleFileRead: 404 not found"));
                WS.send(404, "text/html",fileNotFound);
                return false;
        }
}
//------------
void handleSetUp(){
        if(WS.hasArg("SBM")){                                           //引数に"SBM=1"が含まれていることを確認
                Serial.println(F("SetUp?"));
                int bufSize = sizeof(st_esp);
                EEPROM.begin(bufSize);                                  //構造体のサイズ設定
                st_esp  buf;
                String cmd1,cmd2,cmd3,cmd4,str1,str2,str3,str4;
                cmd1            = WS.arg("DT7");
                buf.DT[6]       = char(cmd1.toInt());
                cmd2            = WS.arg("DT8");
                buf.DT[7]       = char(cmd2.toInt());
                cmd3            = WS.arg("DT9");
                buf.DT[8]       = char(cmd3.toInt());
                cmd4            = WS.arg("DT10");
                buf.DT[9]       = char(cmd4.toInt());
                Serial.println("IP="+cmd1+"."+cmd2+"."+cmd3+"."+cmd4);
                cmd1            = WS.arg("DT11");
                buf.DT[10]      = char(cmd1.toInt());
                cmd2            = WS.arg("DT12");
                buf.DT[11]      = char(cmd2.toInt());
                cmd3            = WS.arg("DT13");
                buf.DT[12]      = char(cmd3.toInt());
                cmd4            = WS.arg("DT14");
                buf.DT[13]      = char(cmd4.toInt());
                Serial.println("MASK="+cmd1+"."+cmd2+"."+cmd3+"."+cmd4);
                cmd1            = WS.arg("DT15");
                buf.DT[14]      = char(cmd1.toInt());
                cmd2            = WS.arg("DT16");
                buf.DT[15]      = char(cmd2.toInt());
                cmd3            = WS.arg("DT17");
                buf.DT[16]      = char(cmd3.toInt());
                cmd4            = WS.arg("DT18");
                buf.DT[17]      = char(cmd4.toInt());
                Serial.println("GW="+cmd1+"."+cmd2+"."+cmd3+"."+cmd4);
                str1            = WS.arg("DT19");
                str1.toCharArray(buf.ssid, 16);
                Serial.println("SSID="+str1);
                str2            = WS.arg("DT20");
                str2.toCharArray(buf.pass, 16);
                Serial.println("PASSWORD="+str2);
                //
                cmd1            = WS.arg("DT27");
                buf.DT[26]      = char(cmd1.toInt());
                cmd2            = WS.arg("DT28");
                buf.DT[27]      = char(cmd2.toInt());
                cmd3            = WS.arg("DT29");
                buf.DT[28]      = char(cmd3.toInt());
                cmd4            = WS.arg("DT30");
                buf.DT[29]      = char(cmd4.toInt());
                Serial.println("AccessPoint IP="+cmd1+"."+cmd2+"."+cmd3+"."+cmd4);
                cmd1            = WS.arg("DT31");
                buf.DT[30]      = char(cmd1.toInt());
                cmd2            = WS.arg("DT32");
                buf.DT[31]      = char(cmd2.toInt());
                cmd3            = WS.arg("DT33");
                buf.DT[32]      = char(cmd3.toInt());
                cmd4            = WS.arg("DT34");
                buf.DT[33]      = char(cmd4.toInt());
                Serial.println("AccessPoint MASK="+cmd1+"."+cmd2+"."+cmd3+"."+cmd4);
                cmd1            = WS.arg("DT35");
                buf.DT[34]      = char(cmd1.toInt());
                cmd2            = WS.arg("DT36");
                buf.DT[35]      = char(cmd2.toInt());
                cmd3            = WS.arg("DT37");
                buf.DT[36]      = char(cmd3.toInt());
                cmd4            = WS.arg("DT38");
                buf.DT[37]      = char(cmd4.toInt());
                Serial.println("AccessPoint GW="+cmd1+"."+cmd2+"."+cmd3+"."+cmd4);
                str1            = WS.arg("DT39");
                str1.toCharArray(buf.apSsid, 16);
                Serial.println("AccessPoint SSID="+str1);
                str2            = WS.arg("DT40");
                str2.toCharArray(buf.apPass, 16);
                Serial.println("AccessPoint PASSWORD="+str2);
                str3            = WS.arg("DT41");
                buf.timeZoneVal = char(str3.toFloat() * 10);
                str4            = WS.arg("DT42");
                str4.toCharArray(buf.ntpServer, 32);
                               
                buf.id  = ID;
                EEPROM.put<st_esp>(0, buf);                
                EEPROM.commit();
                for (int i = 0; i < 4; i++){
                        ip[i]           = buf.DT[i+6];
                }
                for (int i = 0; i < 4; i++){
                        netmask[i]      = buf.DT[i+10];
                }
                for (int i = 0; i < 4; i++){
                        gateway[i]      = buf.DT[i+14];
                }
                for (int i = 0; i < 4; i++){
                        apIp[i]         = buf.DT[i+26];
                }
                for (int i = 0; i < 4; i++){
                        apNetmask[i]      = buf.DT[i+30];
                }
                for (int i = 0; i < 4; i++){
                        apGateway[i]      = buf.DT[i+34];
                }
                WS.send(200, "text/html", SetupHTML());
                ESP.restart();                                          //ESP再起動
        }else{
                Serial.println(F("SetUp"));
                WS.send(200, "text/html", SetupHTML());
        }
}
//------------
String SetupHTML(){
        String ptr =
        "<!DOCTYPE HTML PUBLIC '-//W3C//DTD HTML 4.01 Transitional//EN'>\n"
        "<HTML>\n"
        "<HEAD>\n"
                "\t\t<META HTTP-EQUIV='Content-Type' charset='UTF-8'>\n"
                "\t\t<META HTTP-EQUIV='Content-Style-Type'>\n"
                "\t\t<TITLE>IndoorEnvironmentMonitor Setup Page</TITLE>\n"
        "</HEAD>\n"
        "<BODY MARGINWIDTH='0' MARGINHEIGHT='0' leftmargin='0' style='margin: 0; padding: 0;'>\n"
        "<FORM>\n"
        "\t<BLOCKQUOTE>\n"
                "\t\t<BLOCKQUOTE>\n"
                        "\t\t\t<TABLE BGCOLOR='#17A1A5' BORDER='0' WIDTH='100%' CELLPADDING='1' style='font-family:Verdana;color:#ffffff;font-size:12px;' CELLSPACING='2'>\n"
                        "\t\t\t<TR>\n"
                                "\t\t\t\t<TD>IndoorEnvironmentMonitor Setup Page</TD>\n"
                        "\t\t\t</TR>\n"
                        "\t\t\t</TABLE><BR>\n"
                        "\t\t\t<script>\n"
                                "\t\t\t\tfunction hex2num (s_hex) {\n"
                                        "\t\t\t\t\teval(\"var n_num=0X\" + s_hex);\n"
                                        "\t\t\t\t\treturn n_num;\n"
                                "\t\t\t\t}\n"
                        "\t\t\t</script>\n"
                        "\t\t\t<tbody>\n"
                        "\t\t\t<INPUT TYPE='hidden' NAME='SBM' VALUE='1'>\n"
                        "\t\t\t<TABLE BORDER='0' CELLSPACING='1' CELLPADDING='0' WIDTH='100%' HEIGHT='450'>\n"
                        "\t\t\t<TR>\n"
                                "\t\t\t\t<TD HEIGHT='24' WIDTH='175'>MAC:</TD> \n"
                                "\t\t\t\t<TD WIDTH='75%' NOWRAP HEIGHT='24'>\n"
                                        "\t\t\t\t\t<INPUT ID='T1'  TYPE='text' SIZE='3' MAXLENGTH='2' NAME='DT1' VALUE=" +String(mac[0],HEX) +" readonly>.\n"
                                        "\t\t\t\t\t<INPUT ID='T3'  TYPE='text' SIZE='3' MAXLENGTH='2' NAME='DT2' VALUE=" +String(mac[1],HEX) +" readonly>.\n"
                                        "\t\t\t\t\t<INPUT ID='T5'  TYPE='text' SIZE='3' MAXLENGTH='2' NAME='DT3' VALUE=" +String(mac[2],HEX) +" readonly>.\n"
                                        "\t\t\t\t\t<INPUT ID='T7'  TYPE='text' SIZE='3' MAXLENGTH='2' NAME='DT4' VALUE=" +String(mac[3],HEX) +" readonly>.\n"
                                        "\t\t\t\t\t<INPUT ID='T9'  TYPE='text' SIZE='3' MAXLENGTH='2' NAME='DT5' VALUE=" +String(mac[4],HEX) +" readonly>.\n"
                                        "\t\t\t\t\t<INPUT ID='T11' TYPE='text' SIZE='3' MAXLENGTH='2' NAME='DT6' VALUE=" +String(mac[5],HEX) +" readonly>(HEX FIXED)\n"
                                        "\t\t\t\t\t<INPUT ID='T2'  TYPE='hidden' NAME='DT1'>\n"
                                        "\t\t\t\t\t<INPUT ID='T4'  TYPE='hidden' NAME='DT2'>\n"
                                        "\t\t\t\t\t<INPUT ID='T6'  TYPE='hidden' NAME='DT3'>\n"
                                        "\t\t\t\t\t<INPUT ID='T8'  TYPE='hidden' NAME='DT4'>\n"
                                        "\t\t\t\t\t<INPUT ID='T10' TYPE='hidden' NAME='DT5'>\n"
                                        "\t\t\t\t\t<INPUT ID='T12' TYPE='hidden' NAME='DT6'>\n"
                                "\t\t\t\t</TD> \n"
                        "\t\t\t</TR>\n"
                        "\t\t\t<TR>\n"
                                "\t\t\t\t<TD HEIGHT='32' WIDTH='175'>IP:</TD> \n"
                                "\t\t\t\t<TD WIDTH='75%'>\n"
                                        "\t\t\t\t\t<INPUT TYPE='text' SIZE='3' MAXLENGTH='3' NAME='DT7'  VALUE=" +String(ip[0],DEC) +">.\n"
                                        "\t\t\t\t\t<INPUT TYPE='text' SIZE='3' MAXLENGTH='3' NAME='DT8'  VALUE=" +String(ip[1],DEC) +">.\n"
                                        "\t\t\t\t\t<INPUT TYPE='text' SIZE='3' MAXLENGTH='3' NAME='DT9'  VALUE=" +String(ip[2],DEC) +">.\n"
                                        "\t\t\t\t\t<INPUT TYPE='text' SIZE='3' MAXLENGTH='3' NAME='DT10' VALUE=" +String(ip[3],DEC) +"\n"
                                "\t\t\t\t</TD>\n"
                        "\t\t\t</TR>\n"
                        "\t\t\t<TR>\n"
                                "\t\t\t\t<TD HEIGHT='32' WIDTH='175'>MASK:</TD> \n"
                                "\t\t\t\t<TD WIDTH='75%'>\n"
                                        "\t\t\t\t\t<INPUT TYPE='text' SIZE='3' MAXLENGTH='3' NAME='DT11' VALUE=" +String(netmask[0],DEC) +">.\n"
                                        "\t\t\t\t\t<INPUT TYPE='text' SIZE='3' MAXLENGTH='3' NAME='DT12' VALUE=" +String(netmask[1],DEC) +">.\n"
                                        "\t\t\t\t\t<INPUT TYPE='text' SIZE='3' MAXLENGTH='3' NAME='DT13' VALUE=" +String(netmask[2],DEC) +">.\n"
                                        "\t\t\t\t\t<INPUT TYPE='text' SIZE='3' MAXLENGTH='3' NAME='DT14' VALUE=" +String(netmask[3],DEC) +">\n"
                                "\t\t\t\t</TD> \n"
                        "\t\t\t</TR>\n"
                        "\t\t\t<TR>\n"
                                "\t\t\t\t<TD HEIGHT='32' WIDTH='175'>GW:</TD> \n"
                                "\t\t\t\t<TD WIDTH='75%'>\n"
                                        "\t\t\t\t\t<INPUT TYPE='text' SIZE='3' MAXLENGTH='3' NAME='DT15' VALUE=" +String(gateway[0],DEC) +">.\n"
                                        "\t\t\t\t\t<INPUT TYPE='text' SIZE='3' MAXLENGTH='3' NAME='DT16' VALUE=" +String(gateway[1],DEC) +">.\n"
                                        "\t\t\t\t\t<INPUT TYPE='text' SIZE='3' MAXLENGTH='3' NAME='DT17' VALUE=" +String(gateway[2],DEC) +">.\n"
                                        "\t\t\t\t\t<INPUT TYPE='text' SIZE='3' MAXLENGTH='3' NAME='DT18' VALUE=" +String(gateway[3],DEC) +">\n"
                                "\t\t\t\t</TD>\n"
                        "\t\t\t</TR>\n"
                        "\t\t\t<TR>\n"
                                "\t\t\t\t<TD HEIGHT='24' WIDTH='175'>SSID:</TD>\n"
                                "\t\t\t\t<TD WIDTH='75%'>\n"
                                        "\t\t\t\t\t<INPUT NAME='DT19' TYPE='text' SIZE='25' maxlength='25' VALUE=" +String(ssid) +">(Up to 16 characters)\n"
                                "\t\t\t\t</TD>\n"
                        "\t\t\t</TR>\n"
                        "\t\t\t<TR>\n"
                                "\t\t\t\t<TD HEIGHT='24' WIDTH='175' NOWRAP>PASSWORD:</TD> \n"
                                "\t\t\t\t<TD WIDTH='75%'>\n"
                                        "\t\t\t\t\t<INPUT NAME='DT20' TYPE='text' SIZE='25' maxlength='25' VALUE=" +String(pass) +">(Up to 16 characters)\n"
                                "\t\t\t\t</TD>\n"
                        "\t\t\t</TR>\n"
                        "\t\t\t<TR>\n"
                                "\t\t\t\t<TD COLSPAN='2'><P><CENTER><HR></CENTER></TD>\n"
                        "\t\t\t</TR>\n"
                        "\t\t\t<TR>\n"
                                "\t\t\t\t<TD HEIGHT='24' WIDTH='175'>AccessPoint MAC:</TD> \n"
                                "\t\t\t\t<TD WIDTH='75%' NOWRAP HEIGHT='24'>\n"
                                        "\t\t\t\t\t<INPUT ID='T21'  TYPE='text' SIZE='3' MAXLENGTH='2' NAME='DT21' VALUE=" +String(apMac[0],HEX) +" readonly>.\n"
                                        "\t\t\t\t\t<INPUT ID='T23'  TYPE='text' SIZE='3' MAXLENGTH='2' NAME='DT22' VALUE=" +String(apMac[1],HEX) +" readonly>.\n"
                                        "\t\t\t\t\t<INPUT ID='T25'  TYPE='text' SIZE='3' MAXLENGTH='2' NAME='DT23' VALUE=" +String(apMac[2],HEX) +" readonly>.\n"
                                        "\t\t\t\t\t<INPUT ID='T27'  TYPE='text' SIZE='3' MAXLENGTH='2' NAME='DT24' VALUE=" +String(apMac[3],HEX) +" readonly>.\n"
                                        "\t\t\t\t\t<INPUT ID='T29'  TYPE='text' SIZE='3' MAXLENGTH='2' NAME='DT25' VALUE=" +String(apMac[4],HEX) +" readonly>.\n"
                                        "\t\t\t\t\t<INPUT ID='T31'  TYPE='text' SIZE='3' MAXLENGTH='2' NAME='DT26' VALUE=" +String(apMac[5],HEX) +" readonly>(HEX FIXED)\n"
                                        "\t\t\t\t\t<INPUT ID='T22'  TYPE='hidden' NAME='DT21'>\n"
                                        "\t\t\t\t\t<INPUT ID='T24'  TYPE='hidden' NAME='DT22'>\n"
                                        "\t\t\t\t\t<INPUT ID='T26'  TYPE='hidden' NAME='DT23'>\n"
                                        "\t\t\t\t\t<INPUT ID='T28'  TYPE='hidden' NAME='DT24'>\n"
                                        "\t\t\t\t\t<INPUT ID='T30'  TYPE='hidden' NAME='DT25'>\n"
                                        "\t\t\t\t\t<INPUT ID='T32'  TYPE='hidden' NAME='DT26'>\n"
                                "\t\t\t\t</TD> \n"
                        "\t\t\t</TR>\n"
                        "\t\t\t<TR>\n"
                                "\t\t\t\t<TD HEIGHT='32' WIDTH='175'>AccessPoint IP:</TD> \n"
                                "\t\t\t\t<TD WIDTH='75%'>\n"
                                        "\t\t\t\t\t<INPUT TYPE='text' SIZE='3' MAXLENGTH='3' NAME='DT27' VALUE=" +String(apIp[0],DEC) +">.\n"
                                        "\t\t\t\t\t<INPUT TYPE='text' SIZE='3' MAXLENGTH='3' NAME='DT28' VALUE=" +String(apIp[1],DEC) +">.\n"
                                        "\t\t\t\t\t<INPUT TYPE='text' SIZE='3' MAXLENGTH='3' NAME='DT29' VALUE=" +String(apIp[2],DEC) +">.\n"
                                        "\t\t\t\t\t<INPUT TYPE='text' SIZE='3' MAXLENGTH='3' NAME='DT30' VALUE=" +String(apIp[3],DEC) +"\n"
                                "\t\t\t\t</TD>\n"
                        "\t\t\t</TR>\n"
                        "\t\t\t<TR>\n"
                                "\t\t\t\t<TD HEIGHT='32' WIDTH='175'>AccessPoint MASK:</TD> \n"
                                "\t\t\t\t<TD WIDTH='75%'>\n"
                                        "\t\t\t\t\t<INPUT TYPE='text' SIZE='3' MAXLENGTH='3' NAME='DT31' VALUE=" +String(apNetmask[0],DEC) +">.\n"
                                        "\t\t\t\t\t<INPUT TYPE='text' SIZE='3' MAXLENGTH='3' NAME='DT32' VALUE=" +String(apNetmask[1],DEC) +">.\n"
                                        "\t\t\t\t\t<INPUT TYPE='text' SIZE='3' MAXLENGTH='3' NAME='DT33' VALUE=" +String(apNetmask[2],DEC) +">.\n"
                                        "\t\t\t\t\t<INPUT TYPE='text' SIZE='3' MAXLENGTH='3' NAME='DT34' VALUE=" +String(apNetmask[3],DEC) +">\n"
                                "\t\t\t\t</TD> \n"
                        "\t\t\t</TR>\n"
                        "\t\t\t<TR>\n"
                                "\t\t\t\t<TD HEIGHT='32' WIDTH='175'>AccessPoint GW:</TD> \n"
                                "\t\t\t\t<TD WIDTH='75%'>\n"
                                        "\t\t\t\t\t<INPUT TYPE='text' SIZE='3' MAXLENGTH='3' NAME='DT35' VALUE=" +String(apGateway[0],DEC) +">.\n"
                                        "\t\t\t\t\t<INPUT TYPE='text' SIZE='3' MAXLENGTH='3' NAME='DT36' VALUE=" +String(apGateway[1],DEC) +">.\n"
                                        "\t\t\t\t\t<INPUT TYPE='text' SIZE='3' MAXLENGTH='3' NAME='DT37' VALUE=" +String(apGateway[2],DEC) +">.\n"
                                        "\t\t\t\t\t<INPUT TYPE='text' SIZE='3' MAXLENGTH='3' NAME='DT38' VALUE=" +String(apGateway[3],DEC) +">\n"
                                "\t\t\t\t</TD>\n"
                        "\t\t\t</TR>\n"
                        
                        "\t\t\t<TR>\n"
                                "\t\t\t\t<TD HEIGHT='24' WIDTH='175'>AccessPoint SSID:</TD>\n"
                                "\t\t\t\t<TD WIDTH='75%'>\n"
                                        "\t\t\t\t\t<INPUT NAME='DT39' TYPE='text' SIZE='25' maxlength='25' VALUE=" +String(apSsid) +">(Up to 16 characters)\n"
                                "\t\t\t\t</TD>\n"
                        "\t\t\t</TR>\n"
                        "\t\t\t<TR>\n"
                                "\t\t\t\t<TD HEIGHT='24' WIDTH='175' NOWRAP>AccessPoint PASSWORD:</TD> \n"
                                "\t\t\t\t<TD WIDTH='75%'>\n"
                                        "\t\t\t\t\t<INPUT NAME='DT40' TYPE='text' SIZE='25' maxlength='25' VALUE=" +String(apPassword) +">(Up to 16 characters)\n"
                                "\t\t\t\t</TD>\n"
                        "\t\t\t</TR>\n"
                        "\t\t\t<tr>\n"
                                "\t\t\t\t<td colspan='2'><p></p><center><hr></center></td>\n"
                        "\t\t\t</tr>\n"
                        "\t\t\t<tr>\n"
                                "\t\t\t\t<td height='24' width='175' nowrap=''>TimeZone:</td>\n"
                                "\t\t\t\t<td width='75%'>\n"
                                        "\t\t\t\t\t<input name='DT41' type='text' size='10' maxlength='10' value=" +String(timeZone) +">\n"
                                "\t\t\t\t</td>\n"
                        "\t\t\t</tr>\n"
                        "\t\t\t<tr>\n"
                                "\t\t\t\t<td height='24' width='175' nowrap=''>NTPサーバ:</td>\n"
                                "\t\t\t\t<td width='75%'>\n"
                                        "\t\t\t\t\t<input name='DT42' type='text' size='40' maxlength='40' value=" +String(ntpServer) +">(Up to 32 characters)\n"
                                "\t\t\t\t</td>\n"
                        "\t\t\t</tr>\n"
                        "\t\t\t<TR>\n"
                                "\t\t\t\t<TD COLSPAN='2' WIDTH='16%' HEIGHT='32'>\n"
                                        "\t\t\t\t\t<P ALIGN=RIGHT><INPUT ID='button1' TYPE='submit' VALUE='SUBMIT'\n"
                                        "\t\t\t\t\tOnclick=\"\n"
                                        "\t\t\t\t\t\tdocument.getElementById('T2').value  = hex2num(document.getElementById('T1').value);\n"
                                        "\t\t\t\t\t\tdocument.getElementById('T4').value  = hex2num(document.getElementById('T3').value);\n"
                                        "\t\t\t\t\t\tdocument.getElementById('T6').value  = hex2num(document.getElementById('T5').value);\n"
                                        "\t\t\t\t\t\tdocument.getElementById('T8').value  = hex2num(document.getElementById('T7').value);\n"
                                        "\t\t\t\t\t\tdocument.getElementById('T10').value = hex2num(document.getElementById('T9').value);\n"
                                        "\t\t\t\t\t\tdocument.getElementById('T12').value = hex2num(document.getElementById('T11').value);\n"
                                        "\t\t\t\t\t\tdocument.getElementById('T22').value = hex2num(document.getElementById('T21').value);\n"
                                        "\t\t\t\t\t\tdocument.getElementById('T24').value = hex2num(document.getElementById('T23').value);\n"
                                        "\t\t\t\t\t\tdocument.getElementById('T26').value = hex2num(document.getElementById('T25').value);\n"
                                        "\t\t\t\t\t\tdocument.getElementById('T28').value = hex2num(document.getElementById('T27').value);\n"
                                        "\t\t\t\t\t\tdocument.getElementById('T30').value = hex2num(document.getElementById('T29').value);\n"
                                        "\t\t\t\t\t\tdocument.getElementById('T32').value = hex2num(document.getElementById('T31').value);\">\n"
                                "\t\t\t\t</TD>\n"
                        "\t\t\t</TR></tbody> \n"
                "\t\t</TABLE></BLOCKQUOTE>\n"
        "\t</BLOCKQUOTE>\n"
        "</FORM>\n"
        "</BODY>\n"
        "</HTML>\n";
        return ptr;
}
//------------
//                "\t<script SRC='https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.3.0/Chart.bundle.js'></script>\n"
//                "\t<script SRC='https://cdn.rawgit.com/chartjs/Chart.js/master/samples/utils.js'></script>\n"
String SendHTML(float *tempSensor){
        String ptr =
        "<!DOCTYPE HTML PUBLIC '-//W3C//DTD HTML 4.01 Transitional//EN'>\n"
        "<html lang='ja'><head><META http-equiv='Content-Type' content='text/html; charset=utf-8'>\n"
        "<META http-equiv='Content-Style-Type' content='text/css'>\n"
                "\t<title>IndoorEnvironmentMonitor</title>\n"
                "\t<LINK REL='shortcut icon' HREF='favicon.ico'>\n"
                "\t<script type='application/javascript' SRC='https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.3.0/Chart.bundle.js'></script>\n"
                "\t<script type='application/javascript' SRC='https://cdn.rawgit.com/chartjs/Chart.js/master/samples/utils.js'></script>\n"
                "\t<style>\n"
                        "\t\tcanvas{\n"
                        "\t\t\t-moz-user-select: none;\n"
                        "\t\t\t-webkit-user-select: none;\n"
                        "\t\t\t-ms-user-select: none;\n"
                        "\t\t}\n"
                        "\t\thtml { font-family: Helvetica; display: inline-block; margin: 0px auto; text-align: center;}\n"
                        "\t\tbody{margin-top: 50px;} h1 {color: #444444;margin: 50px auto 30px;}\n"
                        "\t\tp {font-size: 24px;color: #444444;margin-bottom: 10px;}\n"
                "\t</style>\n"
        "</head>\n"
        "<BODY BGCOLOR='#ffffff'>\n"
        "<CENTER><TABLE WIDTH='750' BORDER='0' CELLSPACING='0' CELLPADDING='0'>\n"
                "\t<TR>\n"
                        "\t\t<TD COLSPAN='3' VALIGN='TOP'>\n"
                        "\t\t<H2><CENTER>屋内環境モニタ</CENTER></H2>\n"
                        "\t\t</TD>\n"
                "\t</TR>\n"
                "\t<TR>\n"
                        "\t\t<TD WIDTH='33%'>\n"
                        "\t\t<BR CLEAR='ALL'></TD>\n"
                        "\t\t<TD WIDTH='33%'></TD>\n"
                        "\t\t<TD WIDTH='34%'></TD>\n"
                "\t</TR>\n"
                "\t<TR>\n"
                        "\t\t<TD WIDTH='33%'>\n"
                        "\t\t<BR CLEAR='ALL'></TD>\n"
                        "\t\t<TD WIDTH='33%'></TD>\n"
                        "\t\t<TD WIDTH='34%'></TD>\n"
                "\t</TR>\n"
                "\t<TR>\n"
                        "\t\t<TD WIDTH='33%' VALIGN='TOP' ALIGN='RIGHT'>データ取得時刻:</TD>\n"
                        "\t\t<TD WIDTH='33%' VALIGN='TOP' ALIGN='CENTER'>" +String(buf3) +"</TD>\n"
                        "\t\t<TD WIDTH='34%' VALIGN='TOP'></TD>\n"
                "\t</TR>\n"
                "\t<TR>\n"
                        "\t\t<TD WIDTH='33%' VALIGN='TOP' ALIGN='RIGHT'>気圧(BME280):</TD>\n"
                        "\t\t<TD WIDTH='33%' VALIGN='TOP' ALIGN='CENTER'>" +String(tempSensor[0],0)+"</TD>\n"
                        "\t\t<TD WIDTH='34%' VALIGN='TOP'>[Pa]</TD>\n"
                "\t</TR>\n"
                "\t<TR>\n"
                        "\t\t<TD WIDTH='33%' VALIGN='TOP' ALIGN='RIGHT'>気温(BME280):</TD>\n"
                        "\t\t<TD WIDTH='33%' VALIGN='TOP' ALIGN='CENTER'>" +String(tempSensor[1],1)+"</TD>\n"
                        "\t\t<TD WIDTH='34%' VALIGN='TOP'>[℃]</TD>\n"
                "\t</TR>\n"
                "\t<TR>\n"
                        "\t\t<TD WIDTH='33%' VALIGN='TOP' ALIGN='RIGHT'>湿度(BME280):</TD>\n"
                        "\t\t<TD WIDTH='33%' VALIGN='TOP'></TD>\n"
                        "\t\t<TD WIDTH='34%' VALIGN='TOP'>[%]</TD>\n"
                "\t</TR>\n"
                "\t<TR>\n"
                        "\t\t<TD WIDTH='33%' VALIGN='TOP' ALIGN='RIGHT'>浮遊粒子(GP2Y):</TD>\n"
                        "\t\t<TD WIDTH='33%' VALIGN='TOP'></TD>\n" 
                        "\t\t<TD WIDTH='34%' VALIGN='TOP'>[mg/m3]</TD>\n"
                "\t</TR>\n"
                "\t<TR>\n"
                        "\t\t<TD WIDTH='33%' VALIGN='TOP' ALIGN='RIGHT'>CO2濃度(CSS811):</TD>\n"
                        "\t\t<TD WIDTH='33%' VALIGN='TOP' ALIGN='CENTER'>"+String(tempSensor[4],0)+"</TD>\n"
                        "\t\t<TD WIDTH='34%' VALIGN='TOP'>[ppm]</TD>\n"
                "\t</TR>\n"
                "\t<TR>\n"
                        "\t\t<TD WIDTH='33%' VALIGN='TOP' ALIGN='RIGHT'>照度(TSL2561):</TD>\n"
                        "\t\t<TD WIDTH='33%' VALIGN='TOP' ALIGN='CENTER'>"+String(tempSensor[5],0)+"</TD>\n"
                        "\t\t<TD WIDTH='34%' VALIGN='TOP'>[lux]</TD>\n"
                "\t</TR>\n"
                "\t<TR>\n"
                        "\t\t<TD WIDTH='33%' VALIGN='TOP' ALIGN='RIGHT'>騒音(BOB-12758):</TD>\n"
                        "\t\t<TD WIDTH='33%' VALIGN='TOP' ALIGN='CENTER'>"+String(tempSensor[6],0)+"</TD>\n"
                        "\t\t<TD WIDTH='34%' VALIGN='TOP'>[dBm]</TD>\n"
                "\t</TR>\n"
        "</TABLE>\n"
        "<div style='width:75%;'><canvas id='canvas' width='750' height='400' style='display: block; width: 750px; height: 400px;'></canvas></div>\n"
        "</CENTER>\n"
        "\t<script>\n"
                "\t\tvar randomScalingFactor = function() {\n"
                        "\t\t\treturn 18 + Math.random() * 10;\n"
                "\t\t};\n"
                "\t\tvar data_00 = ["+  (isnan(gDayBuf[0][0]) ? String('\0') : String(gDayBuf[0][0],0))+ ","+    (isnan(gDayBuf[1][0]) ? String('\0') : String(gDayBuf[1][0],0))+ ","+
                                        (isnan(gDayBuf[2][0]) ? String('\0') : String(gDayBuf[2][0],0))+ ","+    (isnan(gDayBuf[3][0]) ? String('\0') : String(gDayBuf[3][0],0))+ ","+
                                        (isnan(gDayBuf[4][0]) ? String('\0') : String(gDayBuf[4][0],0))+ ","+    (isnan(gDayBuf[5][0]) ? String('\0') : String(gDayBuf[5][0],0))+ ","+
                                        (isnan(gDayBuf[6][0]) ? String('\0') : String(gDayBuf[6][0],0))+ ","+    (isnan(gDayBuf[7][0]) ? String('\0') : String(gDayBuf[7][0],0))+ ","+
                                        (isnan(gDayBuf[8][0]) ? String('\0') : String(gDayBuf[8][0],0))+ ","+    (isnan(gDayBuf[9][0]) ? String('\0') : String(gDayBuf[9][0],0))+ ","+
                                        (isnan(gDayBuf[10][0]) ? String('\0') : String(gDayBuf[10][0],0))+","+    (isnan(gDayBuf[11][0]) ? String('\0') : String(gDayBuf[11][0],0))+","+
                                        (isnan(gDayBuf[12][0]) ? String('\0') : String(gDayBuf[12][0],0))+","+    (isnan(gDayBuf[13][0]) ? String('\0') : String(gDayBuf[13][0],0))+","+
                                        (isnan(gDayBuf[14][0]) ? String('\0') : String(gDayBuf[14][0],0))+","+    (isnan(gDayBuf[15][0]) ? String('\0') : String(gDayBuf[15][0],0))+","+
                                        (isnan(gDayBuf[16][0]) ? String('\0') : String(gDayBuf[16][0],0))+","+    (isnan(gDayBuf[17][0]) ? String('\0') : String(gDayBuf[17][0],0))+","+
                                        (isnan(gDayBuf[18][0]) ? String('\0') : String(gDayBuf[18][0],0))+","+    (isnan(gDayBuf[19][0]) ? String('\0') : String(gDayBuf[19][0],0))+","+
                                        (isnan(gDayBuf[20][0]) ? String('\0') : String(gDayBuf[20][0],0))+","+    (isnan(gDayBuf[21][0]) ? String('\0') : String(gDayBuf[21][0],0))+","+
                                        (isnan(gDayBuf[22][0]) ? String('\0') : String(gDayBuf[22][0],0))+","+    (isnan(gDayBuf[23][0]) ? String('\0') : String(gDayBuf[23][0],0))+","+
                "];\n"
                "\t\tvar data_01 = ["+  (isnan(gDayBuf[0][1]) ? String('\0') : String(gDayBuf[0][1],1))+ ","+    (isnan(gDayBuf[1][1]) ? String('\0') : String(gDayBuf[1][1],1))+ ","+
                                        (isnan(gDayBuf[2][1]) ? String('\0') : String(gDayBuf[2][1],1))+ ","+    (isnan(gDayBuf[3][1]) ? String('\0') : String(gDayBuf[3][1],1))+ ","+
                                        (isnan(gDayBuf[4][1]) ? String('\0') : String(gDayBuf[4][1],1))+ ","+    (isnan(gDayBuf[5][1]) ? String('\0') : String(gDayBuf[5][1],1))+ ","+
                                        (isnan(gDayBuf[6][1]) ? String('\0') : String(gDayBuf[6][1],1))+ ","+    (isnan(gDayBuf[7][1]) ? String('\0') : String(gDayBuf[7][1],1))+ ","+
                                        (isnan(gDayBuf[8][1]) ? String('\0') : String(gDayBuf[8][1],1))+ ","+    (isnan(gDayBuf[9][1]) ? String('\0') : String(gDayBuf[9][1],1))+ ","+
                                        (isnan(gDayBuf[10][1]) ? String('\0') : String(gDayBuf[10][1],1))+","+    (isnan(gDayBuf[11][1]) ? String('\0') : String(gDayBuf[11][1],1))+","+
                                        (isnan(gDayBuf[12][1]) ? String('\0') : String(gDayBuf[12][1],1))+","+    (isnan(gDayBuf[13][1]) ? String('\0') : String(gDayBuf[13][1],1))+","+
                                        (isnan(gDayBuf[14][1]) ? String('\0') : String(gDayBuf[14][1],1))+","+    (isnan(gDayBuf[15][1]) ? String('\0') : String(gDayBuf[15][1],1))+","+
                                        (isnan(gDayBuf[16][1]) ? String('\0') : String(gDayBuf[16][1],1))+","+    (isnan(gDayBuf[17][1]) ? String('\0') : String(gDayBuf[17][1],1))+","+
                                        (isnan(gDayBuf[18][1]) ? String('\0') : String(gDayBuf[18][1],1))+","+    (isnan(gDayBuf[19][1]) ? String('\0') : String(gDayBuf[19][1],1))+","+
                                        (isnan(gDayBuf[20][1]) ? String('\0') : String(gDayBuf[20][1],1))+","+    (isnan(gDayBuf[21][1]) ? String('\0') : String(gDayBuf[21][1],1))+","+
                                        (isnan(gDayBuf[22][1]) ? String('\0') : String(gDayBuf[22][1],1))+","+    (isnan(gDayBuf[23][1]) ? String('\0') : String(gDayBuf[23][1],1))+","+
                "];\n"
                "\t\tvar data_02 = ["+  (isnan(gDayBuf[0][2]) ? String('\0') : String(gDayBuf[0][2],0))+ ","+    (isnan(gDayBuf[1][2]) ? String('\0') : String(gDayBuf[1][2],0))+ ","+
                                        (isnan(gDayBuf[2][2]) ? String('\0') : String(gDayBuf[2][2],0))+ ","+    (isnan(gDayBuf[3][2]) ? String('\0') : String(gDayBuf[3][2],0))+ ","+
                                        (isnan(gDayBuf[4][2]) ? String('\0') : String(gDayBuf[4][2],0))+ ","+    (isnan(gDayBuf[5][2]) ? String('\0') : String(gDayBuf[5][2],0))+ ","+
                                        (isnan(gDayBuf[6][2]) ? String('\0') : String(gDayBuf[6][2],0))+ ","+    (isnan(gDayBuf[7][2]) ? String('\0') : String(gDayBuf[7][2],0))+ ","+
                                        (isnan(gDayBuf[8][2]) ? String('\0') : String(gDayBuf[8][2],0))+ ","+    (isnan(gDayBuf[9][2]) ? String('\0') : String(gDayBuf[9][2],0))+ ","+
                                        (isnan(gDayBuf[10][2]) ? String('\0') : String(gDayBuf[10][2],0))+","+    (isnan(gDayBuf[11][2]) ? String('\0') : String(gDayBuf[11][2],0))+","+
                                        (isnan(gDayBuf[12][2]) ? String('\0') : String(gDayBuf[12][2],0))+","+    (isnan(gDayBuf[13][2]) ? String('\0') : String(gDayBuf[13][2],0))+","+
                                        (isnan(gDayBuf[14][2]) ? String('\0') : String(gDayBuf[14][2],0))+","+    (isnan(gDayBuf[15][2]) ? String('\0') : String(gDayBuf[15][2],0))+","+
                                        (isnan(gDayBuf[16][2]) ? String('\0') : String(gDayBuf[16][2],0))+","+    (isnan(gDayBuf[17][2]) ? String('\0') : String(gDayBuf[17][2],0))+","+
                                        (isnan(gDayBuf[18][2]) ? String('\0') : String(gDayBuf[18][2],0))+","+    (isnan(gDayBuf[19][2]) ? String('\0') : String(gDayBuf[19][2],0))+","+
                                        (isnan(gDayBuf[20][2]) ? String('\0') : String(gDayBuf[20][2],0))+","+    (isnan(gDayBuf[21][2]) ? String('\0') : String(gDayBuf[21][2],0))+","+
                                        (isnan(gDayBuf[22][2]) ? String('\0') : String(gDayBuf[22][2],0))+","+    (isnan(gDayBuf[23][2]) ? String('\0') : String(gDayBuf[23][2],0))+","+
                "];\n"
                "\t\tvar data_03 = ["+  (isnan(gDayBuf[0][3]) ? String('\0') : String(gDayBuf[0][3],0))+ ","+    (isnan(gDayBuf[1][3]) ? String('\0') : String(gDayBuf[1][3],0))+ ","+
                                        (isnan(gDayBuf[2][3]) ? String('\0') : String(gDayBuf[2][3],0))+ ","+    (isnan(gDayBuf[3][3]) ? String('\0') : String(gDayBuf[3][3],0))+ ","+
                                        (isnan(gDayBuf[4][3]) ? String('\0') : String(gDayBuf[4][3],0))+ ","+    (isnan(gDayBuf[5][3]) ? String('\0') : String(gDayBuf[5][3],0))+ ","+
                                        (isnan(gDayBuf[6][3]) ? String('\0') : String(gDayBuf[6][3],0))+ ","+    (isnan(gDayBuf[7][3]) ? String('\0') : String(gDayBuf[7][3],0))+ ","+
                                        (isnan(gDayBuf[8][3]) ? String('\0') : String(gDayBuf[8][3],0))+ ","+    (isnan(gDayBuf[9][3]) ? String('\0') : String(gDayBuf[9][3],0))+ ","+
                                        (isnan(gDayBuf[10][3]) ? String('\0') : String(gDayBuf[10][3],0))+","+    (isnan(gDayBuf[11][3]) ? String('\0') : String(gDayBuf[11][3],0))+","+
                                        (isnan(gDayBuf[12][3]) ? String('\0') : String(gDayBuf[12][3],0))+","+    (isnan(gDayBuf[13][3]) ? String('\0') : String(gDayBuf[13][3],0))+","+
                                        (isnan(gDayBuf[14][3]) ? String('\0') : String(gDayBuf[14][3],0))+","+    (isnan(gDayBuf[15][3]) ? String('\0') : String(gDayBuf[15][3],0))+","+
                                        (isnan(gDayBuf[16][3]) ? String('\0') : String(gDayBuf[16][3],0))+","+    (isnan(gDayBuf[17][3]) ? String('\0') : String(gDayBuf[17][3],0))+","+
                                        (isnan(gDayBuf[18][3]) ? String('\0') : String(gDayBuf[18][3],0))+","+    (isnan(gDayBuf[19][3]) ? String('\0') : String(gDayBuf[19][3],0))+","+
                                        (isnan(gDayBuf[20][3]) ? String('\0') : String(gDayBuf[20][3],0))+","+    (isnan(gDayBuf[21][3]) ? String('\0') : String(gDayBuf[21][3],0))+","+
                                        (isnan(gDayBuf[22][3]) ? String('\0') : String(gDayBuf[22][3],0))+","+    (isnan(gDayBuf[23][3]) ? String('\0') : String(gDayBuf[23][3],0))+","+
                "];\n"
                "\t\tvar data_04 = ["+  (isnan(gDayBuf[0][4]) ? String('\0') : String(gDayBuf[0][4],0))+ ","+    (isnan(gDayBuf[1][4]) ? String('\0') : String(gDayBuf[1][4],0))+ ","+
                                        (isnan(gDayBuf[2][4]) ? String('\0') : String(gDayBuf[2][4],0))+ ","+    (isnan(gDayBuf[3][4]) ? String('\0') : String(gDayBuf[3][4],0))+ ","+
                                        (isnan(gDayBuf[4][4]) ? String('\0') : String(gDayBuf[4][4],0))+ ","+    (isnan(gDayBuf[5][4]) ? String('\0') : String(gDayBuf[5][4],0))+ ","+
                                        (isnan(gDayBuf[6][4]) ? String('\0') : String(gDayBuf[6][4],0))+ ","+    (isnan(gDayBuf[7][4]) ? String('\0') : String(gDayBuf[7][4],0))+ ","+
                                        (isnan(gDayBuf[8][4]) ? String('\0') : String(gDayBuf[8][4],0))+ ","+    (isnan(gDayBuf[9][4]) ? String('\0') : String(gDayBuf[9][4],0))+ ","+
                                        (isnan(gDayBuf[10][4]) ? String('\0') : String(gDayBuf[10][4],0))+","+    (isnan(gDayBuf[11][4]) ? String('\0') : String(gDayBuf[11][4],0))+","+
                                        (isnan(gDayBuf[12][4]) ? String('\0') : String(gDayBuf[12][4],0))+","+    (isnan(gDayBuf[13][4]) ? String('\0') : String(gDayBuf[13][4],0))+","+
                                        (isnan(gDayBuf[14][4]) ? String('\0') : String(gDayBuf[14][4],0))+","+    (isnan(gDayBuf[15][4]) ? String('\0') : String(gDayBuf[15][4],0))+","+
                                        (isnan(gDayBuf[16][4]) ? String('\0') : String(gDayBuf[16][4],0))+","+    (isnan(gDayBuf[17][4]) ? String('\0') : String(gDayBuf[17][4],0))+","+
                                        (isnan(gDayBuf[18][4]) ? String('\0') : String(gDayBuf[18][4],0))+","+    (isnan(gDayBuf[19][4]) ? String('\0') : String(gDayBuf[19][4],0))+","+
                                        (isnan(gDayBuf[20][4]) ? String('\0') : String(gDayBuf[20][4],0))+","+    (isnan(gDayBuf[21][4]) ? String('\0') : String(gDayBuf[21][4],0))+","+
                                        (isnan(gDayBuf[22][4]) ? String('\0') : String(gDayBuf[22][4],0))+","+    (isnan(gDayBuf[23][4]) ? String('\0') : String(gDayBuf[23][4],0))+","+
                "];\n"
                "\t\tvar data_05 = ["+  (isnan(gDayBuf[0][5]) ? String('\0') : String(gDayBuf[0][5],0))+ ","+    (isnan(gDayBuf[1][5]) ? String('\0') : String(gDayBuf[1][5],0))+ ","+
                                        (isnan(gDayBuf[2][5]) ? String('\0') : String(gDayBuf[2][5],0))+ ","+    (isnan(gDayBuf[3][5]) ? String('\0') : String(gDayBuf[3][5],0))+ ","+
                                        (isnan(gDayBuf[4][5]) ? String('\0') : String(gDayBuf[4][5],0))+ ","+    (isnan(gDayBuf[5][5]) ? String('\0') : String(gDayBuf[5][5],0))+ ","+
                                        (isnan(gDayBuf[6][5]) ? String('\0') : String(gDayBuf[6][5],0))+ ","+    (isnan(gDayBuf[7][5]) ? String('\0') : String(gDayBuf[7][5],0))+ ","+
                                        (isnan(gDayBuf[8][5]) ? String('\0') : String(gDayBuf[8][5],0))+ ","+    (isnan(gDayBuf[9][5]) ? String('\0') : String(gDayBuf[9][5],0))+ ","+
                                        (isnan(gDayBuf[10][5]) ? String('\0') : String(gDayBuf[10][5],0))+","+    (isnan(gDayBuf[11][5]) ? String('\0') : String(gDayBuf[11][5],0))+","+
                                        (isnan(gDayBuf[12][5]) ? String('\0') : String(gDayBuf[12][5],0))+","+    (isnan(gDayBuf[13][5]) ? String('\0') : String(gDayBuf[13][5],0))+","+
                                        (isnan(gDayBuf[14][5]) ? String('\0') : String(gDayBuf[14][5],0))+","+    (isnan(gDayBuf[15][5]) ? String('\0') : String(gDayBuf[15][5],0))+","+
                                        (isnan(gDayBuf[16][5]) ? String('\0') : String(gDayBuf[16][5],0))+","+    (isnan(gDayBuf[17][5]) ? String('\0') : String(gDayBuf[17][5],0))+","+
                                        (isnan(gDayBuf[18][5]) ? String('\0') : String(gDayBuf[18][5],0))+","+    (isnan(gDayBuf[19][5]) ? String('\0') : String(gDayBuf[19][5],0))+","+
                                        (isnan(gDayBuf[20][5]) ? String('\0') : String(gDayBuf[20][5],0))+","+    (isnan(gDayBuf[21][5]) ? String('\0') : String(gDayBuf[21][5],0))+","+
                                        (isnan(gDayBuf[22][5]) ? String('\0') : String(gDayBuf[22][5],0))+","+    (isnan(gDayBuf[23][5]) ? String('\0') : String(gDayBuf[23][5],0))+","+
                "];\n"
                "\t\tvar data_06 = ["+  (isnan(gDayBuf[0][6]) ? String('\0') : String(gDayBuf[0][6],0))+ ","+    (isnan(gDayBuf[1][6]) ? String('\0') : String(gDayBuf[1][6],0))+ ","+
                                        (isnan(gDayBuf[2][6]) ? String('\0') : String(gDayBuf[2][6],0))+ ","+    (isnan(gDayBuf[3][6]) ? String('\0') : String(gDayBuf[3][6],0))+ ","+
                                        (isnan(gDayBuf[4][6]) ? String('\0') : String(gDayBuf[4][6],0))+ ","+    (isnan(gDayBuf[5][6]) ? String('\0') : String(gDayBuf[5][6],0))+ ","+
                                        (isnan(gDayBuf[6][6]) ? String('\0') : String(gDayBuf[6][6],0))+ ","+    (isnan(gDayBuf[7][6]) ? String('\0') : String(gDayBuf[7][6],0))+ ","+
                                        (isnan(gDayBuf[8][6]) ? String('\0') : String(gDayBuf[8][6],0))+ ","+    (isnan(gDayBuf[9][6]) ? String('\0') : String(gDayBuf[9][6],0))+ ","+
                                        (isnan(gDayBuf[10][6]) ? String('\0') : String(gDayBuf[10][6],0))+","+    (isnan(gDayBuf[11][6]) ? String('\0') : String(gDayBuf[11][6],0))+","+
                                        (isnan(gDayBuf[12][6]) ? String('\0') : String(gDayBuf[12][6],0))+","+    (isnan(gDayBuf[13][6]) ? String('\0') : String(gDayBuf[13][6],0))+","+
                                        (isnan(gDayBuf[14][6]) ? String('\0') : String(gDayBuf[14][6],0))+","+    (isnan(gDayBuf[15][6]) ? String('\0') : String(gDayBuf[15][6],0))+","+
                                        (isnan(gDayBuf[16][6]) ? String('\0') : String(gDayBuf[16][6],0))+","+    (isnan(gDayBuf[17][6]) ? String('\0') : String(gDayBuf[17][6],0))+","+
                                        (isnan(gDayBuf[18][6]) ? String('\0') : String(gDayBuf[18][6],0))+","+    (isnan(gDayBuf[19][6]) ? String('\0') : String(gDayBuf[19][6],0))+","+
                                        (isnan(gDayBuf[20][6]) ? String('\0') : String(gDayBuf[20][6],0))+","+    (isnan(gDayBuf[21][6]) ? String('\0') : String(gDayBuf[21][6],0))+","+
                                        (isnan(gDayBuf[22][6]) ? String('\0') : String(gDayBuf[22][6],0))+","+    (isnan(gDayBuf[23][6]) ? String('\0') : String(gDayBuf[23][6],0))+","+
                "];\n"
                "\t\tvar config = {\n"
                        "\t\t\ttype: 'line',\n"
                        "\t\t\tdata: {\n"
                                "\t\t\t\tlabels: ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12' , '13', '14', '15', '16', '17', '18', '19', '20', '21', '22', '23'],\n"
                                "\t\t\t\tdatasets: [{\n"
                                        "\t\t\t\t\tlabel: '気圧[Pa]',\n"
                                        "\t\t\t\t\tdata: data_00,\n"
                                        "\t\t\t\t\tborderColor: window.chartColors.red,\n"
                                        "\t\t\t\t\tbackgroundColor: 'rgba(0, 0, 0, 0)',\n"
                                        "\t\t\t\t\tfill: false,\n"
                                        "\t\t\t\t\tcubicInterpolationMode: 'monotone',\n"
                                        "\t\t\t\t\tyAxisID: 'y1'\n"
                                "\t\t\t\t}, {\n"
                                        "\t\t\t\t\tlabel: '気温[℃]',\n"
                                        "\t\t\t\t\tdata: data_01,\n"
                                        "\t\t\t\t\tborderColor: window.chartColors.blue,\n"
                                        "\t\t\t\t\tbackgroundColor: 'rgba(0, 0, 0, 0)',\n"
                                        "\t\t\t\t\tfill: false,\n"
                                        "\t\t\t\t\tyAxisID: 'y2'\n"
                                "\t\t\t\t}, {\n"
                                        "\t\t\t\t\tlabel: '湿度[%]',\n"
                                        "\t\t\t\t\tdata: data_02,\n"
                                        "\t\t\t\t\tborderColor: window.chartColors.orange,\n"
                                        "\t\t\t\t\tbackgroundColor: 'rgba(0, 0, 0, 0)',\n"
                                        "\t\t\t\t\tfill: false,\n"
                                        "\t\t\t\t\tyAxisID: 'y3'\n"
                                "\t\t\t\t}, {\n"
                                        "\t\t\t\t\tlabel: '浮遊粒子[mg/m3]',\n"
                                        "\t\t\t\t\tdata: data_03,\n"
                                        "\t\t\t\t\tborderColor: window.chartColors.yellow,\n"
                                        "\t\t\t\t\tbackgroundColor: 'rgba(0, 0, 0, 0)',\n"
                                        "\t\t\t\t\tfill: false,\n"
                                        "\t\t\t\t\tyAxisID: 'y4'\n"
                                "\t\t\t\t}, {\n"
                                        "\t\t\t\t\tlabel: 'CO2濃度[ppm]',\n"
                                        "\t\t\t\t\tdata: data_04,\n"
                                        "\t\t\t\t\tborderColor: window.chartColors.purple,\n"
                                        "\t\t\t\t\tbackgroundColor: 'rgba(0, 0, 0, 0)',\n"
                                        "\t\t\t\t\tfill: false,\n"
                                        "\t\t\t\t\tyAxisID: 'y5'\n"
                                "\t\t\t\t}, {\n"
                                        "\t\t\t\t\tlabel: '照度[lux]',\n"
                                        "\t\t\t\t\tdata: data_05,\n"
                                        "\t\t\t\t\tborderColor: window.chartColors.grey,\n"
                                        "\t\t\t\t\tbackgroundColor: 'rgba(0, 0, 0, 0)',\n"
                                        "\t\t\t\t\tfill: false,\n"
                                        "\t\t\t\t\tyAxisID: 'y6'\n"
                                "\t\t\t\t}, {\n"
                                        "\t\t\t\t\tlabel: '騒音[dBm]',\n"
                                        "\t\t\t\t\tdata: data_06,\n"
                                        "\t\t\t\t\tborderColor: window.chartColors.green,\n"
                                        "\t\t\t\t\tbackgroundColor: 'rgba(0, 0, 0, 0)',\n"
                                        "\t\t\t\t\tfill: false,\n"
                                        "\t\t\t\t\tlineTension: 0,\n"
                                        "\t\t\t\t\tyAxisID: 'y7'\n"
                                "\t\t\t\t}]\n"
                        "\t\t\t},\n"
                        "\t\t\toptions: {\n"
                                "\t\t\t\tresponsive: true,\n"
                                "\t\t\t\ttitle: {\n"
                                        "\t\t\t\t\tdisplay: true,\n"
                                        "\t\t\t\t\ttext: '今日の測定結果'\n"
                                "\t\t\t\t},\n"
                                "\t\t\t\tlegend: {                               //凡例\n"
                                        "\t\t\t\t\tdisplay: true\n"
                                "\t\t\t\t},\n"
                                "\t\t\t\ttooltips: {                             //ツールチップ\n"
                                        "\t\t\t\t\tenabled: true,\n"
                                        "\t\t\t\t\tmode: 'index'\n"
                                "\t\t\t\t},\n"
                                "\t\t\t\tscales: {\n"
                                        "\t\t\t\t\txAxes: [{\n"
                                                "\t\t\t\t\t\tdisplay: true,\n"
                                                "\t\t\t\t\t\tscaleLabel: {\n"
                                                        "\t\t\t\t\t\t\tdisplay: true,\n"
                                                        "\t\t\t\t\t\t\tlabelString: '時刻 [時]'\n"
                                                "\t\t\t\t\t\t}\n"
                                        "\t\t\t\t\t}],\n"
                                        "\t\t\t\t\tyAxes: [{\n"
                                                "\t\t\t\t\t\tid:'y1',\n"
                                                "\t\t\t\t\t\tdisplay: true,\n"
                                                "\t\t\t\t\t\tscaleLabel: {\n"
                                                        "\t\t\t\t\t\t\tfontColor: window.chartColors.red,\n"
                                                        "\t\t\t\t\t\t\tdisplay: true,\n"
                                                        "\t\t\t\t\t\t\tlabelString: '圧力 [Pa]'\n"
                                                "\t\t\t\t\t\t},\n"
                                                "\t\t\t\t\t\tticks: {\n"
                                                        "\t\t\t\t\t\t\tfontColor: window.chartColors.red,\n"
                                                        "\t\t\t\t\t\t\tsuggestedMin:  94000,\n"
                                                        "\t\t\t\t\t\t\tsuggestedMax: 104000\n"
                                                "\t\t\t\t\t\t}\n"
                                        "\t\t\t\t\t},{\n"
                                                "\t\t\t\t\t\tid:'y2',\n"
                                                "\t\t\t\t\t\tscaleLabel: {\n"
                                                        "\t\t\t\t\t\t\tfontColor: window.chartColors.blue,\n"
                                                        "\t\t\t\t\t\t\tdisplay: true,\n"
                                                        "\t\t\t\t\t\t\tlabelString: '温度 [℃]'\n"
                                                "\t\t\t\t\t\t},\n"
                                                "\t\t\t\t\t\tticks: {\n"
                                                        "\t\t\t\t\t\t\tfontColor: window.chartColors.blue,\n"
                                                        "\t\t\t\t\t\t\tsuggestedMin: 0,\n"
                                                        "\t\t\t\t\t\t\tsuggestedMax: 50\n"
                                                "\t\t\t\t\t\t}\n"
                                        "\t\t\t\t\t},{\n"
                                                "\t\t\t\t\t\tid:'y3',\n"
                                                "\t\t\t\t\t\tscaleLabel: {\n"
                                                        "\t\t\t\t\t\t\tfontColor: window.chartColors.orange,\n"
                                                        "\t\t\t\t\t\t\tdisplay: true,\n"
                                                        "\t\t\t\t\t\t\tlabelString: '湿度[%]'\n"
                                                "\t\t\t\t\t\t},\n"
                                                "\t\t\t\t\t\tticks: {\n"
                                                        "\t\t\t\t\t\t\tfontColor: window.chartColors.orange,\n"
                                                        "\t\t\t\t\t\t\tsuggestedMin: 0,\n"
                                                        "\t\t\t\t\t\t\tsuggestedMax: 100\n"
                                                "\t\t\t\t\t\t}\n"
                                        "\t\t\t\t\t},{\n"
                                                "\t\t\t\t\t\tid:'y4',\n"
                                                "\t\t\t\t\t\tposition: 'right',\n"
                                                "\t\t\t\t\t\tscaleLabel: {\n"
                                                        "\t\t\t\t\t\t\tfontColor: window.chartColors.yellow,\n"
                                                        "\t\t\t\t\t\t\tdisplay: true,\n"
                                                        "\t\t\t\t\t\t\tlabelString: '浮遊粒子[mg/m3]'\n"
                                                "\t\t\t\t\t\t},\n"
                                                "\t\t\t\t\t\tticks: {\n"
                                                        "\t\t\t\t\t\t\tfontColor: window.chartColors.yellow,\n"
                                                        "\t\t\t\t\t\t\tsuggestedMin: 0,\n"
                                                        "\t\t\t\t\t\t\tsuggestedMax: 10000\n"
                                                "\t\t\t\t\t\t}\n"
                                        "\t\t\t\t\t},{\n"
                                                "\t\t\t\t\t\tid:'y5',\n"
                                                "\t\t\t\t\t\tposition: 'right',\n"
                                                "\t\t\t\t\t\tscaleLabel: {\n"
                                                        "\t\t\t\t\t\t\tfontColor: window.chartColors.purple,\n"
                                                        "\t\t\t\t\t\t\tdisplay: true,\n"
                                                        "\t\t\t\t\t\t\tlabelString: 'CO2濃度[ppm]'\n"
                                                "\t\t\t\t\t\t},\n"
                                                "\t\t\t\t\t\tticks: {\n"
                                                        "\t\t\t\t\t\t\tfontColor: window.chartColors.purple,\n"
                                                        "\t\t\t\t\t\t\tsuggestedMin:    0,\n"
                                                        "\t\t\t\t\t\t\tsuggestedMax:    5000\n"
                                                "\t\t\t\t\t\t}\n"
                                        "\t\t\t\t\t},{\n"
                                                "\t\t\t\t\t\tid:'y6',\n"
                                                "\t\t\t\t\t\tposition: 'right',\n"
                                                "\t\t\t\t\t\tscaleLabel: {\n"
                                                        "\t\t\t\t\t\t\tfontColor: window.chartColors.grey,\n"
                                                        "\t\t\t\t\t\t\tdisplay: true,\n"
                                                        "\t\t\t\t\t\t\tlabelString: '照度[lux]'\n"
                                                "\t\t\t\t\t\t},\n"
                                                "\t\t\t\t\t\tticks: {\n"
                                                        "\t\t\t\t\t\t\tfontColor: window.chartColors.grey,\n"
                                                        "\t\t\t\t\t\t\tsuggestedMin: 0,\n"
                                                        "\t\t\t\t\t\t\tsuggestedMax: 2000\n"
                                                "\t\t\t\t\t\t}\n"
                                        "\t\t\t\t\t},{\n"
                                                "\t\t\t\t\t\tid:'y7',\n"
                                                "\t\t\t\t\t\tposition: 'right',\n"
                                                "\t\t\t\t\t\tscaleLabel: {\n"
                                                        "\t\t\t\t\t\t\tfontColor: window.chartColors.green,\n"
                                                        "\t\t\t\t\t\t\tdisplay: true,\n"
                                                        "\t\t\t\t\t\t\tlabelString: '騒音[dBm]'\n"
                                                "\t\t\t\t\t\t},\n"
                                                "\t\t\t\t\t\tticks: {\n"
                                                        "\t\t\t\t\t\t\tfontColor: window.chartColors.green,\n"
                                                        "\t\t\t\t\t\t\tsuggestedMin: 0,\n"
                                                        "\t\t\t\t\t\t\tsuggestedMax: 5000\n"
                                                "\t\t\t\t\t\t}\n"
                                        "\t\t\t\t\t}]\n"
                                "\t\t\t\t}\n"
                        "\t\t\t}\n"
                "\t\t};\n"
                "\t\twindow.onload = function() {\n"
                        "\t\t\tvar ctx = document.getElementById('canvas').getContext('2d');\n"
                        "\t\t\twindow.myLine = new Chart(ctx, config);\n"
                "\t\t};\n"
        "\t</script>\n</BODY>\n"
        "</html>\n";
        return ptr;
}
//------------      setup?SBM=1&DT1=24&DT2=a&DT3=c4&DT4=61&DT5=1e&DT6=7c&
//                              DT1=36&DT2=10&DT3=196&DT4=97&DT5=30&DT6=124&
//                              DT7=192&DT8=168&DT9=0&DT10=33&
//                              DT11=255&DT12=255&DT13=255&DT14=0&
//                              DT15=192&DT16=168&DT17=0&DT18=1&
//                              DT19=**********&DT20=**********&
//                              DT21=24&DT22=a&DT23=c4&DT24=61&DT25=1e&DT26=7d&
//                              DT21=36&DT22=10&DT23=196&DT24=97&DT25=30&DT26=125&
//                              DT27=192&DT28=168&DT29=250&DT30=33&
//                              DT31=255&DT32=255&DT33=255&DT34=0&
//                              DT35=192&DT36=168&DT37=250&DT38=1&
//                              DT39=ESP_AP&DT40=password
void handleUpdate() {
        Serial.println("SBM");
        digitalWrite(PO4, false);
        if(!WS.hasArg("SBM=1")){                                        //引数に"SBM=1"が含まれていることを確認
                handleNotFound();
                return;
        }
        String cmd = WS.arg("SBM=1");
        Serial.println(cmd);
        digitalWrite(PO4, true);
}
//------------ BMPprint
void printOLCDValues() {
        BME280::TempUnit tempUnit(BME280::TempUnit_Celsius);
        BME280::PresUnit presUnit(BME280::PresUnit_Pa);
        bmp.read(Vprs, Vtmp, Vhum, tempUnit, presUnit);
        sprintf(buf1,"Temperature:%.2f%1s",Vtmp,String(tempUnit == BME280::TempUnit_Celsius ? 'C' :'F'));
        sprintf(buf2,"Pressure: %.0f Pa",Vprs); 
        buf11   = String(buf1);
        buf22   = String(buf2);
        //Serial.println(buf1);
        //Serial.println(buf2);
        //CO2濃度取得
        if (mySensor.dataAvailable()){
                mySensor.readAlgorithmResults();
                Vco2            =       mySensor.getCO2();
                float TVOC      =       mySensor.getTVOC();
                sprintf(buf4,"eCO2:%4.0f TVOC:%3.0f",Vco2,TVOC);
        }
        //delay(10);                                              //Don't spam the I2C bus
       
        /*
        if(ccs.available()){
                //有効時期を確認してデータ取得しないととんでもない不安定値が取得されてしまう。
                delay(100);
                float temp = ccs.calculateTemperature();
                if(!ccs.readData()){
                        Vco2            = ccs.geteCO2();
                        float TVOC      = ccs.getTVOC();
                        sprintf(buf4,"eCO2:%4.0f TVOC:%3.0f",Vco2,TVOC); 
                }                
        }
        */
        //照度取得
        unsigned int data0, data1;
        if (light.getData(data0,data1)){
                boolean good = light.getLux(TSLgain,TSLms,data0,data1,Vlux);
                sprintf(buf5,"lux:%5.0f lx (%s)",Vlux,good? "Good" : "Bad");
        }
        OLCD.setCursor(0, 24);
        OLCD.fillRect(0,24,128,32,SSD1306_BLACK);
        OLCD.setTextSize(1);OLCD.setTextColor(SSD1306_WHITE);OLCD.setCursor(0, 24);
        OLCD.println(buf1);
        OLCD.println(buf2);
        OLCD.println(buf4);
        OLCD.println(buf5);
        OLCD.display();
}
//------------ printTime
int printOLCDTime() {
        getLocalTime(&timeinfo);
        sprintf(buf3,"%04d/%02d/%02d %02d:%02d:%02d",timeinfo.tm_year+1900,timeinfo.tm_mon+1,timeinfo.tm_mday,timeinfo.tm_hour,timeinfo.tm_min,timeinfo.tm_sec);
        //Serial.println(buf3);
        OLCD.setCursor(0, 56);
        OLCD.fillRect(0,56,128,8,SSD1306_BLACK);
        OLCD.setTextSize(1);OLCD.setTextColor(SSD1306_WHITE);OLCD.setCursor(0, 56);
        OLCD.println(buf3);
        return timeinfo.tm_hour;
}
//------------ printIndexTime
int printOLCDMinIndex(){
        getLocalTime(&timeinfo);
        return  timeinfo.tm_min % 10;
}
//------------ デバッグ用
void printLocalTime(){
        struct tm timeinfo;
        if (!getLocalTime(&timeinfo)) {
                Serial.println(F("Failed to obtain time"));
                return;
        }
        Serial.println(&timeinfo, "%Y %m %d %a %H:%M:%S");              //日本人にわかりやすい表記へ変更
}
//--------ContentTypeを拡張子で判断しているがこれでいいのか???
String getContentType(String filename){
        if(WS.hasArg("download"))               return "application/octet-stream";
        else if(filename.endsWith(".htm"))      return "text/html";
        else if(filename.endsWith(".html"))     return "text/html";
        else if(filename.endsWith(".css"))      return "text/css";
        else if(filename.endsWith(".js"))       return "application/javascript";
        else if(filename.endsWith(".png"))      return "image/png";
        else if(filename.endsWith(".gif"))      return "image/gif";
        else if(filename.endsWith(".jpg"))      return "image/jpeg";
        else if(filename.endsWith(".ico"))      return "image/x-icon";
        else if(filename.endsWith(".xml"))      return "text/xml";
        else if(filename.endsWith(".pdf"))      return "application/x-pdf";
        else if(filename.endsWith(".zip"))      return "application/x-zip";
        else if(filename.endsWith(".gz"))       return "application/x-gzip";
        return "text/plain";
}
//------------------
void printError(byte error){
        // If there's an I2C error, this function will
        // print out an explanation.
        Serial.print(F("I2C error: "));Serial.print(error,DEC);Serial.print(F(", "));
        switch(error){
                case 0:
                        Serial.println(F("success"));
                        break;
                case 1:
                        Serial.println(F("data too long for transmit buffer"));
                        break;
                case 2:
                        Serial.println(F("received NACK on address (disconnected?)"));
                        break;
                case 3:
                        Serial.println(F("received NACK on data"));
                        break;
                case 4:
                        Serial.println(F("other error"));
                        break;
                default:
                        Serial.println(F("unknown error"));
        }
}
//------------ SDカード読み書きに必要な関数群
//----------------
void listDir(fs::FS &fs, const char * dirname, uint8_t levels){
        Serial.printf("Listing directory: %s\n", dirname);
        //
        File root = fs.open(dirname);
        if(!root){
                Serial.println(F("Failed to open directory"));
                return;
        }
        if(!root.isDirectory()){
                Serial.println(F("Not a directory"));
                return;
        }
        //
        File file = root.openNextFile();
        while(file){
                if(file.isDirectory()){
                        Serial.print(F("  DIR : "));
                        Serial.println(file.name());
                        if(levels){
                                listDir(fs, file.name(), levels -1);
                        }
                } else {
                        Serial.print(F("  FILE: "));
                        Serial.print(file.name());
                        Serial.print(F("  SIZE: "));
                        Serial.println(file.size());
                }
                file = root.openNextFile();
        }
}
//----------------
void createDir(fs::FS &fs, const char * path){
        Serial.printf("Creating Dir: %s\n", path);
        if(fs.mkdir(path)){
                Serial.println(F("Dir created"));
        } else {
                Serial.println(F("mkdir failed"));
        }
}
//----------------
void removeDir(fs::FS &fs, const char * path){
        Serial.printf("Removing Dir: %s\n", path);
        if(fs.rmdir(path)){
                Serial.println(F("Dir removed"));
        } else {
                Serial.println(F("rmdir failed"));
        }
}
//----------------
void readFile(fs::FS &fs, const char * path){
        Serial.printf("Reading file: %s\n", path);
        //
        File file = fs.open(path);
        if(!file){
                Serial.println(F("Failed to open file for reading"));
                return;
        }
        //
        Serial.print(F("Read from file: "));
        while(file.available()){
                Serial.write(file.read());
        }
        file.close();
}
//----------------
void writeFile(fs::FS &fs, const char * path, const char * message){
        Serial.printf("Writing file: %s\n", path);
        //
        File file = fs.open(path, FILE_WRITE);
        if(!file){
                Serial.println(F("Failed to open file for writing"));
                return;
        }
        if(file.print(message)){
                Serial.println(F("File written"));
        } else {
                Serial.println(F("Write failed"));
        }
        file.close();
}
//----------------
void appendFile(fs::FS &fs, const char * path, const char * message){
        Serial.printf("Appending to file: %s\n", path);
        //
        File file = fs.open(path, FILE_APPEND);
        if(!file){
                Serial.println(F("Failed to open file for appending"));
                return;
        }
        if(file.print(message)){
                Serial.println(F("Message appended"));
        } else {
                Serial.println(F("Append failed"));
        }
        file.close();
}
//----------------
void renameFile(fs::FS &fs, const char * path1, const char * path2){
        Serial.printf("Renaming file %s to %s\n", path1, path2);
        if (fs.rename(path1, path2)) {
                Serial.println(F("File renamed"));
        } else {
                Serial.println(F("Rename failed"));
        }
}
//----------------
void deleteFile(fs::FS &fs, const char * path){
        Serial.printf("Deleting file: %s\n", path);
        if(fs.remove(path)){
                Serial.println(F("File deleted"));
        } else {
                Serial.println(F("Delete failed"));
        }
}
//------------ setup function
void setup(void) {
        //
        Serial.begin(115200);
        //SDカード周りの初期化
        if(!SD.begin()){
                Serial.println(F("Card Mount Failed"));
                return;
        }
        uint8_t cardType = SD.cardType();
        if(cardType == CARD_NONE){
                Serial.println(F("No SD card attached"));
                return;
        }
        Serial.print(F("SD Card Type: "));
        if(cardType == CARD_MMC){
                Serial.println(F("MMC"));
        } else if(cardType == CARD_SD){
                Serial.println(F("SDSC"));
        } else if(cardType == CARD_SDHC){
                Serial.println(F("SDHC"));
        } else {
                Serial.println(F("UNKNOWN"));
        }
        uint64_t cardSize = SD.cardSize() / (1024 * 1024);
        Serial.printf("SD Card Size: %lluMB\n", cardSize);
        //
        int bufSize = sizeof(st_esp);
        EEPROM.begin(bufSize);                                          //構造体のサイズ設定
        st_esp  buf;
        Serial.print(F("bufSize="));Serial.println(bufSize);
        EEPROM.get<st_esp>(0, buf);                                     //EEPROM読込
        Serial.print(F("bufID=0x"));Serial.println(buf.id,HEX);
        //MACアドレスの取得
        esp_read_mac(mac,       ESP_MAC_WIFI_STA);                      //StationModeのMacAddress
        esp_read_mac(apMac,     ESP_MAC_WIFI_SOFTAP);                   //APModeのMacAddress
        if(buf.id == ID){
                Serial.println(F("bufID:Match"));
                //既に情報がある場合は読込
                //for (int i = 0; i < 6; i++){
                //      mac[i]          = buf.DT[i];
                //}
                for (int i = 0; i < 4; i++){
                        ip[i]           = buf.DT[i+6];
                }
                for (int i = 0; i < 4; i++){
                        netmask[i]      = buf.DT[i+10];
                }
                for (int i = 0; i < 4; i++){
                        gateway[i]      = buf.DT[i+14];
                }
                for (int i = 0; i < 4; i++){
                        apIp[i]         = buf.DT[i+26];
                }
                for (int i = 0; i < 4; i++){
                        apNetmask[i]      = buf.DT[i+30];
                }
                for (int i = 0; i < 4; i++){
                        apGateway[i]      = buf.DT[i+34];
                }
                strcpy(ssid,buf.ssid);
                Serial.print(F("ssid ="));              Serial.println(ssid);
                strcpy(pass,buf.pass);
                Serial.print(F("ap_password ="));       Serial.println(apPassword);
                strcpy(apSsid,buf.apSsid);
                Serial.print(F("ap_ssid ="));           Serial.println(apSsid);
                strcpy(apPassword,buf.apPass);
                Serial.print(F("ap_password ="));       Serial.println(apPassword);
                timeZone        = float(buf.timeZoneVal) / 10;
                Serial.print(F("timeZone ="));       Serial.println(timeZone,2);
                strcpy(ntpServer,buf.ntpServer);
                Serial.print(F("ntpServer ="));       Serial.println(ntpServer);
        }else{
                Serial.println(F("bufID:NotMatch"));
                //情報が書き込まれていないので初期値を書き込んでおく
                for (int i = 0; i < 6; i++){
                        buf.DT[i]     = mac[i];
                }
                for (int i = 0; i < 4; i++){
                        buf.DT[i+6]     = ip[i];
                }
                for (int i = 0; i < 4; i++){
                        buf.DT[i+10]    = netmask[i];
                }
                for (int i = 0; i < 4; i++){
                        buf.DT[i+14]    = gateway[i];
                }
                for (int i = 0; i < 6; i++){
                        buf.DT[i+20]    = apMac[i];
                }
                for (int i = 0; i < 4; i++){
                        buf.DT[i+26]    = apIp[i];
                }
                for (int i = 0; i < 4; i++){
                        buf.DT[i+30]    = apNetmask[i];
                }
                for (int i = 0; i < 4; i++){
                        buf.DT[i+34]    = apGateway[i];
                }
                strcpy(buf.ssid,ssid);
                strcpy(buf.pass,pass);
                strcpy(buf.apSsid,apSsid);
                strcpy(buf.apPass,apPassword);
                buf.timeZoneVal = char(timeZone * 10.0);
                strcpy(buf.ntpServer,ntpServer);
                buf.id  = ID;
                EEPROM.put<st_esp>(0, buf);
                EEPROM.commit();
        }
//        */
        pinMode(LED,OUTPUT);    digitalWrite(LED,LOW);                  //Onboard LED port Direction output
        pinMode(PO1,OUTPUT);    digitalWrite(PO1,LOW);
        pinMode(PO2,OUTPUT);    digitalWrite(PO2,LOW);
        pinMode(PO3,OUTPUT);    digitalWrite(PO3,LOW);
        //pinMode(PO4,OUTPUT);  digitalWrite(PO4,LOW);
        //ledcSetup(LEDC_CHANNEL_0,1000,8);                             //double ledcSetup(uint8_t chan, double freq, uint8_t bit_num);
        //ledcAttachPin(PO3, LEDC_CHANNEL_0);                           //void ledcAttachPin(uint8_t pin, uint8_t chan);
        ledcSetup(LEDC_CHANNEL_1,1000,8);
        ledcAttachPin(PO4, LEDC_CHANNEL_1);
        pinMode(PI1,INPUT);
        pinMode(PI2,INPUT);
        pinMode(PI3,INPUT);
        pinMode(PI4,INPUT);
        //SSD1306_SWITCHCAPVCC =内部で3.3Vから表示電圧を生成
        Wire.setClock(400000);                                          // クロック速度を最も速くなるように設定して、通信を改善します(高速モード)
        if(!OLCD.begin(SSD1306_SWITCHCAPVCC, OLCDAres)) {               // 128x64のスレーブアドレスオリジナル値は0x3Dでしたが手元の元は0x3Cでした。
                Serial.println(F("SSD1306 allocation failed"));
                for(;;);                                                // Don't proceed, loop forever
        }
        status = bmp.begin();
        if (!status) {
                Serial.println(F("Could not find BME280 sensor!"));
        }
        // bme.chipID(); // Deprecated. See chipModel().
        switch(bmp.chipModel()){
                case BME280::ChipModel_BME280:
                        Serial.println(F("Found BME280 sensor! Success."));
                        break;
                case BME280::ChipModel_BMP280:
                        Serial.println(F("Found BMP280 sensor! No Humidity available."));
                        break;
                default:
                        Serial.println(F("Found UNKNOWN sensor! Error!"));
        }
        //TSL2561 setup
        light.begin(TSLAres);
        unsigned char I2CID;
        if(!light.getID(I2CID)){
                //Error
                byte error = light.getError();
                printError(error);
        }
        TSLgain = 0;
        unsigned char time = 2;
        light.setTiming(TSLgain,time,TSLms);
        light.setPowerUp();
        //CO2センサセットアップ SparkFunのライブラリに変更
        if (!mySensor.begin()){
                Serial.println(F("CCS811 error. Please check wiring. Freezing..."));
                fCcsRun = false;
        }
        /*
        if(!ccs.begin(CCSAres)){
                Serial.println(F("CCS skiped.Failed to start sensor! Please check your wiring."));
                fCcsRun = false;
        }
        */
        if(fCcsRun){
                mySensor.setDriveMode(4);
                if (mySensor.dataAvailable()){
                        //If so, have the sensor read and calculate the results.
                        //Get them later
                        mySensor.readAlgorithmResults();
                        //Returns calculated CO2 reading
                        Serial.print(F("CO2["));        Serial.print(mySensor.getCO2());        Serial.println(F("]"));
                        //Returns calculated TVOC reading
                        Serial.print(F("tVOC["));       Serial.print(mySensor.getTVOC());       Serial.println(F("]"));
                        //Display the time since program start
                        //Serial.print(F("millis["));     Serial.print(millis());                 Serial.println("]");
                }
                delay(10);                                              //Don't spam the I2C bus
                /*
                ccs.setDriveMode(CCS811_DRIVE_MODE_60SEC);              //CCS811_DRIVE_MODE_1SEC/CCS811_DRIVE_MODE_60SEC 
                while(!ccs.available());
                float temp = ccs.calculateTemperature();
                ccs.setTempOffset(temp - 25.0);                         //25の根拠不明
                */
        }        
        // バッファをクリアします
        OLCD.clearDisplay();
        // SPIFFSのセットアップ  10.2で追加
        if(!SPIFFS.begin(true)){
                Serial.println("An Error has occurred while mounting SPIFFS");
                return;
        }
        // アクセスポイント(無線LAN親機) + ステーション(無線LAN子機)
        WiFi.mode(WIFI_AP_STA);
        // まず既存のアクセスポイント(ネットワーク)に接続する
        WiFi.config(ip,gateway,netmask);                              //固定IPにする場合に使用
        WiFi.begin(ssid, pass);
        // Wait for connection
        Serial.println(F(""));
        while (WiFi.status() != WL_CONNECTED) {                         //接続するまでループするが、すごく電流を喰うようでchipが熱くなる。要対策。
                delay(1000);
                Serial.print(F("."));
        }
        Serial.println(F(""));
        Serial.print(F("Connected to "));Serial.println(ssid);
        IPAddress myIP =WiFi.localIP();
        Serial.print(F("IP address: "));Serial.println(myIP);
        OLCD.setTextSize(1);OLCD.setTextColor(SSD1306_WHITE);OLCD.setCursor(0, 0);
        OLCD.print(F("LoginID:"));OLCD.println(myID);
        OLCD.print(F("LoginPASS:"));OLCD.println(myPASS);
        OLCD.print(F("IP:"));OLCD.println(myIP);
        //OLCD.display();
        // SoftAPを開始する
        WiFi.softAPConfig(apIp,apGateway,apNetmask);
        WiFi.softAP(apSsid, apPassword);
        //IPAddress apIp = WiFi.softAPIP();
        OLCD.setTextSize(1);OLCD.setTextColor(SSD1306_WHITE);OLCD.setCursor(0, 40);
        OLCD.print(F("SSID:"));OLCD.println(apSsid);
        OLCD.print(F("PASS:"));OLCD.println(apPassword);
        OLCD.print(F("IP:"));OLCD.println(apIp[0]);OLCD.println(F("."));OLCD.println(apIp[1]);OLCD.println(F("."));OLCD.println(apIp[2]);OLCD.println(F("."));OLCD.println(apIp[3]);
        OLCD.display();
        Serial.print(F("SoftAPのIPアドレス(LAN側): "));Serial.println(myIP);
        //init and get the time
        configTime(gmtOffset_sec, daylightOffset_sec, ntpServer);                       //void configTime(long gmtOffset_sec, int daylightOffset_sec, const char* server1, const char* server2, const char* server3);
        printLocalTime();
        //時刻を取得したので、ログファイルを作成する。
        createDir(SD, "/ESPdir");                                                       //ESPフォルダを作成してこの中にログファイルを作成する。
        getLocalTime(&timeinfo);
        sprintf(f_name,"/ESPdir/%04d%02d%02d%02d%02d%02d.txt",timeinfo.tm_year+1900,timeinfo.tm_mon+1,timeinfo.tm_mday,timeinfo.tm_hour,timeinfo.tm_min,timeinfo.tm_sec);
        //ホスト名解決にmdnsを使用する
        if (!MDNS.begin(host)) {                                                        //https://esp32.local
                Serial.println(F("Error setting up MDNS responder!"));
                OLCD.setCursor(0, 56);OLCD.print(F("Error setting up MDNS responder!"));
                while (1) {
                        delay(1000);
                }
        }
        Serial.println(F("mDNS responder started"));
        //serverIndexに格納されているインデックスページを返す
        WS.on("/login", HTTP_GET, []() {
                WS.sendHeader("Connection", "close");
                WS.send(200, "text/html", loginIndex);
                OLCD.fillRect(0,56,128,8,SSD1306_BLACK);
                OLCD.setCursor(0, 56);
                OLCD.setTextColor(SSD1306_WHITE);
                OLCD.print(F("Connection"));
                OLCD.display();
        });
        WS.on("/serverIndex", HTTP_GET, []() {
                WS.sendHeader("Connection", "close");
                WS.send(200, "text/html", serverIndex);
                OLCD.fillRect(0,56,128,8,SSD1306_BLACK);
                OLCD.setCursor(0, 56);
                OLCD.setTextColor(SSD1306_WHITE);
                OLCD.print(F("UpdateReady"));
                OLCD.display();
        });
        //ファームウェアファイルのアップロードの処理
        WS.on("/update", HTTP_POST, []() {
                WS.sendHeader("Connection", "close");
                OLCD.setCursor(0, 56);
                OLCD.drawRect(0,56,128,8,SSD1306_BLACK);
                OLCD.display();
                WS.send(200, "text/plain", (Update.hasError()) ? "FAIL" : "OK");
                ESP.restart();
        }, []() {
                HTTPUpload& upload = WS.upload();
                if (upload.status == UPLOAD_FILE_START) {
                        Serial.printf("Update: %s\n", upload.filename.c_str());
                        OLCD.setCursor(0, 56);
                        OLCD.drawRect(0,56,128,8,SSD1306_BLACK);
                        OLCD.setTextColor(SSD1306_WHITE);
                        OLCD.print(F("Updating..."));
                        OLCD.display();
                        if (!Update.begin(UPDATE_SIZE_UNKNOWN)) {       //利用可能な最大サイズから始める
                                Update.printError(Serial);
                        }
                } else if (upload.status == UPLOAD_FILE_WRITE) {
                        //ESPへのファームウェアのフラッシュ
                        if (Update.write(upload.buf, upload.currentSize) != upload.currentSize) {
                                Update.printError(Serial);
                        }
                } else if (upload.status == UPLOAD_FILE_END) {
                        if (Update.end(true)) {                         //サイズを現在の進行状況に設定する場合はtrue
                                Serial.printf("Update Success: %u\nRebooting...\n", upload.totalSize);
                        } else {
                                Update.printError(Serial);
                        }
                }
        });
        WS.on("/GetData", handleGetData);                               //To get update of ADC Value only
        WS.on("/setup?", handleUpdate);                                 //To SetUpPage
        WS.on("/setup", handleSetUp);                                   //To SetUpPage
        WS.on("/favicon.ico", handleNotFound);
        WS.on("/Chart.bundle.js", handleNotFound);
        WS.on("/utils.js", handleNotFound);
        WS.on("/", HTTP_GET, []() {                                     //ルートアクセス時の応答関数を設定
                WS.sendHeader("Connection", "close");
                for(int j = 0;j<7;j++){
                        gDataBuf[j]     =      gSMAAve[j];
                }
                WS.send(200, "text/html", SendHTML(gDataBuf));
                //OLCD.setCursor(0, 32);
                //OLCD.fillRect(0,32,128,8,SSD1306_BLACK);
                //OLCD.display();
        });
        WS.onNotFound(handleNotFound);                                  //不正アクセス時の応答関数を設定
        WS.begin();                                                     //ウェブサーバ開始
        printOLCDValues();                                             
        for(int i = 0;i<10;i++){
                gSMABuf[i][0]   =       Vprs;
                gSMABuf[i][1]   =       Vtmp;
                gSMABuf[i][2]   =       Vhum;
                gSMABuf[i][3]   =       Vdin;
                gSMABuf[i][4]   =       Vco2;
                gSMABuf[i][5]   =       float(Vlux);
                gSMABuf[i][6]   =       analogRead(AIport);                
        }
        for(int i = 0;i<7;i++){
                gSMAAve[i]      =       gSMABuf[0][i];
        }
        //BufferClear
        for(int i = 0;i<24;i++){
                for(int j = 0;j<7;j++){
                       gDayBuf[i][j]    = NAN; 
                }
        }        
        //WDT
        timer = timerBegin(0, 80, true);                                //timer 0, div 80
        timerAttachInterrupt(timer, &resetModule, true);                //attach callback
        timerAlarmWrite(timer, wdtTimeout * 1000000, false);            //set time in us
        timerAlarmEnable(timer);                                        //enable interrupt
}
//------------
int count       = 0;
int preSeth     = -1;
int preSetS     = -1;
int preIndex    = -1;
void loop(void) {
        timerWrite(timer, 0);                                           //reset timer (feed watchdog)
        WS.handleClient();                                              //クライアントからの要求を処理する
        //delay(100);
        printOLCDValues();                                              //OLCDにデータを更新表示
        for(int i = 0;i<7;i++){
                gSMAAve[i] = gSMAAve[i]*7.0 - gSMABuf[count][i];
        }
        gSMABuf[count][0]   =       Vprs;
        gSMABuf[count][1]   =       Vtmp;
        gSMABuf[count][2]   =       Vhum;
        gSMABuf[count][3]   =       Vdin;
        gSMABuf[count][4]   =       Vco2;
        gSMABuf[count][5]   =       float(Vlux);
        gSMABuf[count][6]   =       analogRead(AIport);
        for(int i = 0;i<7;i++){
                gSMAAve[i] = (gSMAAve[i] + gSMABuf[count][i]) / 7.0;
        }
        getLocalTime(&timeinfo);
        int s = timeinfo.tm_sec;
        if(preSetS != s){
                preSetS = s;
                sprintf(buf6,"%04d%02d%02d%02d%02d%02d,%.1f,%.1f,%.1f,%.1f,%.1f,%.1f,%.1f",timeinfo.tm_year+1900,timeinfo.tm_mon+1,timeinfo.tm_mday,timeinfo.tm_hour,timeinfo.tm_min,timeinfo.tm_sec,gSMAAve[0],gSMAAve[1],gSMAAve[2],gSMAAve[3],gSMAAve[4],gSMAAve[5],gSMAAve[6]);
                logFile = SD.open(f_name,FILE_APPEND);
                if(logFile){
                        logFile.println(buf6);
                        logFile.close();
                }
        }
        int h = timeinfo.tm_hour;                                               //OLCDに時刻表示 返値0-23
        //int m = printOLCDMinIndex();                                          //0-9 index
        //ループして遅延なく点滅する
        unsigned long currentMillis = millis();
        if(preSeth != h){
                //1時間おきに時刻を確認し、データをメモリに確保
                preSeth  = h;
                for(int i = 0;i<7;i++){
                        gDayBuf[h][i]   = gSMAAve[i];
                }
                digitalWrite(PO3, true);
                //次回分のデータをNULL化
                h = (h+1) % 24;
                for(int i = 0;i<7;i++){
                        gDayBuf[h][i]   = NAN;
                }
        }
        if (currentMillis - previousMillis >= interval) {
                //最後にLEDを点滅させた時間を保存します
                previousMillis = currentMillis;
                //LEDがオフの場合はオンにし、逆の場合も同様です。
                ledState = not(ledState);
                //変数のledStateでLEDを設定します。
                digitalWrite(LED, ledState);
                switch(count){
                        case 0:
                                digitalWrite(PO1, true);
                                digitalWrite(PO2, false);
                                //digitalWrite(PO3, false);
                                //digitalWrite(PO4, false);
                                break;
                        case 1:
                                digitalWrite(PO1, false);
                                digitalWrite(PO2, true);
                                //digitalWrite(PO3, false);
                                //digitalWrite(PO4, false);
                                break;
                        case 2:
                                digitalWrite(PO1, false);
                                digitalWrite(PO2, false);
                                //digitalWrite(PO3, true);
                                //digitalWrite(PO4, false);
                                break;
                        case 3:
                                digitalWrite(PO1, false);
                                digitalWrite(PO2, false);
                                //digitalWrite(PO3, false);
                                //digitalWrite(PO4, true);
                                break;
                        default:
                                digitalWrite(PO1, false);
                                digitalWrite(PO2, false);
                                //digitalWrite(PO3, false);
                                //digitalWrite(PO4, false);
                                break;
                }
                //ledcWrite(LEDC_CHANNEL_0, brightness);
                ledcWrite(LEDC_CHANNEL_1, 255 - brightness);
                brightness = brightness + fadeAmount;
                if (brightness <= 0){
                        brightness = 1;
                        fadeAmount = abs(fadeAmount);
                        digitalWrite(PO3, false);
                }else if(brightness >= 255){
                        brightness = 254;
                        fadeAmount = -abs(fadeAmount);
                }
                count = (count + 1) % 10;
                Serial.println(count);
        }
}

SDHCはFAT32でフォーマットします。仕様上2GBのファイルまで作成出来るはずです。
※多分追記だけなら4GBも大丈夫だと思うのですが、シークを考えると2GBに押さえておいた方が必要です。

2GBで1データどの程度のバイト数が許されるのか確認しました。1年間1サンプル毎秒とすると、

2[GB]/365/24/60/20 = 68[B]

改行コード(\r\n)を考慮すると、1レコードは64[Byte]に抑えるべきとなります。上記サンプルスケッチでは1レコード60[Byte]なので、1年間は動くのかなと云う事になります。
※書き込めなくなったときの挙動を確認しておく必要がありそうです。


2021/3/22

SDカードへのデータロギングはちゃんと動いています。ただ、外部LEDの反応がちょっと遅くなっている感じがあります。
LoopScanTimeもロギング内容に追加すべきなのかと思いました。
プログラムコードではtimestampで秒が更新されたら書き込む事にしています。秒数回Loopが回っている可能性が十分あるのですが、ログファイルが大きくなる書込がに要する時間が長くなっているような気がするのです。
当然HTTP要求があればScanTimeが長くなります。それもScanTimeパラメータがロギングされていれば、HTTP要求があったかどうかの判断が出来そうです。

ScanTimeをロギング項に追加して見ました。初期状態では33msec前後でした。

/*
 * 2021/3/22 T.Wanibe
 * 12.3更新:
 *      12.1,12,2がコンパイルエラーとなる原因不明のため ベースを12.0に戻して再構築
 *      exit status 1
 *      invalid conversion from 'int' to 'const char*' [-fpermissive]
 *      変更点は、SDログ内容の見直です。Loop()のScasnTimeをログに含めたいと思いました。
 *      これによりログファイルが巨大になるとScasnTimeがどうなるのか知りたいと思います。
 *      また、HTTP要求が有ったかどうかの判断も出来るのでは無いかと考えます。
 * 12.0更新:
 *      大きな問題を確認し対策を検討しているバージョンです。
 *      問題とは、Stationモードでルータのアクセスポイントに接続していて、何らかの理由でdisconnectしたときの
 *      対処です。当初、ESP32側が接続を監視し自動再接続するモノと思っていたのですが、実際には実現出来ないようです。
 *      自分でWDTで接続確認し、disconnectしていたら、再接続処理を行う必要があるようです。この対策を検討中です。
 *      WiFi.status() != WL_CONNECTED で判断して接続に失敗していたら、一旦DisConnectして再接続して見た
 *      のですがうまく実現出来ない事を確認しました。
 * 11.0更新:
 *      TFカードに計測データを記録するように修正
 *      SDカードモジュールはCATALEX MicroSD Card ModuleでESP32DevCと接続します。
 * 1    CS-----------IO5
 * 2    SCK----------IO18
 * 3    MOSI---------IO17
 * 4    MISO---------IO19
 * 5    VCC----------5V
 * 6    GND----------GND
 *      32GBのSDHCカードが使えることを確認しました。
 * 10.6更新:
 *      WDT機能を追加
 *      計測データの移動平均化
 *      isnan()を使うように修正しました。
 * CCS811の計測値が安定しないためSparkFunのドライバに変更してみる
 * 照度センサ(TSL2561) 空気品質センサ(CCS811)を追加
 * I2Cアドレス管理
 * TSL2561:     0x39
 * CCS811:      0x5A
 * OLCD1306:    0x3C
 * BME280:      0x76
 * SPIFFSをサポートし,WebPageからJavaScriptの読込先を外部に依存しないように変更してみた
 * ChartJSに馴れてきたこともあり、もう少しグラフ表示を凝ったモノにして見る。
 * 24時間更新に関し、更新した次のデータ枠をNULLにするようにした。更新位置を明確にするため
 * /setup 頁を作成 EEPROMに必要な情報を書き込むように変更
 * 『Indoor environment monitor』としてプロジェクトを更新
 * Chart.jsを作り込み強化
 * フラッシュの使用量が多少増えているが、まだまだ大丈夫そう
 * 1日分のデータバッファをサイクリックに使うことにした。そのため初期値をキチンと定義することにした。
 * データが無い場合はNULLにすることでグラフプロットが無くなることが判っているので対応した。
 * RootのHTML表示スタイルを更新した。
 * 時刻表示を追加して見ました。起動時にNTPサーバにアクセスし、時刻取得。ESP32内部のRTCに登録してOLCDに時刻表示する
 * ESP32のGPIOの一部のpinにはArduinoに用意されているanalogWrite()がありません。DACが存在するからかもしれません。
 * その代わりなのかLED_PWMという機能が用意されているようです。https://garretlab.web.fc2.com/arduino/lab/ledc/
 * この機能が利用できるPinは16個のようです。今回PO3(GPIO4)、PO4(GPIO2)が対象pinのようですので、改造してみました。
 * PWMによるLEDの明るさ調整ですが、単純に直線的な輝度制御は難しいですね
 * LEDの配置等を変更
 * OTAを維持したまま、モードをST+APにして普段はPCとの間でトランシーバモード、一方、ファーム更新はSTモードで実現出来ないか確認する
 * Adafruit_BME280.hがESP32でうまく動作しないため、ライブラリを変更し、BME280I2C.hを採用した
 * WebServer  on()の扱いをリファレンスで確認する必要があります。
 * https://garretlab.web.fc2.com/arduino/esp32/reference/libraries/WebServer/WebServer_on.html
 * この関数の為、WiznetのWebServer関数との整合性がとれない。 ESP32とSTM32の共存は難しそう
 * この記述はESP8266/32のみ
 * ※HTMLの記述にてString型を駆使して記述するが、その手法も多岐にわたる。好みの方法を確立しておく必要がある
 * 最大1310720バイトのフラッシュメモリのうち、スケッチが929806バイト(70%)を使っています。
 * 最大327680バイトのRAMのうち、グローバル変数が45216バイト(13%)を使っていて、ローカル変数で282464バイト使うことができます。
 * ブラウザからの転送が20%程度で止まってしまいます。シリアルによる書込は問題ないです。
 */
#include <WiFi.h>
#include <time.h>
#include <WiFiClient.h>
#include <WebServer.h>
#include <ESPmDNS.h>
#include <Update.h>
#include "SPIFFS.h"
#include "FS.h"
#include "SD.h"
#include "SPI.h"
#include "Adafruit_GFX.h"
#include "Adafruit_SSD1306.h"
#include <BME280I2C.h>
#include "SparkFunTSL2561.h"
#include "SparkFunCCS811.h"
//#include <Adafruit_CCS811.h>
#include "arduino_secrets.h"                                            //機密データを「Secret」タブ/arduino_secrets.hに入力してください
#include <EEPROM.h>
#include "esp_system.h"                                                 //MACマックアドレスを読み込む
//#define COMSCANDEC 0xC8
//#define COMSCANINC 0xC0
#define COMSCANDEC      0xC0
#define COMSCANINC      0xC8
//#define SETCOMPINS    0xA0
#define SETCOMPINS      0xA1
//#define SETSEGMENTREMAP 0xA1
#define SETSEGMENTREMAP 0xA0
#define HTTPport        80
#define SCREEN_WIDTH    128                                             // OLED display width, in pixels
#define SCREEN_HEIGHT   64                                              // OLED display height, in pixels
#define OLED_RESET      -1                                              // Reset pin # (or -1 if sharing Arduino reset pin)
#define NUMFLAKES       10                                              // アニメーション例の雪片の数
#define LOGO_HEIGHT     16
#define LOGO_WIDTH      16
#define OLCDAres        0x3C                                            //OLCD1306:     0x3C か 0x3D
#define TSLAres         0x39                                            //TSL2561:      0x39 0x29 0x49
#define CCSAres         0x5A                                            //CCS811:       0x5A 0x5B
#define BMEAres         0x76                                            //BME280:       0x76 0x77
#define LED             13                                              //ボード上のLEDは使えないのでGPIO13にLEDを接続
#define AIport          A6
#define PO1             17
#define PO2             16
#define PO3             4
#define PO4             2
#define PI1             39
#define PI2             34
#define PI3             35
#define PI4             33
#define LEDC_CHANNEL_0  0                                               // use first channel of 16 channels (started from zero)
#define LEDC_CHANNEL_1  1                                               // use first channel of 16 channels (started from zero)
const char*     host            = "esp32";
char            ssid[16]        = SECRET_SSID;                          //ネットワークSSID(名前)
char            pass[16]        = SECRET_PASS;                          //ネットワークパスワード(WPAの使用、またはWEPのキーとして使用)
String          myID            = "admin";
String          myPASS          = "password";
//variabls for blinking an LED with Millis
unsigned long   previousMillis  = 0;                                    //LEDが最後に更新されたときに保存されます
const long      interval        = 1000;                                 //点滅する間隔(ミリ秒)
int             ledState        = LOW;                                  //LEDの設定に使用されるledState
unsigned        status;
//Station用
byte            mac[]           = {0x24,0x0A,0xC4,0x61,0x1E,0x7C};      //Espressif Inc この企業は60個ほど保有している模様
byte            apMac[]         = {0x24,0x0A,0xC4,0x61,0x1E,0x7D};      //mac+1になっている模様 実際には本体に問い合わせ
byte            ip[]            = {192, 168, 0, 33};
byte            gateway[]       = {192, 168, 0, 1};
byte            netmask[]       = {255, 255, 255, 0};
//SoftAP用
byte            apIp[]          = {192, 168, 250, 33};
byte            apGateway[]     = {192, 168, 250, 1};
byte            apNetmask[]     = {255, 255, 255, 0};
char            apSsid[16]      = "ESP_AP";                             //このSSIDが公開される。
char            apPassword[16]  = "password";                           //
char            buf1[128];
char            buf2[128];
char            buf3[128];
char            buf4[128];
char            buf5[128];
char            buf6[128];
String          buf11           = "ABC";
String          buf22           = "DEF";
float           gDataBuf[7];                                            //現在は3要素いずれ7要素
float           gDayBuf[24][7];                                         //24時間分3要素データ
float           gSMABuf[10][7];                                         //1分ごとの要素データを確保し移動平均を求める
float           gSMAAve[7];
float           Vtmp(NAN),Vprs(NAN);                                    // hum(NAN)は無効
float           Vhum            = NAN;
float           Vco2            = NAN;
float           Vdin            = NAN;
float           Vspm            = 0.0;
double          Vlux            = NAN;
int             brightness      = 0;
int             fadeAmount      = 25;
boolean         TSLgain         = 0;                                    // Gain setting, 0 = X1, 1 = X16;
unsigned int    TSLms;                                                  // Integration ("shutter") time in milliseconds
//const char*     ntpServer     ="ntp.jst.mfeed.ad.jp";                 //日本のNTPサーバー選択
char            ntpServer[32]   ="192.168.0.199";                       //LocalTimeServer
float           timeZone        = 9.0;                                  //日本のTimeZone GMT+9
                                                                        //EEPROMには90と入力 -12の場合は -120 +1:30の場合は15
const long      gmtOffset_sec   = long(timeZone * 3600.0);              //9時間の時差を入れる
const int       daylightOffset_sec = 0;                                 //夏時間はないのでゼロ
bool            fCcsRun         = true;
struct tm       timeinfo;
WebServer       WS(HTTPport);
//I2C(SDA、SCLピン)に接続されたSSD1306ディスプレイの宣言
Adafruit_SSD1306 OLCD(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
BME280I2C       bmp;                                                    // Default : forced mode, standby time = 1000 ms
                                                                        // Oversampling = pressure ×1, temperature ×1, humidity ×1, filter off,
SFE_TSL2561     light;
//Adafruit_CCS811 ccs;
CCS811          mySensor(CCSAres);
struct st_esp {                                                         // EEPROMで利用する構造体を宣言
        char id;
        char DT[47];
        char ssid[16];
        char pass[16];
        char apSsid[16];
        char apPass[16];
        char timeZoneVal;
        char ntpServer[32];
};
const byte      ID = 0xA2;
const int       wdtTimeout      = 10;                                    //timeOut 5秒
hw_timer_t      *timer          = NULL;
char            f_name[128];
File            logFile;
String          uname           =       SECRET_FTPUSERNAME;
String          pword           =       SECRET_FTPPASSWORD;
unsigned long   scanTime        =       0;                              //単位はusec
unsigned long   preTime         =       0;
unsigned long   nowTime         =       0;
//--------------WDT timeUpCall
void IRAM_ATTR resetModule() {
  ets_printf("reboot\n");
  esp_restart();
}
//-------------- Style
String style =
"<style>\n"
        "\t#file-input,input{width:100%;height:50px;border-radius:4px;margin:10px auto;font-size:15px}\n"
        "\tinput{background:#f1f1f1;border:0;padding:0 15px}body{background:#17A1A5;font-family:sans-serif;font-size:14px;color:#777}\n"
        "\t#file-input{padding:0;border:1px solid #ddd;line-height:44px;text-align:left;display:block;cursor:pointer}\n"
        "\t#bar,#prgbar{background-color:#f1f1f1;border-radius:10px}#bar{background-color:#17A1A5;width:0%;height:10px}\n"
        "\tform{background:#fff;max-width:500px;margin:150px auto;padding:30px;border-radius:5px;text-align:center}\n"
        "\t.btn{background:#17A1A5;color:#fff;cursor:pointer}\n"
"</style>\n";
//-------------- Login page
String loginIndex = 
"<form name=loginForm>\n"
        "\t<h1>ESP32 Login</h1>\n"
        "\t<input name=userid placeholder='User ID'>\n"
        "\t<input name=pwd placeholder=Password type=Password>\n"
"<input type=submit onclick=check(this.form) class=btn value=Login>\n</form>\n"
"<script>\n"
        "\tfunction check(form) {\n"
                "\t\tif(form.userid.value== '" + myID + "' && form.pwd.value== '" + myPASS +"' )\n"
                        "\t\t\t{window.open('/serverIndex')}\n"
                "\t\telse\n"
                        "\t\t\t{alert('Error Password or Username')}\n"
        "\t}\n"
"</script>\n" + style;
//-------------- Server Index Page
String serverIndex = 
"<script src='https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js'></script>\n"
"<form method='POST' action='#' enctype='multipart/form-data' id='upload_form'>\n"
        "\t<input type='file' name='update' id='file' onchange='sub(this)' style=display:none>\n"
        "\t<label id='file-input' for='file'>   Choose file...</label>\n"
        "\t<input type='submit' class=btn value='Update'>\n"
        "\t<br><br>\n"
        "\t<div id='prg'></div>\n"
        "\t<br><div id='prgbar'><div id='bar'></div></div><br>\n"
"</form>\n"
"<script>\n"
        "\tfunction sub(obj){\n"
                "\t\tvar fileName = obj.value.split('\\\\');\n"
                "\t\tdocument.getElementById('file-input').innerHTML = '   '+ fileName[fileName.length-1];\n"
        "\t};\n"
        "\t$('form').submit(function(e){\n"
                "\t\te.preventDefault();\n"
                "\t\tvar form = $('#upload_form')[0];\n"
                "\t\tvar data = new FormData(form);\n"
                "\t\t$.ajax({\n"
                        "\t\t\turl: '/update',\n"
                        "\t\t\ttype: 'POST',\n"
                        "\t\t\tdata: data,\n"
                        "\t\t\tcontentType: false,\n"
                        "\t\t\tprocessData:false,\n"
                        "\t\t\txhr: function() {\n"
                                "\t\t\t\tvar xhr = new window.XMLHttpRequest();\n"
                                "\t\t\t\txhr.upload.addEventListener('progress', function(evt) {\n"
                                        "\t\t\t\t\tif (evt.lengthComputable) {\n"
                                                "\t\t\t\t\t\tvar per = evt.loaded / evt.total;\n"
                                                "\t\t\t\t\t\t$('#prg').html('progress: ' + Math.round(per*100) + '%');\n"
                                                "\t\t\t\t\t\t$('#bar').css('width',Math.round(per*100) + '%');\n"
                                        "\t\t\t\t\t}\n"
                                "\t\t\t\t}, false);\n"
                                "\t\t\t\treturn xhr;\n"
                        "\t\t\t},\n"
                        "\t\t\tsuccess:function(d, s) {\n"
                                "\t\t\t\tconsole.log('success!')\n"
                        "\t\t\t},\n"
                        "\t\t\terror: function (a, b, c) {\n"
                        "\t\t\t}\n"
                "\t\t});\n"
        "\t});\n"
"</script>\n" + style;
//------------ fileNotFound
String fileNotFound = 
        "<html>\n<head>\n"
                "<META HTTP-EQUIV=\"Content-Type\" CONTENT=\"text/html; charset=UTF-8\">\n"
                "\t<title>ESP32 : 404 Not Found</title>\n"
        "</head>\n<body>\n"
                "\t<center><h1>ESP32 404 : Not Found</h1>\n"
                "\t<p>The requested URL was not found on this server.</p>\n"
                "\t<p>要求されたURLはこのサーバーで見つかりませんでした。</p></center>\n"
        "</body>\n</html>\n";
//------------
void handleGetData() {
        gDataBuf[0]     =       Vprs;
        gDataBuf[1]     =       Vtmp;
        gDataBuf[2]     =       Vhum;
        gDataBuf[3]     =       Vdin;
        gDataBuf[4]     =       Vco2;
        gDataBuf[5]     =       float(Vlux);
        gDataBuf[6]     =       analogRead(AIport);
        WS.send(200, "text/html", SendHTML(gDataBuf));         //Send ADC value only to client ajax request
}
//------------
void handleNotFound(){
        if (!handleFileRead(WS.uri())) {
                //  ファイルが見つかりません
                Serial.println(F("404 not found"));
                WS.send(404, "text/html",fileNotFound);
        }
}
//------------SPIFSS のファイルをクライアントに転送する
bool handleFileRead(String path) {
        Serial.println("handleFileRead: trying to read " + path);
        // パス指定されたファイルがあればクライアントに送信する
        if (path.endsWith("/")) path += "index.html";
                String contentType = getContentType(path);
                if (SPIFFS.exists(path)) {
                        Serial.println("handleFileRead: sending " + path);
                        File file = SPIFFS.open(path, "r");
                        WS.streamFile(file, contentType);
                        file.close();
                        Serial.println("handleFileRead: sent " + path);
                        return true;
        }else {
                Serial.println(F("handleFileRead: 404 not found"));
                WS.send(404, "text/html",fileNotFound);
                return false;
        }
}
//------------
void handleSetUp(){
        if(WS.hasArg("SBM")){                                           //引数に"SBM=1"が含まれていることを確認
                Serial.println(F("SetUp?"));
                int bufSize = sizeof(st_esp);
                EEPROM.begin(bufSize);                                  //構造体のサイズ設定
                st_esp  buf;
                String cmd1,cmd2,cmd3,cmd4,str1,str2,str3,str4;
                cmd1            = WS.arg("DT7");
                buf.DT[6]       = char(cmd1.toInt());
                cmd2            = WS.arg("DT8");
                buf.DT[7]       = char(cmd2.toInt());
                cmd3            = WS.arg("DT9");
                buf.DT[8]       = char(cmd3.toInt());
                cmd4            = WS.arg("DT10");
                buf.DT[9]       = char(cmd4.toInt());
                Serial.println("IP="+cmd1+"."+cmd2+"."+cmd3+"."+cmd4);
                cmd1            = WS.arg("DT11");
                buf.DT[10]      = char(cmd1.toInt());
                cmd2            = WS.arg("DT12");
                buf.DT[11]      = char(cmd2.toInt());
                cmd3            = WS.arg("DT13");
                buf.DT[12]      = char(cmd3.toInt());
                cmd4            = WS.arg("DT14");
                buf.DT[13]      = char(cmd4.toInt());
                Serial.println("MASK="+cmd1+"."+cmd2+"."+cmd3+"."+cmd4);
                cmd1            = WS.arg("DT15");
                buf.DT[14]      = char(cmd1.toInt());
                cmd2            = WS.arg("DT16");
                buf.DT[15]      = char(cmd2.toInt());
                cmd3            = WS.arg("DT17");
                buf.DT[16]      = char(cmd3.toInt());
                cmd4            = WS.arg("DT18");
                buf.DT[17]      = char(cmd4.toInt());
                Serial.println("GW="+cmd1+"."+cmd2+"."+cmd3+"."+cmd4);
                str1            = WS.arg("DT19");
                str1.toCharArray(buf.ssid, 16);
                Serial.println("SSID="+str1);
                str2            = WS.arg("DT20");
                str2.toCharArray(buf.pass, 16);
                Serial.println("PASSWORD="+str2);
                //
                cmd1            = WS.arg("DT27");
                buf.DT[26]      = char(cmd1.toInt());
                cmd2            = WS.arg("DT28");
                buf.DT[27]      = char(cmd2.toInt());
                cmd3            = WS.arg("DT29");
                buf.DT[28]      = char(cmd3.toInt());
                cmd4            = WS.arg("DT30");
                buf.DT[29]      = char(cmd4.toInt());
                Serial.println("AccessPoint IP="+cmd1+"."+cmd2+"."+cmd3+"."+cmd4);
                cmd1            = WS.arg("DT31");
                buf.DT[30]      = char(cmd1.toInt());
                cmd2            = WS.arg("DT32");
                buf.DT[31]      = char(cmd2.toInt());
                cmd3            = WS.arg("DT33");
                buf.DT[32]      = char(cmd3.toInt());
                cmd4            = WS.arg("DT34");
                buf.DT[33]      = char(cmd4.toInt());
                Serial.println("AccessPoint MASK="+cmd1+"."+cmd2+"."+cmd3+"."+cmd4);
                cmd1            = WS.arg("DT35");
                buf.DT[34]      = char(cmd1.toInt());
                cmd2            = WS.arg("DT36");
                buf.DT[35]      = char(cmd2.toInt());
                cmd3            = WS.arg("DT37");
                buf.DT[36]      = char(cmd3.toInt());
                cmd4            = WS.arg("DT38");
                buf.DT[37]      = char(cmd4.toInt());
                Serial.println("AccessPoint GW="+cmd1+"."+cmd2+"."+cmd3+"."+cmd4);
                str1            = WS.arg("DT39");
                str1.toCharArray(buf.apSsid, 16);
                Serial.println("AccessPoint SSID="+str1);
                str2            = WS.arg("DT40");
                str2.toCharArray(buf.apPass, 16);
                Serial.println("AccessPoint PASSWORD="+str2);
                str3            = WS.arg("DT41");
                buf.timeZoneVal = char(str3.toFloat() * 10);
                str4            = WS.arg("DT42");
                str4.toCharArray(buf.ntpServer, 32);
                               
                buf.id  = ID;
                EEPROM.put<st_esp>(0, buf);                
                EEPROM.commit();
                for (int i = 0; i < 4; i++){
                        ip[i]           = buf.DT[i+6];
                }
                for (int i = 0; i < 4; i++){
                        netmask[i]      = buf.DT[i+10];
                }
                for (int i = 0; i < 4; i++){
                        gateway[i]      = buf.DT[i+14];
                }
                for (int i = 0; i < 4; i++){
                        apIp[i]         = buf.DT[i+26];
                }
                for (int i = 0; i < 4; i++){
                        apNetmask[i]      = buf.DT[i+30];
                }
                for (int i = 0; i < 4; i++){
                        apGateway[i]      = buf.DT[i+34];
                }
                WS.send(200, "text/html", SetupHTML());
                ESP.restart();                                          //ESP再起動
        }else{
                Serial.println(F("SetUp"));
                WS.send(200, "text/html", SetupHTML());
        }
}
//------------
String SetupHTML(){
        String ptr =
        "<!DOCTYPE HTML PUBLIC '-//W3C//DTD HTML 4.01 Transitional//EN'>\n"
        "<HTML>\n"
        "<HEAD>\n"
                "\t\t<META HTTP-EQUIV='Content-Type' charset='UTF-8'>\n"
                "\t\t<META HTTP-EQUIV='Content-Style-Type'>\n"
                "\t\t<TITLE>IndoorEnvironmentMonitor Setup Page</TITLE>\n"
        "</HEAD>\n"
        "<BODY MARGINWIDTH='0' MARGINHEIGHT='0' leftmargin='0' style='margin: 0; padding: 0;'>\n"
        "<FORM>\n"
        "\t<BLOCKQUOTE>\n"
                "\t\t<BLOCKQUOTE>\n"
                        "\t\t\t<TABLE BGCOLOR='#17A1A5' BORDER='0' WIDTH='100%' CELLPADDING='1' style='font-family:Verdana;color:#ffffff;font-size:12px;' CELLSPACING='2'>\n"
                        "\t\t\t<TR>\n"
                                "\t\t\t\t<TD>IndoorEnvironmentMonitor Setup Page</TD>\n"
                        "\t\t\t</TR>\n"
                        "\t\t\t</TABLE><BR>\n"
                        "\t\t\t<script>\n"
                                "\t\t\t\tfunction hex2num (s_hex) {\n"
                                        "\t\t\t\t\teval(\"var n_num=0X\" + s_hex);\n"
                                        "\t\t\t\t\treturn n_num;\n"
                                "\t\t\t\t}\n"
                        "\t\t\t</script>\n"
                        "\t\t\t<tbody>\n"
                        "\t\t\t<INPUT TYPE='hidden' NAME='SBM' VALUE='1'>\n"
                        "\t\t\t<TABLE BORDER='0' CELLSPACING='1' CELLPADDING='0' WIDTH='100%' HEIGHT='450'>\n"
                        "\t\t\t<TR>\n"
                                "\t\t\t\t<TD HEIGHT='24' WIDTH='175'>MAC:</TD> \n"
                                "\t\t\t\t<TD WIDTH='75%' NOWRAP HEIGHT='24'>\n"
                                        "\t\t\t\t\t<INPUT ID='T1'  TYPE='text' SIZE='3' MAXLENGTH='2' NAME='DT1' VALUE=" +String(mac[0],HEX) +" readonly>.\n"
                                        "\t\t\t\t\t<INPUT ID='T3'  TYPE='text' SIZE='3' MAXLENGTH='2' NAME='DT2' VALUE=" +String(mac[1],HEX) +" readonly>.\n"
                                        "\t\t\t\t\t<INPUT ID='T5'  TYPE='text' SIZE='3' MAXLENGTH='2' NAME='DT3' VALUE=" +String(mac[2],HEX) +" readonly>.\n"
                                        "\t\t\t\t\t<INPUT ID='T7'  TYPE='text' SIZE='3' MAXLENGTH='2' NAME='DT4' VALUE=" +String(mac[3],HEX) +" readonly>.\n"
                                        "\t\t\t\t\t<INPUT ID='T9'  TYPE='text' SIZE='3' MAXLENGTH='2' NAME='DT5' VALUE=" +String(mac[4],HEX) +" readonly>.\n"
                                        "\t\t\t\t\t<INPUT ID='T11' TYPE='text' SIZE='3' MAXLENGTH='2' NAME='DT6' VALUE=" +String(mac[5],HEX) +" readonly>(HEX FIXED)\n"
                                        "\t\t\t\t\t<INPUT ID='T2'  TYPE='hidden' NAME='DT1'>\n"
                                        "\t\t\t\t\t<INPUT ID='T4'  TYPE='hidden' NAME='DT2'>\n"
                                        "\t\t\t\t\t<INPUT ID='T6'  TYPE='hidden' NAME='DT3'>\n"
                                        "\t\t\t\t\t<INPUT ID='T8'  TYPE='hidden' NAME='DT4'>\n"
                                        "\t\t\t\t\t<INPUT ID='T10' TYPE='hidden' NAME='DT5'>\n"
                                        "\t\t\t\t\t<INPUT ID='T12' TYPE='hidden' NAME='DT6'>\n"
                                "\t\t\t\t</TD> \n"
                        "\t\t\t</TR>\n"
                        "\t\t\t<TR>\n"
                                "\t\t\t\t<TD HEIGHT='32' WIDTH='175'>IP:</TD> \n"
                                "\t\t\t\t<TD WIDTH='75%'>\n"
                                        "\t\t\t\t\t<INPUT TYPE='text' SIZE='3' MAXLENGTH='3' NAME='DT7'  VALUE=" +String(ip[0],DEC) +">.\n"
                                        "\t\t\t\t\t<INPUT TYPE='text' SIZE='3' MAXLENGTH='3' NAME='DT8'  VALUE=" +String(ip[1],DEC) +">.\n"
                                        "\t\t\t\t\t<INPUT TYPE='text' SIZE='3' MAXLENGTH='3' NAME='DT9'  VALUE=" +String(ip[2],DEC) +">.\n"
                                        "\t\t\t\t\t<INPUT TYPE='text' SIZE='3' MAXLENGTH='3' NAME='DT10' VALUE=" +String(ip[3],DEC) +"\n"
                                "\t\t\t\t</TD>\n"
                        "\t\t\t</TR>\n"
                        "\t\t\t<TR>\n"
                                "\t\t\t\t<TD HEIGHT='32' WIDTH='175'>MASK:</TD> \n"
                                "\t\t\t\t<TD WIDTH='75%'>\n"
                                        "\t\t\t\t\t<INPUT TYPE='text' SIZE='3' MAXLENGTH='3' NAME='DT11' VALUE=" +String(netmask[0],DEC) +">.\n"
                                        "\t\t\t\t\t<INPUT TYPE='text' SIZE='3' MAXLENGTH='3' NAME='DT12' VALUE=" +String(netmask[1],DEC) +">.\n"
                                        "\t\t\t\t\t<INPUT TYPE='text' SIZE='3' MAXLENGTH='3' NAME='DT13' VALUE=" +String(netmask[2],DEC) +">.\n"
                                        "\t\t\t\t\t<INPUT TYPE='text' SIZE='3' MAXLENGTH='3' NAME='DT14' VALUE=" +String(netmask[3],DEC) +">\n"
                                "\t\t\t\t</TD> \n"
                        "\t\t\t</TR>\n"
                        "\t\t\t<TR>\n"
                                "\t\t\t\t<TD HEIGHT='32' WIDTH='175'>GW:</TD> \n"
                                "\t\t\t\t<TD WIDTH='75%'>\n"
                                        "\t\t\t\t\t<INPUT TYPE='text' SIZE='3' MAXLENGTH='3' NAME='DT15' VALUE=" +String(gateway[0],DEC) +">.\n"
                                        "\t\t\t\t\t<INPUT TYPE='text' SIZE='3' MAXLENGTH='3' NAME='DT16' VALUE=" +String(gateway[1],DEC) +">.\n"
                                        "\t\t\t\t\t<INPUT TYPE='text' SIZE='3' MAXLENGTH='3' NAME='DT17' VALUE=" +String(gateway[2],DEC) +">.\n"
                                        "\t\t\t\t\t<INPUT TYPE='text' SIZE='3' MAXLENGTH='3' NAME='DT18' VALUE=" +String(gateway[3],DEC) +">\n"
                                "\t\t\t\t</TD>\n"
                        "\t\t\t</TR>\n"
                        "\t\t\t<TR>\n"
                                "\t\t\t\t<TD HEIGHT='24' WIDTH='175'>SSID:</TD>\n"
                                "\t\t\t\t<TD WIDTH='75%'>\n"
                                        "\t\t\t\t\t<INPUT NAME='DT19' TYPE='text' SIZE='25' maxlength='25' VALUE=" +String(ssid) +">(Up to 16 characters)\n"
                                "\t\t\t\t</TD>\n"
                        "\t\t\t</TR>\n"
                        "\t\t\t<TR>\n"
                                "\t\t\t\t<TD HEIGHT='24' WIDTH='175' NOWRAP>PASSWORD:</TD> \n"
                                "\t\t\t\t<TD WIDTH='75%'>\n"
                                        "\t\t\t\t\t<INPUT NAME='DT20' TYPE='text' SIZE='25' maxlength='25' VALUE=" +String(pass) +">(Up to 16 characters)\n"
                                "\t\t\t\t</TD>\n"
                        "\t\t\t</TR>\n"
                        "\t\t\t<TR>\n"
                                "\t\t\t\t<TD COLSPAN='2'><P><CENTER><HR></CENTER></TD>\n"
                        "\t\t\t</TR>\n"
                        "\t\t\t<TR>\n"
                                "\t\t\t\t<TD HEIGHT='24' WIDTH='175'>AccessPoint MAC:</TD> \n"
                                "\t\t\t\t<TD WIDTH='75%' NOWRAP HEIGHT='24'>\n"
                                        "\t\t\t\t\t<INPUT ID='T21'  TYPE='text' SIZE='3' MAXLENGTH='2' NAME='DT21' VALUE=" +String(apMac[0],HEX) +" readonly>.\n"
                                        "\t\t\t\t\t<INPUT ID='T23'  TYPE='text' SIZE='3' MAXLENGTH='2' NAME='DT22' VALUE=" +String(apMac[1],HEX) +" readonly>.\n"
                                        "\t\t\t\t\t<INPUT ID='T25'  TYPE='text' SIZE='3' MAXLENGTH='2' NAME='DT23' VALUE=" +String(apMac[2],HEX) +" readonly>.\n"
                                        "\t\t\t\t\t<INPUT ID='T27'  TYPE='text' SIZE='3' MAXLENGTH='2' NAME='DT24' VALUE=" +String(apMac[3],HEX) +" readonly>.\n"
                                        "\t\t\t\t\t<INPUT ID='T29'  TYPE='text' SIZE='3' MAXLENGTH='2' NAME='DT25' VALUE=" +String(apMac[4],HEX) +" readonly>.\n"
                                        "\t\t\t\t\t<INPUT ID='T31'  TYPE='text' SIZE='3' MAXLENGTH='2' NAME='DT26' VALUE=" +String(apMac[5],HEX) +" readonly>(HEX FIXED)\n"
                                        "\t\t\t\t\t<INPUT ID='T22'  TYPE='hidden' NAME='DT21'>\n"
                                        "\t\t\t\t\t<INPUT ID='T24'  TYPE='hidden' NAME='DT22'>\n"
                                        "\t\t\t\t\t<INPUT ID='T26'  TYPE='hidden' NAME='DT23'>\n"
                                        "\t\t\t\t\t<INPUT ID='T28'  TYPE='hidden' NAME='DT24'>\n"
                                        "\t\t\t\t\t<INPUT ID='T30'  TYPE='hidden' NAME='DT25'>\n"
                                        "\t\t\t\t\t<INPUT ID='T32'  TYPE='hidden' NAME='DT26'>\n"
                                "\t\t\t\t</TD> \n"
                        "\t\t\t</TR>\n"
                        "\t\t\t<TR>\n"
                                "\t\t\t\t<TD HEIGHT='32' WIDTH='175'>AccessPoint IP:</TD> \n"
                                "\t\t\t\t<TD WIDTH='75%'>\n"
                                        "\t\t\t\t\t<INPUT TYPE='text' SIZE='3' MAXLENGTH='3' NAME='DT27' VALUE=" +String(apIp[0],DEC) +">.\n"
                                        "\t\t\t\t\t<INPUT TYPE='text' SIZE='3' MAXLENGTH='3' NAME='DT28' VALUE=" +String(apIp[1],DEC) +">.\n"
                                        "\t\t\t\t\t<INPUT TYPE='text' SIZE='3' MAXLENGTH='3' NAME='DT29' VALUE=" +String(apIp[2],DEC) +">.\n"
                                        "\t\t\t\t\t<INPUT TYPE='text' SIZE='3' MAXLENGTH='3' NAME='DT30' VALUE=" +String(apIp[3],DEC) +"\n"
                                "\t\t\t\t</TD>\n"
                        "\t\t\t</TR>\n"
                        "\t\t\t<TR>\n"
                                "\t\t\t\t<TD HEIGHT='32' WIDTH='175'>AccessPoint MASK:</TD> \n"
                                "\t\t\t\t<TD WIDTH='75%'>\n"
                                        "\t\t\t\t\t<INPUT TYPE='text' SIZE='3' MAXLENGTH='3' NAME='DT31' VALUE=" +String(apNetmask[0],DEC) +">.\n"
                                        "\t\t\t\t\t<INPUT TYPE='text' SIZE='3' MAXLENGTH='3' NAME='DT32' VALUE=" +String(apNetmask[1],DEC) +">.\n"
                                        "\t\t\t\t\t<INPUT TYPE='text' SIZE='3' MAXLENGTH='3' NAME='DT33' VALUE=" +String(apNetmask[2],DEC) +">.\n"
                                        "\t\t\t\t\t<INPUT TYPE='text' SIZE='3' MAXLENGTH='3' NAME='DT34' VALUE=" +String(apNetmask[3],DEC) +">\n"
                                "\t\t\t\t</TD> \n"
                        "\t\t\t</TR>\n"
                        "\t\t\t<TR>\n"
                                "\t\t\t\t<TD HEIGHT='32' WIDTH='175'>AccessPoint GW:</TD> \n"
                                "\t\t\t\t<TD WIDTH='75%'>\n"
                                        "\t\t\t\t\t<INPUT TYPE='text' SIZE='3' MAXLENGTH='3' NAME='DT35' VALUE=" +String(apGateway[0],DEC) +">.\n"
                                        "\t\t\t\t\t<INPUT TYPE='text' SIZE='3' MAXLENGTH='3' NAME='DT36' VALUE=" +String(apGateway[1],DEC) +">.\n"
                                        "\t\t\t\t\t<INPUT TYPE='text' SIZE='3' MAXLENGTH='3' NAME='DT37' VALUE=" +String(apGateway[2],DEC) +">.\n"
                                        "\t\t\t\t\t<INPUT TYPE='text' SIZE='3' MAXLENGTH='3' NAME='DT38' VALUE=" +String(apGateway[3],DEC) +">\n"
                                "\t\t\t\t</TD>\n"
                        "\t\t\t</TR>\n"
                        
                        "\t\t\t<TR>\n"
                                "\t\t\t\t<TD HEIGHT='24' WIDTH='175'>AccessPoint SSID:</TD>\n"
                                "\t\t\t\t<TD WIDTH='75%'>\n"
                                        "\t\t\t\t\t<INPUT NAME='DT39' TYPE='text' SIZE='25' maxlength='25' VALUE=" +String(apSsid) +">(Up to 16 characters)\n"
                                "\t\t\t\t</TD>\n"
                        "\t\t\t</TR>\n"
                        "\t\t\t<TR>\n"
                                "\t\t\t\t<TD HEIGHT='24' WIDTH='175' NOWRAP>AccessPoint PASSWORD:</TD> \n"
                                "\t\t\t\t<TD WIDTH='75%'>\n"
                                        "\t\t\t\t\t<INPUT NAME='DT40' TYPE='text' SIZE='25' maxlength='25' VALUE=" +String(apPassword) +">(Up to 16 characters)\n"
                                "\t\t\t\t</TD>\n"
                        "\t\t\t</TR>\n"
                        "\t\t\t<tr>\n"
                                "\t\t\t\t<td colspan='2'><p></p><center><hr></center></td>\n"
                        "\t\t\t</tr>\n"
                        "\t\t\t<tr>\n"
                                "\t\t\t\t<td height='24' width='175' nowrap=''>TimeZone:</td>\n"
                                "\t\t\t\t<td width='75%'>\n"
                                        "\t\t\t\t\t<input name='DT41' type='text' size='10' maxlength='10' value=" +String(timeZone) +">\n"
                                "\t\t\t\t</td>\n"
                        "\t\t\t</tr>\n"
                        "\t\t\t<tr>\n"
                                "\t\t\t\t<td height='24' width='175' nowrap=''>NTPサーバ:</td>\n"
                                "\t\t\t\t<td width='75%'>\n"
                                        "\t\t\t\t\t<input name='DT42' type='text' size='40' maxlength='40' value=" +String(ntpServer) +">(Up to 32 characters)\n"
                                "\t\t\t\t</td>\n"
                        "\t\t\t</tr>\n"
                        "\t\t\t<TR>\n"
                                "\t\t\t\t<TD COLSPAN='2' WIDTH='16%' HEIGHT='32'>\n"
                                        "\t\t\t\t\t<P ALIGN=RIGHT><INPUT ID='button1' TYPE='submit' VALUE='SUBMIT'\n"
                                        "\t\t\t\t\tOnclick=\"\n"
                                        "\t\t\t\t\t\tdocument.getElementById('T2').value  = hex2num(document.getElementById('T1').value);\n"
                                        "\t\t\t\t\t\tdocument.getElementById('T4').value  = hex2num(document.getElementById('T3').value);\n"
                                        "\t\t\t\t\t\tdocument.getElementById('T6').value  = hex2num(document.getElementById('T5').value);\n"
                                        "\t\t\t\t\t\tdocument.getElementById('T8').value  = hex2num(document.getElementById('T7').value);\n"
                                        "\t\t\t\t\t\tdocument.getElementById('T10').value = hex2num(document.getElementById('T9').value);\n"
                                        "\t\t\t\t\t\tdocument.getElementById('T12').value = hex2num(document.getElementById('T11').value);\n"
                                        "\t\t\t\t\t\tdocument.getElementById('T22').value = hex2num(document.getElementById('T21').value);\n"
                                        "\t\t\t\t\t\tdocument.getElementById('T24').value = hex2num(document.getElementById('T23').value);\n"
                                        "\t\t\t\t\t\tdocument.getElementById('T26').value = hex2num(document.getElementById('T25').value);\n"
                                        "\t\t\t\t\t\tdocument.getElementById('T28').value = hex2num(document.getElementById('T27').value);\n"
                                        "\t\t\t\t\t\tdocument.getElementById('T30').value = hex2num(document.getElementById('T29').value);\n"
                                        "\t\t\t\t\t\tdocument.getElementById('T32').value = hex2num(document.getElementById('T31').value);\">\n"
                                "\t\t\t\t</TD>\n"
                        "\t\t\t</TR></tbody> \n"
                "\t\t</TABLE></BLOCKQUOTE>\n"
        "\t</BLOCKQUOTE>\n"
        "</FORM>\n"
        "</BODY>\n"
        "</HTML>\n";
        return ptr;
}
//------------
//                "\t<script SRC='http://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.3.0/Chart.bundle.js'></script>\n"
//                "\t<script SRC='http://cdn.rawgit.com/chartjs/Chart.js/master/samples/utils.js'></script>\n"
String SendHTML(float *tempSensor){
        String ptr =
        "<!DOCTYPE HTML PUBLIC '-//W3C//DTD HTML 4.01 Transitional//EN'>\n"
        "<html lang='ja'><head><META http-equiv='Content-Type' content='text/html; charset=utf-8'>\n"
        "<META http-equiv='Content-Style-Type' content='text/css'>\n"
                "\t<title>IndoorEnvironmentMonitor</title>\n"
                "\t<LINK REL='shortcut icon' HREF='favicon.ico'>\n"
                "\t<script type='application/javascript' SRC='http://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.3.0/Chart.bundle.js'></script>\n"
                "\t<script type='application/javascript' SRC='http://cdn.rawgit.com/chartjs/Chart.js/master/samples/utils.js'></script>\n"
                "\t<style>\n"
                        "\t\tcanvas{\n"
                        "\t\t\t-moz-user-select: none;\n"
                        "\t\t\t-webkit-user-select: none;\n"
                        "\t\t\t-ms-user-select: none;\n"
                        "\t\t}\n"
                        "\t\thtml { font-family: Helvetica; display: inline-block; margin: 0px auto; text-align: center;}\n"
                        "\t\tbody{margin-top: 50px;} h1 {color: #444444;margin: 50px auto 30px;}\n"
                        "\t\tp {font-size: 24px;color: #444444;margin-bottom: 10px;}\n"
                "\t</style>\n"
        "</head>\n"
        "<BODY BGCOLOR='#ffffff'>\n"
        "<CENTER><TABLE WIDTH='750' BORDER='0' CELLSPACING='0' CELLPADDING='0'>\n"
                "\t<TR>\n"
                        "\t\t<TD COLSPAN='3' VALIGN='TOP'>\n"
                        "\t\t<H2><CENTER>屋内環境モニタ(" +String(scanTime) + "us )</CENTER></H2>\n"
                        "\t\t</TD>\n"
                "\t</TR>\n"
                "\t<TR>\n"
                        "\t\t<TD WIDTH='33%'>\n"
                        "\t\t<BR CLEAR='ALL'></TD>\n"
                        "\t\t<TD WIDTH='33%'></TD>\n"
                        "\t\t<TD WIDTH='34%'></TD>\n"
                "\t</TR>\n"
                "\t<TR>\n"
                        "\t\t<TD WIDTH='33%'>\n"
                        "\t\t<BR CLEAR='ALL'></TD>\n"
                        "\t\t<TD WIDTH='33%'></TD>\n"
                        "\t\t<TD WIDTH='34%'></TD>\n"
                "\t</TR>\n"
                "\t<TR>\n"
                        "\t\t<TD WIDTH='33%' VALIGN='TOP' ALIGN='RIGHT'>データ取得時刻:</TD>\n"
                        "\t\t<TD WIDTH='33%' VALIGN='TOP' ALIGN='CENTER'>" +String(buf3) +"</TD>\n"
                        "\t\t<TD WIDTH='34%' VALIGN='TOP'></TD>\n"
                "\t</TR>\n"
                "\t<TR>\n"
                        "\t\t<TD WIDTH='33%' VALIGN='TOP' ALIGN='RIGHT'>気圧(BME280):</TD>\n"
                        "\t\t<TD WIDTH='33%' VALIGN='TOP' ALIGN='CENTER'>" +String(tempSensor[0],0)+"</TD>\n"
                        "\t\t<TD WIDTH='34%' VALIGN='TOP'>[Pa]</TD>\n"
                "\t</TR>\n"
                "\t<TR>\n"
                        "\t\t<TD WIDTH='33%' VALIGN='TOP' ALIGN='RIGHT'>気温(BME280):</TD>\n"
                        "\t\t<TD WIDTH='33%' VALIGN='TOP' ALIGN='CENTER'>" +String(tempSensor[1],1)+"</TD>\n"
                        "\t\t<TD WIDTH='34%' VALIGN='TOP'>[℃]</TD>\n"
                "\t</TR>\n"
                "\t<TR>\n"
                        "\t\t<TD WIDTH='33%' VALIGN='TOP' ALIGN='RIGHT'>湿度(BME280):</TD>\n"
                        "\t\t<TD WIDTH='33%' VALIGN='TOP'></TD>\n"
                        "\t\t<TD WIDTH='34%' VALIGN='TOP'>[%]</TD>\n"
                "\t</TR>\n"
                "\t<TR>\n"
                        "\t\t<TD WIDTH='33%' VALIGN='TOP' ALIGN='RIGHT'>浮遊粒子(GP2Y):</TD>\n"
                        "\t\t<TD WIDTH='33%' VALIGN='TOP'></TD>\n" 
                        "\t\t<TD WIDTH='34%' VALIGN='TOP'>[mg/m3]</TD>\n"
                "\t</TR>\n"
                "\t<TR>\n"
                        "\t\t<TD WIDTH='33%' VALIGN='TOP' ALIGN='RIGHT'>CO2濃度(CSS811):</TD>\n"
                        "\t\t<TD WIDTH='33%' VALIGN='TOP' ALIGN='CENTER'>"+String(tempSensor[4],0)+"</TD>\n"
                        "\t\t<TD WIDTH='34%' VALIGN='TOP'>[ppm]</TD>\n"
                "\t</TR>\n"
                "\t<TR>\n"
                        "\t\t<TD WIDTH='33%' VALIGN='TOP' ALIGN='RIGHT'>照度(TSL2561):</TD>\n"
                        "\t\t<TD WIDTH='33%' VALIGN='TOP' ALIGN='CENTER'>"+String(tempSensor[5],0)+"</TD>\n"
                        "\t\t<TD WIDTH='34%' VALIGN='TOP'>[lux]</TD>\n"
                "\t</TR>\n"
                "\t<TR>\n"
                        "\t\t<TD WIDTH='33%' VALIGN='TOP' ALIGN='RIGHT'>騒音(BOB-12758):</TD>\n"
                        "\t\t<TD WIDTH='33%' VALIGN='TOP' ALIGN='CENTER'>"+String(tempSensor[6],0)+"</TD>\n"
                        "\t\t<TD WIDTH='34%' VALIGN='TOP'>[dBm]</TD>\n"
                "\t</TR>\n"
        "</TABLE>\n"
        "<div style='width:75%;'><canvas id='canvas' width='750' height='400' style='display: block; width: 750px; height: 400px;'></canvas></div>\n"
        "</CENTER>\n"
        "\t<script>\n"
                "\t\tvar randomScalingFactor = function() {\n"
                        "\t\t\treturn 18 + Math.random() * 10;\n"
                "\t\t};\n"
                "\t\tvar data_00 = ["+  (isnan(gDayBuf[0][0]) ? String('\0') : String(gDayBuf[0][0],0))+ ","+    (isnan(gDayBuf[1][0]) ? String('\0') : String(gDayBuf[1][0],0))+ ","+
                                        (isnan(gDayBuf[2][0]) ? String('\0') : String(gDayBuf[2][0],0))+ ","+    (isnan(gDayBuf[3][0]) ? String('\0') : String(gDayBuf[3][0],0))+ ","+
                                        (isnan(gDayBuf[4][0]) ? String('\0') : String(gDayBuf[4][0],0))+ ","+    (isnan(gDayBuf[5][0]) ? String('\0') : String(gDayBuf[5][0],0))+ ","+
                                        (isnan(gDayBuf[6][0]) ? String('\0') : String(gDayBuf[6][0],0))+ ","+    (isnan(gDayBuf[7][0]) ? String('\0') : String(gDayBuf[7][0],0))+ ","+
                                        (isnan(gDayBuf[8][0]) ? String('\0') : String(gDayBuf[8][0],0))+ ","+    (isnan(gDayBuf[9][0]) ? String('\0') : String(gDayBuf[9][0],0))+ ","+
                                        (isnan(gDayBuf[10][0]) ? String('\0') : String(gDayBuf[10][0],0))+","+    (isnan(gDayBuf[11][0]) ? String('\0') : String(gDayBuf[11][0],0))+","+
                                        (isnan(gDayBuf[12][0]) ? String('\0') : String(gDayBuf[12][0],0))+","+    (isnan(gDayBuf[13][0]) ? String('\0') : String(gDayBuf[13][0],0))+","+
                                        (isnan(gDayBuf[14][0]) ? String('\0') : String(gDayBuf[14][0],0))+","+    (isnan(gDayBuf[15][0]) ? String('\0') : String(gDayBuf[15][0],0))+","+
                                        (isnan(gDayBuf[16][0]) ? String('\0') : String(gDayBuf[16][0],0))+","+    (isnan(gDayBuf[17][0]) ? String('\0') : String(gDayBuf[17][0],0))+","+
                                        (isnan(gDayBuf[18][0]) ? String('\0') : String(gDayBuf[18][0],0))+","+    (isnan(gDayBuf[19][0]) ? String('\0') : String(gDayBuf[19][0],0))+","+
                                        (isnan(gDayBuf[20][0]) ? String('\0') : String(gDayBuf[20][0],0))+","+    (isnan(gDayBuf[21][0]) ? String('\0') : String(gDayBuf[21][0],0))+","+
                                        (isnan(gDayBuf[22][0]) ? String('\0') : String(gDayBuf[22][0],0))+","+    (isnan(gDayBuf[23][0]) ? String('\0') : String(gDayBuf[23][0],0))+","+
                "];\n"
                "\t\tvar data_01 = ["+  (isnan(gDayBuf[0][1]) ? String('\0') : String(gDayBuf[0][1],1))+ ","+    (isnan(gDayBuf[1][1]) ? String('\0') : String(gDayBuf[1][1],1))+ ","+
                                        (isnan(gDayBuf[2][1]) ? String('\0') : String(gDayBuf[2][1],1))+ ","+    (isnan(gDayBuf[3][1]) ? String('\0') : String(gDayBuf[3][1],1))+ ","+
                                        (isnan(gDayBuf[4][1]) ? String('\0') : String(gDayBuf[4][1],1))+ ","+    (isnan(gDayBuf[5][1]) ? String('\0') : String(gDayBuf[5][1],1))+ ","+
                                        (isnan(gDayBuf[6][1]) ? String('\0') : String(gDayBuf[6][1],1))+ ","+    (isnan(gDayBuf[7][1]) ? String('\0') : String(gDayBuf[7][1],1))+ ","+
                                        (isnan(gDayBuf[8][1]) ? String('\0') : String(gDayBuf[8][1],1))+ ","+    (isnan(gDayBuf[9][1]) ? String('\0') : String(gDayBuf[9][1],1))+ ","+
                                        (isnan(gDayBuf[10][1]) ? String('\0') : String(gDayBuf[10][1],1))+","+    (isnan(gDayBuf[11][1]) ? String('\0') : String(gDayBuf[11][1],1))+","+
                                        (isnan(gDayBuf[12][1]) ? String('\0') : String(gDayBuf[12][1],1))+","+    (isnan(gDayBuf[13][1]) ? String('\0') : String(gDayBuf[13][1],1))+","+
                                        (isnan(gDayBuf[14][1]) ? String('\0') : String(gDayBuf[14][1],1))+","+    (isnan(gDayBuf[15][1]) ? String('\0') : String(gDayBuf[15][1],1))+","+
                                        (isnan(gDayBuf[16][1]) ? String('\0') : String(gDayBuf[16][1],1))+","+    (isnan(gDayBuf[17][1]) ? String('\0') : String(gDayBuf[17][1],1))+","+
                                        (isnan(gDayBuf[18][1]) ? String('\0') : String(gDayBuf[18][1],1))+","+    (isnan(gDayBuf[19][1]) ? String('\0') : String(gDayBuf[19][1],1))+","+
                                        (isnan(gDayBuf[20][1]) ? String('\0') : String(gDayBuf[20][1],1))+","+    (isnan(gDayBuf[21][1]) ? String('\0') : String(gDayBuf[21][1],1))+","+
                                        (isnan(gDayBuf[22][1]) ? String('\0') : String(gDayBuf[22][1],1))+","+    (isnan(gDayBuf[23][1]) ? String('\0') : String(gDayBuf[23][1],1))+","+
                "];\n"
                "\t\tvar data_02 = ["+  (isnan(gDayBuf[0][2]) ? String('\0') : String(gDayBuf[0][2],0))+ ","+    (isnan(gDayBuf[1][2]) ? String('\0') : String(gDayBuf[1][2],0))+ ","+
                                        (isnan(gDayBuf[2][2]) ? String('\0') : String(gDayBuf[2][2],0))+ ","+    (isnan(gDayBuf[3][2]) ? String('\0') : String(gDayBuf[3][2],0))+ ","+
                                        (isnan(gDayBuf[4][2]) ? String('\0') : String(gDayBuf[4][2],0))+ ","+    (isnan(gDayBuf[5][2]) ? String('\0') : String(gDayBuf[5][2],0))+ ","+
                                        (isnan(gDayBuf[6][2]) ? String('\0') : String(gDayBuf[6][2],0))+ ","+    (isnan(gDayBuf[7][2]) ? String('\0') : String(gDayBuf[7][2],0))+ ","+
                                        (isnan(gDayBuf[8][2]) ? String('\0') : String(gDayBuf[8][2],0))+ ","+    (isnan(gDayBuf[9][2]) ? String('\0') : String(gDayBuf[9][2],0))+ ","+
                                        (isnan(gDayBuf[10][2]) ? String('\0') : String(gDayBuf[10][2],0))+","+    (isnan(gDayBuf[11][2]) ? String('\0') : String(gDayBuf[11][2],0))+","+
                                        (isnan(gDayBuf[12][2]) ? String('\0') : String(gDayBuf[12][2],0))+","+    (isnan(gDayBuf[13][2]) ? String('\0') : String(gDayBuf[13][2],0))+","+
                                        (isnan(gDayBuf[14][2]) ? String('\0') : String(gDayBuf[14][2],0))+","+    (isnan(gDayBuf[15][2]) ? String('\0') : String(gDayBuf[15][2],0))+","+
                                        (isnan(gDayBuf[16][2]) ? String('\0') : String(gDayBuf[16][2],0))+","+    (isnan(gDayBuf[17][2]) ? String('\0') : String(gDayBuf[17][2],0))+","+
                                        (isnan(gDayBuf[18][2]) ? String('\0') : String(gDayBuf[18][2],0))+","+    (isnan(gDayBuf[19][2]) ? String('\0') : String(gDayBuf[19][2],0))+","+
                                        (isnan(gDayBuf[20][2]) ? String('\0') : String(gDayBuf[20][2],0))+","+    (isnan(gDayBuf[21][2]) ? String('\0') : String(gDayBuf[21][2],0))+","+
                                        (isnan(gDayBuf[22][2]) ? String('\0') : String(gDayBuf[22][2],0))+","+    (isnan(gDayBuf[23][2]) ? String('\0') : String(gDayBuf[23][2],0))+","+
                "];\n"
                "\t\tvar data_03 = ["+  (isnan(gDayBuf[0][3]) ? String('\0') : String(gDayBuf[0][3],0))+ ","+    (isnan(gDayBuf[1][3]) ? String('\0') : String(gDayBuf[1][3],0))+ ","+
                                        (isnan(gDayBuf[2][3]) ? String('\0') : String(gDayBuf[2][3],0))+ ","+    (isnan(gDayBuf[3][3]) ? String('\0') : String(gDayBuf[3][3],0))+ ","+
                                        (isnan(gDayBuf[4][3]) ? String('\0') : String(gDayBuf[4][3],0))+ ","+    (isnan(gDayBuf[5][3]) ? String('\0') : String(gDayBuf[5][3],0))+ ","+
                                        (isnan(gDayBuf[6][3]) ? String('\0') : String(gDayBuf[6][3],0))+ ","+    (isnan(gDayBuf[7][3]) ? String('\0') : String(gDayBuf[7][3],0))+ ","+
                                        (isnan(gDayBuf[8][3]) ? String('\0') : String(gDayBuf[8][3],0))+ ","+    (isnan(gDayBuf[9][3]) ? String('\0') : String(gDayBuf[9][3],0))+ ","+
                                        (isnan(gDayBuf[10][3]) ? String('\0') : String(gDayBuf[10][3],0))+","+    (isnan(gDayBuf[11][3]) ? String('\0') : String(gDayBuf[11][3],0))+","+
                                        (isnan(gDayBuf[12][3]) ? String('\0') : String(gDayBuf[12][3],0))+","+    (isnan(gDayBuf[13][3]) ? String('\0') : String(gDayBuf[13][3],0))+","+
                                        (isnan(gDayBuf[14][3]) ? String('\0') : String(gDayBuf[14][3],0))+","+    (isnan(gDayBuf[15][3]) ? String('\0') : String(gDayBuf[15][3],0))+","+
                                        (isnan(gDayBuf[16][3]) ? String('\0') : String(gDayBuf[16][3],0))+","+    (isnan(gDayBuf[17][3]) ? String('\0') : String(gDayBuf[17][3],0))+","+
                                        (isnan(gDayBuf[18][3]) ? String('\0') : String(gDayBuf[18][3],0))+","+    (isnan(gDayBuf[19][3]) ? String('\0') : String(gDayBuf[19][3],0))+","+
                                        (isnan(gDayBuf[20][3]) ? String('\0') : String(gDayBuf[20][3],0))+","+    (isnan(gDayBuf[21][3]) ? String('\0') : String(gDayBuf[21][3],0))+","+
                                        (isnan(gDayBuf[22][3]) ? String('\0') : String(gDayBuf[22][3],0))+","+    (isnan(gDayBuf[23][3]) ? String('\0') : String(gDayBuf[23][3],0))+","+
                "];\n"
                "\t\tvar data_04 = ["+  (isnan(gDayBuf[0][4]) ? String('\0') : String(gDayBuf[0][4],0))+ ","+    (isnan(gDayBuf[1][4]) ? String('\0') : String(gDayBuf[1][4],0))+ ","+
                                        (isnan(gDayBuf[2][4]) ? String('\0') : String(gDayBuf[2][4],0))+ ","+    (isnan(gDayBuf[3][4]) ? String('\0') : String(gDayBuf[3][4],0))+ ","+
                                        (isnan(gDayBuf[4][4]) ? String('\0') : String(gDayBuf[4][4],0))+ ","+    (isnan(gDayBuf[5][4]) ? String('\0') : String(gDayBuf[5][4],0))+ ","+
                                        (isnan(gDayBuf[6][4]) ? String('\0') : String(gDayBuf[6][4],0))+ ","+    (isnan(gDayBuf[7][4]) ? String('\0') : String(gDayBuf[7][4],0))+ ","+
                                        (isnan(gDayBuf[8][4]) ? String('\0') : String(gDayBuf[8][4],0))+ ","+    (isnan(gDayBuf[9][4]) ? String('\0') : String(gDayBuf[9][4],0))+ ","+
                                        (isnan(gDayBuf[10][4]) ? String('\0') : String(gDayBuf[10][4],0))+","+    (isnan(gDayBuf[11][4]) ? String('\0') : String(gDayBuf[11][4],0))+","+
                                        (isnan(gDayBuf[12][4]) ? String('\0') : String(gDayBuf[12][4],0))+","+    (isnan(gDayBuf[13][4]) ? String('\0') : String(gDayBuf[13][4],0))+","+
                                        (isnan(gDayBuf[14][4]) ? String('\0') : String(gDayBuf[14][4],0))+","+    (isnan(gDayBuf[15][4]) ? String('\0') : String(gDayBuf[15][4],0))+","+
                                        (isnan(gDayBuf[16][4]) ? String('\0') : String(gDayBuf[16][4],0))+","+    (isnan(gDayBuf[17][4]) ? String('\0') : String(gDayBuf[17][4],0))+","+
                                        (isnan(gDayBuf[18][4]) ? String('\0') : String(gDayBuf[18][4],0))+","+    (isnan(gDayBuf[19][4]) ? String('\0') : String(gDayBuf[19][4],0))+","+
                                        (isnan(gDayBuf[20][4]) ? String('\0') : String(gDayBuf[20][4],0))+","+    (isnan(gDayBuf[21][4]) ? String('\0') : String(gDayBuf[21][4],0))+","+
                                        (isnan(gDayBuf[22][4]) ? String('\0') : String(gDayBuf[22][4],0))+","+    (isnan(gDayBuf[23][4]) ? String('\0') : String(gDayBuf[23][4],0))+","+
                "];\n"
                "\t\tvar data_05 = ["+  (isnan(gDayBuf[0][5]) ? String('\0') : String(gDayBuf[0][5],0))+ ","+    (isnan(gDayBuf[1][5]) ? String('\0') : String(gDayBuf[1][5],0))+ ","+
                                        (isnan(gDayBuf[2][5]) ? String('\0') : String(gDayBuf[2][5],0))+ ","+    (isnan(gDayBuf[3][5]) ? String('\0') : String(gDayBuf[3][5],0))+ ","+
                                        (isnan(gDayBuf[4][5]) ? String('\0') : String(gDayBuf[4][5],0))+ ","+    (isnan(gDayBuf[5][5]) ? String('\0') : String(gDayBuf[5][5],0))+ ","+
                                        (isnan(gDayBuf[6][5]) ? String('\0') : String(gDayBuf[6][5],0))+ ","+    (isnan(gDayBuf[7][5]) ? String('\0') : String(gDayBuf[7][5],0))+ ","+
                                        (isnan(gDayBuf[8][5]) ? String('\0') : String(gDayBuf[8][5],0))+ ","+    (isnan(gDayBuf[9][5]) ? String('\0') : String(gDayBuf[9][5],0))+ ","+
                                        (isnan(gDayBuf[10][5]) ? String('\0') : String(gDayBuf[10][5],0))+","+    (isnan(gDayBuf[11][5]) ? String('\0') : String(gDayBuf[11][5],0))+","+
                                        (isnan(gDayBuf[12][5]) ? String('\0') : String(gDayBuf[12][5],0))+","+    (isnan(gDayBuf[13][5]) ? String('\0') : String(gDayBuf[13][5],0))+","+
                                        (isnan(gDayBuf[14][5]) ? String('\0') : String(gDayBuf[14][5],0))+","+    (isnan(gDayBuf[15][5]) ? String('\0') : String(gDayBuf[15][5],0))+","+
                                        (isnan(gDayBuf[16][5]) ? String('\0') : String(gDayBuf[16][5],0))+","+    (isnan(gDayBuf[17][5]) ? String('\0') : String(gDayBuf[17][5],0))+","+
                                        (isnan(gDayBuf[18][5]) ? String('\0') : String(gDayBuf[18][5],0))+","+    (isnan(gDayBuf[19][5]) ? String('\0') : String(gDayBuf[19][5],0))+","+
                                        (isnan(gDayBuf[20][5]) ? String('\0') : String(gDayBuf[20][5],0))+","+    (isnan(gDayBuf[21][5]) ? String('\0') : String(gDayBuf[21][5],0))+","+
                                        (isnan(gDayBuf[22][5]) ? String('\0') : String(gDayBuf[22][5],0))+","+    (isnan(gDayBuf[23][5]) ? String('\0') : String(gDayBuf[23][5],0))+","+
                "];\n"
                "\t\tvar data_06 = ["+  (isnan(gDayBuf[0][6]) ? String('\0') : String(gDayBuf[0][6],0))+ ","+    (isnan(gDayBuf[1][6]) ? String('\0') : String(gDayBuf[1][6],0))+ ","+
                                        (isnan(gDayBuf[2][6]) ? String('\0') : String(gDayBuf[2][6],0))+ ","+    (isnan(gDayBuf[3][6]) ? String('\0') : String(gDayBuf[3][6],0))+ ","+
                                        (isnan(gDayBuf[4][6]) ? String('\0') : String(gDayBuf[4][6],0))+ ","+    (isnan(gDayBuf[5][6]) ? String('\0') : String(gDayBuf[5][6],0))+ ","+
                                        (isnan(gDayBuf[6][6]) ? String('\0') : String(gDayBuf[6][6],0))+ ","+    (isnan(gDayBuf[7][6]) ? String('\0') : String(gDayBuf[7][6],0))+ ","+
                                        (isnan(gDayBuf[8][6]) ? String('\0') : String(gDayBuf[8][6],0))+ ","+    (isnan(gDayBuf[9][6]) ? String('\0') : String(gDayBuf[9][6],0))+ ","+
                                        (isnan(gDayBuf[10][6]) ? String('\0') : String(gDayBuf[10][6],0))+","+    (isnan(gDayBuf[11][6]) ? String('\0') : String(gDayBuf[11][6],0))+","+
                                        (isnan(gDayBuf[12][6]) ? String('\0') : String(gDayBuf[12][6],0))+","+    (isnan(gDayBuf[13][6]) ? String('\0') : String(gDayBuf[13][6],0))+","+
                                        (isnan(gDayBuf[14][6]) ? String('\0') : String(gDayBuf[14][6],0))+","+    (isnan(gDayBuf[15][6]) ? String('\0') : String(gDayBuf[15][6],0))+","+
                                        (isnan(gDayBuf[16][6]) ? String('\0') : String(gDayBuf[16][6],0))+","+    (isnan(gDayBuf[17][6]) ? String('\0') : String(gDayBuf[17][6],0))+","+
                                        (isnan(gDayBuf[18][6]) ? String('\0') : String(gDayBuf[18][6],0))+","+    (isnan(gDayBuf[19][6]) ? String('\0') : String(gDayBuf[19][6],0))+","+
                                        (isnan(gDayBuf[20][6]) ? String('\0') : String(gDayBuf[20][6],0))+","+    (isnan(gDayBuf[21][6]) ? String('\0') : String(gDayBuf[21][6],0))+","+
                                        (isnan(gDayBuf[22][6]) ? String('\0') : String(gDayBuf[22][6],0))+","+    (isnan(gDayBuf[23][6]) ? String('\0') : String(gDayBuf[23][6],0))+","+
                "];\n"
                "\t\tvar config = {\n"
                        "\t\t\ttype: 'line',\n"
                        "\t\t\tdata: {\n"
                                "\t\t\t\tlabels: ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12' , '13', '14', '15', '16', '17', '18', '19', '20', '21', '22', '23'],\n"
                                "\t\t\t\tdatasets: [{\n"
                                        "\t\t\t\t\tlabel: '気圧[Pa]',\n"
                                        "\t\t\t\t\tdata: data_00,\n"
                                        "\t\t\t\t\tborderColor: window.chartColors.red,\n"
                                        "\t\t\t\t\tbackgroundColor: 'rgba(0, 0, 0, 0)',\n"
                                        "\t\t\t\t\tfill: false,\n"
                                        "\t\t\t\t\tcubicInterpolationMode: 'monotone',\n"
                                        "\t\t\t\t\tyAxisID: 'y1'\n"
                                "\t\t\t\t}, {\n"
                                        "\t\t\t\t\tlabel: '気温[℃]',\n"
                                        "\t\t\t\t\tdata: data_01,\n"
                                        "\t\t\t\t\tborderColor: window.chartColors.blue,\n"
                                        "\t\t\t\t\tbackgroundColor: 'rgba(0, 0, 0, 0)',\n"
                                        "\t\t\t\t\tfill: false,\n"
                                        "\t\t\t\t\tyAxisID: 'y2'\n"
                                "\t\t\t\t}, {\n"
                                        "\t\t\t\t\tlabel: '湿度[%]',\n"
                                        "\t\t\t\t\tdata: data_02,\n"
                                        "\t\t\t\t\tborderColor: window.chartColors.orange,\n"
                                        "\t\t\t\t\tbackgroundColor: 'rgba(0, 0, 0, 0)',\n"
                                        "\t\t\t\t\tfill: false,\n"
                                        "\t\t\t\t\tyAxisID: 'y3'\n"
                                "\t\t\t\t}, {\n"
                                        "\t\t\t\t\tlabel: '浮遊粒子[mg/m3]',\n"
                                        "\t\t\t\t\tdata: data_03,\n"
                                        "\t\t\t\t\tborderColor: window.chartColors.yellow,\n"
                                        "\t\t\t\t\tbackgroundColor: 'rgba(0, 0, 0, 0)',\n"
                                        "\t\t\t\t\tfill: false,\n"
                                        "\t\t\t\t\tyAxisID: 'y4'\n"
                                "\t\t\t\t}, {\n"
                                        "\t\t\t\t\tlabel: 'CO2濃度[ppm]',\n"
                                        "\t\t\t\t\tdata: data_04,\n"
                                        "\t\t\t\t\tborderColor: window.chartColors.purple,\n"
                                        "\t\t\t\t\tbackgroundColor: 'rgba(0, 0, 0, 0)',\n"
                                        "\t\t\t\t\tfill: false,\n"
                                        "\t\t\t\t\tyAxisID: 'y5'\n"
                                "\t\t\t\t}, {\n"
                                        "\t\t\t\t\tlabel: '照度[lux]',\n"
                                        "\t\t\t\t\tdata: data_05,\n"
                                        "\t\t\t\t\tborderColor: window.chartColors.grey,\n"
                                        "\t\t\t\t\tbackgroundColor: 'rgba(0, 0, 0, 0)',\n"
                                        "\t\t\t\t\tfill: false,\n"
                                        "\t\t\t\t\tyAxisID: 'y6'\n"
                                "\t\t\t\t}, {\n"
                                        "\t\t\t\t\tlabel: '騒音[dBm]',\n"
                                        "\t\t\t\t\tdata: data_06,\n"
                                        "\t\t\t\t\tborderColor: window.chartColors.green,\n"
                                        "\t\t\t\t\tbackgroundColor: 'rgba(0, 0, 0, 0)',\n"
                                        "\t\t\t\t\tfill: false,\n"
                                        "\t\t\t\t\tlineTension: 0,\n"
                                        "\t\t\t\t\tyAxisID: 'y7'\n"
                                "\t\t\t\t}]\n"
                        "\t\t\t},\n"
                        "\t\t\toptions: {\n"
                                "\t\t\t\tresponsive: true,\n"
                                "\t\t\t\ttitle: {\n"
                                        "\t\t\t\t\tdisplay: true,\n"
                                        "\t\t\t\t\ttext: '今日の測定結果'\n"
                                "\t\t\t\t},\n"
                                "\t\t\t\tlegend: {                               //凡例\n"
                                        "\t\t\t\t\tdisplay: true\n"
                                "\t\t\t\t},\n"
                                "\t\t\t\ttooltips: {                             //ツールチップ\n"
                                        "\t\t\t\t\tenabled: true,\n"
                                        "\t\t\t\t\tmode: 'index'\n"
                                "\t\t\t\t},\n"
                                "\t\t\t\tscales: {\n"
                                        "\t\t\t\t\txAxes: [{\n"
                                                "\t\t\t\t\t\tdisplay: true,\n"
                                                "\t\t\t\t\t\tscaleLabel: {\n"
                                                        "\t\t\t\t\t\t\tdisplay: true,\n"
                                                        "\t\t\t\t\t\t\tlabelString: '時刻 [時]'\n"
                                                "\t\t\t\t\t\t}\n"
                                        "\t\t\t\t\t}],\n"
                                        "\t\t\t\t\tyAxes: [{\n"
                                                "\t\t\t\t\t\tid:'y1',\n"
                                                "\t\t\t\t\t\tdisplay: true,\n"
                                                "\t\t\t\t\t\tscaleLabel: {\n"
                                                        "\t\t\t\t\t\t\tfontColor: window.chartColors.red,\n"
                                                        "\t\t\t\t\t\t\tdisplay: true,\n"
                                                        "\t\t\t\t\t\t\tlabelString: '圧力 [Pa]'\n"
                                                "\t\t\t\t\t\t},\n"
                                                "\t\t\t\t\t\tticks: {\n"
                                                        "\t\t\t\t\t\t\tfontColor: window.chartColors.red,\n"
                                                        "\t\t\t\t\t\t\tsuggestedMin:  94000,\n"
                                                        "\t\t\t\t\t\t\tsuggestedMax: 104000\n"
                                                "\t\t\t\t\t\t}\n"
                                        "\t\t\t\t\t},{\n"
                                                "\t\t\t\t\t\tid:'y2',\n"
                                                "\t\t\t\t\t\tscaleLabel: {\n"
                                                        "\t\t\t\t\t\t\tfontColor: window.chartColors.blue,\n"
                                                        "\t\t\t\t\t\t\tdisplay: true,\n"
                                                        "\t\t\t\t\t\t\tlabelString: '温度 [℃]'\n"
                                                "\t\t\t\t\t\t},\n"
                                                "\t\t\t\t\t\tticks: {\n"
                                                        "\t\t\t\t\t\t\tfontColor: window.chartColors.blue,\n"
                                                        "\t\t\t\t\t\t\tsuggestedMin: 0,\n"
                                                        "\t\t\t\t\t\t\tsuggestedMax: 50\n"
                                                "\t\t\t\t\t\t}\n"
                                        "\t\t\t\t\t},{\n"
                                                "\t\t\t\t\t\tid:'y3',\n"
                                                "\t\t\t\t\t\tscaleLabel: {\n"
                                                        "\t\t\t\t\t\t\tfontColor: window.chartColors.orange,\n"
                                                        "\t\t\t\t\t\t\tdisplay: true,\n"
                                                        "\t\t\t\t\t\t\tlabelString: '湿度[%]'\n"
                                                "\t\t\t\t\t\t},\n"
                                                "\t\t\t\t\t\tticks: {\n"
                                                        "\t\t\t\t\t\t\tfontColor: window.chartColors.orange,\n"
                                                        "\t\t\t\t\t\t\tsuggestedMin: 0,\n"
                                                        "\t\t\t\t\t\t\tsuggestedMax: 100\n"
                                                "\t\t\t\t\t\t}\n"
                                        "\t\t\t\t\t},{\n"
                                                "\t\t\t\t\t\tid:'y4',\n"
                                                "\t\t\t\t\t\tposition: 'right',\n"
                                                "\t\t\t\t\t\tscaleLabel: {\n"
                                                        "\t\t\t\t\t\t\tfontColor: window.chartColors.yellow,\n"
                                                        "\t\t\t\t\t\t\tdisplay: true,\n"
                                                        "\t\t\t\t\t\t\tlabelString: '浮遊粒子[mg/m3]'\n"
                                                "\t\t\t\t\t\t},\n"
                                                "\t\t\t\t\t\tticks: {\n"
                                                        "\t\t\t\t\t\t\tfontColor: window.chartColors.yellow,\n"
                                                        "\t\t\t\t\t\t\tsuggestedMin: 0,\n"
                                                        "\t\t\t\t\t\t\tsuggestedMax: 10000\n"
                                                "\t\t\t\t\t\t}\n"
                                        "\t\t\t\t\t},{\n"
                                                "\t\t\t\t\t\tid:'y5',\n"
                                                "\t\t\t\t\t\tposition: 'right',\n"
                                                "\t\t\t\t\t\tscaleLabel: {\n"
                                                        "\t\t\t\t\t\t\tfontColor: window.chartColors.purple,\n"
                                                        "\t\t\t\t\t\t\tdisplay: true,\n"
                                                        "\t\t\t\t\t\t\tlabelString: 'CO2濃度[ppm]'\n"
                                                "\t\t\t\t\t\t},\n"
                                                "\t\t\t\t\t\tticks: {\n"
                                                        "\t\t\t\t\t\t\tfontColor: window.chartColors.purple,\n"
                                                        "\t\t\t\t\t\t\tsuggestedMin:    0,\n"
                                                        "\t\t\t\t\t\t\tsuggestedMax:    5000\n"
                                                "\t\t\t\t\t\t}\n"
                                        "\t\t\t\t\t},{\n"
                                                "\t\t\t\t\t\tid:'y6',\n"
                                                "\t\t\t\t\t\tposition: 'right',\n"
                                                "\t\t\t\t\t\tscaleLabel: {\n"
                                                        "\t\t\t\t\t\t\tfontColor: window.chartColors.grey,\n"
                                                        "\t\t\t\t\t\t\tdisplay: true,\n"
                                                        "\t\t\t\t\t\t\tlabelString: '照度[lux]'\n"
                                                "\t\t\t\t\t\t},\n"
                                                "\t\t\t\t\t\tticks: {\n"
                                                        "\t\t\t\t\t\t\tfontColor: window.chartColors.grey,\n"
                                                        "\t\t\t\t\t\t\tsuggestedMin: 0,\n"
                                                        "\t\t\t\t\t\t\tsuggestedMax: 2000\n"
                                                "\t\t\t\t\t\t}\n"
                                        "\t\t\t\t\t},{\n"
                                                "\t\t\t\t\t\tid:'y7',\n"
                                                "\t\t\t\t\t\tposition: 'right',\n"
                                                "\t\t\t\t\t\tscaleLabel: {\n"
                                                        "\t\t\t\t\t\t\tfontColor: window.chartColors.green,\n"
                                                        "\t\t\t\t\t\t\tdisplay: true,\n"
                                                        "\t\t\t\t\t\t\tlabelString: '騒音[dBm]'\n"
                                                "\t\t\t\t\t\t},\n"
                                                "\t\t\t\t\t\tticks: {\n"
                                                        "\t\t\t\t\t\t\tfontColor: window.chartColors.green,\n"
                                                        "\t\t\t\t\t\t\tsuggestedMin: 0,\n"
                                                        "\t\t\t\t\t\t\tsuggestedMax: 5000\n"
                                                "\t\t\t\t\t\t}\n"
                                        "\t\t\t\t\t}]\n"
                                "\t\t\t\t}\n"
                        "\t\t\t}\n"
                "\t\t};\n"
                "\t\twindow.onload = function() {\n"
                        "\t\t\tvar ctx = document.getElementById('canvas').getContext('2d');\n"
                        "\t\t\twindow.myLine = new Chart(ctx, config);\n"
                "\t\t};\n"
        "\t</script>\n</BODY>\n"
        "</html>\n";
        return ptr;
}
//------------      setup?SBM=1&DT1=24&DT2=a&DT3=c4&DT4=61&DT5=1e&DT6=7c&
//                              DT1=36&DT2=10&DT3=196&DT4=97&DT5=30&DT6=124&
//                              DT7=192&DT8=168&DT9=0&DT10=33&
//                              DT11=255&DT12=255&DT13=255&DT14=0&
//                              DT15=192&DT16=168&DT17=0&DT18=1&
//                              DT19=**********&DT20=**********&
//                              DT21=24&DT22=a&DT23=c4&DT24=61&DT25=1e&DT26=7d&
//                              DT21=36&DT22=10&DT23=196&DT24=97&DT25=30&DT26=125&
//                              DT27=192&DT28=168&DT29=250&DT30=33&
//                              DT31=255&DT32=255&DT33=255&DT34=0&
//                              DT35=192&DT36=168&DT37=250&DT38=1&
//                              DT39=ESP_AP&DT40=password
void handleUpdate() {
        Serial.println("SBM");
        digitalWrite(PO4, false);
        if(!WS.hasArg("SBM=1")){                                        //引数に"SBM=1"が含まれていることを確認
                handleNotFound();
                return;
        }
        String cmd = WS.arg("SBM=1");
        Serial.println(cmd);
        digitalWrite(PO4, true);
}
//------------ BMPprint
void printOLCDValues() {
        BME280::TempUnit tempUnit(BME280::TempUnit_Celsius);
        BME280::PresUnit presUnit(BME280::PresUnit_Pa);
        bmp.read(Vprs, Vtmp, Vhum, tempUnit, presUnit);
        sprintf(buf1,"Temperature:%.2f%1s",Vtmp,String(tempUnit == BME280::TempUnit_Celsius ? 'C' :'F'));
        sprintf(buf2,"Pressure: %.0f Pa",Vprs); 
        buf11   = String(buf1);
        buf22   = String(buf2);
        //Serial.println(buf1);
        //Serial.println(buf2);
        //CO2濃度取得
        if (mySensor.dataAvailable()){
                mySensor.readAlgorithmResults();
                Vco2            =       mySensor.getCO2();
                float TVOC      =       mySensor.getTVOC();
                sprintf(buf4,"eCO2:%4.0f TVOC:%3.0f",Vco2,TVOC);
        }
        //delay(10);                                              //Don't spam the I2C bus
       
        /*
        if(ccs.available()){
                //有効時期を確認してデータ取得しないととんでもない不安定値が取得されてしまう。
                delay(100);
                float temp = ccs.calculateTemperature();
                if(!ccs.readData()){
                        Vco2            = ccs.geteCO2();
                        float TVOC      = ccs.getTVOC();
                        sprintf(buf4,"eCO2:%4.0f TVOC:%3.0f",Vco2,TVOC); 
                }                
        }
        */
        //照度取得
        unsigned int data0, data1;
        if (light.getData(data0,data1)){
                boolean good = light.getLux(TSLgain,TSLms,data0,data1,Vlux);
                sprintf(buf5,"lux:%5.0f lx (%s)",Vlux,good? "Good" : "Bad");
        }
        OLCD.setCursor(0, 24);
        OLCD.fillRect(0,24,128,32,SSD1306_BLACK);
        OLCD.setTextSize(1);OLCD.setTextColor(SSD1306_WHITE);OLCD.setCursor(0, 24);
        OLCD.println(buf1);
        OLCD.println(buf2);
        OLCD.println(buf4);
        OLCD.println(buf5);
        OLCD.display();
}
//------------ printTime
int printOLCDTime() {
        getLocalTime(&timeinfo);
        sprintf(buf3,"%04d/%02d/%02d %02d:%02d:%02d",timeinfo.tm_year+1900,timeinfo.tm_mon+1,timeinfo.tm_mday,timeinfo.tm_hour,timeinfo.tm_min,timeinfo.tm_sec);
        //Serial.println(buf3);
        OLCD.setCursor(0, 56);
        OLCD.fillRect(0,56,128,8,SSD1306_BLACK);
        OLCD.setTextSize(1);OLCD.setTextColor(SSD1306_WHITE);OLCD.setCursor(0, 56);
        OLCD.println(buf3);
        return timeinfo.tm_hour;
}
//------------ printIndexTime
int printOLCDMinIndex(){
        getLocalTime(&timeinfo);
        return  timeinfo.tm_min % 10;
}
//------------ デバッグ用
void printLocalTime(){
        struct tm timeinfo;
        if (!getLocalTime(&timeinfo)) {
                Serial.println(F("Failed to obtain time"));
                return;
        }
        Serial.println(&timeinfo, "%Y %m %d %a %H:%M:%S");              //日本人にわかりやすい表記へ変更
}
//--------ContentTypeを拡張子で判断しているがこれでいいのか???
String getContentType(String filename){
        if(WS.hasArg("download"))               return "application/octet-stream";
        else if(filename.endsWith(".htm"))      return "text/html";
        else if(filename.endsWith(".html"))     return "text/html";
        else if(filename.endsWith(".css"))      return "text/css";
        else if(filename.endsWith(".js"))       return "application/javascript";
        else if(filename.endsWith(".png"))      return "image/png";
        else if(filename.endsWith(".gif"))      return "image/gif";
        else if(filename.endsWith(".jpg"))      return "image/jpeg";
        else if(filename.endsWith(".ico"))      return "image/x-icon";
        else if(filename.endsWith(".xml"))      return "text/xml";
        else if(filename.endsWith(".pdf"))      return "application/x-pdf";
        else if(filename.endsWith(".zip"))      return "application/x-zip";
        else if(filename.endsWith(".gz"))       return "application/x-gzip";
        return "text/plain";
}
//------------------
void printError(byte error){
        // If there's an I2C error, this function will
        // print out an explanation.
        Serial.print(F("I2C error: "));Serial.print(error,DEC);Serial.print(F(", "));
        switch(error){
                case 0:
                        Serial.println(F("success"));
                        break;
                case 1:
                        Serial.println(F("data too long for transmit buffer"));
                        break;
                case 2:
                        Serial.println(F("received NACK on address (disconnected?)"));
                        break;
                case 3:
                        Serial.println(F("received NACK on data"));
                        break;
                case 4:
                        Serial.println(F("other error"));
                        break;
                default:
                        Serial.println(F("unknown error"));
        }
}
//------------ SDカード読み書きに必要な関数群
//----------------
void listDir(fs::FS &fs, const char *dirname, uint8_t levels){
        Serial.printf("Listing directory: %s\n", dirname);
        //
        File root = fs.open(dirname);
        if(!root){
                Serial.println(F("Failed to open directory"));
                return;
        }
        if(!root.isDirectory()){
                Serial.println(F("Not a directory"));
                return;
        }
        //
        File file = root.openNextFile();
        while(file){
                if(file.isDirectory()){
                        Serial.print(F("  DIR : "));
                        Serial.println(file.name());
                        if(levels){
                                listDir(fs, file.name(), levels -1);
                        }
                } else {
                        Serial.print(F("  FILE: "));
                        Serial.print(file.name());
                        Serial.print(F("  SIZE: "));
                        Serial.println(file.size());
                }
                file = root.openNextFile();
        }
}
//----------------
void createDir(fs::FS &fs, const char *path){
        Serial.printf("Creating Dir: %s\n", path);
        if(fs.mkdir(path)){
                Serial.println(F("Dir created"));
        } else {
                Serial.println(F("mkdir failed"));
        }
}
//----------------
void removeDir(fs::FS &fs, const char *path){
        Serial.printf("Removing Dir: %s\n", path);
        if(fs.rmdir(path)){
                Serial.println(F("Dir removed"));
        } else {
                Serial.println(F("rmdir failed"));
        }
}
//----------------
void readFile(fs::FS &fs, const char *path){
        Serial.printf("Reading file: %s\n", path);
        //
        File file = fs.open(path);
        if(!file){
                Serial.println(F("Failed to open file for reading"));
                return;
        }
        //
        Serial.print(F("Read from file: "));
        while(file.available()){
                Serial.write(file.read());
        }
        file.close();
}
//----------------
void writeFile(fs::FS &fs, const char *path, const char *message){
        Serial.printf("Writing file: %s\n", path);
        //
        File file = fs.open(path, FILE_WRITE);
        if(!file){
                Serial.println(F("Failed to open file for writing"));
                return;
        }
        if(file.print(message)){
                Serial.println(F("File written"));
        } else {
                Serial.println(F("Write failed"));
        }
        file.close();
}
//----------------
void appendFile(fs::FS &fs, const char * path, const char * message){
        Serial.printf("Appending to file: %s\n", path);
        //
        File file = fs.open(path, FILE_APPEND);
        if(!file){
                Serial.println(F("Failed to open file for appending"));
                return;
        }
        if(file.print(message)){
                Serial.println(F("Message appended"));
        } else {
                Serial.println(F("Append failed"));
        }
        file.close();
}
//----------------
void renameFile(fs::FS &fs, const char *path1, const char * path2){
        Serial.printf("Renaming file %s to %s\n", path1, path2);
        if (fs.rename(path1, path2)) {
                Serial.println(F("File renamed"));
        } else {
                Serial.println(F("Rename failed"));
        }
}
//----------------
void deleteFile(fs::FS &fs, const char *path){
        Serial.printf("Deleting file: %s\n", path);
        if(fs.remove(path)){
                Serial.println(F("File deleted"));
        } else {
                Serial.println(F("Delete failed"));
        }
}
//------------ setup function
void setup(void) {
        //
        Serial.begin(115200);
        //SDカード周りの初期化
        if(!SD.begin()){
                Serial.println(F("Card Mount Failed"));
                return;
        }
        uint8_t cardType = SD.cardType();
        if(cardType == CARD_NONE){
                Serial.println(F("No SD card attached"));
                return;
        }
        Serial.print(F("SD Card Type: "));
        if(cardType == CARD_MMC){
                Serial.println(F("MMC"));
        } else if(cardType == CARD_SD){
                Serial.println(F("SDSC"));
        } else if(cardType == CARD_SDHC){
                Serial.println(F("SDHC"));
        } else {
                Serial.println(F("UNKNOWN"));
        }
        uint64_t cardSize = SD.cardSize() / (1024 * 1024);
        Serial.printf("SD Card Size: %lluMB\n", cardSize);
        //
        int bufSize = sizeof(st_esp);
        EEPROM.begin(bufSize);                                          //構造体のサイズ設定
        st_esp  buf;
        Serial.print(F("bufSize="));Serial.println(bufSize);
        EEPROM.get<st_esp>(0, buf);                                     //EEPROM読込
        Serial.print(F("bufID=0x"));Serial.println(buf.id,HEX);
        //MACアドレスの取得
        esp_read_mac(mac,       ESP_MAC_WIFI_STA);                      //StationModeのMacAddress
        esp_read_mac(apMac,     ESP_MAC_WIFI_SOFTAP);                   //APModeのMacAddress
        if(buf.id == ID){
                Serial.println(F("bufID:Match"));
                //既に情報がある場合は読込
                //for (int i = 0; i < 6; i++){
                //      mac[i]          = buf.DT[i];
                //}
                for (int i = 0; i < 4; i++){
                        ip[i]           = buf.DT[i+6];
                }
                for (int i = 0; i < 4; i++){
                        netmask[i]      = buf.DT[i+10];
                }
                for (int i = 0; i < 4; i++){
                        gateway[i]      = buf.DT[i+14];
                }
                for (int i = 0; i < 4; i++){
                        apIp[i]         = buf.DT[i+26];
                }
                for (int i = 0; i < 4; i++){
                        apNetmask[i]      = buf.DT[i+30];
                }
                for (int i = 0; i < 4; i++){
                        apGateway[i]      = buf.DT[i+34];
                }
                strcpy(ssid,buf.ssid);
                Serial.print(F("ssid ="));              Serial.println(ssid);
                strcpy(pass,buf.pass);
                Serial.print(F("ap_password ="));       Serial.println(apPassword);
                strcpy(apSsid,buf.apSsid);
                Serial.print(F("ap_ssid ="));           Serial.println(apSsid);
                strcpy(apPassword,buf.apPass);
                Serial.print(F("ap_password ="));       Serial.println(apPassword);
                timeZone        = float(buf.timeZoneVal) / 10;
                Serial.print(F("timeZone ="));       Serial.println(timeZone,2);
                strcpy(ntpServer,buf.ntpServer);
                Serial.print(F("ntpServer ="));       Serial.println(ntpServer);
        }else{
                Serial.println(F("bufID:NotMatch"));
                //情報が書き込まれていないので初期値を書き込んでおく
                for (int i = 0; i < 6; i++){
                        buf.DT[i]     = mac[i];
                }
                for (int i = 0; i < 4; i++){
                        buf.DT[i+6]     = ip[i];
                }
                for (int i = 0; i < 4; i++){
                        buf.DT[i+10]    = netmask[i];
                }
                for (int i = 0; i < 4; i++){
                        buf.DT[i+14]    = gateway[i];
                }
                for (int i = 0; i < 6; i++){
                        buf.DT[i+20]    = apMac[i];
                }
                for (int i = 0; i < 4; i++){
                        buf.DT[i+26]    = apIp[i];
                }
                for (int i = 0; i < 4; i++){
                        buf.DT[i+30]    = apNetmask[i];
                }
                for (int i = 0; i < 4; i++){
                        buf.DT[i+34]    = apGateway[i];
                }
                strcpy(buf.ssid,ssid);
                strcpy(buf.pass,pass);
                strcpy(buf.apSsid,apSsid);
                strcpy(buf.apPass,apPassword);
                buf.timeZoneVal = char(timeZone * 10.0);
                strcpy(buf.ntpServer,ntpServer);
                buf.id  = ID;
                EEPROM.put<st_esp>(0, buf);
                EEPROM.commit();
        }
//        */
        pinMode(LED,OUTPUT);    digitalWrite(LED,LOW);                  //Onboard LED port Direction output
        pinMode(PO1,OUTPUT);    digitalWrite(PO1,LOW);
        pinMode(PO2,OUTPUT);    digitalWrite(PO2,LOW);
        pinMode(PO3,OUTPUT);    digitalWrite(PO3,LOW);
        //pinMode(PO4,OUTPUT);  digitalWrite(PO4,LOW);
        //ledcSetup(LEDC_CHANNEL_0,1000,8);                             //double ledcSetup(uint8_t chan, double freq, uint8_t bit_num);
        //ledcAttachPin(PO3, LEDC_CHANNEL_0);                           //void ledcAttachPin(uint8_t pin, uint8_t chan);
        ledcSetup(LEDC_CHANNEL_1,1000,8);
        ledcAttachPin(PO4, LEDC_CHANNEL_1);
        pinMode(PI1,INPUT);
        pinMode(PI2,INPUT);
        pinMode(PI3,INPUT);
        pinMode(PI4,INPUT);
        //SSD1306_SWITCHCAPVCC =内部で3.3Vから表示電圧を生成
        Wire.setClock(400000);                                          // クロック速度を最も速くなるように設定して、通信を改善します(高速モード)
        if(!OLCD.begin(SSD1306_SWITCHCAPVCC, OLCDAres)) {               // 128x64のスレーブアドレスオリジナル値は0x3Dでしたが手元の元は0x3Cでした。
                Serial.println(F("SSD1306 allocation failed"));
                for(;;);                                                // Don't proceed, loop forever
        }
        status = bmp.begin();
        if (!status) {
                Serial.println(F("Could not find BME280 sensor!"));
        }
        // bme.chipID(); // Deprecated. See chipModel().
        switch(bmp.chipModel()){
                case BME280::ChipModel_BME280:
                        Serial.println(F("Found BME280 sensor! Success."));
                        break;
                case BME280::ChipModel_BMP280:
                        Serial.println(F("Found BMP280 sensor! No Humidity available."));
                        break;
                default:
                        Serial.println(F("Found UNKNOWN sensor! Error!"));
        }
        //TSL2561 setup
        light.begin(TSLAres);
        unsigned char I2CID;
        if(!light.getID(I2CID)){
                //Error
                byte error = light.getError();
                printError(error);
        }
        TSLgain = 0;
        unsigned char time = 2;
        light.setTiming(TSLgain,time,TSLms);
        light.setPowerUp();
        //CO2センサセットアップ SparkFunのライブラリに変更
        if (!mySensor.begin()){
                Serial.println(F("CCS811 error. Please check wiring. Freezing..."));
                fCcsRun = false;
        }
        /*
        if(!ccs.begin(CCSAres)){
                Serial.println(F("CCS skiped.Failed to start sensor! Please check your wiring."));
                fCcsRun = false;
        }
        */
        if(fCcsRun){
                mySensor.setDriveMode(4);
                if (mySensor.dataAvailable()){
                        //If so, have the sensor read and calculate the results.
                        //Get them later
                        mySensor.readAlgorithmResults();
                        //Returns calculated CO2 reading
                        Serial.print(F("CO2["));        Serial.print(mySensor.getCO2());        Serial.println(F("]"));
                        //Returns calculated TVOC reading
                        Serial.print(F("tVOC["));       Serial.print(mySensor.getTVOC());       Serial.println(F("]"));
                        //Display the time since program start
                        //Serial.print(F("millis["));     Serial.print(millis());                 Serial.println("]");
                }
                delay(10);                                              //Don't spam the I2C bus
                /*
                ccs.setDriveMode(CCS811_DRIVE_MODE_60SEC);              //CCS811_DRIVE_MODE_1SEC/CCS811_DRIVE_MODE_60SEC 
                while(!ccs.available());
                float temp = ccs.calculateTemperature();
                ccs.setTempOffset(temp - 25.0);                         //25の根拠不明
                */
        }        
        // バッファをクリアします
        OLCD.clearDisplay();
        // SPIFFSのセットアップ  10.2で追加
        if(!SPIFFS.begin(true)){
                Serial.println("An Error has occurred while mounting SPIFFS");
                return;
        }
        // アクセスポイント(無線LAN親機) + ステーション(無線LAN子機)
        WiFi.mode(WIFI_AP_STA);
        // まず既存のアクセスポイント(ネットワーク)に接続する
        WiFi.config(ip,gateway,netmask);                              //固定IPにする場合に使用
        WiFi.begin(ssid, pass);
        // Wait for connection
        Serial.println(F(""));
        while (WiFi.status() != WL_CONNECTED) {                         //接続するまでループするが、すごく電流を喰うようでchipが熱くなる。要対策。
                delay(1000);
                Serial.print(F("."));
        }
        Serial.println(F(""));
        Serial.print(F("Connected to "));Serial.println(ssid);
        IPAddress myIP =WiFi.localIP();
        Serial.print(F("IP address: "));Serial.println(myIP);
        OLCD.setTextSize(1);OLCD.setTextColor(SSD1306_WHITE);OLCD.setCursor(0, 0);
        OLCD.print(F("LoginID:"));OLCD.println(myID);
        OLCD.print(F("LoginPASS:"));OLCD.println(myPASS);
        OLCD.print(F("IP:"));OLCD.println(myIP);
        //OLCD.display();
        // SoftAPを開始する
        WiFi.softAPConfig(apIp,apGateway,apNetmask);
        WiFi.softAP(apSsid, apPassword);
        //IPAddress apIp = WiFi.softAPIP();
        OLCD.setTextSize(1);OLCD.setTextColor(SSD1306_WHITE);OLCD.setCursor(0, 40);
        OLCD.print(F("SSID:"));OLCD.println(apSsid);
        OLCD.print(F("PASS:"));OLCD.println(apPassword);
        OLCD.print(F("IP:"));OLCD.println(apIp[0]);OLCD.println(F("."));OLCD.println(apIp[1]);OLCD.println(F("."));OLCD.println(apIp[2]);OLCD.println(F("."));OLCD.println(apIp[3]);
        OLCD.display();
        Serial.print(F("SoftAPのIPアドレス(LAN側): "));Serial.println(myIP);
        //init and get the time
        configTime(gmtOffset_sec, daylightOffset_sec, ntpServer);                       //void configTime(long gmtOffset_sec, int daylightOffset_sec, const char* server1, const char* server2, const char* server3);
        printLocalTime();
        //時刻を取得したので、ログファイルを作成する。
        createDir(SD, "/ESPdir");                                                       //ESPフォルダを作成してこの中にログファイルを作成する。
        getLocalTime(&timeinfo);
        sprintf(f_name,"/ESPdir/%04d%02d%02d%02d%02d%02d.txt",timeinfo.tm_year+1900,timeinfo.tm_mon+1,timeinfo.tm_mday,timeinfo.tm_hour,timeinfo.tm_min,timeinfo.tm_sec);
        //ホスト名解決にmdnsを使用する
        if (!MDNS.begin(host)) {                                                        //http://esp32.local
                Serial.println(F("Error setting up MDNS responder!"));
                OLCD.setCursor(0, 56);OLCD.print(F("Error setting up MDNS responder!"));
                while (1) {
                        delay(1000);
                }
        }
        Serial.println(F("mDNS responder started"));
        //serverIndexに格納されているインデックスページを返す
        WS.on("/login", HTTP_GET, []() {
                WS.sendHeader("Connection", "close");
                WS.send(200, "text/html", loginIndex);
                OLCD.fillRect(0,56,128,8,SSD1306_BLACK);
                OLCD.setCursor(0, 56);
                OLCD.setTextColor(SSD1306_WHITE);
                OLCD.print(F("Connection"));
                OLCD.display();
        });
        WS.on("/serverIndex", HTTP_GET, []() {
                WS.sendHeader("Connection", "close");
                WS.send(200, "text/html", serverIndex);
                OLCD.fillRect(0,56,128,8,SSD1306_BLACK);
                OLCD.setCursor(0, 56);
                OLCD.setTextColor(SSD1306_WHITE);
                OLCD.print(F("UpdateReady"));
                OLCD.display();
        });
        //ファームウェアファイルのアップロードの処理
        WS.on("/update", HTTP_POST, []() {
                WS.sendHeader("Connection", "close");
                OLCD.setCursor(0, 56);
                OLCD.drawRect(0,56,128,8,SSD1306_BLACK);
                OLCD.display();
                WS.send(200, "text/plain", (Update.hasError()) ? "FAIL" : "OK");
                ESP.restart();
        }, []() {
                HTTPUpload& upload = WS.upload();
                if (upload.status == UPLOAD_FILE_START) {
                        Serial.printf("Update: %s\n", upload.filename.c_str());
                        OLCD.setCursor(0, 56);
                        OLCD.drawRect(0,56,128,8,SSD1306_BLACK);
                        OLCD.setTextColor(SSD1306_WHITE);
                        OLCD.print(F("Updating..."));
                        OLCD.display();
                        if (!Update.begin(UPDATE_SIZE_UNKNOWN)) {       //利用可能な最大サイズから始める
                                Update.printError(Serial);
                        }
                } else if (upload.status == UPLOAD_FILE_WRITE) {
                        //ESPへのファームウェアのフラッシュ
                        if (Update.write(upload.buf, upload.currentSize) != upload.currentSize) {
                                Update.printError(Serial);
                        }
                } else if (upload.status == UPLOAD_FILE_END) {
                        if (Update.end(true)) {                         //サイズを現在の進行状況に設定する場合はtrue
                                Serial.printf("Update Success: %u\nRebooting...\n", upload.totalSize);
                        } else {
                                Update.printError(Serial);
                        }
                }
        });
        WS.on("/GetData", handleGetData);                               //To get update of ADC Value only
        WS.on("/setup?", handleUpdate);                                 //To SetUpPage
        WS.on("/setup", handleSetUp);                                   //To SetUpPage
        WS.on("/favicon.ico", handleNotFound);
        WS.on("/Chart.bundle.js", handleNotFound);
        WS.on("/utils.js", handleNotFound);
        WS.on("/", HTTP_GET, []() {                                     //ルートアクセス時の応答関数を設定
                WS.sendHeader("Connection", "close");
                for(int j = 0;j<7;j++){
                        gDataBuf[j]     =      gSMAAve[j];
                }
                WS.send(200, "text/html", SendHTML(gDataBuf));
                //OLCD.setCursor(0, 32);
                //OLCD.fillRect(0,32,128,8,SSD1306_BLACK);
                //OLCD.display();
        });
        WS.onNotFound(handleNotFound);                                  //不正アクセス時の応答関数を設定
        WS.begin();                                                     //ウェブサーバ開始
        printOLCDValues();                                             
        for(int i = 0;i<10;i++){
                gSMABuf[i][0]   =       Vprs;
                gSMABuf[i][1]   =       Vtmp;
                gSMABuf[i][2]   =       Vhum;
                gSMABuf[i][3]   =       Vdin;
                gSMABuf[i][4]   =       Vco2;
                gSMABuf[i][5]   =       float(Vlux);
                gSMABuf[i][6]   =       analogRead(AIport);                
        }
        for(int i = 0;i<7;i++){
                gSMAAve[i]      =       gSMABuf[0][i];
        }
        //BufferClear
        for(int i = 0;i<24;i++){
                for(int j = 0;j<7;j++){
                       gDayBuf[i][j]    = NAN; 
                }
        }        
        //WDT
        timer = timerBegin(0, 80, true);                                //timer 0, div 80
        timerAttachInterrupt(timer, &resetModule, true);                //attach callback
        timerAlarmWrite(timer, wdtTimeout * 1000000, false);            //set time in us
        timerAlarmEnable(timer);                                        //enable interrupt
        preTime = micros();
}
//------------
int count       = 0;
int preSeth     = -1;
int preSetS     = -1;
int preIndex    = -1;
void loop(void) {
        timerWrite(timer, 0);                                           //reset timer (feed watchdog)
        nowTime         = micros();
        scanTime        = nowTime - preTime;
        preTime         = nowTime;
        if(WiFi.status() != WL_CONNECTED){                              //WiFiStatusを確認し、切断されている場合は
                WiFi.disconnect();                                      //一旦disconnectを実行し改めて再接続する。
                delay(1000);
                WiFi.config(ip,gateway,netmask);                        //固定IPにする場合に使用
                WiFi.begin(ssid, pass);
                // Wait for connection
                Serial.println(F(""));
                while (WiFi.status() != WL_CONNECTED) {                 //接続するまでループするが、すごく電流を喰うようでchipが熱くなる。要対策。
                        delay(1000);
                        Serial.print(F("."));
                }
                Serial.println(F("ReConnect"));
        }
        WS.handleClient();                                              //クライアントからの要求を処理する
        //delay(100);
        printOLCDValues();                                              //OLCDにデータを更新表示
        for(int i = 0;i<7;i++){
                gSMAAve[i] = gSMAAve[i]*7.0 - gSMABuf[count][i];
        }
        gSMABuf[count][0]   =       Vprs;
        gSMABuf[count][1]   =       Vtmp;
        gSMABuf[count][2]   =       Vhum;
        gSMABuf[count][3]   =       Vdin;
        gSMABuf[count][4]   =       Vco2;
        gSMABuf[count][5]   =       float(Vlux);
        gSMABuf[count][6]   =       analogRead(AIport);
        for(int i = 0;i<7;i++){
                gSMAAve[i] = (gSMAAve[i] + gSMABuf[count][i]) / 7.0;
        }
        getLocalTime(&timeinfo);
        sprintf(buf3,"%04d/%02d/%02d %02d:%02d:%02d",timeinfo.tm_year+1900,timeinfo.tm_mon+1,timeinfo.tm_mday,timeinfo.tm_hour,timeinfo.tm_min,timeinfo.tm_sec);
        OLCD.setCursor(0, 56);
        OLCD.fillRect(0,56,128,8,SSD1306_BLACK);
        OLCD.setTextSize(1);OLCD.setTextColor(SSD1306_WHITE);OLCD.setCursor(0, 56);
        OLCD.println(buf3);
        int s = timeinfo.tm_sec;
        if(preSetS != s){
                preSetS = s;
                //12.3でフォーマット変更
                sprintf(buf6,"%04d%02d%02d%02d%02d%02d,%d,%.1f,%.1f,%.1f,%.1f,%.1f,%.1f,%.1f",timeinfo.tm_year+1900,timeinfo.tm_mon+1,timeinfo.tm_mday,timeinfo.tm_hour,timeinfo.tm_min,timeinfo.tm_sec,scanTime,gSMAAve[0],gSMAAve[1],gSMAAve[2],gSMAAve[3],gSMAAve[4],gSMAAve[5],gSMAAve[6]);
                logFile = SD.open(f_name,FILE_APPEND);
                if(logFile){
                        logFile.println(buf6);
                        logFile.close();
                }
        }
        int h = timeinfo.tm_hour;                                               //OLCDに時刻表示 返値0-23
        //int m = printOLCDMinIndex();                                          //0-9 index
        //ループして遅延なく点滅する
        unsigned long currentMillis = millis();
        if(preSeth != h){
                //1時間おきに時刻を確認し、データをメモリに確保
                preSeth  = h;
                for(int i = 0;i<7;i++){
                        gDayBuf[h][i]   = gSMAAve[i];
                }
                digitalWrite(PO3, true);
                //次回分のデータをNULL化
                h = (h+1) % 24;
                for(int i = 0;i<7;i++){
                        gDayBuf[h][i]   = NAN;
                }
        }
        if (currentMillis - previousMillis >= interval) {
                //最後にLEDを点滅させた時間を保存します
                previousMillis = currentMillis;
                //LEDがオフの場合はオンにし、逆の場合も同様です。
                ledState = not(ledState);
                //変数のledStateでLEDを設定します。
                digitalWrite(LED, ledState);
                switch(count){
                        case 0:
                                digitalWrite(PO1, true);
                                digitalWrite(PO2, false);
                                //digitalWrite(PO3, false);
                                //digitalWrite(PO4, false);
                                break;
                        case 1:
                                digitalWrite(PO1, true);
                                digitalWrite(PO2, true);
                                //digitalWrite(PO3, false);
                                //digitalWrite(PO4, false);
                                break;
                        case 2:
                                digitalWrite(PO1, true);
                                digitalWrite(PO2, false);
                                //digitalWrite(PO3, true);
                                //digitalWrite(PO4, false);
                                break;
                        case 3:
                                digitalWrite(PO1, false);
                                digitalWrite(PO2, true);
                                //digitalWrite(PO3, false);
                                //digitalWrite(PO4, true);
                                break;
                        case 4:
                                digitalWrite(PO1, false);
                                digitalWrite(PO2, false);
                                //digitalWrite(PO3, false);
                                //digitalWrite(PO4, false);
                                break;
                        case 5:
                                digitalWrite(PO1, false);
                                digitalWrite(PO2, true);
                                //digitalWrite(PO3, false);
                                //digitalWrite(PO4, false);
                                break;
                        case 6:
                                digitalWrite(PO1, true);
                                digitalWrite(PO2, true);
                                //digitalWrite(PO3, true);
                                //digitalWrite(PO4, false);
                                break;
                        case 7:
                                digitalWrite(PO1, true);
                                digitalWrite(PO2, true);
                                //digitalWrite(PO3, false);
                                //digitalWrite(PO4, true);
                                break;
                        case 8:
                                digitalWrite(PO1, false);
                                digitalWrite(PO2, false);
                                //digitalWrite(PO3, false);
                                //digitalWrite(PO4, false);
                                break;
                        case 9:
                                digitalWrite(PO1, false);
                                digitalWrite(PO2, false);
                                //digitalWrite(PO3, false);
                                //digitalWrite(PO4, false);
                                break;
                        default:
                                digitalWrite(PO1, false);
                                digitalWrite(PO2, false);
                                //digitalWrite(PO3, false);
                                //digitalWrite(PO4, false);
                                break;
                }
                //ledcWrite(LEDC_CHANNEL_0, brightness);
                ledcWrite(LEDC_CHANNEL_1, 255 - brightness);
                brightness = brightness + fadeAmount;
                if (brightness <= 0){
                        brightness = 50;
                        fadeAmount = abs(fadeAmount);
                        digitalWrite(PO4, false);
                }else if(brightness >= 250){
                        brightness = 254;
                        fadeAmount = -abs(fadeAmount);
                }
                count = (count + 1) % 10;
                Serial.println(count);
        }
}


2021/06/18:SDカードロギング方法を修正しました

起動時にログファイルを作成し、リセットが掛かるまで1秒毎に追記する方法を採っていました。この場合100MB程度のファイルが普通に作成されています。しかし、数日経つと他の処理時間が取れなくなることが判ってきました。MainLoopが通常34msec程度のスキャンタイムなのに950msecにまで遅延してしまいます。
理由を考えました。Append処理に時間が掛かるようになるのかと仮定を立て、毎日正時(0:00:00)に新規ファイル作成を実施するように変更してみました。数日様子見をしていたのですが、この場合MainLoopは遅くなること無く、35msecを維持できています。期待できそうです。
※確認方法はWebPageを開きタイトルに添えてあるLoopTimeで確認しています。

/*
 * 2021/6/14 T.Wanibe
 * 13.1更新:
 *      長期間動作しているとLOOP時間が長くなっている。この原因を特定したいが判らない。
 *      過程としてファイルシステム上の問題かと考える。そこで、毎日更新するような仕組みに変更してみる
 * 13.0更新:
 *      外部にアクセスしていたライブラリutils.jsが、サーバ閉鎖となり、グラフ表示が出来なくなってしまった。
 *      この対策をするためSPIFFSにutils.jsを置き、Localアクセスに変更します。
 *      また、FTPもアクセス出来るようにして置きます。SPIFFSにFTPでアクセス出来るようにして置きます。
 * 12.3更新:
 *      12.1,12,2がコンパイルエラーとなる原因不明のため ベースを12.0に戻して再構築
 *      exit status 1
 *      invalid conversion from 'int' to 'const char*' [-fpermissive]
 *      変更点は、SDログ内容の見直です。Loop()のScasnTimeをログに含めたいと思いました。
 *      これによりログファイルが巨大になるとScasnTimeがどうなるのか知りたいと思います。
 *      また、HTTP要求が有ったかどうかの判断も出来るのでは無いかと考えます。
 * 12.0更新:
 *      大きな問題を確認し対策を検討しているバージョンです。
 *      問題とは、Stationモードでルータのアクセスポイントに接続していて、何らかの理由でdisconnectしたときの
 *      対処です。当初、ESP32側が接続を監視し自動再接続するモノと思っていたのですが、実際には実現出来ないようです。
 *      自分でWDTで接続確認し、disconnectしていたら、再接続処理を行う必要があるようです。この対策を検討中です。
 *      WiFi.status() != WL_CONNECTED で判断して接続に失敗していたら、一旦DisConnectして再接続して見た
 *      のですがうまく実現出来ない事を確認しました。
 * 11.0更新:
 *      TFカードに計測データを記録するように修正
 *      SDカードモジュールはCATALEX MicroSD Card ModuleでESP32DevCと接続します。
 * 1    CS-----------IO5
 * 2    SCK----------IO18
 * 3    MOSI---------IO17
 * 4    MISO---------IO19
 * 5    VCC----------5V
 * 6    GND----------GND
 *      32GBのSDHCカードが使えることを確認しました。
 * 10.6更新:
 *      WDT機能を追加
 *      計測データの移動平均化
 *      isnan()を使うように修正しました。
 * CCS811の計測値が安定しないためSparkFunのドライバに変更してみる
 * 照度センサ(TSL2561) 空気品質センサ(CCS811)を追加
 * I2Cアドレス管理
 * TSL2561:     0x39
 * CCS811:      0x5A
 * OLCD1306:    0x3C
 * BME280:      0x76
 * SPIFFSをサポートし,WebPageからJavaScriptの読込先を外部に依存しないように変更してみた
 * ChartJSに馴れてきたこともあり、もう少しグラフ表示を凝ったモノにして見る。
 * 24時間更新に関し、更新した次のデータ枠をNULLにするようにした。更新位置を明確にするため
 * /setup 頁を作成 EEPROMに必要な情報を書き込むように変更
 * 『Indoor environment monitor』としてプロジェクトを更新
 * Chart.jsを作り込み強化
 * フラッシュの使用量が多少増えているが、まだまだ大丈夫そう
 * 1日分のデータバッファをサイクリックに使うことにした。そのため初期値をキチンと定義することにした。
 * データが無い場合はNULLにすることでグラフプロットが無くなることが判っているので対応した。
 * RootのHTML表示スタイルを更新した。
 * 時刻表示を追加して見ました。起動時にNTPサーバにアクセスし、時刻取得。ESP32内部のRTCに登録してOLCDに時刻表示する
 * ESP32のGPIOの一部のpinにはArduinoに用意されているanalogWrite()がありません。DACが存在するからかもしれません。
 * その代わりなのかLED_PWMという機能が用意されているようです。https://garretlab.web.fc2.com/arduino/lab/ledc/
 * この機能が利用できるPinは16個のようです。今回PO3(GPIO4)、PO4(GPIO2)が対象pinのようですので、改造してみました。
 * PWMによるLEDの明るさ調整ですが、単純に直線的な輝度制御は難しいですね
 * LEDの配置等を変更
 * OTAを維持したまま、モードをST+APにして普段はPCとの間でトランシーバモード、一方、ファーム更新はSTモードで実現出来ないか確認する
 * Adafruit_BME280.hがESP32でうまく動作しないため、ライブラリを変更し、BME280I2C.hを採用した
 * WebServer  on()の扱いをリファレンスで確認する必要があります。
 * https://garretlab.web.fc2.com/arduino/esp32/reference/libraries/WebServer/WebServer_on.html
 * この関数の為、WiznetのWebServer関数との整合性がとれない。 ESP32とSTM32の共存は難しそう
 * この記述はESP8266/32のみ
 * ※HTMLの記述にてString型を駆使して記述するが、その手法も多岐にわたる。好みの方法を確立しておく必要がある
 * 最大1310720バイトのフラッシュメモリのうち、スケッチが942118バイト(71%)を使っています。
 * 最大327680バイトのRAMのうち、グローバル変数が45544バイト(13%)を使っていて、ローカル変数で282136バイト使うことができます。
 * ブラウザからの転送が20%程度で止まってしまいます。シリアルによる書込は問題ないです。
 */
#include <WiFi.h>
#include <time.h>
#include <WiFiClient.h>
#include <WebServer.h>
#include <ESPmDNS.h>
#include <Update.h>
#include <FTPServer.h>
#include "SPIFFS.h"
#include "FS.h"
#include "SD.h"
#include "SPI.h"
#include "Adafruit_GFX.h"
#include "Adafruit_SSD1306.h"
#include <BME280I2C.h>
#include "SparkFunTSL2561.h"
#include "SparkFunCCS811.h"
//#include <Adafruit_CCS811.h>
#include "arduino_secrets.h"                                            //機密データを「Secret」タブ/arduino_secrets.hに入力してください
#include <EEPROM.h>
#include "esp_system.h"                                                 //MACマックアドレスを読み込む
//#define COMSCANDEC 0xC8
//#define COMSCANINC 0xC0
#define COMSCANDEC      0xC0
#define COMSCANINC      0xC8
//#define SETCOMPINS    0xA0
#define SETCOMPINS      0xA1
//#define SETSEGMENTREMAP 0xA1
#define SETSEGMENTREMAP 0xA0
#define HTTPport        80
#define SCREEN_WIDTH    128                                             // OLED display width, in pixels
#define SCREEN_HEIGHT   64                                              // OLED display height, in pixels
#define OLED_RESET      -1                                              // Reset pin # (or -1 if sharing Arduino reset pin)
#define NUMFLAKES       10                                              // アニメーション例の雪片の数
#define LOGO_HEIGHT     16
#define LOGO_WIDTH      16
#define OLCDAres        0x3C                                            //OLCD1306:     0x3C か 0x3D
#define TSLAres         0x39                                            //TSL2561:      0x39 0x29 0x49
#define CCSAres         0x5A                                            //CCS811:       0x5A 0x5B
#define BMEAres         0x76                                            //BME280:       0x76 0x77
#define LED             13                                              //ボード上のLEDは使えないのでGPIO13にLEDを接続
#define AIport          A6
#define PO1             17
#define PO2             16
#define PO3             4
#define PO4             2
#define PI1             39
#define PI2             34
#define PI3             35
#define PI4             33
#define LEDC_CHANNEL_0  0                                               // use first channel of 16 channels (started from zero)
#define LEDC_CHANNEL_1  1                                               // use first channel of 16 channels (started from zero)
const char*     host            = "esp32";
char            ssid[16]        = SECRET_SSID;                          //ネットワークSSID(名前)
char            pass[16]        = SECRET_PASS;                          //ネットワークパスワード(WPAの使用、またはWEPのキーとして使用)
String          myID            = "admin";
String          myPASS          = "password";
//variabls for blinking an LED with Millis
unsigned long   previousMillis  = 0;                                    //LEDが最後に更新されたときに保存されます
const long      interval        = 1000;                                 //点滅する間隔(ミリ秒)
int             ledState        = LOW;                                  //LEDの設定に使用されるledState
unsigned        status;
//Station用
byte            mac[]           = {0x24,0x0A,0xC4,0x61,0x1E,0x7C};      //Espressif Inc この企業は60個ほど保有している模様
byte            apMac[]         = {0x24,0x0A,0xC4,0x61,0x1E,0x7D};      //mac+1になっている模様 実際には本体に問い合わせ
byte            ip[]            = {192, 168, 0, 33};
byte            gateway[]       = {192, 168, 0, 1};
byte            netmask[]       = {255, 255, 255, 0};
//SoftAP用
byte            apIp[]          = {192, 168, 250, 33};
byte            apGateway[]     = {192, 168, 250, 1};
byte            apNetmask[]     = {255, 255, 255, 0};
char            apSsid[16]      = "ESP_AP";                             //このSSIDが公開される。
char            apPassword[16]  = "password";                           //
char            buf1[128];
char            buf2[128];
char            buf3[128];
char            buf4[128];
char            buf5[128];
char            buf6[128];
String          buf11           = "ABC";
String          buf22           = "DEF";
float           gDataBuf[7];                                            //現在は3要素いずれ7要素
float           gDayBuf[24][7];                                         //24時間分3要素データ
float           gSMABuf[10][7];                                         //1分ごとの要素データを確保し移動平均を求める
float           gSMAAve[7];
float           Vtmp(NAN),Vprs(NAN);                                    // hum(NAN)は無効
float           Vhum            = NAN;
float           Vco2            = NAN;
float           Vdin            = NAN;
float           Vspm            = 0.0;
double          Vlux            = NAN;
int             brightness      = 0;
int             fadeAmount      = 25;
boolean         TSLgain         = 0;                                    // Gain setting, 0 = X1, 1 = X16;
unsigned int    TSLms;                                                  // Integration ("shutter") time in milliseconds
//const char*     ntpServer     ="ntp.jst.mfeed.ad.jp";                 //日本のNTPサーバー選択
char            ntpServer[32]   ="192.168.0.199";                       //LocalTimeServer
float           timeZone        = 9.0;                                  //日本のTimeZone GMT+9
                                                                        //EEPROMには90と入力 -12の場合は -120 +1:30の場合は15
const long      gmtOffset_sec   = long(timeZone * 3600.0);              //9時間の時差を入れる
const int       daylightOffset_sec = 0;                                 //夏時間はないのでゼロ
bool            fCcsRun         = true;
struct tm       timeinfo;
WebServer       WS(HTTPport);
//I2C(SDA、SCLピン)に接続されたSSD1306ディスプレイの宣言
Adafruit_SSD1306 OLCD(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
BME280I2C       bmp;                                                    // Default : forced mode, standby time = 1000 ms
                                                                        // Oversampling = pressure ×1, temperature ×1, humidity ×1, filter off,
SFE_TSL2561     light;
//Adafruit_CCS811 ccs;
CCS811          mySensor(CCSAres);
struct st_esp {                                                         // EEPROMで利用する構造体を宣言
        char id;
        char DT[47];
        char ssid[16];
        char pass[16];
        char apSsid[16];
        char apPass[16];
        char timeZoneVal;
        char ntpServer[32];
};
const byte      ID = 0xA2;
const int       wdtTimeout      = 10;                                    //timeOut 5秒
hw_timer_t      *timer          = NULL;
char            f_name[128];
File            logFile;
String          uname           =       SECRET_FTPUSERNAME;
String          pword           =       SECRET_FTPPASSWORD;
unsigned long   scanTime        =       0;                              //単位はusec
unsigned long   preTime         =       0;
unsigned long   nowTime         =       0;
//SPIFFSは非推奨になりつつありますが、FtpServerにはSPIFFSを使用するしかないようです。SDにアクセス出来ない!
FTPServer       ftpSrv(SPIFFS);
//--------------WDT timeUpCall
void IRAM_ATTR resetModule() {
  ets_printf("reboot\n");
  esp_restart();
}
//-------------- Style
String style =
"<style>\n"
        "\t#file-input,input{width:100%;height:50px;border-radius:4px;margin:10px auto;font-size:15px}\n"
        "\tinput{background:#f1f1f1;border:0;padding:0 15px}body{background:#17A1A5;font-family:sans-serif;font-size:14px;color:#777}\n"
        "\t#file-input{padding:0;border:1px solid #ddd;line-height:44px;text-align:left;display:block;cursor:pointer}\n"
        "\t#bar,#prgbar{background-color:#f1f1f1;border-radius:10px}#bar{background-color:#17A1A5;width:0%;height:10px}\n"
        "\tform{background:#fff;max-width:500px;margin:150px auto;padding:30px;border-radius:5px;text-align:center}\n"
        "\t.btn{background:#17A1A5;color:#fff;cursor:pointer}\n"
"</style>\n";
//-------------- Login page
String loginIndex = 
"<form name=loginForm>\n"
        "\t<h1>ESP32 Login</h1>\n"
        "\t<input name=userid placeholder='User ID'>\n"
        "\t<input name=pwd placeholder=Password type=Password>\n"
"<input type=submit onclick=check(this.form) class=btn value=Login>\n</form>\n"
"<script>\n"
        "\tfunction check(form) {\n"
                "\t\tif(form.userid.value== '" + myID + "' && form.pwd.value== '" + myPASS +"' )\n"
                        "\t\t\t{window.open('/serverIndex')}\n"
                "\t\telse\n"
                        "\t\t\t{alert('Error Password or Username')}\n"
        "\t}\n"
"</script>\n" + style;
//-------------- Server Index Page
String serverIndex = 
"<script src='https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js'></script>\n"
"<form method='POST' action='#' enctype='multipart/form-data' id='upload_form'>\n"
        "\t<input type='file' name='update' id='file' onchange='sub(this)' style=display:none>\n"
        "\t<label id='file-input' for='file'>   Choose file...</label>\n"
        "\t<input type='submit' class=btn value='Update'>\n"
        "\t<br><br>\n"
        "\t<div id='prg'></div>\n"
        "\t<br><div id='prgbar'><div id='bar'></div></div><br>\n"
"</form>\n"
"<script>\n"
        "\tfunction sub(obj){\n"
                "\t\tvar fileName = obj.value.split('\\\\');\n"
                "\t\tdocument.getElementById('file-input').innerHTML = '   '+ fileName[fileName.length-1];\n"
        "\t};\n"
        "\t$('form').submit(function(e){\n"
                "\t\te.preventDefault();\n"
                "\t\tvar form = $('#upload_form')[0];\n"
                "\t\tvar data = new FormData(form);\n"
                "\t\t$.ajax({\n"
                        "\t\t\turl: '/update',\n"
                        "\t\t\ttype: 'POST',\n"
                        "\t\t\tdata: data,\n"
                        "\t\t\tcontentType: false,\n"
                        "\t\t\tprocessData:false,\n"
                        "\t\t\txhr: function() {\n"
                                "\t\t\t\tvar xhr = new window.XMLHttpRequest();\n"
                                "\t\t\t\txhr.upload.addEventListener('progress', function(evt) {\n"
                                        "\t\t\t\t\tif (evt.lengthComputable) {\n"
                                                "\t\t\t\t\t\tvar per = evt.loaded / evt.total;\n"
                                                "\t\t\t\t\t\t$('#prg').html('progress: ' + Math.round(per*100) + '%');\n"
                                                "\t\t\t\t\t\t$('#bar').css('width',Math.round(per*100) + '%');\n"
                                        "\t\t\t\t\t}\n"
                                "\t\t\t\t}, false);\n"
                                "\t\t\t\treturn xhr;\n"
                        "\t\t\t},\n"
                        "\t\t\tsuccess:function(d, s) {\n"
                                "\t\t\t\tconsole.log('success!')\n"
                        "\t\t\t},\n"
                        "\t\t\terror: function (a, b, c) {\n"
                        "\t\t\t}\n"
                "\t\t});\n"
        "\t});\n"
"</script>\n" + style;
//------------ fileNotFound
String fileNotFound = 
        "<html>\n<head>\n"
                "<META HTTP-EQUIV=\"Content-Type\" CONTENT=\"text/html; charset=UTF-8\">\n"
                "\t<title>ESP32 : 404 Not Found</title>\n"
        "</head>\n<body>\n"
                "\t<center><h1>ESP32 404 : Not Found</h1>\n"
                "\t<p>The requested URL was not found on this server.</p>\n"
                "\t<p>要求されたURLはこのサーバーで見つかりませんでした。</p></center>\n"
        "</body>\n</html>\n";
//------------
void handleGetData() {
        gDataBuf[0]     =       Vprs;
        gDataBuf[1]     =       Vtmp;
        gDataBuf[2]     =       Vhum;
        gDataBuf[3]     =       Vdin;
        gDataBuf[4]     =       Vco2;
        gDataBuf[5]     =       float(Vlux);
        gDataBuf[6]     =       analogRead(AIport);
        WS.send(200, "text/html", SendHTML(gDataBuf));         //Send ADC value only to client ajax request
}
//------------
void handleNotFound(){
        if (!handleFileRead(WS.uri())) {
                //  ファイルが見つかりません
                Serial.println(F("404 not found"));
                WS.send(404, "text/html",fileNotFound);
        }
}
//------------SPIFSS のファイルをクライアントに転送する
bool handleFileRead(String path) {
        Serial.println("handleFileRead: trying to read " + path);
        // パス指定されたファイルがあればクライアントに送信する
        if (path.endsWith("/")) path += "index.html";
                String contentType = getContentType(path);
                if (SPIFFS.exists(path)) {
                        Serial.println("handleFileRead: sending " + path);
                        File file = SPIFFS.open(path, "r");
                        WS.streamFile(file, contentType);
                        file.close();
                        Serial.println("handleFileRead: sent " + path);
                        return true;
        }else {
                Serial.println(F("handleFileRead: 404 not found"));
                WS.send(404, "text/html",fileNotFound);
                return false;
        }
}
//------------
void handleSetUp(){
        if(WS.hasArg("SBM")){                                           //引数に"SBM=1"が含まれていることを確認
                Serial.println(F("SetUp?"));
                int bufSize = sizeof(st_esp);
                EEPROM.begin(bufSize);                                  //構造体のサイズ設定
                st_esp  buf;
                String cmd1,cmd2,cmd3,cmd4,str1,str2,str3,str4;
                cmd1            = WS.arg("DT7");
                buf.DT[6]       = char(cmd1.toInt());
                cmd2            = WS.arg("DT8");
                buf.DT[7]       = char(cmd2.toInt());
                cmd3            = WS.arg("DT9");
                buf.DT[8]       = char(cmd3.toInt());
                cmd4            = WS.arg("DT10");
                buf.DT[9]       = char(cmd4.toInt());
                Serial.println("IP="+cmd1+"."+cmd2+"."+cmd3+"."+cmd4);
                cmd1            = WS.arg("DT11");
                buf.DT[10]      = char(cmd1.toInt());
                cmd2            = WS.arg("DT12");
                buf.DT[11]      = char(cmd2.toInt());
                cmd3            = WS.arg("DT13");
                buf.DT[12]      = char(cmd3.toInt());
                cmd4            = WS.arg("DT14");
                buf.DT[13]      = char(cmd4.toInt());
                Serial.println("MASK="+cmd1+"."+cmd2+"."+cmd3+"."+cmd4);
                cmd1            = WS.arg("DT15");
                buf.DT[14]      = char(cmd1.toInt());
                cmd2            = WS.arg("DT16");
                buf.DT[15]      = char(cmd2.toInt());
                cmd3            = WS.arg("DT17");
                buf.DT[16]      = char(cmd3.toInt());
                cmd4            = WS.arg("DT18");
                buf.DT[17]      = char(cmd4.toInt());
                Serial.println("GW="+cmd1+"."+cmd2+"."+cmd3+"."+cmd4);
                str1            = WS.arg("DT19");
                str1.toCharArray(buf.ssid, 16);
                Serial.println("SSID="+str1);
                str2            = WS.arg("DT20");
                str2.toCharArray(buf.pass, 16);
                Serial.println("PASSWORD="+str2);
                //
                cmd1            = WS.arg("DT27");
                buf.DT[26]      = char(cmd1.toInt());
                cmd2            = WS.arg("DT28");
                buf.DT[27]      = char(cmd2.toInt());
                cmd3            = WS.arg("DT29");
                buf.DT[28]      = char(cmd3.toInt());
                cmd4            = WS.arg("DT30");
                buf.DT[29]      = char(cmd4.toInt());
                Serial.println("AccessPoint IP="+cmd1+"."+cmd2+"."+cmd3+"."+cmd4);
                cmd1            = WS.arg("DT31");
                buf.DT[30]      = char(cmd1.toInt());
                cmd2            = WS.arg("DT32");
                buf.DT[31]      = char(cmd2.toInt());
                cmd3            = WS.arg("DT33");
                buf.DT[32]      = char(cmd3.toInt());
                cmd4            = WS.arg("DT34");
                buf.DT[33]      = char(cmd4.toInt());
                Serial.println("AccessPoint MASK="+cmd1+"."+cmd2+"."+cmd3+"."+cmd4);
                cmd1            = WS.arg("DT35");
                buf.DT[34]      = char(cmd1.toInt());
                cmd2            = WS.arg("DT36");
                buf.DT[35]      = char(cmd2.toInt());
                cmd3            = WS.arg("DT37");
                buf.DT[36]      = char(cmd3.toInt());
                cmd4            = WS.arg("DT38");
                buf.DT[37]      = char(cmd4.toInt());
                Serial.println("AccessPoint GW="+cmd1+"."+cmd2+"."+cmd3+"."+cmd4);
                str1            = WS.arg("DT39");
                str1.toCharArray(buf.apSsid, 16);
                Serial.println("AccessPoint SSID="+str1);
                str2            = WS.arg("DT40");
                str2.toCharArray(buf.apPass, 16);
                Serial.println("AccessPoint PASSWORD="+str2);
                str3            = WS.arg("DT41");
                buf.timeZoneVal = char(str3.toFloat() * 10);
                str4            = WS.arg("DT42");
                str4.toCharArray(buf.ntpServer, 32);
                               
                buf.id  = ID;
                EEPROM.put<st_esp>(0, buf);                
                EEPROM.commit();
                for (int i = 0; i < 4; i++){
                        ip[i]           = buf.DT[i+6];
                }
                for (int i = 0; i < 4; i++){
                        netmask[i]      = buf.DT[i+10];
                }
                for (int i = 0; i < 4; i++){
                        gateway[i]      = buf.DT[i+14];
                }
                for (int i = 0; i < 4; i++){
                        apIp[i]         = buf.DT[i+26];
                }
                for (int i = 0; i < 4; i++){
                        apNetmask[i]      = buf.DT[i+30];
                }
                for (int i = 0; i < 4; i++){
                        apGateway[i]      = buf.DT[i+34];
                }
                WS.send(200, "text/html", SetupHTML());
                ESP.restart();                                          //ESP再起動
        }else{
                Serial.println(F("SetUp"));
                WS.send(200, "text/html", SetupHTML());
        }
}
//------------
String SetupHTML(){
        String ptr =
        "<!DOCTYPE HTML PUBLIC '-//W3C//DTD HTML 4.01 Transitional//EN'>\n"
        "<HTML>\n"
        "<HEAD>\n"
                "\t\t<META HTTP-EQUIV='Content-Type' charset='UTF-8'>\n"
                "\t\t<META HTTP-EQUIV='Content-Style-Type'>\n"
                "\t\t<TITLE>IndoorEnvironmentMonitor Setup Page</TITLE>\n"
        "</HEAD>\n"
        "<BODY MARGINWIDTH='0' MARGINHEIGHT='0' leftmargin='0' style='margin: 0; padding: 0;'>\n"
        "<FORM>\n"
        "\t<BLOCKQUOTE>\n"
                "\t\t<BLOCKQUOTE>\n"
                        "\t\t\t<TABLE BGCOLOR='#17A1A5' BORDER='0' WIDTH='100%' CELLPADDING='1' style='font-family:Verdana;color:#ffffff;font-size:12px;' CELLSPACING='2'>\n"
                        "\t\t\t<TR>\n"
                                "\t\t\t\t<TD>IndoorEnvironmentMonitor Setup Page</TD>\n"
                        "\t\t\t</TR>\n"
                        "\t\t\t</TABLE><BR>\n"
                        "\t\t\t<script>\n"
                                "\t\t\t\tfunction hex2num (s_hex) {\n"
                                        "\t\t\t\t\teval(\"var n_num=0X\" + s_hex);\n"
                                        "\t\t\t\t\treturn n_num;\n"
                                "\t\t\t\t}\n"
                        "\t\t\t</script>\n"
                        "\t\t\t<tbody>\n"
                        "\t\t\t<INPUT TYPE='hidden' NAME='SBM' VALUE='1'>\n"
                        "\t\t\t<TABLE BORDER='0' CELLSPACING='1' CELLPADDING='0' WIDTH='100%' HEIGHT='450'>\n"
                        "\t\t\t<TR>\n"
                                "\t\t\t\t<TD HEIGHT='24' WIDTH='175'>MAC:</TD> \n"
                                "\t\t\t\t<TD WIDTH='75%' NOWRAP HEIGHT='24'>\n"
                                        "\t\t\t\t\t<INPUT ID='T1'  TYPE='text' SIZE='3' MAXLENGTH='2' NAME='DT1' VALUE=" +String(mac[0],HEX) +" readonly>.\n"
                                        "\t\t\t\t\t<INPUT ID='T3'  TYPE='text' SIZE='3' MAXLENGTH='2' NAME='DT2' VALUE=" +String(mac[1],HEX) +" readonly>.\n"
                                        "\t\t\t\t\t<INPUT ID='T5'  TYPE='text' SIZE='3' MAXLENGTH='2' NAME='DT3' VALUE=" +String(mac[2],HEX) +" readonly>.\n"
                                        "\t\t\t\t\t<INPUT ID='T7'  TYPE='text' SIZE='3' MAXLENGTH='2' NAME='DT4' VALUE=" +String(mac[3],HEX) +" readonly>.\n"
                                        "\t\t\t\t\t<INPUT ID='T9'  TYPE='text' SIZE='3' MAXLENGTH='2' NAME='DT5' VALUE=" +String(mac[4],HEX) +" readonly>.\n"
                                        "\t\t\t\t\t<INPUT ID='T11' TYPE='text' SIZE='3' MAXLENGTH='2' NAME='DT6' VALUE=" +String(mac[5],HEX) +" readonly>(HEX FIXED)\n"
                                        "\t\t\t\t\t<INPUT ID='T2'  TYPE='hidden' NAME='DT1'>\n"
                                        "\t\t\t\t\t<INPUT ID='T4'  TYPE='hidden' NAME='DT2'>\n"
                                        "\t\t\t\t\t<INPUT ID='T6'  TYPE='hidden' NAME='DT3'>\n"
                                        "\t\t\t\t\t<INPUT ID='T8'  TYPE='hidden' NAME='DT4'>\n"
                                        "\t\t\t\t\t<INPUT ID='T10' TYPE='hidden' NAME='DT5'>\n"
                                        "\t\t\t\t\t<INPUT ID='T12' TYPE='hidden' NAME='DT6'>\n"
                                "\t\t\t\t</TD> \n"
                        "\t\t\t</TR>\n"
                        "\t\t\t<TR>\n"
                                "\t\t\t\t<TD HEIGHT='32' WIDTH='175'>IP:</TD> \n"
                                "\t\t\t\t<TD WIDTH='75%'>\n"
                                        "\t\t\t\t\t<INPUT TYPE='text' SIZE='3' MAXLENGTH='3' NAME='DT7'  VALUE=" +String(ip[0],DEC) +">.\n"
                                        "\t\t\t\t\t<INPUT TYPE='text' SIZE='3' MAXLENGTH='3' NAME='DT8'  VALUE=" +String(ip[1],DEC) +">.\n"
                                        "\t\t\t\t\t<INPUT TYPE='text' SIZE='3' MAXLENGTH='3' NAME='DT9'  VALUE=" +String(ip[2],DEC) +">.\n"
                                        "\t\t\t\t\t<INPUT TYPE='text' SIZE='3' MAXLENGTH='3' NAME='DT10' VALUE=" +String(ip[3],DEC) +"\n"
                                "\t\t\t\t</TD>\n"
                        "\t\t\t</TR>\n"
                        "\t\t\t<TR>\n"
                                "\t\t\t\t<TD HEIGHT='32' WIDTH='175'>MASK:</TD> \n"
                                "\t\t\t\t<TD WIDTH='75%'>\n"
                                        "\t\t\t\t\t<INPUT TYPE='text' SIZE='3' MAXLENGTH='3' NAME='DT11' VALUE=" +String(netmask[0],DEC) +">.\n"
                                        "\t\t\t\t\t<INPUT TYPE='text' SIZE='3' MAXLENGTH='3' NAME='DT12' VALUE=" +String(netmask[1],DEC) +">.\n"
                                        "\t\t\t\t\t<INPUT TYPE='text' SIZE='3' MAXLENGTH='3' NAME='DT13' VALUE=" +String(netmask[2],DEC) +">.\n"
                                        "\t\t\t\t\t<INPUT TYPE='text' SIZE='3' MAXLENGTH='3' NAME='DT14' VALUE=" +String(netmask[3],DEC) +">\n"
                                "\t\t\t\t</TD> \n"
                        "\t\t\t</TR>\n"
                        "\t\t\t<TR>\n"
                                "\t\t\t\t<TD HEIGHT='32' WIDTH='175'>GW:</TD> \n"
                                "\t\t\t\t<TD WIDTH='75%'>\n"
                                        "\t\t\t\t\t<INPUT TYPE='text' SIZE='3' MAXLENGTH='3' NAME='DT15' VALUE=" +String(gateway[0],DEC) +">.\n"
                                        "\t\t\t\t\t<INPUT TYPE='text' SIZE='3' MAXLENGTH='3' NAME='DT16' VALUE=" +String(gateway[1],DEC) +">.\n"
                                        "\t\t\t\t\t<INPUT TYPE='text' SIZE='3' MAXLENGTH='3' NAME='DT17' VALUE=" +String(gateway[2],DEC) +">.\n"
                                        "\t\t\t\t\t<INPUT TYPE='text' SIZE='3' MAXLENGTH='3' NAME='DT18' VALUE=" +String(gateway[3],DEC) +">\n"
                                "\t\t\t\t</TD>\n"
                        "\t\t\t</TR>\n"
                        "\t\t\t<TR>\n"
                                "\t\t\t\t<TD HEIGHT='24' WIDTH='175'>SSID:</TD>\n"
                                "\t\t\t\t<TD WIDTH='75%'>\n"
                                        "\t\t\t\t\t<INPUT NAME='DT19' TYPE='text' SIZE='25' maxlength='25' VALUE=" +String(ssid) +">(Up to 16 characters)\n"
                                "\t\t\t\t</TD>\n"
                        "\t\t\t</TR>\n"
                        "\t\t\t<TR>\n"
                                "\t\t\t\t<TD HEIGHT='24' WIDTH='175' NOWRAP>PASSWORD:</TD> \n"
                                "\t\t\t\t<TD WIDTH='75%'>\n"
                                        "\t\t\t\t\t<INPUT NAME='DT20' TYPE='text' SIZE='25' maxlength='25' VALUE=" +String(pass) +">(Up to 16 characters)\n"
                                "\t\t\t\t</TD>\n"
                        "\t\t\t</TR>\n"
                        "\t\t\t<TR>\n"
                                "\t\t\t\t<TD COLSPAN='2'><P><CENTER><HR></CENTER></TD>\n"
                        "\t\t\t</TR>\n"
                        "\t\t\t<TR>\n"
                                "\t\t\t\t<TD HEIGHT='24' WIDTH='175'>AccessPoint MAC:</TD> \n"
                                "\t\t\t\t<TD WIDTH='75%' NOWRAP HEIGHT='24'>\n"
                                        "\t\t\t\t\t<INPUT ID='T21'  TYPE='text' SIZE='3' MAXLENGTH='2' NAME='DT21' VALUE=" +String(apMac[0],HEX) +" readonly>.\n"
                                        "\t\t\t\t\t<INPUT ID='T23'  TYPE='text' SIZE='3' MAXLENGTH='2' NAME='DT22' VALUE=" +String(apMac[1],HEX) +" readonly>.\n"
                                        "\t\t\t\t\t<INPUT ID='T25'  TYPE='text' SIZE='3' MAXLENGTH='2' NAME='DT23' VALUE=" +String(apMac[2],HEX) +" readonly>.\n"
                                        "\t\t\t\t\t<INPUT ID='T27'  TYPE='text' SIZE='3' MAXLENGTH='2' NAME='DT24' VALUE=" +String(apMac[3],HEX) +" readonly>.\n"
                                        "\t\t\t\t\t<INPUT ID='T29'  TYPE='text' SIZE='3' MAXLENGTH='2' NAME='DT25' VALUE=" +String(apMac[4],HEX) +" readonly>.\n"
                                        "\t\t\t\t\t<INPUT ID='T31'  TYPE='text' SIZE='3' MAXLENGTH='2' NAME='DT26' VALUE=" +String(apMac[5],HEX) +" readonly>(HEX FIXED)\n"
                                        "\t\t\t\t\t<INPUT ID='T22'  TYPE='hidden' NAME='DT21'>\n"
                                        "\t\t\t\t\t<INPUT ID='T24'  TYPE='hidden' NAME='DT22'>\n"
                                        "\t\t\t\t\t<INPUT ID='T26'  TYPE='hidden' NAME='DT23'>\n"
                                        "\t\t\t\t\t<INPUT ID='T28'  TYPE='hidden' NAME='DT24'>\n"
                                        "\t\t\t\t\t<INPUT ID='T30'  TYPE='hidden' NAME='DT25'>\n"
                                        "\t\t\t\t\t<INPUT ID='T32'  TYPE='hidden' NAME='DT26'>\n"
                                "\t\t\t\t</TD> \n"
                        "\t\t\t</TR>\n"
                        "\t\t\t<TR>\n"
                                "\t\t\t\t<TD HEIGHT='32' WIDTH='175'>AccessPoint IP:</TD> \n"
                                "\t\t\t\t<TD WIDTH='75%'>\n"
                                        "\t\t\t\t\t<INPUT TYPE='text' SIZE='3' MAXLENGTH='3' NAME='DT27' VALUE=" +String(apIp[0],DEC) +">.\n"
                                        "\t\t\t\t\t<INPUT TYPE='text' SIZE='3' MAXLENGTH='3' NAME='DT28' VALUE=" +String(apIp[1],DEC) +">.\n"
                                        "\t\t\t\t\t<INPUT TYPE='text' SIZE='3' MAXLENGTH='3' NAME='DT29' VALUE=" +String(apIp[2],DEC) +">.\n"
                                        "\t\t\t\t\t<INPUT TYPE='text' SIZE='3' MAXLENGTH='3' NAME='DT30' VALUE=" +String(apIp[3],DEC) +"\n"
                                "\t\t\t\t</TD>\n"
                        "\t\t\t</TR>\n"
                        "\t\t\t<TR>\n"
                                "\t\t\t\t<TD HEIGHT='32' WIDTH='175'>AccessPoint MASK:</TD> \n"
                                "\t\t\t\t<TD WIDTH='75%'>\n"
                                        "\t\t\t\t\t<INPUT TYPE='text' SIZE='3' MAXLENGTH='3' NAME='DT31' VALUE=" +String(apNetmask[0],DEC) +">.\n"
                                        "\t\t\t\t\t<INPUT TYPE='text' SIZE='3' MAXLENGTH='3' NAME='DT32' VALUE=" +String(apNetmask[1],DEC) +">.\n"
                                        "\t\t\t\t\t<INPUT TYPE='text' SIZE='3' MAXLENGTH='3' NAME='DT33' VALUE=" +String(apNetmask[2],DEC) +">.\n"
                                        "\t\t\t\t\t<INPUT TYPE='text' SIZE='3' MAXLENGTH='3' NAME='DT34' VALUE=" +String(apNetmask[3],DEC) +">\n"
                                "\t\t\t\t</TD> \n"
                        "\t\t\t</TR>\n"
                        "\t\t\t<TR>\n"
                                "\t\t\t\t<TD HEIGHT='32' WIDTH='175'>AccessPoint GW:</TD> \n"
                                "\t\t\t\t<TD WIDTH='75%'>\n"
                                        "\t\t\t\t\t<INPUT TYPE='text' SIZE='3' MAXLENGTH='3' NAME='DT35' VALUE=" +String(apGateway[0],DEC) +">.\n"
                                        "\t\t\t\t\t<INPUT TYPE='text' SIZE='3' MAXLENGTH='3' NAME='DT36' VALUE=" +String(apGateway[1],DEC) +">.\n"
                                        "\t\t\t\t\t<INPUT TYPE='text' SIZE='3' MAXLENGTH='3' NAME='DT37' VALUE=" +String(apGateway[2],DEC) +">.\n"
                                        "\t\t\t\t\t<INPUT TYPE='text' SIZE='3' MAXLENGTH='3' NAME='DT38' VALUE=" +String(apGateway[3],DEC) +">\n"
                                "\t\t\t\t</TD>\n"
                        "\t\t\t</TR>\n"
                        
                        "\t\t\t<TR>\n"
                                "\t\t\t\t<TD HEIGHT='24' WIDTH='175'>AccessPoint SSID:</TD>\n"
                                "\t\t\t\t<TD WIDTH='75%'>\n"
                                        "\t\t\t\t\t<INPUT NAME='DT39' TYPE='text' SIZE='25' maxlength='25' VALUE=" +String(apSsid) +">(Up to 16 characters)\n"
                                "\t\t\t\t</TD>\n"
                        "\t\t\t</TR>\n"
                        "\t\t\t<TR>\n"
                                "\t\t\t\t<TD HEIGHT='24' WIDTH='175' NOWRAP>AccessPoint PASSWORD:</TD> \n"
                                "\t\t\t\t<TD WIDTH='75%'>\n"
                                        "\t\t\t\t\t<INPUT NAME='DT40' TYPE='text' SIZE='25' maxlength='25' VALUE=" +String(apPassword) +">(Up to 16 characters)\n"
                                "\t\t\t\t</TD>\n"
                        "\t\t\t</TR>\n"
                        "\t\t\t<tr>\n"
                                "\t\t\t\t<td colspan='2'><p></p><center><hr></center></td>\n"
                        "\t\t\t</tr>\n"
                        "\t\t\t<tr>\n"
                                "\t\t\t\t<td height='24' width='175' nowrap=''>TimeZone:</td>\n"
                                "\t\t\t\t<td width='75%'>\n"
                                        "\t\t\t\t\t<input name='DT41' type='text' size='10' maxlength='10' value=" +String(timeZone) +">\n"
                                "\t\t\t\t</td>\n"
                        "\t\t\t</tr>\n"
                        "\t\t\t<tr>\n"
                                "\t\t\t\t<td height='24' width='175' nowrap=''>NTPサーバ:</td>\n"
                                "\t\t\t\t<td width='75%'>\n"
                                        "\t\t\t\t\t<input name='DT42' type='text' size='40' maxlength='40' value=" +String(ntpServer) +">(Up to 32 characters)\n"
                                "\t\t\t\t</td>\n"
                        "\t\t\t</tr>\n"
                        "\t\t\t<TR>\n"
                                "\t\t\t\t<TD COLSPAN='2' WIDTH='16%' HEIGHT='32'>\n"
                                        "\t\t\t\t\t<P ALIGN=RIGHT><INPUT ID='button1' TYPE='submit' VALUE='SUBMIT'\n"
                                        "\t\t\t\t\tOnclick=\"\n"
                                        "\t\t\t\t\t\tdocument.getElementById('T2').value  = hex2num(document.getElementById('T1').value);\n"
                                        "\t\t\t\t\t\tdocument.getElementById('T4').value  = hex2num(document.getElementById('T3').value);\n"
                                        "\t\t\t\t\t\tdocument.getElementById('T6').value  = hex2num(document.getElementById('T5').value);\n"
                                        "\t\t\t\t\t\tdocument.getElementById('T8').value  = hex2num(document.getElementById('T7').value);\n"
                                        "\t\t\t\t\t\tdocument.getElementById('T10').value = hex2num(document.getElementById('T9').value);\n"
                                        "\t\t\t\t\t\tdocument.getElementById('T12').value = hex2num(document.getElementById('T11').value);\n"
                                        "\t\t\t\t\t\tdocument.getElementById('T22').value = hex2num(document.getElementById('T21').value);\n"
                                        "\t\t\t\t\t\tdocument.getElementById('T24').value = hex2num(document.getElementById('T23').value);\n"
                                        "\t\t\t\t\t\tdocument.getElementById('T26').value = hex2num(document.getElementById('T25').value);\n"
                                        "\t\t\t\t\t\tdocument.getElementById('T28').value = hex2num(document.getElementById('T27').value);\n"
                                        "\t\t\t\t\t\tdocument.getElementById('T30').value = hex2num(document.getElementById('T29').value);\n"
                                        "\t\t\t\t\t\tdocument.getElementById('T32').value = hex2num(document.getElementById('T31').value);\">\n"
                                "\t\t\t\t</TD>\n"
                        "\t\t\t</TR></tbody> \n"
                "\t\t</TABLE></BLOCKQUOTE>\n"
        "\t</BLOCKQUOTE>\n"
        "</FORM>\n"
        "</BODY>\n"
        "</HTML>\n";
        return ptr;
}
//------------
//                "\t<script SRC='http://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.3.0/Chart.bundle.js'></script>\n"
//                "\t<script SRC='http://cdn.rawgit.com/chartjs/Chart.js/master/samples/utils.js'></script>\n"
//              グラフ表示用ライブラリをSPIFFSにコピーしました。cdn.rawgit.comが閉鎖されたことに伴います。
String SendHTML(float *tempSensor){
        String ptr =
        "<!DOCTYPE HTML PUBLIC '-//W3C//DTD HTML 4.01 Transitional//EN'>\n"
        "<html lang='ja'><head><META http-equiv='Content-Type' content='text/html; charset=utf-8'>\n"
        "<META http-equiv='Content-Style-Type' content='text/css'>\n"
                "\t<title>IndoorEnvironmentMonitor</title>\n"
                "\t<LINK REL='shortcut icon' HREF='favicon.ico'>\n"
                "\t<script type='application/javascript' SRC='./Chart.bundle.js'></script>\n"
                "\t<script type='application/javascript' SRC='./utils.js'></script>\n"
                "\t<style>\n"
                        "\t\tcanvas{\n"
                        "\t\t\t-moz-user-select: none;\n"
                        "\t\t\t-webkit-user-select: none;\n"
                        "\t\t\t-ms-user-select: none;\n"
                        "\t\t}\n"
                        "\t\thtml { font-family: Helvetica; display: inline-block; margin: 0px auto; text-align: center;}\n"
                        "\t\tbody{margin-top: 50px;} h1 {color: #444444;margin: 50px auto 30px;}\n"
                        "\t\tp {font-size: 24px;color: #444444;margin-bottom: 10px;}\n"
                "\t</style>\n"
        "</head>\n"
        "<BODY BGCOLOR='#ffffff'>\n"
        "<CENTER><TABLE WIDTH='750' BORDER='0' CELLSPACING='0' CELLPADDING='0'>\n"
                "\t<TR>\n"
                        "\t\t<TD COLSPAN='3' VALIGN='TOP'>\n"
                        "\t\t<H2><CENTER>屋内環境モニタ(" +String(scanTime) + "us )</CENTER></H2>\n"
                        "\t\t</TD>\n"
                "\t</TR>\n"
                "\t<TR>\n"
                        "\t\t<TD WIDTH='33%'>\n"
                        "\t\t<BR CLEAR='ALL'></TD>\n"
                        "\t\t<TD WIDTH='33%'></TD>\n"
                        "\t\t<TD WIDTH='34%'></TD>\n"
                "\t</TR>\n"
                "\t<TR>\n"
                        "\t\t<TD WIDTH='33%'>\n"
                        "\t\t<BR CLEAR='ALL'></TD>\n"
                        "\t\t<TD WIDTH='33%'></TD>\n"
                        "\t\t<TD WIDTH='34%'></TD>\n"
                "\t</TR>\n"
                "\t<TR>\n"
                        "\t\t<TD WIDTH='33%' VALIGN='TOP' ALIGN='RIGHT'>データ取得時刻:</TD>\n"
                        "\t\t<TD WIDTH='33%' VALIGN='TOP' ALIGN='CENTER'>" +String(buf3) +"</TD>\n"
                        "\t\t<TD WIDTH='34%' VALIGN='TOP'></TD>\n"
                "\t</TR>\n"
                "\t<TR>\n"
                        "\t\t<TD WIDTH='33%' VALIGN='TOP' ALIGN='RIGHT'>気圧(BME280):</TD>\n"
                        "\t\t<TD WIDTH='33%' VALIGN='TOP' ALIGN='CENTER'>" +String(tempSensor[0],0)+"</TD>\n"
                        "\t\t<TD WIDTH='34%' VALIGN='TOP'>[Pa]</TD>\n"
                "\t</TR>\n"
                "\t<TR>\n"
                        "\t\t<TD WIDTH='33%' VALIGN='TOP' ALIGN='RIGHT'>気温(BME280):</TD>\n"
                        "\t\t<TD WIDTH='33%' VALIGN='TOP' ALIGN='CENTER'>" +String(tempSensor[1],1)+"</TD>\n"
                        "\t\t<TD WIDTH='34%' VALIGN='TOP'>[℃]</TD>\n"
                "\t</TR>\n"
                "\t<TR>\n"
                        "\t\t<TD WIDTH='33%' VALIGN='TOP' ALIGN='RIGHT'>湿度(BME280):</TD>\n"
                        "\t\t<TD WIDTH='33%' VALIGN='TOP'></TD>\n"
                        "\t\t<TD WIDTH='34%' VALIGN='TOP'>[%]</TD>\n"
                "\t</TR>\n"
                "\t<TR>\n"
                        "\t\t<TD WIDTH='33%' VALIGN='TOP' ALIGN='RIGHT'>浮遊粒子(GP2Y):</TD>\n"
                        "\t\t<TD WIDTH='33%' VALIGN='TOP'></TD>\n" 
                        "\t\t<TD WIDTH='34%' VALIGN='TOP'>[mg/m3]</TD>\n"
                "\t</TR>\n"
                "\t<TR>\n"
                        "\t\t<TD WIDTH='33%' VALIGN='TOP' ALIGN='RIGHT'>CO2濃度(CSS811):</TD>\n"
                        "\t\t<TD WIDTH='33%' VALIGN='TOP' ALIGN='CENTER'>"+String(tempSensor[4],0)+"</TD>\n"
                        "\t\t<TD WIDTH='34%' VALIGN='TOP'>[ppm]</TD>\n"
                "\t</TR>\n"
                "\t<TR>\n"
                        "\t\t<TD WIDTH='33%' VALIGN='TOP' ALIGN='RIGHT'>照度(TSL2561):</TD>\n"
                        "\t\t<TD WIDTH='33%' VALIGN='TOP' ALIGN='CENTER'>"+String(tempSensor[5],0)+"</TD>\n"
                        "\t\t<TD WIDTH='34%' VALIGN='TOP'>[lux]</TD>\n"
                "\t</TR>\n"
                "\t<TR>\n"
                        "\t\t<TD WIDTH='33%' VALIGN='TOP' ALIGN='RIGHT'>騒音(BOB-12758):</TD>\n"
                        "\t\t<TD WIDTH='33%' VALIGN='TOP' ALIGN='CENTER'>"+String(tempSensor[6],0)+"</TD>\n"
                        "\t\t<TD WIDTH='34%' VALIGN='TOP'>[dBm]</TD>\n"
                "\t</TR>\n"
        "</TABLE>\n"
        "<div style='width:75%;'><canvas id='canvas' width='750' height='400' style='display: block; width: 750px; height: 400px;'></canvas></div>\n"
        "</CENTER>\n"
        "\t<script>\n"
                "\t\tvar randomScalingFactor = function() {\n"
                        "\t\t\treturn 18 + Math.random() * 10;\n"
                "\t\t};\n"
                "\t\tvar data_00 = ["+  (isnan(gDayBuf[0][0]) ? String('\0') : String(gDayBuf[0][0],0))+ ","+    (isnan(gDayBuf[1][0]) ? String('\0') : String(gDayBuf[1][0],0))+ ","+
                                        (isnan(gDayBuf[2][0]) ? String('\0') : String(gDayBuf[2][0],0))+ ","+    (isnan(gDayBuf[3][0]) ? String('\0') : String(gDayBuf[3][0],0))+ ","+
                                        (isnan(gDayBuf[4][0]) ? String('\0') : String(gDayBuf[4][0],0))+ ","+    (isnan(gDayBuf[5][0]) ? String('\0') : String(gDayBuf[5][0],0))+ ","+
                                        (isnan(gDayBuf[6][0]) ? String('\0') : String(gDayBuf[6][0],0))+ ","+    (isnan(gDayBuf[7][0]) ? String('\0') : String(gDayBuf[7][0],0))+ ","+
                                        (isnan(gDayBuf[8][0]) ? String('\0') : String(gDayBuf[8][0],0))+ ","+    (isnan(gDayBuf[9][0]) ? String('\0') : String(gDayBuf[9][0],0))+ ","+
                                        (isnan(gDayBuf[10][0]) ? String('\0') : String(gDayBuf[10][0],0))+","+    (isnan(gDayBuf[11][0]) ? String('\0') : String(gDayBuf[11][0],0))+","+
                                        (isnan(gDayBuf[12][0]) ? String('\0') : String(gDayBuf[12][0],0))+","+    (isnan(gDayBuf[13][0]) ? String('\0') : String(gDayBuf[13][0],0))+","+
                                        (isnan(gDayBuf[14][0]) ? String('\0') : String(gDayBuf[14][0],0))+","+    (isnan(gDayBuf[15][0]) ? String('\0') : String(gDayBuf[15][0],0))+","+
                                        (isnan(gDayBuf[16][0]) ? String('\0') : String(gDayBuf[16][0],0))+","+    (isnan(gDayBuf[17][0]) ? String('\0') : String(gDayBuf[17][0],0))+","+
                                        (isnan(gDayBuf[18][0]) ? String('\0') : String(gDayBuf[18][0],0))+","+    (isnan(gDayBuf[19][0]) ? String('\0') : String(gDayBuf[19][0],0))+","+
                                        (isnan(gDayBuf[20][0]) ? String('\0') : String(gDayBuf[20][0],0))+","+    (isnan(gDayBuf[21][0]) ? String('\0') : String(gDayBuf[21][0],0))+","+
                                        (isnan(gDayBuf[22][0]) ? String('\0') : String(gDayBuf[22][0],0))+","+    (isnan(gDayBuf[23][0]) ? String('\0') : String(gDayBuf[23][0],0))+","+
                "];\n"
                "\t\tvar data_01 = ["+  (isnan(gDayBuf[0][1]) ? String('\0') : String(gDayBuf[0][1],1))+ ","+    (isnan(gDayBuf[1][1]) ? String('\0') : String(gDayBuf[1][1],1))+ ","+
                                        (isnan(gDayBuf[2][1]) ? String('\0') : String(gDayBuf[2][1],1))+ ","+    (isnan(gDayBuf[3][1]) ? String('\0') : String(gDayBuf[3][1],1))+ ","+
                                        (isnan(gDayBuf[4][1]) ? String('\0') : String(gDayBuf[4][1],1))+ ","+    (isnan(gDayBuf[5][1]) ? String('\0') : String(gDayBuf[5][1],1))+ ","+
                                        (isnan(gDayBuf[6][1]) ? String('\0') : String(gDayBuf[6][1],1))+ ","+    (isnan(gDayBuf[7][1]) ? String('\0') : String(gDayBuf[7][1],1))+ ","+
                                        (isnan(gDayBuf[8][1]) ? String('\0') : String(gDayBuf[8][1],1))+ ","+    (isnan(gDayBuf[9][1]) ? String('\0') : String(gDayBuf[9][1],1))+ ","+
                                        (isnan(gDayBuf[10][1]) ? String('\0') : String(gDayBuf[10][1],1))+","+    (isnan(gDayBuf[11][1]) ? String('\0') : String(gDayBuf[11][1],1))+","+
                                        (isnan(gDayBuf[12][1]) ? String('\0') : String(gDayBuf[12][1],1))+","+    (isnan(gDayBuf[13][1]) ? String('\0') : String(gDayBuf[13][1],1))+","+
                                        (isnan(gDayBuf[14][1]) ? String('\0') : String(gDayBuf[14][1],1))+","+    (isnan(gDayBuf[15][1]) ? String('\0') : String(gDayBuf[15][1],1))+","+
                                        (isnan(gDayBuf[16][1]) ? String('\0') : String(gDayBuf[16][1],1))+","+    (isnan(gDayBuf[17][1]) ? String('\0') : String(gDayBuf[17][1],1))+","+
                                        (isnan(gDayBuf[18][1]) ? String('\0') : String(gDayBuf[18][1],1))+","+    (isnan(gDayBuf[19][1]) ? String('\0') : String(gDayBuf[19][1],1))+","+
                                        (isnan(gDayBuf[20][1]) ? String('\0') : String(gDayBuf[20][1],1))+","+    (isnan(gDayBuf[21][1]) ? String('\0') : String(gDayBuf[21][1],1))+","+
                                        (isnan(gDayBuf[22][1]) ? String('\0') : String(gDayBuf[22][1],1))+","+    (isnan(gDayBuf[23][1]) ? String('\0') : String(gDayBuf[23][1],1))+","+
                "];\n"
                "\t\tvar data_02 = ["+  (isnan(gDayBuf[0][2]) ? String('\0') : String(gDayBuf[0][2],0))+ ","+    (isnan(gDayBuf[1][2]) ? String('\0') : String(gDayBuf[1][2],0))+ ","+
                                        (isnan(gDayBuf[2][2]) ? String('\0') : String(gDayBuf[2][2],0))+ ","+    (isnan(gDayBuf[3][2]) ? String('\0') : String(gDayBuf[3][2],0))+ ","+
                                        (isnan(gDayBuf[4][2]) ? String('\0') : String(gDayBuf[4][2],0))+ ","+    (isnan(gDayBuf[5][2]) ? String('\0') : String(gDayBuf[5][2],0))+ ","+
                                        (isnan(gDayBuf[6][2]) ? String('\0') : String(gDayBuf[6][2],0))+ ","+    (isnan(gDayBuf[7][2]) ? String('\0') : String(gDayBuf[7][2],0))+ ","+
                                        (isnan(gDayBuf[8][2]) ? String('\0') : String(gDayBuf[8][2],0))+ ","+    (isnan(gDayBuf[9][2]) ? String('\0') : String(gDayBuf[9][2],0))+ ","+
                                        (isnan(gDayBuf[10][2]) ? String('\0') : String(gDayBuf[10][2],0))+","+    (isnan(gDayBuf[11][2]) ? String('\0') : String(gDayBuf[11][2],0))+","+
                                        (isnan(gDayBuf[12][2]) ? String('\0') : String(gDayBuf[12][2],0))+","+    (isnan(gDayBuf[13][2]) ? String('\0') : String(gDayBuf[13][2],0))+","+
                                        (isnan(gDayBuf[14][2]) ? String('\0') : String(gDayBuf[14][2],0))+","+    (isnan(gDayBuf[15][2]) ? String('\0') : String(gDayBuf[15][2],0))+","+
                                        (isnan(gDayBuf[16][2]) ? String('\0') : String(gDayBuf[16][2],0))+","+    (isnan(gDayBuf[17][2]) ? String('\0') : String(gDayBuf[17][2],0))+","+
                                        (isnan(gDayBuf[18][2]) ? String('\0') : String(gDayBuf[18][2],0))+","+    (isnan(gDayBuf[19][2]) ? String('\0') : String(gDayBuf[19][2],0))+","+
                                        (isnan(gDayBuf[20][2]) ? String('\0') : String(gDayBuf[20][2],0))+","+    (isnan(gDayBuf[21][2]) ? String('\0') : String(gDayBuf[21][2],0))+","+
                                        (isnan(gDayBuf[22][2]) ? String('\0') : String(gDayBuf[22][2],0))+","+    (isnan(gDayBuf[23][2]) ? String('\0') : String(gDayBuf[23][2],0))+","+
                "];\n"
                "\t\tvar data_03 = ["+  (isnan(gDayBuf[0][3]) ? String('\0') : String(gDayBuf[0][3],0))+ ","+    (isnan(gDayBuf[1][3]) ? String('\0') : String(gDayBuf[1][3],0))+ ","+
                                        (isnan(gDayBuf[2][3]) ? String('\0') : String(gDayBuf[2][3],0))+ ","+    (isnan(gDayBuf[3][3]) ? String('\0') : String(gDayBuf[3][3],0))+ ","+
                                        (isnan(gDayBuf[4][3]) ? String('\0') : String(gDayBuf[4][3],0))+ ","+    (isnan(gDayBuf[5][3]) ? String('\0') : String(gDayBuf[5][3],0))+ ","+
                                        (isnan(gDayBuf[6][3]) ? String('\0') : String(gDayBuf[6][3],0))+ ","+    (isnan(gDayBuf[7][3]) ? String('\0') : String(gDayBuf[7][3],0))+ ","+
                                        (isnan(gDayBuf[8][3]) ? String('\0') : String(gDayBuf[8][3],0))+ ","+    (isnan(gDayBuf[9][3]) ? String('\0') : String(gDayBuf[9][3],0))+ ","+
                                        (isnan(gDayBuf[10][3]) ? String('\0') : String(gDayBuf[10][3],0))+","+    (isnan(gDayBuf[11][3]) ? String('\0') : String(gDayBuf[11][3],0))+","+
                                        (isnan(gDayBuf[12][3]) ? String('\0') : String(gDayBuf[12][3],0))+","+    (isnan(gDayBuf[13][3]) ? String('\0') : String(gDayBuf[13][3],0))+","+
                                        (isnan(gDayBuf[14][3]) ? String('\0') : String(gDayBuf[14][3],0))+","+    (isnan(gDayBuf[15][3]) ? String('\0') : String(gDayBuf[15][3],0))+","+
                                        (isnan(gDayBuf[16][3]) ? String('\0') : String(gDayBuf[16][3],0))+","+    (isnan(gDayBuf[17][3]) ? String('\0') : String(gDayBuf[17][3],0))+","+
                                        (isnan(gDayBuf[18][3]) ? String('\0') : String(gDayBuf[18][3],0))+","+    (isnan(gDayBuf[19][3]) ? String('\0') : String(gDayBuf[19][3],0))+","+
                                        (isnan(gDayBuf[20][3]) ? String('\0') : String(gDayBuf[20][3],0))+","+    (isnan(gDayBuf[21][3]) ? String('\0') : String(gDayBuf[21][3],0))+","+
                                        (isnan(gDayBuf[22][3]) ? String('\0') : String(gDayBuf[22][3],0))+","+    (isnan(gDayBuf[23][3]) ? String('\0') : String(gDayBuf[23][3],0))+","+
                "];\n"
                "\t\tvar data_04 = ["+  (isnan(gDayBuf[0][4]) ? String('\0') : String(gDayBuf[0][4],0))+ ","+    (isnan(gDayBuf[1][4]) ? String('\0') : String(gDayBuf[1][4],0))+ ","+
                                        (isnan(gDayBuf[2][4]) ? String('\0') : String(gDayBuf[2][4],0))+ ","+    (isnan(gDayBuf[3][4]) ? String('\0') : String(gDayBuf[3][4],0))+ ","+
                                        (isnan(gDayBuf[4][4]) ? String('\0') : String(gDayBuf[4][4],0))+ ","+    (isnan(gDayBuf[5][4]) ? String('\0') : String(gDayBuf[5][4],0))+ ","+
                                        (isnan(gDayBuf[6][4]) ? String('\0') : String(gDayBuf[6][4],0))+ ","+    (isnan(gDayBuf[7][4]) ? String('\0') : String(gDayBuf[7][4],0))+ ","+
                                        (isnan(gDayBuf[8][4]) ? String('\0') : String(gDayBuf[8][4],0))+ ","+    (isnan(gDayBuf[9][4]) ? String('\0') : String(gDayBuf[9][4],0))+ ","+
                                        (isnan(gDayBuf[10][4]) ? String('\0') : String(gDayBuf[10][4],0))+","+    (isnan(gDayBuf[11][4]) ? String('\0') : String(gDayBuf[11][4],0))+","+
                                        (isnan(gDayBuf[12][4]) ? String('\0') : String(gDayBuf[12][4],0))+","+    (isnan(gDayBuf[13][4]) ? String('\0') : String(gDayBuf[13][4],0))+","+
                                        (isnan(gDayBuf[14][4]) ? String('\0') : String(gDayBuf[14][4],0))+","+    (isnan(gDayBuf[15][4]) ? String('\0') : String(gDayBuf[15][4],0))+","+
                                        (isnan(gDayBuf[16][4]) ? String('\0') : String(gDayBuf[16][4],0))+","+    (isnan(gDayBuf[17][4]) ? String('\0') : String(gDayBuf[17][4],0))+","+
                                        (isnan(gDayBuf[18][4]) ? String('\0') : String(gDayBuf[18][4],0))+","+    (isnan(gDayBuf[19][4]) ? String('\0') : String(gDayBuf[19][4],0))+","+
                                        (isnan(gDayBuf[20][4]) ? String('\0') : String(gDayBuf[20][4],0))+","+    (isnan(gDayBuf[21][4]) ? String('\0') : String(gDayBuf[21][4],0))+","+
                                        (isnan(gDayBuf[22][4]) ? String('\0') : String(gDayBuf[22][4],0))+","+    (isnan(gDayBuf[23][4]) ? String('\0') : String(gDayBuf[23][4],0))+","+
                "];\n"
                "\t\tvar data_05 = ["+  (isnan(gDayBuf[0][5]) ? String('\0') : String(gDayBuf[0][5],0))+ ","+    (isnan(gDayBuf[1][5]) ? String('\0') : String(gDayBuf[1][5],0))+ ","+
                                        (isnan(gDayBuf[2][5]) ? String('\0') : String(gDayBuf[2][5],0))+ ","+    (isnan(gDayBuf[3][5]) ? String('\0') : String(gDayBuf[3][5],0))+ ","+
                                        (isnan(gDayBuf[4][5]) ? String('\0') : String(gDayBuf[4][5],0))+ ","+    (isnan(gDayBuf[5][5]) ? String('\0') : String(gDayBuf[5][5],0))+ ","+
                                        (isnan(gDayBuf[6][5]) ? String('\0') : String(gDayBuf[6][5],0))+ ","+    (isnan(gDayBuf[7][5]) ? String('\0') : String(gDayBuf[7][5],0))+ ","+
                                        (isnan(gDayBuf[8][5]) ? String('\0') : String(gDayBuf[8][5],0))+ ","+    (isnan(gDayBuf[9][5]) ? String('\0') : String(gDayBuf[9][5],0))+ ","+
                                        (isnan(gDayBuf[10][5]) ? String('\0') : String(gDayBuf[10][5],0))+","+    (isnan(gDayBuf[11][5]) ? String('\0') : String(gDayBuf[11][5],0))+","+
                                        (isnan(gDayBuf[12][5]) ? String('\0') : String(gDayBuf[12][5],0))+","+    (isnan(gDayBuf[13][5]) ? String('\0') : String(gDayBuf[13][5],0))+","+
                                        (isnan(gDayBuf[14][5]) ? String('\0') : String(gDayBuf[14][5],0))+","+    (isnan(gDayBuf[15][5]) ? String('\0') : String(gDayBuf[15][5],0))+","+
                                        (isnan(gDayBuf[16][5]) ? String('\0') : String(gDayBuf[16][5],0))+","+    (isnan(gDayBuf[17][5]) ? String('\0') : String(gDayBuf[17][5],0))+","+
                                        (isnan(gDayBuf[18][5]) ? String('\0') : String(gDayBuf[18][5],0))+","+    (isnan(gDayBuf[19][5]) ? String('\0') : String(gDayBuf[19][5],0))+","+
                                        (isnan(gDayBuf[20][5]) ? String('\0') : String(gDayBuf[20][5],0))+","+    (isnan(gDayBuf[21][5]) ? String('\0') : String(gDayBuf[21][5],0))+","+
                                        (isnan(gDayBuf[22][5]) ? String('\0') : String(gDayBuf[22][5],0))+","+    (isnan(gDayBuf[23][5]) ? String('\0') : String(gDayBuf[23][5],0))+","+
                "];\n"
                "\t\tvar data_06 = ["+  (isnan(gDayBuf[0][6]) ? String('\0') : String(gDayBuf[0][6],0))+ ","+    (isnan(gDayBuf[1][6]) ? String('\0') : String(gDayBuf[1][6],0))+ ","+
                                        (isnan(gDayBuf[2][6]) ? String('\0') : String(gDayBuf[2][6],0))+ ","+    (isnan(gDayBuf[3][6]) ? String('\0') : String(gDayBuf[3][6],0))+ ","+
                                        (isnan(gDayBuf[4][6]) ? String('\0') : String(gDayBuf[4][6],0))+ ","+    (isnan(gDayBuf[5][6]) ? String('\0') : String(gDayBuf[5][6],0))+ ","+
                                        (isnan(gDayBuf[6][6]) ? String('\0') : String(gDayBuf[6][6],0))+ ","+    (isnan(gDayBuf[7][6]) ? String('\0') : String(gDayBuf[7][6],0))+ ","+
                                        (isnan(gDayBuf[8][6]) ? String('\0') : String(gDayBuf[8][6],0))+ ","+    (isnan(gDayBuf[9][6]) ? String('\0') : String(gDayBuf[9][6],0))+ ","+
                                        (isnan(gDayBuf[10][6]) ? String('\0') : String(gDayBuf[10][6],0))+","+    (isnan(gDayBuf[11][6]) ? String('\0') : String(gDayBuf[11][6],0))+","+
                                        (isnan(gDayBuf[12][6]) ? String('\0') : String(gDayBuf[12][6],0))+","+    (isnan(gDayBuf[13][6]) ? String('\0') : String(gDayBuf[13][6],0))+","+
                                        (isnan(gDayBuf[14][6]) ? String('\0') : String(gDayBuf[14][6],0))+","+    (isnan(gDayBuf[15][6]) ? String('\0') : String(gDayBuf[15][6],0))+","+
                                        (isnan(gDayBuf[16][6]) ? String('\0') : String(gDayBuf[16][6],0))+","+    (isnan(gDayBuf[17][6]) ? String('\0') : String(gDayBuf[17][6],0))+","+
                                        (isnan(gDayBuf[18][6]) ? String('\0') : String(gDayBuf[18][6],0))+","+    (isnan(gDayBuf[19][6]) ? String('\0') : String(gDayBuf[19][6],0))+","+
                                        (isnan(gDayBuf[20][6]) ? String('\0') : String(gDayBuf[20][6],0))+","+    (isnan(gDayBuf[21][6]) ? String('\0') : String(gDayBuf[21][6],0))+","+
                                        (isnan(gDayBuf[22][6]) ? String('\0') : String(gDayBuf[22][6],0))+","+    (isnan(gDayBuf[23][6]) ? String('\0') : String(gDayBuf[23][6],0))+","+
                "];\n"
                "\t\tvar config = {\n"
                        "\t\t\ttype: 'line',\n"
                        "\t\t\tdata: {\n"
                                "\t\t\t\tlabels: ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12' , '13', '14', '15', '16', '17', '18', '19', '20', '21', '22', '23'],\n"
                                "\t\t\t\tdatasets: [{\n"
                                        "\t\t\t\t\tlabel: '気圧[Pa]',\n"
                                        "\t\t\t\t\tdata: data_00,\n"
                                        "\t\t\t\t\tborderColor: window.chartColors.red,\n"
                                        "\t\t\t\t\tbackgroundColor: 'rgba(0, 0, 0, 0)',\n"
                                        "\t\t\t\t\tfill: false,\n"
                                        "\t\t\t\t\tcubicInterpolationMode: 'monotone',\n"
                                        "\t\t\t\t\tyAxisID: 'y1'\n"
                                "\t\t\t\t}, {\n"
                                        "\t\t\t\t\tlabel: '気温[℃]',\n"
                                        "\t\t\t\t\tdata: data_01,\n"
                                        "\t\t\t\t\tborderColor: window.chartColors.blue,\n"
                                        "\t\t\t\t\tbackgroundColor: 'rgba(0, 0, 0, 0)',\n"
                                        "\t\t\t\t\tfill: false,\n"
                                        "\t\t\t\t\tyAxisID: 'y2'\n"
                                "\t\t\t\t}, {\n"
                                        "\t\t\t\t\tlabel: '湿度[%]',\n"
                                        "\t\t\t\t\tdata: data_02,\n"
                                        "\t\t\t\t\tborderColor: window.chartColors.orange,\n"
                                        "\t\t\t\t\tbackgroundColor: 'rgba(0, 0, 0, 0)',\n"
                                        "\t\t\t\t\tfill: false,\n"
                                        "\t\t\t\t\tyAxisID: 'y3'\n"
                                "\t\t\t\t}, {\n"
                                        "\t\t\t\t\tlabel: '浮遊粒子[mg/m3]',\n"
                                        "\t\t\t\t\tdata: data_03,\n"
                                        "\t\t\t\t\tborderColor: window.chartColors.yellow,\n"
                                        "\t\t\t\t\tbackgroundColor: 'rgba(0, 0, 0, 0)',\n"
                                        "\t\t\t\t\tfill: false,\n"
                                        "\t\t\t\t\tyAxisID: 'y4'\n"
                                "\t\t\t\t}, {\n"
                                        "\t\t\t\t\tlabel: 'CO2濃度[ppm]',\n"
                                        "\t\t\t\t\tdata: data_04,\n"
                                        "\t\t\t\t\tborderColor: window.chartColors.purple,\n"
                                        "\t\t\t\t\tbackgroundColor: 'rgba(0, 0, 0, 0)',\n"
                                        "\t\t\t\t\tfill: false,\n"
                                        "\t\t\t\t\tyAxisID: 'y5'\n"
                                "\t\t\t\t}, {\n"
                                        "\t\t\t\t\tlabel: '照度[lux]',\n"
                                        "\t\t\t\t\tdata: data_05,\n"
                                        "\t\t\t\t\tborderColor: window.chartColors.grey,\n"
                                        "\t\t\t\t\tbackgroundColor: 'rgba(0, 0, 0, 0)',\n"
                                        "\t\t\t\t\tfill: false,\n"
                                        "\t\t\t\t\tyAxisID: 'y6'\n"
                                "\t\t\t\t}, {\n"
                                        "\t\t\t\t\tlabel: '騒音[dBm]',\n"
                                        "\t\t\t\t\tdata: data_06,\n"
                                        "\t\t\t\t\tborderColor: window.chartColors.green,\n"
                                        "\t\t\t\t\tbackgroundColor: 'rgba(0, 0, 0, 0)',\n"
                                        "\t\t\t\t\tfill: false,\n"
                                        "\t\t\t\t\tlineTension: 0,\n"
                                        "\t\t\t\t\tyAxisID: 'y7'\n"
                                "\t\t\t\t}]\n"
                        "\t\t\t},\n"
                        "\t\t\toptions: {\n"
                                "\t\t\t\tresponsive: true,\n"
                                "\t\t\t\ttitle: {\n"
                                        "\t\t\t\t\tdisplay: true,\n"
                                        "\t\t\t\t\ttext: '今日の測定結果'\n"
                                "\t\t\t\t},\n"
                                "\t\t\t\tlegend: {                               //凡例\n"
                                        "\t\t\t\t\tdisplay: true\n"
                                "\t\t\t\t},\n"
                                "\t\t\t\ttooltips: {                             //ツールチップ\n"
                                        "\t\t\t\t\tenabled: true,\n"
                                        "\t\t\t\t\tmode: 'index'\n"
                                "\t\t\t\t},\n"
                                "\t\t\t\tscales: {\n"
                                        "\t\t\t\t\txAxes: [{\n"
                                                "\t\t\t\t\t\tdisplay: true,\n"
                                                "\t\t\t\t\t\tscaleLabel: {\n"
                                                        "\t\t\t\t\t\t\tdisplay: true,\n"
                                                        "\t\t\t\t\t\t\tlabelString: '時刻 [時]'\n"
                                                "\t\t\t\t\t\t}\n"
                                        "\t\t\t\t\t}],\n"
                                        "\t\t\t\t\tyAxes: [{\n"
                                                "\t\t\t\t\t\tid:'y1',\n"
                                                "\t\t\t\t\t\tdisplay: true,\n"
                                                "\t\t\t\t\t\tscaleLabel: {\n"
                                                        "\t\t\t\t\t\t\tfontColor: window.chartColors.red,\n"
                                                        "\t\t\t\t\t\t\tdisplay: true,\n"
                                                        "\t\t\t\t\t\t\tlabelString: '圧力 [Pa]'\n"
                                                "\t\t\t\t\t\t},\n"
                                                "\t\t\t\t\t\tticks: {\n"
                                                        "\t\t\t\t\t\t\tfontColor: window.chartColors.red,\n"
                                                        "\t\t\t\t\t\t\tsuggestedMin:  94000,\n"
                                                        "\t\t\t\t\t\t\tsuggestedMax: 104000\n"
                                                "\t\t\t\t\t\t}\n"
                                        "\t\t\t\t\t},{\n"
                                                "\t\t\t\t\t\tid:'y2',\n"
                                                "\t\t\t\t\t\tscaleLabel: {\n"
                                                        "\t\t\t\t\t\t\tfontColor: window.chartColors.blue,\n"
                                                        "\t\t\t\t\t\t\tdisplay: true,\n"
                                                        "\t\t\t\t\t\t\tlabelString: '温度 [℃]'\n"
                                                "\t\t\t\t\t\t},\n"
                                                "\t\t\t\t\t\tticks: {\n"
                                                        "\t\t\t\t\t\t\tfontColor: window.chartColors.blue,\n"
                                                        "\t\t\t\t\t\t\tsuggestedMin: 0,\n"
                                                        "\t\t\t\t\t\t\tsuggestedMax: 50\n"
                                                "\t\t\t\t\t\t}\n"
                                        "\t\t\t\t\t},{\n"
                                                "\t\t\t\t\t\tid:'y3',\n"
                                                "\t\t\t\t\t\tscaleLabel: {\n"
                                                        "\t\t\t\t\t\t\tfontColor: window.chartColors.orange,\n"
                                                        "\t\t\t\t\t\t\tdisplay: true,\n"
                                                        "\t\t\t\t\t\t\tlabelString: '湿度[%]'\n"
                                                "\t\t\t\t\t\t},\n"
                                                "\t\t\t\t\t\tticks: {\n"
                                                        "\t\t\t\t\t\t\tfontColor: window.chartColors.orange,\n"
                                                        "\t\t\t\t\t\t\tsuggestedMin: 0,\n"
                                                        "\t\t\t\t\t\t\tsuggestedMax: 100\n"
                                                "\t\t\t\t\t\t}\n"
                                        "\t\t\t\t\t},{\n"
                                                "\t\t\t\t\t\tid:'y4',\n"
                                                "\t\t\t\t\t\tposition: 'right',\n"
                                                "\t\t\t\t\t\tscaleLabel: {\n"
                                                        "\t\t\t\t\t\t\tfontColor: window.chartColors.yellow,\n"
                                                        "\t\t\t\t\t\t\tdisplay: true,\n"
                                                        "\t\t\t\t\t\t\tlabelString: '浮遊粒子[mg/m3]'\n"
                                                "\t\t\t\t\t\t},\n"
                                                "\t\t\t\t\t\tticks: {\n"
                                                        "\t\t\t\t\t\t\tfontColor: window.chartColors.yellow,\n"
                                                        "\t\t\t\t\t\t\tsuggestedMin: 0,\n"
                                                        "\t\t\t\t\t\t\tsuggestedMax: 10000\n"
                                                "\t\t\t\t\t\t}\n"
                                        "\t\t\t\t\t},{\n"
                                                "\t\t\t\t\t\tid:'y5',\n"
                                                "\t\t\t\t\t\tposition: 'right',\n"
                                                "\t\t\t\t\t\tscaleLabel: {\n"
                                                        "\t\t\t\t\t\t\tfontColor: window.chartColors.purple,\n"
                                                        "\t\t\t\t\t\t\tdisplay: true,\n"
                                                        "\t\t\t\t\t\t\tlabelString: 'CO2濃度[ppm]'\n"
                                                "\t\t\t\t\t\t},\n"
                                                "\t\t\t\t\t\tticks: {\n"
                                                        "\t\t\t\t\t\t\tfontColor: window.chartColors.purple,\n"
                                                        "\t\t\t\t\t\t\tsuggestedMin:    0,\n"
                                                        "\t\t\t\t\t\t\tsuggestedMax:    5000\n"
                                                "\t\t\t\t\t\t}\n"
                                        "\t\t\t\t\t},{\n"
                                                "\t\t\t\t\t\tid:'y6',\n"
                                                "\t\t\t\t\t\tposition: 'right',\n"
                                                "\t\t\t\t\t\tscaleLabel: {\n"
                                                        "\t\t\t\t\t\t\tfontColor: window.chartColors.grey,\n"
                                                        "\t\t\t\t\t\t\tdisplay: true,\n"
                                                        "\t\t\t\t\t\t\tlabelString: '照度[lux]'\n"
                                                "\t\t\t\t\t\t},\n"
                                                "\t\t\t\t\t\tticks: {\n"
                                                        "\t\t\t\t\t\t\tfontColor: window.chartColors.grey,\n"
                                                        "\t\t\t\t\t\t\tsuggestedMin: 0,\n"
                                                        "\t\t\t\t\t\t\tsuggestedMax: 2000\n"
                                                "\t\t\t\t\t\t}\n"
                                        "\t\t\t\t\t},{\n"
                                                "\t\t\t\t\t\tid:'y7',\n"
                                                "\t\t\t\t\t\tposition: 'right',\n"
                                                "\t\t\t\t\t\tscaleLabel: {\n"
                                                        "\t\t\t\t\t\t\tfontColor: window.chartColors.green,\n"
                                                        "\t\t\t\t\t\t\tdisplay: true,\n"
                                                        "\t\t\t\t\t\t\tlabelString: '騒音[dBm]'\n"
                                                "\t\t\t\t\t\t},\n"
                                                "\t\t\t\t\t\tticks: {\n"
                                                        "\t\t\t\t\t\t\tfontColor: window.chartColors.green,\n"
                                                        "\t\t\t\t\t\t\tsuggestedMin: 0,\n"
                                                        "\t\t\t\t\t\t\tsuggestedMax: 5000\n"
                                                "\t\t\t\t\t\t}\n"
                                        "\t\t\t\t\t}]\n"
                                "\t\t\t\t}\n"
                        "\t\t\t}\n"
                "\t\t};\n"
                "\t\twindow.onload = function() {\n"
                        "\t\t\tvar ctx = document.getElementById('canvas').getContext('2d');\n"
                        "\t\t\twindow.myLine = new Chart(ctx, config);\n"
                "\t\t};\n"
        "\t</script>\n</BODY>\n"
        "</html>\n";
        return ptr;
}
//------------      setup?SBM=1&DT1=24&DT2=a&DT3=c4&DT4=61&DT5=1e&DT6=7c&
//                              DT1=36&DT2=10&DT3=196&DT4=97&DT5=30&DT6=124&
//                              DT7=192&DT8=168&DT9=0&DT10=33&
//                              DT11=255&DT12=255&DT13=255&DT14=0&
//                              DT15=192&DT16=168&DT17=0&DT18=1&
//                              DT19=**********&DT20=**********&
//                              DT21=24&DT22=a&DT23=c4&DT24=61&DT25=1e&DT26=7d&
//                              DT21=36&DT22=10&DT23=196&DT24=97&DT25=30&DT26=125&
//                              DT27=192&DT28=168&DT29=250&DT30=33&
//                              DT31=255&DT32=255&DT33=255&DT34=0&
//                              DT35=192&DT36=168&DT37=250&DT38=1&
//                              DT39=ESP_AP&DT40=password
void handleUpdate() {
        Serial.println("SBM");
        digitalWrite(PO4, false);
        if(!WS.hasArg("SBM=1")){                                        //引数に"SBM=1"が含まれていることを確認
                handleNotFound();
                return;
        }
        String cmd = WS.arg("SBM=1");
        Serial.println(cmd);
        digitalWrite(PO4, true);
}
//------------ BMPprint
void printOLCDValues() {
        BME280::TempUnit tempUnit(BME280::TempUnit_Celsius);
        BME280::PresUnit presUnit(BME280::PresUnit_Pa);
        bmp.read(Vprs, Vtmp, Vhum, tempUnit, presUnit);
        sprintf(buf1,"Temperature:%.2f%1s",Vtmp,String(tempUnit == BME280::TempUnit_Celsius ? 'C' :'F'));
        sprintf(buf2,"Pressure: %.0f Pa",Vprs); 
        buf11   = String(buf1);
        buf22   = String(buf2);
        //Serial.println(buf1);
        //Serial.println(buf2);
        //CO2濃度取得
        if (mySensor.dataAvailable()){
                mySensor.readAlgorithmResults();
                Vco2            =       mySensor.getCO2();
                float TVOC      =       mySensor.getTVOC();
                sprintf(buf4,"eCO2:%4.0f TVOC:%3.0f",Vco2,TVOC);
        }
        //delay(10);                                              //Don't spam the I2C bus
       
        /*
        if(ccs.available()){
                //有効時期を確認してデータ取得しないととんでもない不安定値が取得されてしまう。
                delay(100);
                float temp = ccs.calculateTemperature();
                if(!ccs.readData()){
                        Vco2            = ccs.geteCO2();
                        float TVOC      = ccs.getTVOC();
                        sprintf(buf4,"eCO2:%4.0f TVOC:%3.0f",Vco2,TVOC); 
                }                
        }
        */
        //照度取得
        unsigned int data0, data1;
        if (light.getData(data0,data1)){
                boolean good = light.getLux(TSLgain,TSLms,data0,data1,Vlux);
                sprintf(buf5,"lux:%5.0f lx (%s)",Vlux,good? "Good" : "Bad");
        }
        OLCD.setCursor(0, 24);
        OLCD.fillRect(0,24,128,32,SSD1306_BLACK);
        OLCD.setTextSize(1);OLCD.setTextColor(SSD1306_WHITE);OLCD.setCursor(0, 24);
        OLCD.println(buf1);
        OLCD.println(buf2);
        OLCD.println(buf4);
        OLCD.println(buf5);
        OLCD.display();
}
//------------ printTime
int printOLCDTime() {
        getLocalTime(&timeinfo);
        sprintf(buf3,"%04d/%02d/%02d %02d:%02d:%02d",timeinfo.tm_year+1900,timeinfo.tm_mon+1,timeinfo.tm_mday,timeinfo.tm_hour,timeinfo.tm_min,timeinfo.tm_sec);
        //Serial.println(buf3);
        OLCD.setCursor(0, 56);
        OLCD.fillRect(0,56,128,8,SSD1306_BLACK);
        OLCD.setTextSize(1);OLCD.setTextColor(SSD1306_WHITE);OLCD.setCursor(0, 56);
        OLCD.println(buf3);
        return timeinfo.tm_hour;
}
//------------ printIndexTime
int printOLCDMinIndex(){
        getLocalTime(&timeinfo);
        return  timeinfo.tm_min % 10;
}
//------------ デバッグ用
void printLocalTime(){
        struct tm timeinfo;
        if (!getLocalTime(&timeinfo)) {
                Serial.println(F("Failed to obtain time"));
                return;
        }
        Serial.println(&timeinfo, "%Y %m %d %a %H:%M:%S");              //日本人にわかりやすい表記へ変更
}
//--------ContentTypeを拡張子で判断しているがこれでいいのか???
String getContentType(String filename){
        if(WS.hasArg("download"))               return "application/octet-stream";
        else if(filename.endsWith(".htm"))      return "text/html";
        else if(filename.endsWith(".html"))     return "text/html";
        else if(filename.endsWith(".css"))      return "text/css";
        else if(filename.endsWith(".js"))       return "application/javascript";
        else if(filename.endsWith(".png"))      return "image/png";
        else if(filename.endsWith(".gif"))      return "image/gif";
        else if(filename.endsWith(".jpg"))      return "image/jpeg";
        else if(filename.endsWith(".ico"))      return "image/x-icon";
        else if(filename.endsWith(".xml"))      return "text/xml";
        else if(filename.endsWith(".pdf"))      return "application/x-pdf";
        else if(filename.endsWith(".zip"))      return "application/x-zip";
        else if(filename.endsWith(".gz"))       return "application/x-gzip";
        return "text/plain";
}
//------------------
void printError(byte error){
        // If there's an I2C error, this function will
        // print out an explanation.
        Serial.print(F("I2C error: "));Serial.print(error,DEC);Serial.print(F(", "));
        switch(error){
                case 0:
                        Serial.println(F("success"));
                        break;
                case 1:
                        Serial.println(F("data too long for transmit buffer"));
                        break;
                case 2:
                        Serial.println(F("received NACK on address (disconnected?)"));
                        break;
                case 3:
                        Serial.println(F("received NACK on data"));
                        break;
                case 4:
                        Serial.println(F("other error"));
                        break;
                default:
                        Serial.println(F("unknown error"));
        }
}
//------------ SDカード読み書きに必要な関数群
//----------------
void listDir(fs::FS &fs, const char *dirname, uint8_t levels){
        Serial.printf("Listing directory: %s\n", dirname);
        //
        File root = fs.open(dirname);
        if(!root){
                Serial.println(F("Failed to open directory"));
                return;
        }
        if(!root.isDirectory()){
                Serial.println(F("Not a directory"));
                return;
        }
        //
        File file = root.openNextFile();
        while(file){
                if(file.isDirectory()){
                        Serial.print(F("  DIR : "));
                        Serial.println(file.name());
                        if(levels){
                                listDir(fs, file.name(), levels -1);
                        }
                } else {
                        Serial.print(F("  FILE: "));
                        Serial.print(file.name());
                        Serial.print(F("  SIZE: "));
                        Serial.println(file.size());
                }
                file = root.openNextFile();
        }
}
//----------------
void createDir(fs::FS &fs, const char *path){
        Serial.printf("Creating Dir: %s\n", path);
        if(fs.mkdir(path)){
                Serial.println(F("Dir created"));
        } else {
                Serial.println(F("mkdir failed"));
        }
}
//----------------
void removeDir(fs::FS &fs, const char *path){
        Serial.printf("Removing Dir: %s\n", path);
        if(fs.rmdir(path)){
                Serial.println(F("Dir removed"));
        } else {
                Serial.println(F("rmdir failed"));
        }
}
//----------------
void readFile(fs::FS &fs, const char *path){
        Serial.printf("Reading file: %s\n", path);
        //
        File file = fs.open(path);
        if(!file){
                Serial.println(F("Failed to open file for reading"));
                return;
        }
        //
        Serial.print(F("Read from file: "));
        while(file.available()){
                Serial.write(file.read());
        }
        file.close();
}
//----------------
void writeFile(fs::FS &fs, const char *path, const char *message){
        Serial.printf("Writing file: %s\n", path);
        //
        File file = fs.open(path, FILE_WRITE);
        if(!file){
                Serial.println(F("Failed to open file for writing"));
                return;
        }
        if(file.print(message)){
                Serial.println(F("File written"));
        } else {
                Serial.println(F("Write failed"));
        }
        file.close();
}
//----------------
void appendFile(fs::FS &fs, const char * path, const char * message){
        Serial.printf("Appending to file: %s\n", path);
        //
        File file = fs.open(path, FILE_APPEND);
        if(!file){
                Serial.println(F("Failed to open file for appending"));
                return;
        }
        if(file.print(message)){
                Serial.println(F("Message appended"));
        } else {
                Serial.println(F("Append failed"));
        }
        file.close();
}
//----------------
void renameFile(fs::FS &fs, const char *path1, const char * path2){
        Serial.printf("Renaming file %s to %s\n", path1, path2);
        if (fs.rename(path1, path2)) {
                Serial.println(F("File renamed"));
        } else {
                Serial.println(F("Rename failed"));
        }
}
//----------------
void deleteFile(fs::FS &fs, const char *path){
        Serial.printf("Deleting file: %s\n", path);
        if(fs.remove(path)){
                Serial.println(F("File deleted"));
        } else {
                Serial.println(F("Delete failed"));
        }
}
//------------ setup function
void setup(void) {
        //
        Serial.begin(115200);
        //SDカード周りの初期化
        if(!SD.begin()){
                Serial.println(F("Card Mount Failed"));
                return;
        }
        uint8_t cardType = SD.cardType();
        if(cardType == CARD_NONE){
                Serial.println(F("No SD card attached"));
                return;
        }
        Serial.print(F("SD Card Type: "));
        if(cardType == CARD_MMC){
                Serial.println(F("MMC"));
        } else if(cardType == CARD_SD){
                Serial.println(F("SDSC"));
        } else if(cardType == CARD_SDHC){
                Serial.println(F("SDHC"));
        } else {
                Serial.println(F("UNKNOWN"));
        }
        uint64_t cardSize = SD.cardSize() / (1024 * 1024);
        Serial.printf("SD Card Size: %lluMB\n", cardSize);
        //
        int bufSize = sizeof(st_esp);
        EEPROM.begin(bufSize);                                          //構造体のサイズ設定
        st_esp  buf;
        Serial.print(F("bufSize="));Serial.println(bufSize);
        EEPROM.get<st_esp>(0, buf);                                     //EEPROM読込
        Serial.print(F("bufID=0x"));Serial.println(buf.id,HEX);
        //MACアドレスの取得
        esp_read_mac(mac,       ESP_MAC_WIFI_STA);                      //StationModeのMacAddress
        esp_read_mac(apMac,     ESP_MAC_WIFI_SOFTAP);                   //APModeのMacAddress
        if(buf.id == ID){
                Serial.println(F("bufID:Match"));
                //既に情報がある場合は読込
                //for (int i = 0; i < 6; i++){
                //      mac[i]          = buf.DT[i];
                //}
                for (int i = 0; i < 4; i++){
                        ip[i]           = buf.DT[i+6];
                }
                for (int i = 0; i < 4; i++){
                        netmask[i]      = buf.DT[i+10];
                }
                for (int i = 0; i < 4; i++){
                        gateway[i]      = buf.DT[i+14];
                }
                for (int i = 0; i < 4; i++){
                        apIp[i]         = buf.DT[i+26];
                }
                for (int i = 0; i < 4; i++){
                        apNetmask[i]      = buf.DT[i+30];
                }
                for (int i = 0; i < 4; i++){
                        apGateway[i]      = buf.DT[i+34];
                }
                strcpy(ssid,buf.ssid);
                Serial.print(F("ssid ="));              Serial.println(ssid);
                strcpy(pass,buf.pass);
                Serial.print(F("ap_password ="));       Serial.println(apPassword);
                strcpy(apSsid,buf.apSsid);
                Serial.print(F("ap_ssid ="));           Serial.println(apSsid);
                strcpy(apPassword,buf.apPass);
                Serial.print(F("ap_password ="));       Serial.println(apPassword);
                timeZone        = float(buf.timeZoneVal) / 10;
                Serial.print(F("timeZone ="));       Serial.println(timeZone,2);
                strcpy(ntpServer,buf.ntpServer);
                Serial.print(F("ntpServer ="));       Serial.println(ntpServer);
        }else{
                Serial.println(F("bufID:NotMatch"));
                //情報が書き込まれていないので初期値を書き込んでおく
                for (int i = 0; i < 6; i++){
                        buf.DT[i]     = mac[i];
                }
                for (int i = 0; i < 4; i++){
                        buf.DT[i+6]     = ip[i];
                }
                for (int i = 0; i < 4; i++){
                        buf.DT[i+10]    = netmask[i];
                }
                for (int i = 0; i < 4; i++){
                        buf.DT[i+14]    = gateway[i];
                }
                for (int i = 0; i < 6; i++){
                        buf.DT[i+20]    = apMac[i];
                }
                for (int i = 0; i < 4; i++){
                        buf.DT[i+26]    = apIp[i];
                }
                for (int i = 0; i < 4; i++){
                        buf.DT[i+30]    = apNetmask[i];
                }
                for (int i = 0; i < 4; i++){
                        buf.DT[i+34]    = apGateway[i];
                }
                strcpy(buf.ssid,ssid);
                strcpy(buf.pass,pass);
                strcpy(buf.apSsid,apSsid);
                strcpy(buf.apPass,apPassword);
                buf.timeZoneVal = char(timeZone * 10.0);
                strcpy(buf.ntpServer,ntpServer);
                buf.id  = ID;
                EEPROM.put<st_esp>(0, buf);
                EEPROM.commit();
        }
//        */
        pinMode(LED,OUTPUT);    digitalWrite(LED,LOW);                  //Onboard LED port Direction output
        pinMode(PO1,OUTPUT);    digitalWrite(PO1,LOW);
        pinMode(PO2,OUTPUT);    digitalWrite(PO2,LOW);
        pinMode(PO3,OUTPUT);    digitalWrite(PO3,LOW);
        //pinMode(PO4,OUTPUT);  digitalWrite(PO4,LOW);
        //ledcSetup(LEDC_CHANNEL_0,1000,8);                             //double ledcSetup(uint8_t chan, double freq, uint8_t bit_num);
        //ledcAttachPin(PO3, LEDC_CHANNEL_0);                           //void ledcAttachPin(uint8_t pin, uint8_t chan);
        ledcSetup(LEDC_CHANNEL_1,1000,8);
        ledcAttachPin(PO4, LEDC_CHANNEL_1);
        pinMode(PI1,INPUT);
        pinMode(PI2,INPUT);
        pinMode(PI3,INPUT);
        pinMode(PI4,INPUT);
        //SSD1306_SWITCHCAPVCC =内部で3.3Vから表示電圧を生成
        Wire.setClock(400000);                                          // クロック速度を最も速くなるように設定して、通信を改善します(高速モード)
        if(!OLCD.begin(SSD1306_SWITCHCAPVCC, OLCDAres)) {               // 128x64のスレーブアドレスオリジナル値は0x3Dでしたが手元の元は0x3Cでした。
                Serial.println(F("SSD1306 allocation failed"));
                for(;;);                                                // Don't proceed, loop forever
        }
        status = bmp.begin();
        if (!status) {
                Serial.println(F("Could not find BME280 sensor!"));
        }
        // bme.chipID(); // Deprecated. See chipModel().
        switch(bmp.chipModel()){
                case BME280::ChipModel_BME280:
                        Serial.println(F("Found BME280 sensor! Success."));
                        break;
                case BME280::ChipModel_BMP280:
                        Serial.println(F("Found BMP280 sensor! No Humidity available."));
                        break;
                default:
                        Serial.println(F("Found UNKNOWN sensor! Error!"));
        }
        //TSL2561 setup
        light.begin(TSLAres);
        unsigned char I2CID;
        if(!light.getID(I2CID)){
                //Error
                byte error = light.getError();
                printError(error);
        }
        TSLgain = 0;
        unsigned char time = 2;
        light.setTiming(TSLgain,time,TSLms);
        light.setPowerUp();
        //CO2センサセットアップ SparkFunのライブラリに変更
        if (!mySensor.begin()){
                Serial.println(F("CCS811 error. Please check wiring. Freezing..."));
                fCcsRun = false;
        }
        /*
        if(!ccs.begin(CCSAres)){
                Serial.println(F("CCS skiped.Failed to start sensor! Please check your wiring."));
                fCcsRun = false;
        }
        */
        if(fCcsRun){
                mySensor.setDriveMode(4);
                if (mySensor.dataAvailable()){
                        //If so, have the sensor read and calculate the results.
                        //Get them later
                        mySensor.readAlgorithmResults();
                        //Returns calculated CO2 reading
                        Serial.print(F("CO2["));        Serial.print(mySensor.getCO2());        Serial.println(F("]"));
                        //Returns calculated TVOC reading
                        Serial.print(F("tVOC["));       Serial.print(mySensor.getTVOC());       Serial.println(F("]"));
                        //Display the time since program start
                        //Serial.print(F("millis["));     Serial.print(millis());                 Serial.println("]");
                }
                delay(10);                                              //Don't spam the I2C bus
                /*
                ccs.setDriveMode(CCS811_DRIVE_MODE_60SEC);              //CCS811_DRIVE_MODE_1SEC/CCS811_DRIVE_MODE_60SEC 
                while(!ccs.available());
                float temp = ccs.calculateTemperature();
                ccs.setTempOffset(temp - 25.0);                         //25の根拠不明
                */
        }        
        // バッファをクリアします
        OLCD.clearDisplay();
        // SPIFFSのセットアップ  10.2で追加
        if(!SPIFFS.begin(true)){
                Serial.println("An Error has occurred while mounting SPIFFS");
                return;
        }
        // アクセスポイント(無線LAN親機) + ステーション(無線LAN子機)
        WiFi.mode(WIFI_AP_STA);
        // まず既存のアクセスポイント(ネットワーク)に接続する
        WiFi.config(ip,gateway,netmask);                              //固定IPにする場合に使用
        WiFi.begin(ssid, pass);
        // Wait for connection
        Serial.println(F(""));
        while (WiFi.status() != WL_CONNECTED) {                         //接続するまでループするが、すごく電流を喰うようでchipが熱くなる。要対策。
                delay(1000);
                Serial.print(F("."));
        }
        Serial.println(F(""));
        Serial.print(F("Connected to "));Serial.println(ssid);
        IPAddress myIP =WiFi.localIP();
        Serial.print(F("IP address: "));Serial.println(myIP);
        OLCD.setTextSize(1);OLCD.setTextColor(SSD1306_WHITE);OLCD.setCursor(0, 0);
        OLCD.print(F("LoginID:"));OLCD.println(myID);
        OLCD.print(F("LoginPASS:"));OLCD.println(myPASS);
        OLCD.print(F("IP:"));OLCD.println(myIP);
        //OLCD.display();
        // SoftAPを開始する
        WiFi.softAPConfig(apIp,apGateway,apNetmask);
        WiFi.softAP(apSsid, apPassword);
        //IPAddress apIp = WiFi.softAPIP();
        OLCD.setTextSize(1);OLCD.setTextColor(SSD1306_WHITE);OLCD.setCursor(0, 40);
        OLCD.print(F("SSID:"));OLCD.println(apSsid);
        OLCD.print(F("PASS:"));OLCD.println(apPassword);
        OLCD.print(F("IP:"));OLCD.println(apIp[0]);OLCD.println(F("."));OLCD.println(apIp[1]);OLCD.println(F("."));OLCD.println(apIp[2]);OLCD.println(F("."));OLCD.println(apIp[3]);
        OLCD.display();
        Serial.print(F("SoftAPのIPアドレス(LAN側): "));Serial.println(myIP);
        //
        ftpSrv.begin(uname,pword);
        //init and get the time
        configTime(gmtOffset_sec, daylightOffset_sec, ntpServer);                       //void configTime(long gmtOffset_sec, int daylightOffset_sec, const char* server1, const char* server2, const char* server3);
        printLocalTime();
        //時刻を取得したので、ログファイルを作成する。
        createDir(SD, "/ESPdir");                                                       //ESPフォルダを作成してこの中にログファイルを作成する。
        getLocalTime(&timeinfo);
        sprintf(f_name,"/ESPdir/%04d%02d%02d%02d%02d%02d.txt",timeinfo.tm_year+1900,timeinfo.tm_mon+1,timeinfo.tm_mday,timeinfo.tm_hour,timeinfo.tm_min,timeinfo.tm_sec);
        //ホスト名解決にmdnsを使用する
        if (!MDNS.begin(host)) {                                                        //http://esp32.local
                Serial.println(F("Error setting up MDNS responder!"));
                OLCD.setCursor(0, 56);OLCD.print(F("Error setting up MDNS responder!"));
                while (1) {
                        delay(1000);
                }
        }
        Serial.println(F("mDNS responder started"));
        //serverIndexに格納されているインデックスページを返す
        WS.on("/login", HTTP_GET, []() {
                WS.sendHeader("Connection", "close");
                WS.send(200, "text/html", loginIndex);
                OLCD.fillRect(0,56,128,8,SSD1306_BLACK);
                OLCD.setCursor(0, 56);
                OLCD.setTextColor(SSD1306_WHITE);
                OLCD.print(F("Connection"));
                OLCD.display();
        });
        WS.on("/serverIndex", HTTP_GET, []() {
                WS.sendHeader("Connection", "close");
                WS.send(200, "text/html", serverIndex);
                OLCD.fillRect(0,56,128,8,SSD1306_BLACK);
                OLCD.setCursor(0, 56);
                OLCD.setTextColor(SSD1306_WHITE);
                OLCD.print(F("UpdateReady"));
                OLCD.display();
        });
        //ファームウェアファイルのアップロードの処理
        WS.on("/update", HTTP_POST, []() {
                WS.sendHeader("Connection", "close");
                OLCD.setCursor(0, 56);
                OLCD.drawRect(0,56,128,8,SSD1306_BLACK);
                OLCD.display();
                WS.send(200, "text/plain", (Update.hasError()) ? "FAIL" : "OK");
                ESP.restart();
        }, []() {
                HTTPUpload& upload = WS.upload();
                if (upload.status == UPLOAD_FILE_START) {
                        Serial.printf("Update: %s\n", upload.filename.c_str());
                        OLCD.setCursor(0, 56);
                        OLCD.drawRect(0,56,128,8,SSD1306_BLACK);
                        OLCD.setTextColor(SSD1306_WHITE);
                        OLCD.print(F("Updating..."));
                        OLCD.display();
                        if (!Update.begin(UPDATE_SIZE_UNKNOWN)) {       //利用可能な最大サイズから始める
                                Update.printError(Serial);
                        }
                } else if (upload.status == UPLOAD_FILE_WRITE) {
                        //ESPへのファームウェアのフラッシュ
                        if (Update.write(upload.buf, upload.currentSize) != upload.currentSize) {
                                Update.printError(Serial);
                        }
                } else if (upload.status == UPLOAD_FILE_END) {
                        if (Update.end(true)) {                         //サイズを現在の進行状況に設定する場合はtrue
                                Serial.printf("Update Success: %u\nRebooting...\n", upload.totalSize);
                        } else {
                                Update.printError(Serial);
                        }
                }
        });
        WS.on("/GetData", handleGetData);                               //To get update of ADC Value only
        WS.on("/setup?", handleUpdate);                                 //To SetUpPage
        WS.on("/setup", handleSetUp);                                   //To SetUpPage
        WS.on("/favicon.ico", handleNotFound);
        WS.on("/Chart.bundle.js", handleNotFound);
        WS.on("/utils.js", handleNotFound);
        WS.on("/", HTTP_GET, []() {                                     //ルートアクセス時の応答関数を設定
                WS.sendHeader("Connection", "close");
                for(int j = 0;j<7;j++){
                        gDataBuf[j]     =      gSMAAve[j];
                }
                WS.send(200, "text/html", SendHTML(gDataBuf));
                //OLCD.setCursor(0, 32);
                //OLCD.fillRect(0,32,128,8,SSD1306_BLACK);
                //OLCD.display();
        });
        WS.onNotFound(handleNotFound);                                  //不正アクセス時の応答関数を設定
        WS.begin();                                                     //ウェブサーバ開始
        printOLCDValues();                                             
        for(int i = 0;i<10;i++){
                gSMABuf[i][0]   =       Vprs;
                gSMABuf[i][1]   =       Vtmp;
                gSMABuf[i][2]   =       Vhum;
                gSMABuf[i][3]   =       Vdin;
                gSMABuf[i][4]   =       Vco2;
                gSMABuf[i][5]   =       float(Vlux);
                gSMABuf[i][6]   =       analogRead(AIport);                
        }
        for(int i = 0;i<7;i++){
                gSMAAve[i]      =       gSMABuf[0][i];
        }
        //BufferClear
        for(int i = 0;i<24;i++){
                for(int j = 0;j<7;j++){
                       gDayBuf[i][j]    = NAN; 
                }
        }        
        //WDT
        timer = timerBegin(0, 80, true);                                //timer 0, div 80
        timerAttachInterrupt(timer, &resetModule, true);                //attach callback
        timerAlarmWrite(timer, wdtTimeout * 1000000, false);            //set time in us
        timerAlarmEnable(timer);                                        //enable interrupt
        preTime = micros();
}
//------------
int count       = 0;
int preSeth     = -1;
int preSetS     = -1;
int preIndex    = -1;
void loop(void) {
        timerWrite(timer, 0);                                           //reset timer (feed watchdog)
        nowTime         = micros();
        scanTime        = nowTime - preTime;
        preTime         = nowTime;
        if(WiFi.status() != WL_CONNECTED){                              //WiFiStatusを確認し、切断されている場合は
                WiFi.disconnect();                                      //一旦disconnectを実行し改めて再接続する。
                delay(1000);
                WiFi.config(ip,gateway,netmask);                        //固定IPにする場合に使用
                WiFi.begin(ssid, pass);
                // Wait for connection
                Serial.println(F(""));
                while (WiFi.status() != WL_CONNECTED) {                 //接続するまでループするが、すごく電流を喰うようでchipが熱くなる。要対策。
                        delay(1000);
                        Serial.print(F("."));
                }
                Serial.println(F("ReConnect"));
        }
        WS.handleClient();                                              //クライアントからの要求を処理する
        ftpSrv.handleFTP();                                             //FTP要求処理
        //delay(100);
        printOLCDValues();                                              //OLCDにデータを更新表示
        for(int i = 0;i<7;i++){
                gSMAAve[i] = gSMAAve[i]*7.0 - gSMABuf[count][i];
        }
        gSMABuf[count][0]   =       Vprs;
        gSMABuf[count][1]   =       Vtmp;
        gSMABuf[count][2]   =       Vhum;
        gSMABuf[count][3]   =       Vdin;
        gSMABuf[count][4]   =       Vco2;
        gSMABuf[count][5]   =       float(Vlux);
        gSMABuf[count][6]   =       analogRead(AIport);
        for(int i = 0;i<7;i++){
                gSMAAve[i] = (gSMAAve[i] + gSMABuf[count][i]) / 7.0;
        }
        getLocalTime(&timeinfo);
        sprintf(buf3,"%04d/%02d/%02d %02d:%02d:%02d",timeinfo.tm_year+1900,timeinfo.tm_mon+1,timeinfo.tm_mday,timeinfo.tm_hour,timeinfo.tm_min,timeinfo.tm_sec);
        if((timeinfo.tm_hour == 0) && (timeinfo.tm_min == 0) && (timeinfo.tm_sec == 0)){
                //ファイル更新
                logFile.close();
                sprintf(f_name,"/ESPdir/%04d%02d%02d%02d%02d%02d.txt",timeinfo.tm_year+1900,timeinfo.tm_mon+1,timeinfo.tm_mday,timeinfo.tm_hour,timeinfo.tm_min,timeinfo.tm_sec);
                logFile = SD.open(f_name,FILE_APPEND);
        }
        OLCD.setCursor(0, 56);
        OLCD.fillRect(0,56,128,8,SSD1306_BLACK);
        OLCD.setTextSize(1);OLCD.setTextColor(SSD1306_WHITE);OLCD.setCursor(0, 56);
        OLCD.println(buf3);
        int s = timeinfo.tm_sec;
        if(preSetS != s){
                preSetS = s;
                //12.3でフォーマット変更
                sprintf(buf6,"%04d%02d%02d%02d%02d%02d,%d,%.1f,%.1f,%.1f,%.1f,%.1f,%.1f,%.1f",timeinfo.tm_year+1900,timeinfo.tm_mon+1,timeinfo.tm_mday,timeinfo.tm_hour,timeinfo.tm_min,timeinfo.tm_sec,scanTime,gSMAAve[0],gSMAAve[1],gSMAAve[2],gSMAAve[3],gSMAAve[4],gSMAAve[5],gSMAAve[6]);
                logFile = SD.open(f_name,FILE_APPEND);
                if(logFile){
                        logFile.println(buf6);
                        logFile.close();
                }
        }
        int h = timeinfo.tm_hour;                                               //OLCDに時刻表示 返値0-23
        //int m = printOLCDMinIndex();                                          //0-9 index
        //ループして遅延なく点滅する
        unsigned long currentMillis = millis();
        if(preSeth != h){
                //1時間おきに時刻を確認し、データをメモリに確保
                preSeth  = h;
                for(int i = 0;i<7;i++){
                        gDayBuf[h][i]   = gSMAAve[i];
                }
                digitalWrite(PO3, true);
                //次回分のデータをNULL化
                h = (h+1) % 24;
                for(int i = 0;i<7;i++){
                        gDayBuf[h][i]   = NAN;
                }
        }
        if (currentMillis - previousMillis >= interval) {
                //最後にLEDを点滅させた時間を保存します
                previousMillis = currentMillis;
                //LEDがオフの場合はオンにし、逆の場合も同様です。
                ledState = not(ledState);
                //変数のledStateでLEDを設定します。
                digitalWrite(LED, ledState);
                switch(count){
                        case 0:
                                digitalWrite(PO1, true);
                                digitalWrite(PO2, false);
                                //digitalWrite(PO3, false);
                                //digitalWrite(PO4, false);
                                break;
                        case 1:
                                digitalWrite(PO1, true);
                                digitalWrite(PO2, true);
                                //digitalWrite(PO3, false);
                                //digitalWrite(PO4, false);
                                break;
                        case 2:
                                digitalWrite(PO1, true);
                                digitalWrite(PO2, false);
                                //digitalWrite(PO3, true);
                                //digitalWrite(PO4, false);
                                break;
                        case 3:
                                digitalWrite(PO1, false);
                                digitalWrite(PO2, true);
                                //digitalWrite(PO3, false);
                                //digitalWrite(PO4, true);
                                break;
                        case 4:
                                digitalWrite(PO1, false);
                                digitalWrite(PO2, false);
                                //digitalWrite(PO3, false);
                                //digitalWrite(PO4, false);
                                break;
                        case 5:
                                digitalWrite(PO1, false);
                                digitalWrite(PO2, true);
                                //digitalWrite(PO3, false);
                                //digitalWrite(PO4, false);
                                break;
                        case 6:
                                digitalWrite(PO1, true);
                                digitalWrite(PO2, true);
                                //digitalWrite(PO3, true);
                                //digitalWrite(PO4, false);
                                break;
                        case 7:
                                digitalWrite(PO1, true);
                                digitalWrite(PO2, true);
                                //digitalWrite(PO3, false);
                                //digitalWrite(PO4, true);
                                break;
                        case 8:
                                digitalWrite(PO1, false);
                                digitalWrite(PO2, false);
                                //digitalWrite(PO3, false);
                                //digitalWrite(PO4, false);
                                break;
                        case 9:
                                digitalWrite(PO1, false);
                                digitalWrite(PO2, false);
                                //digitalWrite(PO3, false);
                                //digitalWrite(PO4, false);
                                break;
                        default:
                                digitalWrite(PO1, false);
                                digitalWrite(PO2, false);
                                //digitalWrite(PO3, false);
                                //digitalWrite(PO4, false);
                                break;
                }
                //ledcWrite(LEDC_CHANNEL_0, brightness);
                ledcWrite(LEDC_CHANNEL_1, 255 - brightness);
                brightness = brightness + fadeAmount;
                if (brightness <= 0){
                        brightness = 50;
                        fadeAmount = abs(fadeAmount);
                        digitalWrite(PO4, false);
                }else if(brightness >= 250){
                        brightness = 254;
                        fadeAmount = -abs(fadeAmount);
                }
                count = (count + 1) % 10;
                Serial.println(count);
        }
}


2022/1/17:ファイルシステムの問題点があるみたいですが判らない

トンガ近郊の海底火山の噴火において気圧変化が確認出来たという報道がありました。この気圧変化は、このESP32/BME280でも確認出来ていました。ところがSDカードにデータ保存出来ていませんでした。理由は判りませんが、20211214000000.txtの追記途中で途絶えていました。
1日ごとにファイル更新していますので追記のためのEOFが見つからなかったという事は無いはずです。が、1秒毎の書込というのは問題が発生したかもしれません。と云う事で1分ごとのデータ更新に変更しようと思います。
また、空き容量をブラウザで確認したいと思いました。ファイルシステムのエラーチェックがしたかったので。

ESP32で使用しているSD.h/FS.hを見る限り直接FreeSpaceを求める関数は無いようです。totalBytes() とusedBytes()からFreeSpaceを求めます。64bit型ですが、Strings型への変更がうまく出来ないのでKBYTE単位に変更し32bit型でStrings型への変更を行いブラウザに表示することにしました。

chipに書き込んだソースコードのバージョンは起動時にシリアルアウトしか確認出来ませんでした。ブラウザで確認出来るように変更しました。

2022/1/18:ESP32のコードをレタッチ後ビルドは通るのに実行エラーになる原因がやっと判りました。

普通に実行出来たコードなのですが、ある時点から実行出来なくなった理由がやっと判りました。setup()の先頭でSD.begin()でエラーとなり、すぐにreturnを掛けていたため実行出来ませんでした。
なぜSD.begin()で実行時エラーになるのかに関してはライブラリの問題も含めて今後の調査とし、とりあえずSD.begin()でエラーとなってもreturnは止めて先に進めるようにしました。※Exampleをコピペしてからレタッチするので、こんなことは しょっちゅう なんですが。

SD.begin()でエラーとなる原因については先人の書込にも多々見受けられる現象です。ESP32は特にそうなのかもしれません。コードサイズが小さいときは殆ど気にならなかったのにコードサイズが大きくなると頻発するようになったと感じます。

もう一つ対策しました。setup()内でSD.begin()がエラーになっていたとき、loop()で毎ループ毎にSD.begin()の再実行を検討しました。


2022/2/2:カードリーダモジュールを変更してみました。改善!

同じカードリーダと思われる別ロットであろうモジュールを入手し、交換して、使っていたTFカードをそのまま挿して実行して見ました。
SD.begin()が実行出来ました。
カードモジュールに何らかの問題が発生していたのでしょうか?モジュールから何らかのエラーコードが得られればいいのですが、SPI接続に失敗しているわけで何もメッセージを得ることは出来ません。
もう少し様子を見ながら使って見ることにします。


戯言(nonsense)に戻る


問い合わせ頁の表示


免責事項

本ソフトウエアは、あなたに対して何も保証しません。本ソフトウエアの関係者(他の利用者も含む)は、あなたに対して一切責任を負いません。
あなたが、本ソフトウエアを利用(コンパイル後の再利用など全てを含む)する場合は、自己責任で行う必要があります。

本ソフトウエアの著作権はToolsBoxに帰属します。
本ソフトウエアをご利用の結果生じた損害について、ToolsBoxは一切責任を負いません。
ToolsBoxはコンテンツとして提供する全ての文章、画像等について、内容の合法性・正確性・安全性等、において最善の注意をし、作成していますが、保証するものではありません。
ToolsBoxはリンクをしている外部サイトについては、何ら保証しません。
ToolsBoxは事前の予告無く、本ソフトウエアの開発・提供を中止する可能性があります。

商標・登録商標

Microsoft、Windows、WindowsNTは米国Microsoft Corporationの米国およびその他の国における登録商標です。
Windows Vista、Windows XPは、米国Microsoft Corporation.の商品名称です。
LabVIEW、National Instruments、NI、ni.comはNational Instrumentsの登録商標です。
I2Cは、NXP Semiconductors社の登録商標です。
その他の企業名ならびに製品名は、それぞれの会社の商標もしくは登録商標です。
すべての商標および登録商標は、それぞれの所有者に帰属します。