/*
    File Name : pickle_m5stack.ino
    Created   : 2025/10/15
    Author    : K.Uchibori
    --------------------------------------------------------------------------------
    <M5Stack BASIC の場合>
    ボードマネージャー： 公式M5Stack Ver 1.0.9
    ライブラリ　　　　： M5Stack Ver 0.4.6, LovyanGUI(改造版), LovyanGFX Ver 1.1.7
    対応ボード　　　　： M5Stack-Core-ESP32
    --------------------------------------------------------------------------------
     <M5Stack FIRE の場合>
    ボードマネージャー： 公式M5Stack Ver 1.0.9
    ライブラリ　　　　： M5Stack Ver 0.4.6, LovyanGUI(改造版), LovyanGFX Ver 1.1.7
    対応ボード　　　　： M5Stack-FIRE
    　　　　　　　　　　 ※オプション：PSRAM->Enabled
    --------------------------------------------------------------------------------
*/

#include <M5Stack.h>
#include <LovyanGUI.hpp>
#include "pickle.h"

static constexpr char TITLE_HEXFILES[] = "hex Files";

static LGFX lcd;                        // GFXライブラリのインスタンス
static lgui::LovyanGUI gui;             // GUIライブラリのインスタンス
static lgui::LovyanGUI gui2;            // GUIライブラリのインスタンス
static lgui::LGUI_Label label1;         // ラベル
static lgui::LGUI_TreeView tv;          // ツリービュー
static lgui::LGUI_TreeItem* tiC;        // ツリーアイテム(Choices)
static lgui::LGUI_TreeItem* tiP;        // ツリーアイテム(Preparation)
static lgui::LGUI_TreeItem* tiSD;       // ツリーアイテム(SD)
static lgui::LGUI_TreeItem* tiO;        // ツリーアイテム(Operation)
static lgui::LGUI_TreeItem* tiHL;       // ツリーアイテム(hvp/lvp)
static lgui::LGUI_TreeItem* tiSet;      // ツリーアイテム(Settings)
static lgui::LGUI_Container panel1;     // コンテナ
static lgui::LGUI_TextBox textbox1;     // テキストボックス
static lgui::LGUI_TextBox textbox2;     // テキストボックス
static lgui::LGUI_TextBox textbox3;     // テキストボックス
static lgui::LGUI_GridView grid;        // グリッドビュー
static char gridstrs[150][100];         // グリッドの文字列保持用変数
static LGFX_Sprite sprite(&lcd);        // スプライトを使う場合はLGFX_Spriteのインスタンスを作成

static int label_height = 16;
static int flg_list_sd = 0;

char buf[200];
unsigned char p_f;
char dat[202];   // 格納用文字列
int count = 0;  // 文字数のカウンタ
unsigned long lastTime;
String filepath;
static int giBattery = 0;
int connectMode = 1;
int gridNum = 0;
int OKflag = 0;
extern const uint16_t image_data_logo[];

void SerialLoop();

/*
 * pickle session
 */
struct pickle p = {0};

void programer_reset() {
  pic_data data;
  File fp;
  
  inhx32_get(fp, &data, 1);
  pic_write_panel(0, 0, 0, 1);
  pic_dumpaddr(0, 0, 1);
} 

/*
 * p12 help
 */
void
usage_p12(char *msg)
{
  if (msg)
    Serial.printf("Error: %s.\n\n", msg);

  Serial.printf( 
    " p12 SELECT DEVICE OPERATION [ARG]\n"
    "\t\t12-bit word PIC10F/12F/16F operations.\n"
    "\n");

  Serial.printf("EXAMPLES:\n");
  Serial.printf(" p12 select\n"
    "\t\tShow supported devices.\n");
  Serial.printf(" p12 select PIC1XFXXX blank\n"
    "\t\tBlank device (disable protection and bulk erase).\n", UL_ON, UL_OFF);
  Serial.printf(" p12 select PIC1XFXXX config\n"
    "\t\tDisplay device configuration.\n", UL_ON, UL_OFF);
  Serial.printf(" p12 select PIC1XFXXX data\n"
    "\t\tDisplay data flash content.\n", UL_ON, UL_OFF);
  Serial.printf(" p12 select PIC1XFXXX dump\n"
    "\t\tDump device content (INHX32 format).\n", UL_ON, UL_OFF);
  Serial.printf(" p12 select PIC1XFXXX flash [n] [address]\n"
    "\t\tDisplay all or n words of program flash content from address.\n", UL_ON, UL_OFF);
  Serial.printf(" p12 select PIC1XFXXX id\n"
    "\t\tDisplay device identification.\n", UL_ON, UL_OFF);
  Serial.printf(" p12 select PIC1XFXXX osccal\n"
    "\t\tDisplay oscillator calibration.\n", UL_ON, UL_OFF);
  Serial.printf(" p12 select PIC1XFXXX osccal 0x0c1a\n"
    "\t\tRestore oscillator calibration as 0x0c1a.\n", UL_ON, UL_OFF);
  Serial.printf(" p12 select PIC1XFXXX program [file.hex] [noblank]\n"
    "\t\tBlank and program file.hex or stdin to flash (INHX32 format).\n", UL_ON, UL_OFF);
  Serial.printf(" p12 select PIC1XFXXX verify [file.hex]\n"
    "\t\tVerify file.hex or stdin in flash (INHX32 format).\n", UL_ON, UL_OFF);
  Serial.printf(" p12 select PIC1XFXXX view [file.hex] [raw]\n"
    "\t\tView file.hex or stdin (INHX32 format).\n", UL_ON, UL_OFF);
  Serial.printf("\n");

  Serial.printf("VERSION:\n %s\n", VERSION);
  rxEND();
}

/*
 * p14 help
 */
void
usage_p14(char *msg)
{
  if (msg)
    Serial.printf("Error: %s.\n\n", msg);

  Serial.printf( 
    " p14 [SELECT DEVICE] [LVP] OPERATION [ARG]\n"
    "\t\t14-bit word PIC10F/12F/16F operations.\n"
    "\n");

  Serial.printf("EXAMPLES:\n");
  Serial.printf(" p14 select\n"
    "\t\tShow supported devices.\n");
  Serial.printf(" p14 select 16F84 OPERATION [ARG]\n"
    "\t\tSelect device PIC16F84.\n");
  Serial.printf(" p14 lvp OPERATION [ARG]\n"
    "\t\tLVP 32-bit key entry.\n");
  Serial.printf(" p14 blank\n"
    "\t\tBlank device (disable protection and bulk erase).\n");
  Serial.printf(" p14 config\n"
    "\t\tDisplay device configuration.\n");
  Serial.printf(" p14 config 0x3000\n"
    "\t\tRestore band-gap configuration as 0x3000.\n");
  Serial.printf(" p14 data\n"
    "\t\tDisplay data EEPROM content.\n");
  Serial.printf(" p14 dump\n"
    "\t\tDump device content (INHX32 format).\n");
  Serial.printf(" p14 erase eeprom | flash | id | row [n]\n"
    "\t\tErase EEPROM, flash, id or flash at row for n rows.\n");
  Serial.printf(" p14 flash [n] [address]\n"
    "\t\tDisplay all or n words of program flash content from address.\n");
  Serial.printf(" p14 file\n"
    "\t\tDisplay List of filenames in the SD.\n");
  Serial.printf(" p14 id\n"
    "\t\tDisplay device identification.\n");
  Serial.printf(" p14 osccal\n"
    "\t\tDisplay oscillator calibration.\n");
  Serial.printf(" p14 osccal 0x343c\n"
    "\t\tRestore oscillator calibration as 0x343c.\n");
  Serial.printf(" p14 program [file.hex] [noblank]\n"
    "\t\tBlank and program file.hex or stdin to flash (INHX32 format).\n");
  Serial.printf(" p14 verify [file.hex]\n"
    "\t\tVerify file.hex or stdin in flash (INHX32 format).\n");
  Serial.printf(" p14 view [file.hex] [raw]\n"
    "\t\tView file.hex or stdin (INHX32 format).\n");
  Serial.printf("\n");

  Serial.printf("VERSION:\n %s\n", VERSION);
  rxEND();
}

/*
 * n14 help
 */
void
usage_n14(char *msg)
{
  if (msg)
    Serial.printf("Error: %s.\n\n", msg);

  Serial.printf( 
    " n14 [LVP] OPERATION [ARG]\n"
    "\t\t14-bit word PIC16F operations.\n"
    "\n");

  Serial.printf("EXAMPLES:\n");
  Serial.printf(" n14 select\n"
    "\t\tShow supported devices.\n");
  Serial.printf(" n14 lvp OPERATION [ARG]\n"
    "\t\tLVP 32-bit key entry.\n");
  Serial.printf(" n14 blank\n"
    "\t\tBlank device (disable protection and bulk erase).\n");
  Serial.printf(" n14 config\n"
    "\t\tDisplay device configuration.\n");
  Serial.printf(" n14 data\n"
    "\t\tDisplay data EEPROM content.\n");
  Serial.printf(" n14 dump\n"
    "\t\tDump device content (INHX32 format).\n");
  Serial.printf(" n14 erase eeprom | flash | id | row [n]\n"
    "\t\tErase EEPROM, flash, id or flash at row for n rows.\n");
  Serial.printf(" n14 flash [n] [address]\n"
    "\t\tDisplay all or n words of program flash content from address.\n");
  Serial.printf(" n14 id\n"
    "\t\tDisplay device identification.\n");
  Serial.printf(" n14 program [file.hex] [noblank]\n"
    "\t\tBlank and program file.hex or stdin to flash (INHX32 format).\n");
  Serial.printf(" n14 verify [file.hex]\n"
    "\t\tVerify file.hex or stdin in flash (INHX32 format).\n");
  Serial.printf(" n14 view [file.hex] [raw]\n"
    "\t\tView file.hex or stdin (INHX32 format).\n");
  Serial.printf("\n");

  Serial.printf("VERSION:\n %s\n", VERSION);
  rxEND();
}

/*
 * p16 help
 */
void
usage_p16(char *msg)
{
  if (msg)
    Serial.printf("Error: %s.\n\n", msg);

  Serial.printf( 
    " p16 [SELECT DEVICE] [LVP|HVP] OPERATION [ARG]\n"
    "\t\t16-bit word PIC18F operations.\n"
    "\n");

  Serial.printf("EXAMPLES:\n");
  Serial.printf(" p16 select\n"
    "\t\tShow supported devices.\n");
  Serial.printf(" p16 select 18LF2539 OPERATION [ARG]\n"
    "\t\tSelect device PIC18LF2539.\n");
  Serial.printf(" p16 lvp OPERATION [ARG]\n"
    "\t\tLVP 32-bit key entry.\n");
  Serial.printf(" p16 hvp OPERATION [ARG]\n"
    "\t\tHVP 32-bit key entry.\n");
  Serial.printf(" p16 blank\n"
    "\t\tBlank device (disable protection and bulk erase).\n");
  Serial.printf(" p16 config\n"
    "\t\tDisplay device configuration.\n");
  Serial.printf(" p16 data\n"
    "\t\tDisplay data EEPROM content.\n");
  Serial.printf(" p16 dump\n"
    "\t\tDump device content (INHX32 format).\n");
  Serial.printf(" p16 erase eeprom | flash | id | row [n]\n"
    "\t\tErase EEPROM, flash, id or flash at row for n rows.\n");
  Serial.printf(" p16 flash [n] [address]\n"
    "\t\tDisplay all or n words of program flash content from address.\n");
  Serial.printf(" p16 id\n"
    "\t\tDisplay device identification.\n");
  Serial.printf(" p16 program [file.hex] [noblank]\n"
    "\t\tBlank and program file.hex or stdin to flash (INHX32 format).\n");
  Serial.printf(" p16 verify [file.hex]\n"
    "\t\tVerify file.hex or stdin in flash (INHX32 format).\n");
  Serial.printf(" p16 view [file.hex] [raw]\n"
    "\t\tView file.hex or stdin (INHX32 format).\n");
  Serial.printf("\n");
  
  Serial.printf("VERSION:\n %s\n", VERSION);
  rxEND();
}

/*
 * n16 help
 */
void
usage_n16(char *msg)
{
  if (msg)
    Serial.printf("Error: %s.\n\n", msg);

  Serial.printf( 
    " n16 [LVP] OPERATION [ARG]\n"
    "\t\t16-bit word PIC18F operations.\n"
    "\n");

  Serial.printf("EXAMPLES:\n");
  Serial.printf(" n16 select\n"
    "\t\tShow supported devices.\n", UL_ON, UL_OFF);
  Serial.printf(" n16 lvp OPERATION [ARG]\n"
    "\t\tLVP 32-bit key entry.\n", UL_ON, UL_OFF);
  Serial.printf(" n16 blank\n"
    "\t\tBlank device (disable protection and bulk erase).\n", UL_ON, UL_OFF);
  Serial.printf(" n16 config\n"
    "\t\tDisplay device configuration.\n", UL_ON, UL_OFF);
  Serial.printf(" n16 data\n"
    "\t\tDisplay data EEPROM content.\n", UL_ON, UL_OFF);
  Serial.printf(" n16 dump\n"
    "\t\tDump device content (INHX32 format).\n", UL_ON, UL_OFF);
  Serial.printf(" n16 erase eeprom | flash | id | row [n]\n"
    "\t\tErase EEPROM, flash, id or flash at row for n rows.\n", UL_ON, UL_OFF);
  Serial.printf(" n16 flash [n] [address]\n"
    "\t\tDisplay all or n words of program flash content from address.\n", UL_ON, UL_OFF);
  Serial.printf(" n16 id\n"
    "\t\tDisplay device identification.\n", UL_ON, UL_OFF);
  Serial.printf(" n16 program [file.hex] [noblank]\n"
    "\t\tBlank and program file.hex or stdin to flash (INHX32 format).\n", UL_ON, UL_OFF);
  Serial.printf(" n16 verify [file.hex]\n"
    "\t\tVerify file.hex or stdin in flash (INHX32 format).\n", UL_ON, UL_OFF);
  Serial.printf(" n16 view [file.hex] [raw]\n"
    "\t\tView file.hex or stdin (INHX32 format).\n", UL_ON, UL_OFF);
  Serial.printf("\n");

  Serial.printf("VERSION:\n %s\n", VERSION);
  rxEND();
}

/*
 * p24 help
 */
void
usage_p24(char *msg)
{
  if (msg)
    Serial.printf("Error: %s.\n\n", msg);

  Serial.printf( 
    " p24 [SELECT|PARTITION DEVICE] [LVP|HVP] OPERATION [ARG]\n"
    "\t\t24-bit word PIC24/dsPIC operations.\n"
    "\n");

  Serial.printf("EXAMPLES:\n");
  Serial.printf(" p24 select\n"
    "\t\tShow supported devices.\n", UL_ON, UL_OFF);
  Serial.printf(" p24 select 33EP128GP502 OPERATION [ARG]\n"
    "\t\tSelect device dsPIC33EP128GP502.\n", UL_ON, UL_OFF);
  Serial.printf(" p24 select 24FJ128GB202 OPERATION [ARG]\n"
    "\t\tSelect device PIC24FJ128GB202.\n", UL_ON, UL_OFF);
  Serial.printf(" p24 partition 33EP64GS502 OPERATION [ARG]\n"
    "\t\tSelect and partition device dsPIC33EP64GS502.\n", UL_ON, UL_OFF);
  Serial.printf(" p24 lvp OPERATION [ARG]\n"
    "\t\tLVP 32-bit key entry.\n", UL_ON, UL_OFF);
  Serial.printf(" p24 hvp OPERATION [ARG]\n"
    "\t\tHVP 32-bit key entry.\n", UL_ON, UL_OFF);
  Serial.printf(" p24 blank\n"
    "\t\tBlank device (disable protection and bulk erase).\n", UL_ON, UL_OFF);
  Serial.printf(" p24 config\n"
    "\t\tDisplay device configuration.\n", UL_ON, UL_OFF);
  Serial.printf(" p24 data\n"
    "\t\tDisplay data EEPROM content.\n", UL_ON, UL_OFF);
  Serial.printf(" p24 dump\n"
    "\t\tDump device content (INHX32 format).\n", UL_ON, UL_OFF);
  Serial.printf(" p24 exec [n] [address]\n"
    "\t\tDisplay all or n words of executive flash content from address.\n", UL_ON, UL_OFF);
  Serial.printf(" p24 flash [n] [address]\n"
    "\t\tDisplay all or n words of program flash content from address.\n", UL_ON, UL_OFF);
  Serial.printf(" p24 id\n"
    "\t\tDisplay device identification.\n", UL_ON, UL_OFF);
  Serial.printf(" p24 program [file.hex] [noblank]\n"
    "\t\tBlank and program file.hex or stdin to flash (INHX32 format).\n", UL_ON, UL_OFF);
  Serial.printf(" p24 verify [file.hex]\n"
    "\t\tVerify file.hex or stdin in flash (INHX32 format).\n", UL_ON, UL_OFF);
  Serial.printf(" p24 view [file.hex] [raw]\n"
    "\t\tView file.hex or stdin (INHX32 format).\n", UL_ON, UL_OFF);
  Serial.printf("\n");
  
  Serial.printf("VERSION:\n %s\n", VERSION);
  rxEND();
}

/*
 * p32 help
 */
void
usage_p32(char *msg)
{
  if (msg)
    Serial.printf("Error: %s.\n\n", msg);

  Serial.printf( 
    " p32 OPERATION [ARG]\n"
    "\t\t32-bit word PIC32 operations.\n"
    "\n");

  Serial.printf("EXAMPLES:\n");
  Serial.printf(" p32 select\n"
    "\t\tShow supported devices.\n", UL_ON, UL_OFF);
  Serial.printf(" p32 boot [n] [address]\n"
    "\t\tDisplay all or n words of boot flash content from address.\n", UL_ON, UL_OFF);
  Serial.printf(" p32 blank\n"
    "\t\tBlank device (erase).\n", UL_ON, UL_OFF);
  Serial.printf(" p32 config\n"
    "\t\tDisplay device configuration.\n", UL_ON, UL_OFF);
  Serial.printf(" p32 dump\n"
    "\t\tDump device content (INHX32 format).\n", UL_ON, UL_OFF);
  Serial.printf(" p32 flash [n] [address]\n"
    "\t\tDisplay all or n words of program flash content from address.\n", UL_ON, UL_OFF);
  Serial.printf(" p32 id\n"
    "\t\tDisplay device identification.\n", UL_ON, UL_OFF);
  Serial.printf(" p32 program [file.hex] [noblank]\n"
    "\t\tBlank and program file.hex or stdin to flash (INHX32 format).\n", UL_ON, UL_OFF);
  Serial.printf(" p32 verify [file.hex]\n"
    "\t\tVerify file.hex or stdin in flash (INHX32 format).\n", UL_ON, UL_OFF);
  Serial.printf(" p32 view [file.hex] [raw]\n"
    "\t\tView file.hex or stdin (INHX32 format).\n", UL_ON, UL_OFF);
  Serial.printf("\n");
  
  Serial.printf("VERSION:\n %s\n", VERSION);
  rxEND();
}

/*
 * p12/p14/p16/p24/p32 help
 */
void
usage(char *execname, char *msg)
{
  if (strcmp("p12", execname) == 0)
    usage_p12(msg);
  if (strcmp("p14", execname) == 0)
    usage_p14(msg);
  if (strcmp("n14", execname) == 0)
    usage_n14(msg);
  if (strcmp("p16", execname) == 0)
    usage_p16(msg);
  if (strcmp("n16", execname) == 0)
    usage_n16(msg);
  if (strcmp("p24", execname) == 0)
    usage_p24(msg);
  if (strcmp("p32", execname) == 0)
    usage_p32(msg);
  rxEND();
}

/*
 * full help
 */
void
usage_full()
{
	Serial.println(" p12 help   : help");
	Serial.println(" p12 select : Show supported devices.");
	Serial.println(" p14 help   : help");
	Serial.println(" p14 select : Show supported devices.");
	Serial.println(" n14 help   : help");
	Serial.println(" n14 select : Show supported devices.");
	Serial.println(" p16 help   : help");
	Serial.println(" p16 select : Show supported devices.");
	Serial.println(" n16 help   : help");
	Serial.println(" n16 select : Show supported devices.");
	Serial.println(" p24 help   : help");
	Serial.println(" p24 select : Show supported devices.");
	Serial.println(" p32 help   : help");
	Serial.println(" p32 select : Show supported devices.");
    Serial.println();

	Serial.printf("VERSION:\n %s\n", VERSION);
    rxEND();
}

void SdFileList() {  //SDファイル一覧
    File root;
    File file;
    String path = "";
    String fn;
    String ext;
    int i;
    int max_fn = 0;

    root = SD.open(path.length() ? path : "/");
    file = root.openNextFile();
    while (file) {
        fn = file.name();
        if (!file.isDirectory()) {
            int idx = fn.lastIndexOf('.');
            ext = fn.substring(idx + 1);
            ext.toLowerCase();
            //fn = fn.substring(0, idx);
            if (ext == "hex" && !fn.startsWith(".") && (fn != "/temp.hex")) {
                if (max_fn < fn.length()) max_fn = fn.length();
            }
        }
        file = root.openNextFile();
    }
    root.close();

    for(i = 0; i < max_fn + 12; i++){
        Serial.print("-");
    }
    Serial.print("\n");   

    root = SD.open(path.length() ? path : "/");
    file = root.openNextFile();
    while (file) {
        fn = file.name();
        if (!file.isDirectory()) {
            int idx = fn.lastIndexOf('.');
            ext = fn.substring(idx + 1);
            ext.toLowerCase();
            //fn = fn.substring(0, idx);
            if (ext == "hex" && !fn.startsWith(".") && (fn != "/temp.hex")) {
                Serial.print("  "); Serial.print(fn);
                for(i = 0; i < max_fn - fn.length(); i++){
                    Serial.print(" ");
                }
                Serial.printf("%10u\n", file.size());
            }
        }
        file = root.openNextFile();
    }
    root.close();

    for(i = 0; i < max_fn + 12; i++){
        Serial.print("-");
    }
    Serial.print("\n");
}

void
serial_read() {
  int count = 0;
  int input;
  File file;

  SD.remove("/temp.hex");
  file = SD.open("/temp.hex", FILE_APPEND);
 
  while(1){
    Serial.write('~');
    while(1){
      input = Serial.read();
      if ( input == -1 ) continue;
      if ( input == 0 ) continue;
      if ( input == '~') break;
      file.printf("%c", input);
      if ( input == 10 || input == 13) break;
    }
    if ( input == '~') break;
  }
  file.close();
}

void
SD_write(char* filename, char* w) {
  File file;

  file = SD.open(filename, FILE_APPEND);
  file.printf("%s\r\n", w);
  file.close();
  Serial.write('~');
}
void

SD_file_del(char* filename) {
    SD.remove(filename);
    Serial.write('~');
}

int
SD_read(File fp,char* r) {
    int count = 0;  // 文字数のカウンタ
    unsigned int input;

    if(fp){
        //---1byteずつ読み込んだ文字を結合
        while (fp.available()) {
            r[count] = char(fp.read());
            if (count > STRLEN-2 || r[count] == '\n') {  // 文字数が既定の個数を超えた場合、又は終了文字を受信した場合
              r[count] = '\0';                    // 末尾に終端文字を入れる
              break;
            }
            count++;                              // 文字カウンタに 1 加算
        }
    }
    return count;
}

static void SetVolume( unsigned char ucVol )
{
    ledcWrite( TONE_PIN_CHANNEL, 0x1FF>>(9-ucVol) );
    
    return;
}

void myprintf(char *buf)
{
  switch(connectMode){
    case 1:
      sprite.printf(buf);  // LCDに表示
      break;
    case 2:
      Serial.printf(buf);
      break;
    case 3:
      if (gridNum==0) {
        for (int i=0; i<100; i++) {
          strcpy(gridstrs[i], "");
        }
      }
      if (gridNum<100) {
        if (strstr(buf, "\n")>0) {
          strcat(dat, buf);
          strcpy(gridstrs[gridNum], dat);  
          strcpy(dat, "");
          ++gridNum;
        } else {
          strcat(dat, buf);  
        }
        Serial.printf(buf);
      }
      break;
    default:
      break;
  }
    
    return;
}

typedef std::vector<lgui::LGUI_TreeItem*> items;

static lgui::LGUI_TreeItem* treeitem(const char* title)
{
  auto res = new lgui::LGUI_TreeItem();
  res->title = title;
  res->setDestRect(0, 0, std::max(160, lcd.width()/2), std::max(lcd.touch() ? 32 : 24, lcd.height()/10));
  tv.addControl(res);
  return res;
}

static lgui::LGUI_TreeItem* treeitem(const char* title, items subitems)
{
  auto res = treeitem(title);
  for (auto& ctl : subitems)
  {
    ctl->setTreeParent(res);
  }
  return res;
}

static void onChangeExpand_FSitem(lgui::LGUI_TreeItem* sender, bool& expand)
{
  std::string temp; 
  
  if (!expand) return;

  label1.setText(sender->title.c_str());
  label1.setRedraw();

  temp = "/" + sender->title;            // 1文字目に/を追加
  filepath = temp.c_str();

  lastTime = millis();                   // 終了時間を保存
}

static void list_sd(void)
{
  auto items = tv.getSubItems(tiSD, true);
  for (auto ctl : items)
  {
    tv.removeControl(ctl);
    delete ctl;
  }

  lcd.endWrite();

  lgui::LGUI_TreeItem* ti;
  String path = "";

  File root = SD.open(path.length() ? path : "/");
  File file = root.openNextFile();
  String fn;
  String ext;
  while (file) {
    fn = file.name();
    if (!file.isDirectory()) {
      int idx = fn.lastIndexOf('.');
      ext = fn.substring(idx + 1);
      ext.toLowerCase();
      fn = fn.substring(1);   // 1文字目の/を削除
      if (ext == "hex" && !fn.startsWith(".")) {
        ti = treeitem(fn.c_str());
        ti->setTreeParent(tiSD);
        ti->onChangeExpand = onChangeExpand_FSitem;
      }
    }
    file = root.openNextFile();
  }
  root.close();

  lcd.startWrite();
  tiSD->title = TITLE_HEXFILES;
  tv.expand(tiSD);
  flg_list_sd = false;
}

static void onChangeExpand_FS(lgui::LGUI_TreeItem* sender, bool& expand)
{
  if (expand && !flg_list_sd)
  {
    tiSD->title = "SD loading...";
    expand = false;
    flg_list_sd = true;
  }
}

static void onChangeExpand_Choices1(lgui::LGUI_TreeItem* sender, bool& expand)
{
  if (!expand) return;
  
  char strtmp[50];
  sprintf(strtmp, sender->title.c_str());
  pic_arch(strtmp);

  textbox1.setDestRect(8, 25,42,30);
  strcpy(buf, " ");
  strcat(buf, strtmp);
  textbox1.setText(buf);
  textbox1.setRedraw();
  textbox2.hide();

  strncpy(p.devicename, "", sizeof(p.devicename));

  lastTime = millis();      // 終了時間を保存
}

static void onChangeExpand_Choices2(lgui::LGUI_TreeItem* sender, bool& expand)
{
  if (!expand) return;
  
  pic_arch("p12");

  char strtmp[50];
  sprintf(strtmp, " p12");
  textbox1.setDestRect(8, 25,42,30);
  textbox1.setText(strtmp);
  textbox1.setRedraw();
  strcpy(buf, " ");
  sprintf(strtmp, sender->title.c_str());
  strcat(buf, strtmp);
  textbox2.show();
  textbox2.setDestRect(8, 55,87,30);
  textbox2.setText(buf);
  textbox2.setRedraw();

  strcpy(p.devicename, sender->title.c_str());

  lastTime = millis();      // 終了時間を保存
}

static void onChangeExpand_Preparation1(lgui::LGUI_TreeItem* sender, bool& expand)
{
  if (!expand) return;
  if (OKflag) return;
   
  int TxtSize=1;
  char strtmp[50];
  sprintf(strtmp, sender->title.c_str());

  lcd.endWrite();            // LovyanGUIの場合はこれが必要

  gui.hide();
  lcd.clear();
  lcd.setTextSize(TxtSize);
  lcd.setCursor(0, 0);

  if (strcmp(textbox1.getText(), "") == 0) {
    lcd.setTextSize(2);
    myprintf("Error:\n p12/../p32 not entered.");
    strcpy(strtmp, "***");
  }
  
  ledcSetup( TONE_PIN_CHANNEL, 250, 13);
  SetVolume(1);

  if (strcmp(strtmp, "id") == 0) {
    connectMode=3;
    gridNum = 0;
    pic_dumpdeviceid();
    programer_reset();
    lcd.clear();
    lcd.display();
    lcd.startWrite();         // 復帰
    grid.selectCell(0,0);
    grid.selectCell(0,0);
    grid.setRedraw();
    gui2.show();
  }
 
  if (strcmp(strtmp, "erase") == 0) {
    lcd.fillCircle(160, 120, 80, RED);
    pic_erase(0, UINT32_MAX);
    SetVolume(0);
    programer_reset();
    lcd.startWrite();         // 復帰
    lcd.clear();
    gui.loop();
    lcd.display();
    gui.show();
    return;
  }
 
  if (strcmp(strtmp, "blank") == 0) {
    lcd.fillCircle(160, 120, 80, RED);
    pic_blank(0);
    SetVolume(0);
    programer_reset();
    lcd.startWrite();         // 復帰
    lcd.clear();
    gui.loop();
    lcd.display();
    gui.show();
    return;
  }
 
  SetVolume(0);
  lastTime = millis();      // 終了時間を保存
  while(1){
    if (strcmp(strtmp, "id") == 0) {
      gui2.loop();
      lcd.display();
    }
    if ( M5.BtnA.pressedFor(500) ) {
      gui2.hide();
      lcd.clear();
      OKflag=1;
      gui.loop();
      OKflag=0;
      lcd.display();
      gui.show();
      connectMode=1;
      lastTime = millis();      // 終了時間を保存
      return;
    }
    if (M5.BtnA.wasPressed()) {
      lastTime = millis();      // 終了時間を保存
    }
    if (M5.BtnB.wasPressed()) {
      lastTime = millis();      // 終了時間を保存
    }
    if (M5.BtnC.wasPressed()) {
      lastTime = millis();      // 終了時間を保存
    }
    M5.update();
    // 3分
    unsigned long oneHour = 1000 * 60 * 60 * 24;
    unsigned long twoHour = 1000 * 60 * 60 * 24;
    unsigned long currentTime = millis();
    if (M5.Power.isCharging()==false) {
      if (oneHour < (currentTime - lastTime)) {
        // 3分起動がなかったら、M5StackをDeep Sleepに切り替えます
        M5.Power.powerOFF();
      }
    } else {
      if (twoHour < (currentTime - lastTime)) {
        // 3分起動がなかったら、M5StackをDeep Sleepに切り替えます
        M5.Power.powerOFF();
      }
    }
  }
}

static void onChangeExpand_Operation1(lgui::LGUI_TreeItem* sender, bool& expand)
{
  if (!expand) return;
  if (OKflag) return;

  int TxtSize=1;
  char* filepathc;
  unsigned long startTime;
  unsigned long stopTime;
  float s;
  char strtmp[50];
  sprintf(strtmp, sender->title.c_str());
  filepathc=(char*)filepath.c_str();
  strcpy(buf, "/");
  strcat(buf, filepathc);

  lcd.endWrite();            // LovyanGUIの場合はこれが必要
  
  gui.hide();
  lcd.clear();
  lcd.setTextSize(TxtSize);
  lcd.setCursor(0, 0);

  if (strcmp(textbox1.getText(), "") == 0) {
    lcd.setTextSize(2);
    myprintf("Error:\n p12/../p32 not entered.\n\n");
    strcpy(strtmp, "***");
  }
  if (strcmp(buf, "/") == 0) {
    lcd.setTextSize(2);
    myprintf("Error:\n Hex File not entered.");
    strcpy(strtmp, "***");
  }
  
  ledcSetup( TONE_PIN_CHANNEL, 250, 13);
  SetVolume(1);

  if (strcmp(strtmp, "program") == 0) {
    lcd.fillCircle(160, 120, 80, RED);
    sprite.setColorDepth(8);                         // カラーモード設定
    sprite.createSprite(lcd.width(), lcd.height());  // メモリ描画領域を設定（画面サイズに設定）
    sprite.fillScreen(BLACK);                        // 画面の塗りつぶし
    sprite.setTextFont(2);                           // フォント番号
    sprite.setCursor(0, 0);                          // 文字列の書き出し位置
    sprite.setTextSize(1);                           // 文字サイズを設定
    startTime = millis();                            // 開始時刻を記録
    pic_program(buf, 1);
    programer_reset();
    stopTime = millis();                             // 終了時刻を記録する
    s=(stopTime - startTime)/1000.0;                 //経過時間（秒）を算出
    sprintf(buf, "Time : %.3fs\n", s);               //少数点以下3桁で表示
    myprintf(buf);
    lcd.clear();
    sprite.pushSprite(0, 0);                         // lcdの座標0,0にスプライトを描画
    sprite.deleteSprite();                           // スプライト１を開放
    lcd.startWrite();                                // 復帰
  }
 
  if (strcmp(strtmp, "verify") == 0) {
    lcd.fillCircle(160, 120, 80, RED);
    connectMode=3;
    gridNum = 0;
    pic_verify(buf);
    programer_reset();
    lcd.clear();
    lcd.display();
    lcd.startWrite();         // 復帰
    grid.selectCell(0,0);
    grid.selectCell(0,0);
    grid.setRedraw();
    gui2.show();
 }
 
  if (strcmp(strtmp, "view") == 0) {
    lcd.fillCircle(160, 120, 80, RED);
    connectMode=3;
    gridNum = 0;
    pic_view(buf, 0);
    programer_reset();
    lcd.clear();
    lcd.display();
    lcd.startWrite();         // 復帰
    grid.selectCell(0,0);
    grid.selectCell(0,0);
    grid.setRedraw();
    gui2.show();
  }
 
  SetVolume(0);
  lastTime = millis();      // 終了時間を保存
  while(1){
    if ((strcmp(strtmp, "view") == 0) || (strcmp(strtmp, "verify") == 0)) {
      gui2.loop();
      lcd.display();
    }
    if ( M5.BtnA.pressedFor(500) ) {
      gui2.hide();
      lcd.clear();
      OKflag=1;
      gui.loop();
      OKflag=0;
      lcd.display();
      gui.show();
      connectMode=1;
      lastTime = millis();      // 終了時間を保存
      return;
    }
    if (M5.BtnA.wasPressed()) {
      lastTime = millis();      // 終了時間を保存
    }
    if (M5.BtnB.wasPressed()) {
      lastTime = millis();      // 終了時間を保存
    }
    if (M5.BtnC.wasPressed()) {
      lastTime = millis();      // 終了時間を保存
    }
    M5.update();
    // 3分
    unsigned long oneHour = 1000 * 60 * 60 * 24;
    unsigned long twoHour = 1000 * 60 * 60 * 24;
    unsigned long currentTime = millis();
    if (M5.Power.isCharging()==false) {
      if (oneHour < (currentTime - lastTime)) {
        // 3分起動がなかったら、M5StackをDeep Sleepに切り替えます
        M5.Power.powerOFF();
      }
    } else {
      if (twoHour < (currentTime - lastTime)) {
        // 3分起動がなかったら、M5StackをDeep Sleepに切り替えます
        M5.Power.powerOFF();
      }
    }
  }
}

static void onChangeExpand_hvp_lvp(lgui::LGUI_TreeItem* sender, bool& expand)
{
  if (!expand) return;

  char strtmp[50];
  sprintf(strtmp, sender->title.c_str());
  
  if (strcmp(strtmp, "none") == 0) {
    p.key = NOKEY;

    textbox3.hide();
  }
  
  if (strcmp(strtmp, "hvp") == 0) {
    p.key = HVPKEY;

    textbox3.setDestRect(50, 25,45,30);
    textbox3.show();
    textbox3.setText(" hvp");
    textbox3.setRedraw();
  }
 
  if (strcmp(strtmp, "lvp") == 0) {
    p.key = LVPKEY;

    textbox3.setDestRect(50, 25,45,30);
    textbox3.show();
    textbox3.setText(" lvp");
    textbox3.setRedraw();
  }
}

void
getBattery() {
  giBattery = M5.Power.getBatteryLevel();
  Serial.printf("Battery:%3d%%\n",giBattery);
  // 10%以下
  if( giBattery <= 10 )
  {
    lcd.drawRect(210,55,40,90,RED);
    lcd.fillRect(210,55,40,90,RED);
  }
  else
  {
    lcd.drawRect(210,55,40,90,BLACK);
    lcd.fillRect(210,55,40,90,BLACK);
  }
  // 50%以上
  if( giBattery >= 50 )
  {
    lcd.drawRect(165,55,40,90,BLACK);
    lcd.fillRect(165,55,40,90,BLACK);
  }
  // 75%以上
  if( giBattery >= 75 )
  {
    lcd.drawRect(120,55,40,90,BLACK);
    lcd.fillRect(120,55,40,90,BLACK);
  }
  // 90%以上
  if( giBattery >= 90 )
  {
    lcd.drawRect(75,55,40,90,BLACK);
    lcd.fillRect(75,55,40,90,BLACK);
  }
}

static void onChangeExpand_Settings1(lgui::LGUI_TreeItem* sender, bool& expand)
{
  if (!expand) return;

  int TxtSize=1;
  unsigned long alarm = 0;
  char strtmp[50];
  sprintf(strtmp, sender->title.c_str());
  
  lcd.endWrite();            // LovyanGUIの場合はこれが必要

  gui.hide();
  lcd.clear();
  lcd.setTextSize(TxtSize);
  lcd.setCursor(0, 0);

  if (strcmp(strtmp, "Battery") == 0) {
    // 電池へそ
    lcd.drawRoundRect(40,75,25,50,5,WHITE);
    lcd.fillRoundRect(40,75,25,50,5,WHITE);
    // 大外枠
    lcd.drawRoundRect(60,50,200,100,5,WHITE);
    lcd.fillRoundRect(60,50,200,100,5,WHITE);

    getBattery();
  }
 
  if (strcmp(strtmp, "USB mode") == 0) {
    SerialLoop();
  }
 
  lastTime = millis();      // 終了時間を保存
  while(1){
    if (M5.BtnA.wasPressed()) {
      lcd.clear();
      gui.loop();
      lcd.display();
      gui.show();
      lastTime = millis();      // 終了時間を保存
      return;
    }
    if (M5.BtnB.wasPressed()) {
      lastTime = millis();      // 終了時間を保存
    }
    if (M5.BtnC.wasPressed()) {
      lastTime = millis();      // 終了時間を保存
    }
    M5.update();
    // 3分
    unsigned long oneHour = 1000 * 60 * 60 * 24;
    unsigned long twoHour = 1000 * 60 * 60 * 24;
    unsigned long currentTime = millis();
    if (M5.Power.isCharging()==false) {
      if (oneHour < (currentTime - lastTime)) {
        // 3分起動がなかったら、M5StackをDeep Sleepに切り替えます
        M5.Power.powerOFF();
      }
    } else {
      if (twoHour < (currentTime - lastTime)) {
        // 3分起動がなかったら、M5StackをDeep Sleepに切り替えます
        M5.Power.powerOFF();
      }
    }
    if((alarm<=(currentTime - lastTime)) && (strcmp(strtmp, "Battery") == 0)){
      alarm+=60000;
      getBattery(); // Batteryの表示更新
    }
  }
}

/**
 * @fn
 * グリッドのセルが描画される時のコールバック関数
 * @brief グリッドのセルが描画される時のコールバック関数 onDrawCellに代入する
 * @param (sender) コールバック関数を呼び出したグリッドのポインタ
 * @param (canvas) 描画対象スプライトのポインタ (セル一つ分の範囲) ※背景色・カーソル・グリッド枠線(右端と下端) が描画済
 * @param (column) 描画対象の列番号
 * @param (row) 描画対象の行番号
 * @param (&redraw) 再描画指示の有無 (trueの場合は必ず描画を行う。 falseの場合は描画するか否かは任意) ※ redraw がfalseの場合に描画更新したい場合は redrawにtrue を代入してください
 */
static void onDrawCell_grid(lgui::LGUI_GridView* sender, LovyanGFX* canvas, std::int32_t column, std::int32_t row, bool& redraw)
{
  // 再描画指示がない場合は終了
  if (!redraw) return;

  // 縦方向の中心座標を求める (テキストを描画する際に使用します)
  std::int32_t y = (canvas->height() - sender->borderWidth)>>1;

  // 描画対象のセルが左端の固定列の場合
  if (column < sender->fixedColumnCount)
  {
    // 左上隅の固定列かつ固定行の場合は何も描画しないので終了
    if (row < sender->fixedRowCount) return;

    // テキスト描画の縦位置を中心、横位置を右寄せに設定
    canvas->setTextDatum(middle_right);

    // 行数を表示。右端は２ドット空ける
    canvas->drawNumber(row - sender->fixedRowCount + 1, canvas->width()-2, y);

    return;
  }

  if (row < sender->fixedRowCount)
  {
    canvas->setTextDatum(middle_left);
    char buf[2] = { 0 };
    buf[0] = 'A' + column - sender->fixedColumnCount;
    canvas->drawString(buf, 2, y);
  }
  else
  {
    size_t index = (row - sender->fixedRowCount);
    if (index < 100 && !(strcmp(gridstrs[index],"")==0))
    {
      canvas->setTextDatum(middle_left);
      canvas->drawString(gridstrs[index], 2, y);
/*
} else {
static int count;
char buf[20] = { 0 };
sprintf(buf, "%d", ++count);
canvas->setTextDatum(middle_left);
canvas->drawString(buf, 2, y);
//*/
    }
  }
}

void rxSTART()
{
    lcd.clear();
    lcd.fillCircle(160, 120, 80, RED);
    ledcSetup( TONE_PIN_CHANNEL, 250, 13);
    SetVolume(1);
    digitalWrite(PIN_VPP, VPPLOW);
}

void rxEND()
{
    Serial.printf("%c",0x0D);
    Serial.printf("%c",0x0D);
    Serial.printf("%c",0x0A);
}

void setup(void)
{
  M5.begin();
  Serial.begin( 57600 );     // シリアル通信を初期化する。通信速度は57600bps
  Serial.printf("\ninitializing.....OK\n");
  M5.Power.begin();
  M5.Power.setPowerBoostSet(true);
  M5.Power.setPowerBoostKeepOn(false);
  M5.Power.setAutoBootOnLoad(false);
  M5.Speaker.begin(); // これが無いとmuteしても無意味です。
  M5.Speaker.mute();
  lastTime = millis();      // 終了時間を保存
  pinMode(PIN_VPP, OUTPUT);
  pinMode(PIN_VDD, OUTPUT);
  pinMode(PIN_CLOCK, OUTPUT);
  pinMode(PIN_DATA, OUTPUT);
  digitalWrite(PIN_VPP, VPPLOW);
  digitalWrite(PIN_VDD, LOW);
  digitalWrite(PIN_CLOCK, LOW);
  digitalWrite(PIN_DATA, LOW);

  /* Config defaults */
  io_config();
  /* Determine back-end */
  io_backend();

  /* No key entry */
  /* ARCH12BIT || ARCH14BIT || ARCH16BIT || ARCH24BIT */
  p.key = NOKEY;

  lcd.begin();
  lcd.setEpdMode(epd_mode_t::epd_fast);
  lcd.clear();
  lcd.display();
  lcd.setTextFont(2);
  lcd.setTextColor(WHITE);
  lcd.setTextSize(1);
  lcd.setBrightness(64);
  if (lcd.width() < lcd.height()) lcd.setRotation(lcd.getRotation() ^ 1);

  if (!SD.begin()) {
    lcd.setTextSize(2);
    lcd.println("SD Card failed,\n         or not present.");
    while (true) {
      delay(1000);
    }
  }

  grid.setFont(&fonts::Font2);
  // グリッドの設定
  grid.onDrawCell     = onDrawCell_grid;     // セル描画関数の設定
  grid.frameWidth = 0;            // 外枠の幅
  grid.borderWidth = 1;           // グリッド間の枠の幅
  grid.simpleMode = true;         // シンプルモード解除(縦横移動可能になる)
  grid.rowCount = 100;            // 行数 (固定行数を含む)
  grid.columnCount = 2;           // 列数 (固定列数を含む)
  grid.fixedRowCount = 0;         // 固定行数
  grid.fixedColumnCount = 1;      // 固定列数
  grid.visibleVScroll = true;     // 縦スクロールバー表示フラグ
  grid.visibleHScroll = true;     // 横スクロールバー表示フラグ
  grid.scrollBarWidth = 5;        // スクロールバー表示幅
  grid.setDefaultRowHeight(16);   // 行の高さのデフォルト値
  grid.setDefaultColumnWidth(580);// 列の幅のデフォルト値
  grid.setColumnWidth(0, 24);     // 列の幅の個別設定 (先頭列の幅を24に
  grid.setDestRect(0, 0, lcd.width(), lcd.height());
  gui2.addControl(&grid);

  label_height = std::max(16, lcd.height() / 14);

//  if (lcd.panel()->isEPD()) 
  {
    gui.smoothMove = 0; // EPDはスムーズ移動をオフにする
  }

  if (lcd.getBoard() == lgfx::board_t::board_M5Paper)
  {
    gui.setFont(&fonts::FreeMono24pt7b);
  }
  else
  {
    gui.setFont(&fonts::Font2);
  }

  tiC = treeitem("p12/p14/p16/p24/p32");
  items subitemsC1 = items{treeitem("p12"),
                           treeitem("p14"),
                           treeitem("n14"),
                           treeitem("p16"),
                           treeitem("n16"),
                           treeitem("p24"),
                           treeitem("p32")};
  items subitemsC2 = items{treeitem("PIC12F508"),
                           treeitem("PIC12F509"),
                           treeitem("PIC16F505"),
                           treeitem("PIC12F510"),
                           treeitem("PIC16F506"),
                           treeitem("PIC10F200"),
                           treeitem("PIC10F202"),
                           treeitem("PIC10F204"),
                           treeitem("PIC10F206"),
                           treeitem("PIC16F54"),
                           treeitem("PIC16F57"),
                           treeitem("PIC16F59"),
                           treeitem("PIC10F220"),
                           treeitem("PIC10F222"),
                           treeitem("PIC12F519"),
                           treeitem("PIC16F570"),
                           treeitem("PIC16F527"),
                           treeitem("PIC16F526"),
                           };
  for (auto& ctl1 : subitemsC1)
  {
    ctl1->setTreeParent(tiC);
    if (ctl1!=subitemsC1[0]) ctl1->onChangeExpand = onChangeExpand_Choices1;
  }
  for (auto& ctl2 : subitemsC2) {
    ctl2->setTreeParent(subitemsC1[0]);
    ctl2->onChangeExpand = onChangeExpand_Choices2;      
  }

  tiHL = treeitem("hvp / lvp");
  items subitemsHL = items{treeitem("hvp"),
                           treeitem("lvp"),
                           treeitem("none")};
  for (auto& ctl3 : subitemsHL)
  {
    ctl3->setTreeParent(tiHL);
    ctl3->onChangeExpand = onChangeExpand_hvp_lvp;
  }

  tiP = treeitem("Preparation");
  items subitemsP1 = items{treeitem("id"),
                           treeitem("erase"),
                           treeitem("blank")};
  for (auto& ctl4 : subitemsP1)
  {
    ctl4->setTreeParent(tiP);
    ctl4->onChangeExpand = onChangeExpand_Preparation1;
  }

  tiSD = treeitem(TITLE_HEXFILES);
  tiSD->canExpand = true;
  tiSD->onChangeExpand = onChangeExpand_FS;

  tiO = treeitem("Operation");
  items subitemsO1 = items{treeitem("program"),
                           treeitem("verify"),
                           treeitem("view")};
  for (auto& ctl5 : subitemsO1)
  {
    ctl5->setTreeParent(tiO);
    ctl5->onChangeExpand = onChangeExpand_Operation1;
  }

  label1.setDestRect(0, 0, lcd.width(), label_height);

  // グリッドの設定
  //tv.onInput        = onInput_grid;        // 入力処理関数の設定
  //tv.onTouch        = onTouch_grid;        // タッチ処理関数の設定
  tv.frameWidth = 2;            // 外枠の幅
  //tv.borderWidth = 1;           // グリッド間の枠の幅
  //tv.simpleMode = false;        // シンプルモード解除(縦横移動可能になる)
  tv.visibleVScroll = true;     // 縦スクロールバー表示フラグ
  //tv.visibleHScroll = true;     // 横スクロールバー表示フラグ
  //tv.scrollBarWidth = 5;        // スクロールバー表示幅
  //tv.setDefaultRowHeight(16);   // 行の高さのデフォルト値
  //tv.setDefaultColumnWidth(72); // 列の幅のデフォルト値
  //tv.setColumnWidth(0, 24);     // 列の幅の個別設定 (先頭列の幅を24に
  //std::int32_t treewidth = lcd.width()*2/3;
  std::int32_t treewidth = lcd.width() > 240 ? lcd.width()*2/3 : lcd.width();
  tv.setDestRect(0, label_height, treewidth, lcd.height()-label_height);

  gui.addControl(&tv);
  gui.addControl(&label1);

//  panel1.setDestRect(treewidth, 16, lcd.width() - treewidth, lcd.height() - 16);

  tiSet = treeitem("Settings");
  items subitemsS1 = items{treeitem("Battery"),
                           treeitem("USB mode")};
  for (auto& ctl6 : subitemsS1)
  {
    ctl6->setTreeParent(tiSet);
    ctl6->onChangeExpand = onChangeExpand_Settings1;
  }

  tv.initTree();

  gui.addControl(&panel1);
  panel1.setDestRect(treewidth, 0, lcd.width() - treewidth, lcd.height());
  panel1.addControl(&textbox1);
  panel1.addControl(&textbox2);
  panel1.addControl(&textbox3);

  disableCore0WDT();
  lcd.startWrite();
  gui2.hide();
  gui2.colorDepth = 8;
  gui2.setup(&lcd);
  gui.show();
  gui.colorDepth = 8;
  gui.setup(&lcd);
}

void SerialLoop() {
  int  argc = 0;
  char *argv[10];
  char str_argv[202];
  String aStr;
  int rc;
  int high, low;
  unsigned int command;
  unsigned int data1;
  int i;
  bool flag_arch;
  pic_data data;
  File fp;

  connectMode=2;
  lcd.endWrite();            // LovyanGUIの場合はこれが必要
  gui.hide();
  lcd.clear();
  lcd.pushImage(0, 0, 320, 240, (uint16_t *)image_data_logo);

  while(1){
    inhx32_get(fp, &data, 1);
    pic_write_panel(0, 0, 0, 1);
    pic_dumpaddr(0, 0, 1);
    argc = 0;
    str_argv[0] = '/';
    str_argv[1] = '\0';
    // ===============シリアル通信===============
    if (Serial.available()) {
      high=Serial.read();
      strncpy(p.devicename, "", sizeof(p.devicename));
      if (high==0x80 || high==0x81 || high==0x82 || high==0x83 || high==0x84 || high==0x85 || high==0x86) {
        /* Determine arch: 12 | 14 | 16 | 24 | 32 */
        if (high==0x80) pic_arch("p12");
        if (high==0x81) pic_arch("p14");
        if (high==0x82) pic_arch("n14");
        if (high==0x83) pic_arch("p16");
        if (high==0x84) pic_arch("n16");
        if (high==0x85) pic_arch("p24");
        if (high==0x86) pic_arch("p32");
        /* No key entry */
        /* ARCH12BIT || ARCH14BIT || ARCH16BIT || ARCH24BIT */
        p.key = NOKEY;
        high=0x80;

        low=Serial.read();
        command=(((unsigned int)high)<<8)|((unsigned int)low);

        rxSTART();
        switch(command) {
          case COMMAND_ERASE:
            pic_erase(0, UINT32_MAX);
            break;
          case COMMAND_BLANK:
            data1=serialReadOneWordBlock();
            pic_blank(data1);
            break;
          case COMMAND_WRITE_PROGRAM:
            data1=serialReadOneWordBlock();
            serial_read();
            //pic_program("/temp.hex", data1);
            break;
          case COMMAND_VIEW_FILE:
            data1=serialReadOneWordBlock();
            serial_read();
            //pic_view("/temp.hex", data1);
            break;
          case COMMAND_VERIFY_PROGRAM:
            serial_read();
            //pic_verify("/temp.hex");
            break;
          case COMMAND_READ_PROGRAM:
            pic_dumpdevice();
            break;
          case COMMAND_READ_ID_AND_CONFIG:
            pic_dumpdeviceid();
            break;
          case COMMAND_READ_CONFIG:
            pic_dumpconfig();
            break;
        }
        lcd.clear();
        lcd.pushImage(0, 0, 320, 240, (uint16_t *)image_data_logo);
        lastTime = millis();      // 終了時間を保存
        SetVolume(0);
        continue;
      }
        
      dat[count] = high;
      if (count > 200 || dat[count] == '\n') {  // 文字数が既定の個数を超えた場合、又は終了文字を受信した場合
        dat[count] = '\0';                    // 末尾に終端文字を入れる
        count = 0;                            // 文字カウンタをリセット
        aStr=dat;
        argv[argc]=strtok(dat," ");
        do
          {
            argv[++argc] = strtok(NULL, " ");
          } while ( (argc < 10) && (argv[argc] != NULL));

        /* Determine arch: 12 | 14 | 16 | 24 | 32 */
        flag_arch = false;
        if (strcmp(argv[0], "p12")==0) {pic_arch("p12");flag_arch = true;};
        if (strcmp(argv[0], "p14")==0) {pic_arch("p14");flag_arch = true;};
        if (strcmp(argv[0], "n14")==0) {pic_arch("n14");flag_arch = true;};
        if (strcmp(argv[0], "p16")==0) {pic_arch("p16");flag_arch = true;};
        if (strcmp(argv[0], "n16")==0) {pic_arch("n16");flag_arch = true;};
        if (strcmp(argv[0], "p24")==0) {pic_arch("p24");flag_arch = true;};
        if (strcmp(argv[0], "p32")==0) {pic_arch("p32");flag_arch = true;};
        if (flag_arch == false) {
          usage_full();
          continue;
        }

        /* No key entry */
        /* ARCH12BIT || ARCH14BIT || ARCH16BIT || ARCH24BIT */
        p.key = NOKEY;

        /* Check args */
        if (argc < 2) {
          usage(argv[0], "Missing arg(s)");
          continue;
        }

        /*
         * Get args & perform operation
         */

        /* Device selection and partition */
        int argv1 = tolower((int)argv[1][0]);
        int argv11 = tolower((int)argv[1][1]);
        if (argv1 == 'h') {    /* HELP */
          if (argv11 == 'e') {
            usage(argv[0], 0);
          	continue;
          }
        }
        if (argv1 == 's' || (argv1 == 'p' && argv11 == 'a')) {
          /* Select or partition device */
          if (argc < 3) {
            pic_selector();
            rxEND();
          	continue;
          }
          p.partition = (argv1 == 'p') ? TRUE : FALSE;
          if (mystrcasestr(argv[2], "dspic") == argv[2]) {
            strncpy(p.devicename, argv[2], STRLEN);
          } else if (mystrcasestr(argv[2], "pic") == argv[2]) {
            strncpy(p.devicename, argv[2], STRLEN);
          } else {
            int32_t temp = strtol(argv[2], NULL, 0);
            if (temp < 10 || temp > 33) {
              usage(argv[0], "Invalid arg [select]");
              continue;
            }
            if (temp == 30 || temp == 33) {
              strcpy(p.devicename, "dspic");
              strncpy(&p.devicename[5], argv[2], STRLEN - 5);
            } else {
              strcpy(p.devicename, "pic");
              strncpy(&p.devicename[3], argv[2], STRLEN - 3);
            }
          }
          argc -= 2;
          for (i=1;i<8;i++) {argv[i]=argv[i+2];}
          argv[8]="";argv[9]=""; 
          if (argc < 2) {
            usage(argv[0], "Missing arg(s)");
            continue;
          }
        } else if (p.pic->arch == ARCH12BIT && argv1 != 'f') {
          usage(argv[0], "Missing select");
          continue;
        }

      /* Key entry or loader output */
      argv1 = tolower((int)argv[1][0]);
      if (argv1 == 'l') {     /* LVP 32-bit key entry or loader output */
        if (argv11 == 'o') {    /* LOADER OUTPUT */
            if (argc > 2) {
                usage(argv[0], "Too many args [loader]");
                continue;
            }
            pic_bootloader();
            rxEND();
        	continue;
        }
        if (p.pic->arch == ARCH12BIT) {
          /* NOT SUPPORTED */
          usage(argv[0], "Invalid arg [lvp]");
          continue;
        }
        /* ARCH14BIT || ARCH16BIT || ARCH24BIT || ARCH32BIT */
        p.key = LVPKEY;
        argc -= 1;
        for (i=1;i<9;i++) {argv[i]=argv[i+1];}
        argv[9]=""; 
        if (argc < 2) {
          usage(argv[0], "Missing arg(s)");
          continue;
        }
      }
      else if (argv1 == 'h') {    /* HVP 32-bit key entry */
         if (p.pic->arch == ARCH12BIT || p.pic->arch == ARCH14BIT || p.pic->arch == ARCH32BIT) {
          /* NOT SUPPORTED */
          usage(argv[0], "Invalid arg [hvp]");
          continue;
        }
        /* ARCH16BIT || ARCH24BIT */
        p.key = HVPKEY;
        argc -= 1;
        for (i=1;i<9;i++) {argv[i]=argv[i+1];}
        argv[9]=""; 
        if (argc < 2) {
          usage(argv[0], "Missing arg(s)");
          continue;
        }
      }
      else if (p.pic->arch == ARCH32BIT) {  /* LVP 32-bit key entry */
        /* ARCH32BIT */
        p.key = LVPKEY;
      }
      else {          /* No key entry */
        /* ARCH12BIT || ARCH14BIT || ARCH16BIT || ARCH24BIT */
        p.key = NOKEY;
      }

        /* Command */
        rxSTART();
        argv1 = tolower((int)argv[1][0]);
        argv11 = tolower((int)argv[1][1]);
        switch (argv1) {
        case 'b': if (argv11 == 'o') {    /* BOOT */
          uint32_t addr = UINT32_MAX, words = UINT32_MAX;
          if (argc > 4) {
            usage(argv[0], "Too many args [boot]");
            continue;
          }
          if (argc >= 3) {
            words = strtoul(argv[2], NULL, 0);
            if (words == 0) {
              usage(argv[0], "Invalid arg [boot]");
              continue;
            }
          }
          if (argc == 4) {
            addr = strtoul(argv[3], NULL, 0);
          }
          pic_dumpboot(addr, words);
        } else {      /* BLANK */
          int config = 1;
          if (argc > 3) {
            usage(argv[0], "Too many args [blank]");
            continue;
          }
          if (argc == 3) switch (argv[2][0]) {
            case 'n':
            case 'N':
            case '0': config = 0;
              break;
            case 'y':
            case 'Y':
            case '1': config = 1;
              break;
            default:usage(argv[0], "invalid arg [blank]");
              continue;
              break;
          }
          pic_blank(config);
          Serial.printf("OK\n");
        }
        break;
          
        case 'c': if (argc > 3) {
            usage(argv[0], "Too many args [config]");
            continue;
          }
          if (argc == 2)
            pic_dumpconfig();
          else
            pic_writebandgap(strtoul(argv[2], NULL, 0));
          break;

        case 'd': if (argv11 == 'a') {    /* DATA */
            if (argc > 2) {
              usage(argv[0], "Too many args [data]");
              continue;
            }
            pic_dumpdata();
          } else if (argv11 == 'e') {   /* DEBUG */
             pic_debug();
          } else {      /* DUMP */
            if (argc > 2) {
              usage(argv[0], "Too many args [dump]");
              continue;
            }
            unsigned long startTime = millis();    // 開始時刻を記録
            pic_dumpdevice();
            unsigned long stopTime = millis();     // 終了時刻を記録する
            float s=(stopTime - startTime)/1000.0; //経過時間（秒）を算出
            Serial.printf("Time : %.3fs\n", s);    //少数点以下3桁で表示
          }
          break;

        case 'e': if (argv11 == 'r') {    /* ERASE FLASH | ID | ROW[NROWS] */
          uint32_t row = 0, nrows = 1;
          char prompt[STRLEN] = {0}, *endptr = NULL;

          if (argc < 3) {
            usage(argv[0], "Missing arg [erase]");
            continue;
          }
          if (argc > 4) {
            usage(argv[0], "Too many args [erase]");
            continue;
          }
          
          int argv2 = tolower((int)argv[2][0]);
          switch (argv2) {
          case 'i': /* IDLOCATION    */
          case 'u': /* USERID/CONFIG */
            row = PIC_ERASE_ID;
            strncpy(prompt, "Erase id", STRLEN);
            break;
          case 'c': /* CONFIG */
            row = PIC_ERASE_CONFIG;
            strncpy(prompt, "Erase config", STRLEN);
            break;
          case 'e': /* EEPROM */
            row = PIC_ERASE_EEPROM;
            strncpy(prompt, "Erase EEPROM", STRLEN);
            break;
          case 'f': /* FLASH */
            nrows = UINT32_MAX;
            strncpy(prompt, "Erase program flash", STRLEN);
            break;
          default:  /* FLASH ROW */
            {
            row = strtoul(argv[2], &endptr, 0);
            if (endptr == argv[2]) {
              usage(argv[0], "Invalid arg [erase]");
              continue;
            }
            if (argc == 4) {
              nrows = strtoul(argv[3], NULL, 0);
              if (nrows == 0) {
                usage(argv[0], "Invalid arg [erase]");
                continue;
              }
            }
            int _rc = snprintf(prompt, STRLEN, "Erase %u row(s) at row %u", nrows, row);
            if (_rc < 0) {
              Serial.printf("%s: fatal error: snprintf failed\n", __func__);
              continue;
            } else if (_rc >= STRLEN) {
              Serial.printf("%s: fatal error: snprintf overrun\n", __func__);
              continue;
            }
            }
            break;
          }
          //if (areyousure(prompt))
            pic_erase(row, nrows);
          Serial.printf("OK\n");
        } else if (argv11 == 'x') { /* EXECUTIVE */
          uint32_t addr = UINT32_MAX, words = UINT32_MAX;
          if (argc > 4) {
            usage(argv[0], "Too many args [executive]");
            continue;
          }
          if (argc >= 3) {
            words = strtoul(argv[2], NULL, 0);
            if (words == 0) {
              usage(argv[0], "Invalid arg [executive]");
              continue;
            }
          }
          if (argc == 4) {
            addr = strtoul(argv[3], NULL, 0);
          }
          pic_dumpexec(addr, words);
        } else {      /* EEPROM */
          if (argc > 2) {
            usage(argv[0], "Too many args [eeprom]");
            continue;
          }
          pic_dumpdata();
        }
        break;

        case '0': if (argc > 3) {
            usage(argv[0], "too many args [file]");
            continue;
          }
          SD_file_del(argv[2]);
          break;

        case '1': if (argc > 4) {
            usage(argv[0], "too many args [file]");
            continue;
          }
          SD_write(argv[2],argv[3]);
          break;

        case 'f': if (argv11 == 'l') {    /* flash */
          uint32_t words = UINT32_MAX, addr = UINT32_MAX;
          if (argc > 4) {
            usage(argv[0], "Too many args [program flash]");
            continue;
          }
          if (argc >= 3) {
            words = strtoul(argv[2], NULL, 0);
            if (words == 0) {
              usage(argv[0], "Invalid arg [program flash]");
              continue;
            }
          }
          if (argc == 4) {
            addr = strtoul(argv[3], NULL, 0);
          }
          pic_dumpprogram(addr, words);
        } else {      /* file */
            if (argc > 2) {
                usage(argv[0], "too many args [file]");
                continue;
            }
            SdFileList();
        }
        break;

        case 'i': if (argc > 2) {
            usage(argv[0], "too many args [id]");
            continue;
          }
          pic_dumpdeviceid();
          break;

        case 'o': if (argc > 3) {
            usage(argv[0], "Too many args [osccal]");
            continue;
          }
          if (argc == 2)
            pic_dumposccal();
          else
            pic_writeosccal(strtoul(argv[2], NULL, 0));
          break;

        case 'p': {
          int blank = 1;
          if (argc > 4) {
            usage(argv[0], "Too many args [program]");
            continue;
          }
          if (argc == 4) switch (argv[3][0]) {
            case 'n':
            case 'N':
            case '0': blank = 0;
              break;
            case 'y':
            case 'Y':
            case '1': blank = 1;
              break;
            default:usage(argv[0], "invalid arg [program]");
              continue;
              break;
          }
          unsigned long startTime = millis();    // 開始時刻を記録
          /* {filename} -> {/filename} */
          if (argv[2][0] != '/') {
            strcat(str_argv, argv[2]);
            argv[2] = str_argv;
          }
          pic_program(argv[2], blank);
          unsigned long stopTime = millis();     // 終了時刻を記録する
          float s=(stopTime - startTime)/1000.0; //経過時間（秒）を算出
          Serial.printf("Time : %.3fs\n", s);    //少数点以下3桁で表示
          }
          break;

    case 'v': if (argv11 == 'i') {    /* VIEW */
          int raw = 0;
          if (argc > 4) {
            usage(argv[0], "Too many args [view]");
            continue;
          }
          if (argc == 4) switch (argv[3][0]) {
            case 'r':
            case 'R': raw = 1;
              break;
            default:usage(argv[0], "invalid arg [view]");
              continue;
              break;
          }
          if (argc > 2) {
            /* {filename} -> {/filename} */
            if (argv[2][0] != '/') {
              strcat(str_argv, argv[2]);
              argv[2] = str_argv;
            }
            pic_view(argv[2], raw);
          }
        } else {      /* VERIFY */
          if (argv11 == 'p') {
            digitalWrite(PIN_VPP, VPPHIGH);
            Serial.printf("OK\n");
          } else {
            if (argc > 3) {
              usage(argv[0], "Too many args [verify]");
              continue;
            }
            if (argc == 3) {
              /* {filename} -> {/filename} */
              if (argv[2][0] != '/') {
                strcat(str_argv, argv[2]);
                argv[2] = str_argv;
              }
              rc = 0 - pic_verify(argv[2]);
            }
          }
        }
        break;

        default:  usage(argv[0], "Unknown operation");
          continue;
          break;
        }
        rxEND();
      	lcd.clear();
        lcd.pushImage(0, 0, 320, 240, (uint16_t *)image_data_logo);
        lastTime = millis();      // 終了時間を保存
        SetVolume(0);
      } else {
        count++;                              // 文字カウンタに 1 加算
      }
    }

    gui.loop();
    lcd.display();
    lastTime = millis();      // 終了時間を保存
    if (M5.BtnA.wasPressed()) {
      connectMode=1;            // USBモードから通常モードに戻す
      lcd.startWrite();         // 復帰
      lcd.clear();
      gui.loop();
      lcd.display();
      gui.show();
      lastTime = millis();      // 終了時間を保存
      return;
    }
    if (M5.BtnB.wasPressed()) {
      lastTime = millis();      // 終了時間を保存
    }
    if (M5.BtnC.wasPressed()) {
      lastTime = millis();      // 終了時間を保存
    }
    M5.update();
    // 3分
    unsigned long oneHour = 1000 * 60 * 60 * 24;
    unsigned long twoHour = 1000 * 60 * 60 * 24;
    unsigned long currentTime = millis();
    if (M5.Power.isCharging()==false) {
      if (oneHour < (currentTime - lastTime)) {
        // 3分起動がなかったら、M5StackをDeep Sleepに切り替えます
        M5.Power.powerOFF();
      }
    } else {
      if (twoHour < (currentTime - lastTime)) {
        // 3分起動がなかったら、M5StackをDeep Sleepに切り替えます
        M5.Power.powerOFF();
      }
    }
  }
}

void loop(void)
{
  gui.loop();
  lcd.display();

  if (flg_list_sd) { list_sd(); }

  if (M5.BtnA.wasPressed()) {
    lastTime = millis();      // 終了時間を保存
  }
  if (M5.BtnB.wasPressed()) {
    lastTime = millis();      // 終了時間を保存
  }
  if (M5.BtnC.wasPressed()) {
    lastTime = millis();      // 終了時間を保存
  }
  M5.update();
  // 3分
  unsigned long oneHour = 1000 * 60 * 60 * 24;
  unsigned long twoHour = 1000 * 60 * 60 * 24;
  unsigned long currentTime = millis();
  if (M5.Power.isCharging()==false) {
    if (oneHour < (currentTime - lastTime)) {
      // 3分起動がなかったら、M5StackをDeep Sleepに切り替えます
      M5.Power.powerOFF();
    }
  } else {
    if (twoHour < (currentTime - lastTime)) {
      // 3分起動がなかったら、M5StackをDeep Sleepに切り替えます
      M5.Power.powerOFF();
    }
  }
}
