01.T-Watch S3で時計表示


01.T-Watch S3 で時計表示

今までは、Lilygo T-Watch 2020 V3 を使用していましたが、今回、Lilygo T-Watch S3 を購入しました。ESP32-S3の方が低消費電力らしいです。拼多多で381元(約8000円)でした。今までV3で作ったスケッチを移し替えます。また、今後はさらに内容を増やし、メニュー画面を更新します。

商品紹介

・Lilygo T-Watch S3
https://lilygo.cc/products/t-watch-s3

・Lilygo T-Watch 2020 V3
https://lilygo.cc/products/t-watch-2020-v3

仕様比較

T-Watch S3 (T-Watch 2020 V3)
・MCU: ESP32-S3-R8 (ESP32) *1
・LoRa: SX1262 (無し) *2
・Bluetooth: V5.0 (V4.2) *3
・電源管理PMU: AXP2101 (AXP202) *4
・振動Motor: DRV2605 (?) *5

*1 低電力デュアルコア (シングルコア)
*2 トランシーバー 433/868/915MHz
*3 通信速度はV4.2の2倍, 通信範囲が4倍
*4 502530サイズ(5x25x30mm)を使用
*5 触覚+振動モーター

同じ仕様

・Flash: 16MB
・PSRAM: 8MB
・LCD: 1.54inch 240x240 ST7789V
・Wi-Fi: 2.4GHz 802.11 b/g/n
・Touch Screen: FT6336
・RTC: PCF8563 (PCF853 ?)
・3軸加速度センサー: BMA423
・Button: Power on/off のみ
・I2Sオーディオアンプ: MAX98357A
・IR (赤外線LED): IR12-2C
・マイク: SPM1423HM4H-B PDM(パルス密度変調)出力

電源

・ALDO1: RTCバックアップバッテリー 3.1-3.3V
・ALDO2: バックライト
・ALDO3: FT6336 および ST7889用 3V3
・ALDO4: SX1262
・BLDO2: DRV
・BLDO2: DRV2605イネーブルpin
・DC1 : ESP32 3V3
・VRTC : 何もしない

そのICについて

・FT6336: 自己容量式タッチパネルIC
  シングルポイント,ジェスチャータッチ,2ポイントをサポート

・ST7889: 262KカラーグラフィックTFT-LCD用IC
  240x320x18ビットの表示データRAMに保存可能

・SX1262: ロングレンジ,低消費電力,サブGHz,RFトランシーバー
  150-960MHz帯 半二重

・DRV2605: 触覚,振動ドライバ
(イネーブルpinとは、ICの全体動作を有効にするpinです。)

LoRaWANとは

長距離広域ネットワーク
低消費電力,長距離通信(市街地で3〜5km), 低ビットレート(拡散率により、1チャンネル0.3〜27kbit/s)のネットワークプロトコル
・ヨーロッパ: EU868 (863-870/873MHz)
・南アメリカ: AU915/AS923-1 (915-928MHz)
・北アメリカ: US915 (902-928MHz)
・インド: IN865 (865–867MHz)
・アジア: AS923 (915–928MHz)
・世界中: 2.4GHz
・日本: (920-928MHz) 0.25-50kbps

ESP32-S3

「違いを理解するESP32とESP32-S3の技術的およびパフォーマンス分析」の「ESP32およびESP32-S3のアプリケーションシナリオ」より
https://jp.ariat-tech.com/blog/understanding-the-differences-esp32-and-esp32-s3-technical-and-performance-analysis.html

ESP32

・2.4GHzおよび5GHzのデュアルバンドWi-Fi
・ビデオストリーミングや大規模なデータボリュームの管理、高速かつ信頼性の高いネット、データ交換(スマートホームシステムやビジネスオートメーション

ESP32-S3

・低消費電力
・スマートウォッチやフィットネストラッカーのウェアラブルデバイス

T-Watch S3 (vs 2020) のPin

https://t-watch-document-en.readthedocs.io/en/latest/introduction/product/2020.html#description
・ST7789V Display
12 (05) TFT_CS
18 (18) TFT_SCLK
13 (19) TFT_MOSI
38 (27) TFT_DC
45 (12) TFT_BL
Null (?)TFT_MISO 7789V 1.54 LCD
Null (?)TFT_RST

・BMA423 3軸加速度センサー
10 (21) I2C PN532_SDA
11 (22) I2C PN532_SCL
14 (39) INT PN532_INT

・PCF8563 RTC Clock
10 (--) I2C_SDA
11 (--) I2C_SCL
17 (--) INT
-- (37) 1-Wire

・AXP2101 (AXP202) 電源管理PMU
10 (21) I2C_SDA
11 (22) I2C_SCL
21 (35) INT_INT

・DRV2605 (?) Motor
10 (--) SDA DRV2605 Haptic Motor
11 (--) SCL
?? (--) INT 回路図でIN:open,EN:LD05
-- (04) 1-Wire

・FT6336 Touch
39 (23) I2C_SDA
40 (32) I2C_SCL
16 (38) I2C_INT

・MAX98357A スピーカアンプ
15 (25) I2S_WS
48 (26) I2S_BCK
46 (33) I2S_DOUT

・IR LED
02 (13) 1-Wire

・SPM1423HM4H-B PDM MIC
47 (??) Data
44 (??) Sclk

・SX1262 (無し) LoRa
03 RADIO_SCK
04 RADIO_MISO
01 RADIO_MOSI
05 RADIO_SS
09 RADIO_DI01
08 RADIO_RST
07 RADIO_BUSY

インストール

https://github.com/Xinyuan-LilyGO/TTGO_TWatch_Library/tree/t-watch-s3
上記ライブラリは、現在ではT-Watch S3のみです。
今まで使用していた2020 V3ライブラリとは"バージョンが異なりインストール失敗"と表示され共存できないので、ちょうど新しく買ったPCにインストールしました。

1.Arduino-IDE 2.3.4 (2024/12/3)がインストール済です

2.(esp32 2.0.9ボードをインストール)
注意:2.0.9 を使用。それ以降のVerは未適応。
ツール > ボード > ボードマネージャー > "esp"で検索 > "esp32 by Espressif Systems"の 2.0.9 > インストール

3.(TTGO_TWatch_Libraryをインストール)
今回インストール以降のVerがすでに入っているとインストールできません。また、Verが一致せず、正常にコンパイル・実行できる保証はありません。と言われます。
https://github.com/Xinyuan-LilyGO/TTGO_TWatch_Library/tree/t-watch-s3 にて > コード > "ZIPをダウンロード"(解凍はしない)
スケッチ > "ライブラリをインクルード" > ".ZIP形式のライブラリをインストール" > 先の"TTGO_TWatch_Library-t-watch-s3.zip"を選択 > 開く

4.(T-Watch-Depsをインストール)
https://github.com/Xinyuan-LilyGO/T-Watch-Deps
にて > コード > "ZIPをダウンロード" > 解凍
解凍した"T-Watch-Deps-master"の下にある9つのホルダを
C:\Users\UserName\Documents\Arduino\libraries
にコピー

5.(日本語ライブラリをインストール)
LovyanGFX by lovyan03 1.2.0 を使用します
(C:\Users\...\Documents\Arduino\libraries\LovyanGFXへ入りました)
LGFX_TTGO_TWATCH // TTGO T-Watch
では動作しなかったので LovyanGFX_S3.hpp を自作しました。

設定

下記◎の4点はデフォルトから変更が必要です。
Arduino-IDE > ツール にて
〇ボード: esp32 > "ESP32S3 Dev Module"
〇ボード:"COM□"

◎USB CDC On Boot: "Enabled"
 {Enabled, Disabled(USB未接続時)}

・CPU Frequency:"240MHz(WiFi)"
 {240MHz(WiFi),160MHz(WiFi),80MHz(WiFi),
 40MHz,20MHz,10MHz}

・Core Debug Level: "None"
 {None,Error,Warn,Info,Debug,Verbose}

・USB DFU On Boot: "Disabled"
 {Disabled,Enabled(Requires USB-OTG Mode)}

・Erase All Flash Before Sketch Upload:"Disabled"
 {Disabled,Enabled}

・Events Run On: "Core 1"
 {Core 1,Core 0}

・Flash Mode: "QIO 80MHz"
 {QIO 80MHz,QIO 120MHz,DIO 80MHz,OPI 80MHz}

◎Flash Size: 16MB(128Mb)
 {4MB(32Mb),8MB(64Mb),16MB(128b)}

・JTAG Adapter: "Disabled"
 {Disabled,Integrated USB JTAG,FTDI Adapter,ESP USB Bridge}

・Arduino Runs On: "Core 1"
 {Core 1,Core 0}

・USB Firmware MSC On Boot: "Disabled"
 {Disabled,Enabled(Requires USB-OTG Mode)}

◎Partition Scheme: "16M Flash(3MB APP/9.9MB FATFS)"
 {SPIFFSを選んでいない 13種類ある APP容量順 単位:MB}
[APP / FATFSの類] ←選択
 16M Flash (3 / 9.9) ←選択
 16M Flash (2 /12.5)
 No OTA (2 / 2 )
 Default 4MB with ffat(1.2/ 1.5)
 No OTA (1 / 3 )

[APP / SPIFFSの類]
 8M with spiffs (3 /1.5 )
 Huge APP (3 No OTA /1 )
 No OTA (2 /2 )
 Minimal SPIFFS (1.9 with OTA/0.19)
 Minimal (1.3 /0.7 )
 Default 4MB with spiffs (1.2 /1.5 )
 No OTA (1 /3 )

[その他]
 RainMaker

◎PSRAM: "OPI PSRAM" (Octal Parallel Interface PSRAM)
 {Disabled,QSPI PSRAM,OPI PSRAM}

・Upload Mode: "UART0/Hardware CDC"
 {UART0/Hardware CDC,USB-OTG CDC(TinyUSB)}

・Upload Speed: "921600"(最高速)
 {115200-921600の5種類}

・USB Mode: "Hardware CDC and JIAG"
 {Hardware CDC and JIAG,USB-OTG(TinyUSB)}

boardのjson

TTGO_TWatch_Library-t-watch-s3\board\LilyGoWatch-S3\LilyGoWatch-S3.json
には、下記設定が書かれています。
memory_type: qio_opi
DARDUINO_USB_MODE = 1
DARDUINO_RUNNING_CORE = 1
DARDUINO_EVENT_RUNNING_CORE = 1
f_cpu: 240000000L (=240M)
f_flash: 80000000L (=80M)
flash_mode: qio
name: LilyGo T-Watch S3 (16M Flash 8M OPI PSRAM)

LilyGoLibでインクルード

TTGO_TWatch_Library-t-watch-s3\src\LilyGoLib.h (Ver2.0.0)
では、以下をincludeしています。
・Arduino.h
・FFat.h // FatFs Flashにファイル保存 SPIFFSは使用しない
・FS.h // ファイルを使用
・Wire.h // GPIO使用
・TFT_eSPI.h // 画面 Ver2.5.43
・lvgl.h // メニュー Ver8.4.0
・XPowersLib.h // 電源 Ver0.2.4
・TouchDrvFT6X36.hpp // タッチ
・SensorBMA423.hpp // 加速度センサー
・SensorPCF8563.hpp // RTC
・SensorDRV2605.hpp // 振動
・RadioLib.h // LoRa SX1262 Ver7.1.0
・LilyGoLib_Warning.h
・driver/i2s.h
・driver/temp_sensor.h or driver/temperature_sensor.h

その他

・ESP8266Audio Ver1.9.7
・IRremoteESP8266 Ver2.8.6
・SensorLib Ver0.1.8

スケッチ解説

icon9.h

画面表示用3x3アイコン用png1枚 現在は、仮画面
1:目覚まし時計
(アラーム時刻)
2:時計+手
(時刻set 手動)
3:袋
(未)
4:時計+wifi
(時刻set wifi)
5:太陽
(未)
6:歯車
(輝度調整)
7:トレイ
(未)
8:風車
(未)
9:戻る
(時計へ戻る)
数字はMcase値。Mcase=0は時計画面
touch3x3関数でタッチ番号Mcase取得

void timeAvailable

NTPで時刻調整時に呼出される

void SetFont()

LovyanGFXで、固定幅ゴシック体28にデフォルト設定

void printBT()

時計画面右上にバッテリー%表示
水色:充電中 "充"も表示
赤色:20%以下
黄色:40%以下
緑色:80%以下
白色:それ以外

void RTCread()

RTCから年月日時分秒を取得
曜日はツェラーの公式で計算

void flashRead()

flashからアラーム設定時間を読込む
sleepから復帰時に、前に設定した値を表示させるため

void flashWrite()

flashにアラーム設定時間を書込む
設定時間 = 時 x 100 + 分

void setWifi()

wifiは、3つのssidを登録し、最も受信強度の強いssidに接続
5秒待っても接続できない時は接続を中止します。

void touch3x3()

均等3x3のタッチ画面を想定し、タッチした場所(1-9)を取得
画面タッチ時に振動します

void appWatch()

RTCデータの時分,アラーム設定時分,残り分を表示
Mcase=0の時実行します
現在時分は12時間表示,アラームは24時間表示
残り分は
  白字:過去のアラーム時分
  0:60分以上前の時
  赤字:5分以内
  緑字:それ以外
  * 日またがりは機能しません
バッテリー残量表示
画面の一部タッチ(81,201)-(159,239)で、振動してメニュー画面へ
分の右に5秒ごとに横線を追加して、動作していることを表示
時計画面20秒(period)でsleep。常時点灯では5H程度しかもたなかったからです。

void appMenu()

3x3アイコンメニュー表示
Mcase=10 の時実行します
3x3タッチ入力touch3x3()へ
Mcaseが変わるまでループ

void appalarmSet()

アラーム時分(24時間表示)の設定
Mcase=1 の時実行します
現在時刻をアラーム時分の初期値とします
時の次に分を設定して時計画面へ
+10,+(1),-(1),-10をタッチし数値を可変
画面のSETタッチ時に振動し、flashにアラーム時分を書込みます

void apptimeSet()

手動で時刻合わせ
Mcase=2 の時実行
RTC時刻を初期値とします
年月日時分を順に設定してRTCに書込後、時計画面へ
秒は30秒とします
+(1),-(1)をタッチして数値を可変
年は2024-2026年とします
時計画面に行く前に振動します

void apptimeAutoSet()

wifiで時刻合わせ
Mcase=4 の時実行します
時差選択用タッチ画面
 
Japan
 
China Spain
(夏)
Portugal
(夏)
Canada
Toronto
(夏)
Thai
予備 予備  
予備
 
現在場所をタッチするとその国のGMTオフセット時間(H)を設定します。国のオフセットを登録しておく必要があります。
 +9:Japan
 +8:China
 +7:タイThai
 +2:Spain(夏)
 +1:Portugal(夏)
 -4:Canada Toronto(夏)
選択後そのオフセット時間でnict(日本),google,appleのNTPから時刻を取得し時計画面へ。もし、時刻が取得できないと再起動します。

void appBright()

Mcase=6 の時実行します
"+","-"のタッチ(x=1~10)で2*x^2-1のバックライトの明るさを変化をさせます。初期値x=5
SETタッチ時に振動し、時計画面へ

void appReturn()

メニュー画面から時計画面へ
Mcase=9 の時実行します

void setup()

起動時,振動(システム上)
各種設定
flashのデータ(alm)を読込む

void loop()

各app*()関数終了後,Mcaseの値により次のapp*()関数を実行

void app3()

Mcase=3 未作成

void app5()

Mcase=5 未作成

void app7()

Mcase=7 未作成

void app8()

Mcase=8 未作成

スケッチ

3つのファイルが必要です。
・icon9.h
 メニューpngファイル。"06.T-watchでpng表示"参照
・LovyanGFX_S3.hpp
 T-Watch S3用LovyanGFX設定ファイル。下記参照
・TWatchS3_105.ino
 T-Watch 2020 V3から変更。下記参照

LovyanGFX_S3.hpp

今のところ動作していますが、不具合があれば修正します

#include <LovyanGFX.hpp>
/// 独自の設定を行うクラスを、LGFX_Deviceから派生して作成します。
class LGFX : public lgfx::LGFX_Device {
  // クラス名は"LGFX"から別の名前に変更しても構いません。
  // AUTODETECTと併用する場合は"LGFX"は使用されているため、LGFX以外の名前に変更してください。
  // また、複数枚のパネルを同時使用する場合もそれぞれに異なる名前を付けてください。
  // クラス名を変更する場合はコンストラクタの名前も併せて同じ名前に変更が必要です。
  // 名前の付け方は自由に決めて構いませんが、設定が増えた場合を想定し、
  // 例えばESP32 DevKit-CでSPI接続のILI9341の設定を行った場合、
  //  LGFX_DevKitC_SPI_ILI9341
  // のような名前にし、ファイル名とクラス名を一致させておくことで、利用時に迷いにくくなります。
  lgfx::Panel_ST7789 _panel_instance;  // ST7789Vだが良いか
  lgfx::Bus_SPI _bus_instance;         // SPIバスのインスタンス
  lgfx::Light_PWM _light_instance;     // バックライト制御のインスタンス
  lgfx::Touch_FT5x06 _touch_instance;  // FT6336等 タッチスクリーンのインスタンス
public:
  // コンストラクタを作成し、ここで各種設定を行います。
  // クラス名を変更した場合はコンストラクタも同じ名前を指定してください。
  LGFX(void) {
    {
      auto cfg = _bus_instance.config();  // バス設定用の構造体を取得します。
      cfg.spi_host = SPI3_HOST;           // 使用するSPIを選択  ESP32-S2,C3:SPI2_HOST or SPI3_HOST / ESP32:VSPI_HOST or HSPI_HOST
      // ※ VSPI_HOST,HSPI_HOSTは非推奨になるため、エラーが出る場合は代わりにSPI2_HOST,SPI3_HOSTを使用
      cfg.spi_mode = 0;                   // SPI通信モードを設定 (0 ~ 3)
      cfg.freq_write = 40000000;          // 送信時のSPIクロック (最大80MHz, 80MHzを整数で割った値に丸められます)
      cfg.freq_read = 16000000;           // 受信時のSPIクロック
      cfg.spi_3wire = true;               // 受信をMOSIピンで行う場合はtrueを設定
      cfg.use_lock = true;                // トランザクションロックを使用する場合はtrueを設定
      cfg.dma_channel = SPI_DMA_CH_AUTO;  // 使用DMAチャンネルを設定(0=DMA不使用,1=1ch(非推奨),2=2ch(非推奨),SPI_DMA_CH_AUTO=自動設定(推奨))
      cfg.pin_sclk = 18;                  // SPIのSCLKピン番号を設定
      cfg.pin_mosi = 13;                  // SPIのMOSIピン番号を設定
      cfg.pin_miso = -1;                  // SPIのMISOピン番号を設定 (-1 = disable)
      cfg.pin_dc = 38;                    // SPIのD/Cピン番号を設定  (-1 = disable)

      _bus_instance.config(cfg);               // 設定値をバスに反映します。
      _panel_instance.setBus(&_bus_instance);  // バスをパネルにセットします。
    }
    {
      auto cfg = _panel_instance.config();  // 表示パネル設定用の構造体を取得
      cfg.pin_cs = 12;                      // CSが接続されているピン番号   (-1 = disable)
      cfg.pin_rst = -1;                     // RSTが接続されているピン番号  (-1 = disable)
      cfg.pin_busy = -1;                    // BUSYが接続されているピン番号 (-1 = disable)

      // ※ 以下の設定値はパネル毎に一般的な初期値が設定されていますので、不明な項目はコメントアウトして試してみてください。
      cfg.panel_width = 240;     // 実際に表示可能な幅
      cfg.panel_height = 240;    // 実際に表示可能な高さ
      cfg.offset_x = 0;          // パネルのX方向オフセット量
      cfg.offset_y = 0;          // パネルのY方向オフセット量
      cfg.offset_rotation = 0;   // 回転方向の値のオフセット 0~7 (4~7は上下反転)
      cfg.dummy_read_pixel = 8;  // ピクセル読出し前のダミーリードのビット数
      cfg.dummy_read_bits = 1;   // ピクセル以外のデータ読出し前のダミーリードのビット数
      cfg.readable = true;       // データ読出しが可能な場合 trueに設定
      cfg.invert = true;        // パネルの明暗が反転してしまう場合 trueに設定
      cfg.rgb_order = false;     // パネルの赤と青が入れ替わってしまう場合 trueに設定
      cfg.dlen_16bit = false;    // 16bitパラレルやSPIでデータ長を16bit単位で送信するパネルの場合 trueに設定
      //cfg.bus_shared = true;     // SDカードとバスを共有している場合 trueに設定(drawJpgFile等でバス制御を行います)

      // 以下はST7735やILI9163のようにピクセル数が可変のドライバで表示がずれる場合にのみ設定してください。
      //    cfg.memory_width     =   240;  // ドライバICがサポートしている最大の幅
      //    cfg.memory_height    =   320;  // ドライバICがサポートしている最大の高さ
      _panel_instance.config(cfg);
    }
    {
      auto cfg = _light_instance.config();  // バックライト設定用の構造体を取得
      cfg.pin_bl = 45;                      // バックライトが接続されているピン番号
      cfg.invert = false;                   // バックライトの輝度を反転させる場合 true
      cfg.freq = 44100;                     // バックライトのPWM周波数
      cfg.pwm_channel = 7;                  // 使用するPWMのチャンネル番号
      _light_instance.config(cfg);
      _panel_instance.setLight(&_light_instance);  // バックライトをパネルにセットします。
    }
    {
      auto cfg = _touch_instance.config();  // タッチスクリーン制御の設定
      cfg.x_min = 0;                        // タッチスクリーンから得られる最小のX値(生の値)
      cfg.x_max = 239;                      // タッチスクリーンから得られる最大のX値(生の値)
      cfg.y_min = 0;                        // タッチスクリーンから得られる最小のY値(生の値)
      cfg.y_max = 239;                      // タッチスクリーンから得られる最大のY値(生の値)
      cfg.pin_int = 16;                     // INTが接続されているピン番号
      cfg.bus_shared = false;               // 画面と共通のバスを使用している場合 trueを設定
      cfg.offset_rotation = 0;              // 表示とタッチの向きのが一致しない場合の調整 0~7の値で設定
      cfg.i2c_port = 1;                     // 使用するI2Cを選択 (0 or 1)
      cfg.i2c_addr = 0x38;                  // I2Cデバイスアドレス番号??????
      cfg.pin_sda = 39;                     // SDAが接続されているピン番号
      cfg.pin_scl = 40;                     // SCLが接続されているピン番号
      cfg.freq = 400000;                    // I2Cクロックを設定
      _touch_instance.config(cfg);
      _panel_instance.setTouch(&_touch_instance);  // タッチスクリーンをパネルにセット
    }
    setPanel(&_panel_instance);  // 使用するパネルをセット
  }
};
LGFX display;  // 準備したクラスのインスタンスを作成

TWatchS3_105.ino

時計画面は20秒で電源offするのでそれ以外にセットして書き込みます。"*****"6点は自分のssid,pwに書換えてください。

// LilyGo T-Watch S3 輝度調整6追加
#include "icon9.h"  // 3x3アイコンpng1枚
// 1:目覚まし時計,2:時計+手,3:袋
// 4:時計+wifi  ,5:太陽   ,6:歯車
// 7:トレイ     ,8:風車   ,9:戻る
#include "LovyanGFX_S3.hpp"  // 自作LovyanGFXヘッダ
static LGFX lcd;             // LGFXのインスタンスを作成
#include <LilyGoLib.h>       // LilyGoLibを使用 LilyGoWatch.hから変更
#include <FFat.h>            // FatFs Flashにデータ保存
/////////////////////////////////////////////////////////////////////////
//#define FORMAT_FFAT true // FFatをフォーマットするのは、最初に実行する時だけ
#define FORMAT_FFAT false  // FFatをフォーマットするのは、最初に実行する時だけ
/////////////////////////////////////////////////////////////////////////
#include <WiFi.h>       // wifiを使用
#include <WiFiMulti.h>  // wifiを複数アクセス
WiFiMulti wMulti;       // WiFiMultiのインスタンスを準備
#include <sntp.h>       // Simple NTPを使用

#define SENSOR_SDA 39  // Touch
#define SENSOR_SCL 40  // Touch
#define SENSOR_IRQ 16  // Touch INT
TouchDrvFT6X36 touch;  // インスタンスを準備

#define RTC_SDA 10  // RTC
#define RTC_SCL 11  // RTC
#define RTC_IRQ 17  // RTC
SensorPCF8563 rtc;  // インスタンスを準備

extern const unsigned char img[];       // 表示png画像1枚
uint8_t Mtouch, Mcase = 0;              // 3x3タッチ番号,メニュー番号
int16_t x, y;                           // タッチ位置変数
int8_t mmonth, dday, week, hh, mm, ss;  // 月日週時分秒等の変数 0-255
int16_t yyear;                          // 年は16bit整数 0-65535
int8_t almH, almM;                      // アラーム時分
char dayWeek[7][4] = { "土", "日", "月", "火",
                        "水", "木", "金" };  // 曜日
uint8_t timeSetNo = 1;                      // 1:年,2:月,3:日,4:時,4:分
uint8_t touchNo;                            // 1:+1,2:SET,3:-1,10:+10,30:-10
uint32_t Stime, Ctime, period = 20 * 1000;  // 開始時間,現在時間,off時間(mS)
String flashF = "/flashData.txt";           // flashに保存するtxtファイル
const char *ntp1 = "ntp.nict.jp";           // 情報通信研究機構のntp
const char *ntp2 = "time.google.com";       // googleのntp
const char *ntp3 = "time.apple.com";        // appleのntp
const char *ssid1 = "*****";                // 部屋のwifiのssid
const char *pass1 = "*****";                // そのパスワード
const char *ssid2 = "*****";                // 自分の携帯1のssid
const char *pass2 = "*****";                // そのパスワード
const char *ssid3 = "*****";                // 自分の携帯2のssid
const char *pass3 = "*****";                // そのパスワード
bool sensorIRQ = false;                     // 加速度センサー
bool pmuIRQ = false;                        // 電源ボタン
uint8_t waveT = 100;                        // 振動パターン1-123
//RTC_DATA_ATTR int bootCount = 0; // sleepしても値を保持する変数
bool pmu_flag = false;  // 割込フラグを0に
/*
const char *get_wakeup_reason() {
  switch (esp_sleep_get_wakeup_cause()) {
    case ESP_SLEEP_WAKEUP_EXT0: return ("Wakeup caused by external signal using RTC_IO");
    case ESP_SLEEP_WAKEUP_EXT1: return ("Wakeup caused by external signal using RTC_CNTL");
    case ESP_SLEEP_WAKEUP_TIMER: return ("Wakeup caused by timer");
    case ESP_SLEEP_WAKEUP_TOUCHPAD: return ("Wakeup caused by touchpad");
    case ESP_SLEEP_WAKEUP_ULP: return ("Wakeup caused by ULP program");
    default: return ("Wakeup was not caused");
  }
}

void setFlag() {     // 加速度センサー割込み
  sensorIRQ = true;  // 割込む
}
void setFlag2() {
  pmu_flag = true;
}
*/

void timeAvailable(struct timeval *t) {  // コールバック関数 NTPで時刻調整時に呼出される
  watch.hwClockWrite();                  // 同期時間をハードウェアに書込む
}


void SetFont() {                            // Font等初期化関数
  watch.begin();                            // 本体を初期化し"Hello T-Watch"を表示
  lcd.init();                               // LCDの初期化
  lcd.setRotation(2);                       // 画面回転 0-3
  lcd.setBrightness(10);                    // バックライト輝度 0-255(実際は256通りの輝度ではない)1でも薄く点灯
  lcd.fillScreen(TFT_BLACK);                // 全画面黒
  lcd.setFont(&fonts::lgfxJapanGothic_28);  // 固定幅ゴシック体 8,12,16,20,24,28,32,36,40
  lcd.setTextColor(TFT_GREEN, TFT_BLACK);   // (緑字,黒地)
  lcd.setTextSize(1);                       // 文字サイズの倍数 1-7
  lcd.setTextWrap(true, true);              // 自動折返し(横,縦)
}


void printBT() {                                // バッテリー残量表示関数
  int8_t per = watch.getBatteryPercent();       // バッテリー%取得
  if (per < 0) per = 0;                         // 最低0%とする
  if (per > 100) per = 100;                     // 最高100%とする
  if (watch.isCharging()) {                     // 充電中の時
    lcd.setTextColor(TFT_CYAN, TFT_BLACK);      // (水字,黒地)
    lcd.print("充");                            // 画面表示
  } else {                                      // 充電中でない時
    lcd.print(" ");                            // 画面表示
    if (per <= 20) {                            // 0-20%時
      lcd.setTextColor(TFT_RED, TFT_BLACK);     // (赤字,黒地)
    } else if (per <= 40) {                     // 21-40%
      lcd.setTextColor(TFT_YELLOW, TFT_BLACK);  // (黄字,黒地)
    } else if (per <= 80) {                     // 41-80%時
      lcd.setTextColor(TFT_GREEN, TFT_BLACK);   // (緑字,黒地)
    } else {                                    // 81-100%時
      lcd.setTextColor(TFT_WHITE, TFT_BLACK);   // (白字,黒地)
    }
  }
  lcd.printf("%03d%%", per);  // 電池%表示
  lcd.setTextSize(1);         // 文字サイズを戻す
}


void RTCread() {       // RTCの時刻を読込む
  struct tm timeinfo;  // 時間Cライブラリ構造体を取得
  watch.getDateTime(&timeinfo);
  yyear = timeinfo.tm_year + 1900;  // 年
  mmonth = timeinfo.tm_mon + 1;     // 月
  dday = timeinfo.tm_mday;          // 日
  hh = timeinfo.tm_hour;            // 時
  mm = timeinfo.tm_min;             // 分
  ss = timeinfo.tm_sec;             // 秒

  uint16_t yearA = yyear;  // ツェラーの公式 年月日から曜日weekを求める
  uint8_t monthA = mmonth;
  if (monthA < 3) {
    monthA += 12;
    yearA--;
  }
  uint16_t year1 = yearA / 100;
  uint16_t year2 = yearA % 100;
  week = (dday + (int)(26 * (monthA + 1) / 10) + year2 + (int)(year2 / 4)
          + (5 * year1) + (int)(year1 / 4))
          % 7;
}


void flashRead() {                            // flashのデータ読込関数
  File flashF = FFat.open("/flashData.txt");  // ファイルを読込モードで開く
  String str;
  if (flashF) {                    // ファイルが開けば
    while (flashF.available()) {   // 読み込み可能なバイトがあれば/// SDクラス
      str += char(flashF.read());  // 追加
    }
    flashF.close();             // ファイルをclose
    int16_t alm = str.toInt();  // 整数化 HHMM
    almH = alm / 100;           // アラーム設定時
    almM = alm % 100;           // アラーム設定分
  } else {                      // ファイルが開かないなら
    almH = 0;                   // 0にする
    almM = 0;                   // 0にする
  }
}


void flashWrite() {                                       // flashにデータ書込関数
  File flashF = FFat.open("/flashData.txt", FILE_WRITE);  // ファイルを書込モードで開く
  if (!flashF) {                                          // ファイルが開けなければ
    Serial.println("Flash 書込 Error");                   // 表示
    return;                                               // 戻る
  }                                                       // ファイルが開けば
  flashF.print(String(almH * 100 + almM));                // almMを書込む
  flashF.close();                                         // ファイルをclose
}


void setWifi() {                                              // wifiの設定関数
  lcd.setTextColor(TFT_WHITE, TFT_BLACK);                     // (字,地)
  lcd.fillScreen(TFT_BLACK);                                  // 全画面黒 改頁
  lcd.setCursor(0, 0);                                        // カーソル位置
  lcd.println("時刻のwifi設定");                              // 表示
  wMulti.addAP(ssid1, pass1);                                 // wifiを初期化し設定1
  wMulti.addAP(ssid2, pass2);                                 // wifiを初期化し設定2
  wMulti.addAP(ssid3, pass3);                                 // wifiを初期化し設定3
  lcd.print("wifiに接続開始");                                // 表示
  uint8_t maxWifi = 10, nWifi = 0;                             // wifi最大試行回数,試行回数
  while (WiFi.status() != WL_CONNECTED && nWifi < maxWifi) {  // 接続完or maxWifi回まで繰返す
    wMulti.run();                                             // WiFiMultiを実行
    lcd.print(".");                                           // 表示
    delay(500);                                               // 接続実行後は少し待つ
    nWifi++;                                                  // 試行回数に1加算
  }
  lcd.println();                        // 改行
  if (WiFi.status() == WL_CONNECTED) {  // 接続出来たら
    lcd.print(WiFi.SSID());             // ssidを表示
    lcd.println("に接続完");            // 表示
  } else {
    lcd.println("接続できません");  // 表示
  }
}


void touch3x3() {                             // 3x3タッチ入力関数
  Mtouch = 10;                                // タッチ変数初期値
  int16_t touchX[1], touchY[1];               // タッチx,y変数
  if (digitalRead(SENSOR_IRQ) == LOW) {       // タッチしたら
    watch.getPoint(touchX, touchY, 1);        // 1点マルチ
    x = touchX[0];                            // タッチx値
    y = touchY[0];                            // タッチy値
    watch.setWaveform(0, waveT);              // エフェクト効果値を設定
    watch.run();                              // エフェクトを再生
    while (digitalRead(SENSOR_IRQ) == LOW) {  // 指を離すまで待つ
      delay(100);                             // 0.1秒待つ
    }
    if (y < 80) {            // 1行目なら
      if (x < 80) {          // 1行1列目なら
        Mtouch = 1;          // アイコン1をタッチ
      } else if (x < 160) {  // 1行2列目なら
        Mtouch = 2;          // アイコン2をタッチ
      } else {               // 1行3列目なら
        Mtouch = 3;          // アイコン3をタッチ
      }
    } else if (y < 160) {    // 2行目なら
      if (x < 80) {          // 2行1列目なら
        Mtouch = 4;          // アイコン4をタッチ
      } else if (x < 160) {  // 2行2列目なら
        Mtouch = 5;          // アイコン5をタッチ
      } else {               // 2行3列目なら
        Mtouch = 6;          // アイコン6をタッチ
      }
    } else {                 // 3行目なら
      if (x < 80) {          // 3行1列目なら
        Mtouch = 7;          // アイコン7をタッチ
      } else if (x < 160) {  // 3行2列目なら
        Mtouch = 8;          // アイコン8をタッチ
      } else {               // 3行3列目なら
        Mtouch = 9;          // アイコン9をタッチ
      }
    }
  }
}


//-------------------------------------------------// 0 時計
void appWatch() {             // Mcase=0 時計表示
  Stime = millis();           // 開始時間(mS)
  lcd.fillScreen(TFT_BLACK);  // 全画面黒
  uint8_t sleepCount = 20;    // 0でsleep
  do {
    RTCread();                                // RTCデータを読込む
    lcd.setTextColor(TFT_GREEN, TFT_BLACK);   // (字,地)
    lcd.setFont(&fonts::lgfxJapanGothic_28);  // 固定幅ゴシック体 8,12,16,20,24,28,32,36,40
    lcd.setTextSize(1, 1.8);                  // 文字サイズ(倍)
    lcd.setCursor(0, 66 * 0 + 43 + 5);        // カーソル位置 [AM/PM]
    uint8_t hhh;                              // hhの12時間用
    if (hh > 11) {                            // PM時 修正12→11
      hhh = hh - 12;                          // 12時間表示
      lcd.print("PM");                        // 表示
    } else {                                  // AM時
      hhh = hh;                               // そのまま
      lcd.print("AM");                        // 表示
    }
    lcd.setTextSize(1);  // 文字サイズ(倍)

    lcd.setCursor(60, 66 * 0 + 43);    // カーソル位置
    lcd.setFont(&fonts::Font7);        // Font 7セグ
    lcd.printf("%02d:%02d", hhh, mm);  // 時:分を表示

    lcd.setCursor(60, 66 * 1 + 43);       // カーソル位置
    lcd.printf("%02d:%02d", almH, almM);  // アラーム時:分を表示

    lcd.setFont(&fonts::lgfxJapanGothic_28);  // 固定幅ゴシック体 8,12,16,20,24,28,32,36,40
    lcd.setTextSize(2.3, 2);                  // 文字サイズ 倍(横,縦)
    lcd.setCursor(70, 66 * 2 + 43 - 5);       // カーソル位置
    int16_t mmm = almH * 60 + almM
                  - hh * 60 - mm;              // 残り分計算
    if (mmm < -60) mmm = 0;                    // 60分以上前は0とする
    if (mmm <= 0) {                            // 過去の設定時刻なら
      lcd.setTextColor(TFT_WHITE, TFT_BLACK);  // (白字,地)
    } else if (mmm <= 5) {                     // 設定時刻が5分以内なら
      lcd.setTextColor(TFT_RED, TFT_BLACK);    // (赤字,地)
    }
    lcd.printf("%4d", mmm);                  // 残り分を表示
    lcd.setTextColor(TFT_GREEN, TFT_BLACK);  // (字,地)

    lcd.setFont(&fonts::lgfxJapanGothic_28);  // 固定幅ゴシック体 8,12,16,20,24,28,32,36,40
    lcd.setTextSize(0.7, 1.6);                // 文字サイズ(倍)
    lcd.setCursor(0, 66 * 1 + 43 + 5);        // カーソル位置 [アラーム時:分]
    lcd.print("設定");                        // 表示

    lcd.setCursor(0, 66 * 2 + 43 + 5);  // カーソル位置 [残り分]
    lcd.print("あと");                  // 表示

    lcd.setCursor(200, 66 * 2 + 43);  // カーソル位置 [残り分] 200
    lcd.print("分");                  // 表示
    lcd.setTextSize(1);               // 文字サイズ戻す

    lcd.setCursor(0, 2);                     // カーソル位置
    lcd.setTextSize(0.8);                    // 文字サイズ(倍)
    lcd.setTextColor(TFT_WHITE, TFT_BLACK);  // (字,地)
    lcd.printf("%04d/%02d/%02d(%s) ",
                yyear, mmonth, dday, dayWeek[week]);  // 年月日表示
    printBT();                                       // バッテリー残量表示関数へ

    int16_t touchX[1], touchY[1];
    if (digitalRead(SENSOR_IRQ) == LOW) {
      watch.getPoint(touchX, touchY, 1);  // タッチ点取得
      x = touchX[0];
      y = touchY[0];
      while (digitalRead(SENSOR_IRQ) == LOW) {  // 指を離すまで待つ
        delay(100);                             // 0.1秒待つ
      }
      if (y > 200 && x > 80 && x < 160) {      // y=201-239 かつ x=81-159なら
        watch.setWaveform(0, random(1, 123));  // エフェクト効果値を設定
        watch.run();                           // エフェクトを再生
        Mcase = 10;                            // メニューへ
      }
    }
    int16_t x0 = 210, y0 = 85, dy = 4;                         // 一番下の横線の書始めx,yと線間の刻み
    if (ss < 5) {                                              // 5秒未満なら 5秒ごとに横線を引く
      lcd.fillRect(x0, y0 - dy * 11, 30, dy * 11, TFT_BLACK);  // 四角塗潰(x,y,w,h,色)
      lcd.drawFastHLine(x0, y0 - dy * 0, 30, TFT_DARKGREY);    // 水平線(X,y,W,色)
    } else if (ss < 10) {                                      // 5秒以上10秒未満なら
      lcd.drawFastHLine(x0, y0 - dy * 1, 30, TFT_DARKGREY);    // 水平線(X,y,W,色)
    } else if (ss < 15) {                                      // 10秒以上15秒未満なら
      lcd.drawFastHLine(x0, y0 - dy * 2, 30, TFT_BROWN);       // 水平線(X,y,W,色)
    } else if (ss < 20) {                                      // 15秒以上20秒未満なら
      lcd.drawFastHLine(x0, y0 - dy * 3, 30, TFT_BROWN);       // 水平線(X,y,W,色)
    } else if (ss < 25) {                                      // 20秒以上25秒未満なら
      lcd.drawFastHLine(x0, y0 - dy * 4, 30, TFT_RED);         // 水平線(X,y,W,色)
    } else if (ss < 30) {                                      // 25秒以上30秒未満なら
      lcd.drawFastHLine(x0, y0 - dy * 5, 30, TFT_RED);         // 水平線(X,y,W,色)
    } else if (ss < 35) {                                      // 30秒以上35秒未満なら
      lcd.drawFastHLine(x0, y0 - dy * 6, 30, TFT_ORANGE);      // 水平線(X,y,W,色)
    } else if (ss < 40) {                                      // 35秒以上40秒未満なら
      lcd.drawFastHLine(x0, y0 - dy * 7, 30, TFT_ORANGE);      // 水平線(X,y,W,色)
    } else if (ss < 45) {                                      // 40秒以上45秒未満なら
      lcd.drawFastHLine(x0, y0 - dy * 8, 30, TFT_YELLOW);      // 水平線(X,y,W,色)
    } else if (ss < 50) {                                      // 45秒以上50秒未満なら
      lcd.drawFastHLine(x0, y0 - dy * 9, 30, TFT_YELLOW);      // 水平線(X,y,W,色)
    } else if (ss < 55) {                                      // 50秒以上55秒未満なら
      lcd.drawFastHLine(x0, y0 - dy * 10, 30, TFT_GREEN);      // 水平線(X,y,W,色)
    } else {                                                   // 55秒以上なら
      lcd.drawFastHLine(x0, y0 - dy * 11, 30, TFT_GREEN);      // 水平線(X,y,W,色)
    }
    Ctime = millis();                     // 現在時間
    if (Ctime - Stime >= period) {        // 判定時間(20秒)が経過したら
      watch.clearPMU();                   // PMU割込トリガー後、割込状態をクリア、でなければ次の割込みはトリガされない
      watch.setSleepMode(SENSOR_WAKEUP);  //2 復帰はダブルクリック
      watch.sleep();                      // sleepする
      Serial.println("ここは表示されません");
    }
  } while (Mcase == 0);  // Mcaseが変わるまでループ
}


//-------------------------------------------------// 10 メニュー
void appMenu() {                                  // Mcase=10 アイコンメニュー
  lcd.drawPng((std::uint8_t *)img, 34644, 7, 7);  // png表示 (配列名,サイズ,x,y)
  delay(1);                                       // 1mS待つ
  do {
    touch3x3();           // 3x3タッチ入力関数へ
    Mcase = Mtouch;       // アイコン番号へ
  } while (Mcase == 10);  // Mcaseが変わるまでループ
}


//-------------------------------------------------// 1 アラーム設定
void appalarmSet() {                       // Mcase=1 アラーム時刻設定
  RTCread();                               // RTCデータを読込む アラームの初期値
  lcd.setCursor(40, 0);                    // カーソル位置
  lcd.setTextColor(TFT_GREEN, TFT_BLACK);  // (字,地)
  lcd.setTextSize(0.9);                    // 文字サイズの倍数
  lcd.print("アラーム設定");               // 表示 タイトル
  lcd.setTextSize(1.6);                    // 文字サイズの倍数
  lcd.setCursor(20, 85);                   // カーソル位置 タッチ表示用
  lcd.print("+10  -10");                 // 表示
  lcd.setCursor(30, 165);                  // カーソル位置 タッチ表示用
  lcd.print("+ SET -");                  // 表示
  lcd.setTextSize(1);                      // 文字サイズの倍数
  timeSetNo = 4;                           // 時から始める

  do {
    delay(100);                              // 0.1秒待つ
    lcd.setTextSize(1.6);                    // 文字サイズの倍数
    lcd.setCursor(60, 30);                   // カーソル位置 時:分
    if (timeSetNo == 4) {                    // 時の変更なら
      lcd.setTextColor(TFT_RED, TFT_BLACK);  // (字,地)
    } else {
      lcd.setTextColor(TFT_WHITE, TFT_BLACK);  // (字,地)
    }
    lcd.printf("%02d", hh);                  // 表示 時
    lcd.setTextColor(TFT_WHITE, TFT_BLACK);  // (字,地)
    lcd.print(":");                          // 表示
    if (timeSetNo == 5) {                    // 分の変更なら
      lcd.setTextColor(TFT_RED, TFT_BLACK);  // (字,地)
    } else {
      lcd.setTextColor(TFT_WHITE, TFT_BLACK);  // (字,地)
    }
    lcd.printf("%02d", mm);  // 表示 分
    lcd.setTextSize(1);      // 文字サイズを戻す

    int16_t touchX[1], touchY[1];
    if (digitalRead(SENSOR_IRQ) == LOW) {
      watch.getPoint(touchX, touchY, 1);
      x = touchX[0];
      y = touchY[0];
      while (digitalRead(SENSOR_IRQ) == LOW) {  // 指を離すまで待つ
        delay(100);                             // 0.1秒待つ
      }
      delay(100);        // 0.1秒待つ
      if (y < 150) {     // タッチ上段なら
        if (x < 120) {   // 上段左側なら
          touchNo = 10;  // +10
        } else {         // 上段右側なら
          touchNo = 30;  // -10
        }
      } else {                 // タッチ下段なら
        if (x < 80) {          // 下段左側なら
          touchNo = 1;         // +1
        } else if (x < 160) {  // 下段中央なら
          touchNo = 2;         // SET
        } else {               // 下段右側なら
          touchNo = 3;         // -1
        }
      }
    }
    switch (timeSetNo) {                // する動作の場所
      case 4:                           // 時なら
        if (touchNo == 1) hh++;         // +は1up
        if (touchNo == 3) hh--;         // -は1down
        if (touchNo == 10) hh += 10;    // +10は10up
        if (touchNo == 30) hh -= 10;    // -10は10down
        if (hh > 23) hh = 23;           // ~23時とする
        if (hh < 0) hh = 0;             // 0時~とする
        if (touchNo == 2) timeSetNo++;  // SETは次の設定 分へ
        break;
      case 5:                           // 分なら
        if (touchNo == 1) mm++;         // +は1up
        if (touchNo == 3) mm--;         // -は1down
        if (touchNo == 10) mm += 10;    // +10は10up
        if (touchNo == 30) mm -= 10;    // -10は10down
        if (mm > 59) mm = 59;           // ~59分とする
        if (mm < 0) mm = 0;             // 0分~とする
        if (touchNo == 2) {             // SETなら
          almH = hh;                    // アラーム時
          almM = mm;                    // アラーム分
          flashWrite();                 // flashにデータ書込関数へ
          Mcase = 0;                    // 時計画面へ
          watch.setWaveform(0, waveT);  // エフェクト効果値を設定
          watch.run();                  // エフェクトを再生
        }
        break;
      default:  // それ以外
        break;
    }
    touchNo = 0;         // 勝手にループ動作しないように
  } while (Mcase == 1);  // Mcaseが変わるまでループ
}


//-------------------------------------------------// 2 時刻手動設定
void apptimeSet() {                        // Mcase=2 時刻合わせ 手動
  RTCread();                               // RTCデータを読込む
  if (yyear < 2024) yyear = 2024;          // 年の最低値
  lcd.setCursor(70, 0);                    // カーソル位置
  lcd.setTextColor(TFT_GREEN, TFT_BLACK);  // (字,地)
  lcd.setTextSize(0.9);                    // 文字サイズの倍数
  lcd.print("時刻設定");                   // 表示タイトル
  lcd.setTextSize(1.6);                    // 文字サイズの倍数
  lcd.setCursor(20, 165);                  // カーソル位置 タッチ表示用
  lcd.print("+ SET -");                  // 表示
  lcd.setTextSize(1);                      // 文字サイズの倍数
  timeSetNo = 1;                           // 年から始める
  do {
    delay(100);                              // 100mS待つ
    lcd.setTextSize(1.6);                    // 文字サイズの倍数
    lcd.setCursor(10, 40);                   // カーソル位置 年月日
    if (timeSetNo == 1) {                    // 変更場所が年なら
      lcd.setTextColor(TFT_RED, TFT_BLACK);  // (字,地)
    } else {
      lcd.setTextColor(TFT_WHITE, TFT_BLACK);  // (字,地)
    }
    lcd.printf("%4d", yyear);                // 表示 年
    lcd.setTextColor(TFT_WHITE, TFT_BLACK);  // (字,地)
    lcd.print("/");                          // 表示
    if (timeSetNo == 2) {                    // 月なら
      lcd.setTextColor(TFT_RED, TFT_BLACK);  // (字,地)
    } else {
      lcd.setTextColor(TFT_WHITE, TFT_BLACK);  // (字,地)
    }
    lcd.printf("%02d", mmonth);              // 表示 月
    lcd.setTextColor(TFT_WHITE, TFT_BLACK);  // (字,地)
    lcd.print("/");                          // 表示
    if (timeSetNo == 3) {                    // 日なら
      lcd.setTextColor(TFT_RED, TFT_BLACK);  // (字,地)
    } else {
      lcd.setTextColor(TFT_WHITE, TFT_BLACK);  // (字,地)
    }
    lcd.printf("%02d", dday);                // 表示 日
    lcd.setCursor(60, 90);                   // カーソル位置 時分
    if (timeSetNo == 4) {                    // 時なら
      lcd.setTextColor(TFT_RED, TFT_BLACK);  // (字,地)
    } else {
      lcd.setTextColor(TFT_WHITE, TFT_BLACK);  // (字,地)
    }
    lcd.printf("%02d", hh);                  // 表示 時
    lcd.setTextColor(TFT_WHITE, TFT_BLACK);  // (字,地)
    lcd.print(":");                          // 表示
    if (timeSetNo == 5) {                    // 分なら
      lcd.setTextColor(TFT_RED, TFT_BLACK);  // (字,地)
    } else {
      lcd.setTextColor(TFT_WHITE, TFT_BLACK);  // (字,地)
    }
    lcd.printf("%02d", mm);  // 表示 分
    lcd.setTextSize(1);      // 文字サイズを戻す
    int16_t touchX[1], touchY[1];
    if (digitalRead(SENSOR_IRQ) == LOW) {  // センサー割込pinがL(割込み)なら
      watch.getPoint(touchX, touchY, 1);
      x = touchX[0];
      y = touchY[0];
      while (digitalRead(SENSOR_IRQ) == LOW) {  // 指を離すまで待つ
        delay(100);                             // 0.1秒待つ
      }
      delay(100);            // 100mS待つ
      if (x < 80) {          // 左側なら
        touchNo = 1;         // +
      } else if (x < 160) {  // 中央なら
        touchNo = 2;         // SET
      } else {               // 右側なら
        touchNo = 3;         // -
      }
      switch (timeSetNo) {                   // する動作の場所
        case 1:                              // 年で
          if (touchNo == 1) {                // +なら
            yyear++;                         // 1up
            if (yyear > 2026) yyear = 2026;  // ~2026年とする
          } else if (touchNo == 3) {         // -なら
            yyear--;                         // 1down
            if (yyear < 2024) yyear = 2024;  // 2024年~とする
          } else {                           // SETなら
            timeSetNo++;                     // 次の設定 月へ
          }
          break;
        case 2:                            // 月で
          if (touchNo == 1) {              // +なら
            mmonth++;                      // 1up
            if (mmonth > 12) mmonth = 12;  // ~12月とする
          } else if (touchNo == 3) {       // -なら
            mmonth--;                      // 1down
            if (mmonth < 1) mmonth = 1;    // 1月~とする
          } else {                         // SETなら
            timeSetNo++;                   // 次の設定 日へ
          }
          break;
        case 3:                        // 日で
          if (touchNo == 1) {          // +なら
            dday++;                    // 1up
            if (dday > 31) dday = 31;  // ~31日とする
          } else if (touchNo == 3) {   // -なら
            dday--;                    // 1down
            if (dday < 1) dday = 1;    // 1日~とする
          } else {                     // SETなら
            timeSetNo++;               // 次の設定 時へ
          }
          break;
        case 4:                       // 時で
          if (touchNo == 1) {         // +なら
            hh++;                     // 1up
            if (hh > 23) hh = 23;     // ~23時とする
          } else if (touchNo == 3) {  // -なら
            hh--;                     // 1down
            if (hh < 0) hh = 0;       // 0時~とする
          } else {                    // SETなら
            timeSetNo++;              // 次の設定 分へ
            //Serial.print("mmへ");
          }
          break;
        case 5:                       // 分で
          if (touchNo == 1) {         // +なら
            mm++;                     // 1up
            if (mm > 59) mm = 59;     // ~59分とする
          } else if (touchNo == 3) {  // -なら
            mm--;                     // 1down
            if (mm < 0) mm = 0;       // 0分~とする
          } else {                    // SETなら
            struct tm timeinfo;
            timeinfo.tm_year = yyear - 1900;  // 年
            timeinfo.tm_mon = mmonth - 1;     // 月
            timeinfo.tm_mday = dday;          // 日
            timeinfo.tm_hour = hh;            // 時
            timeinfo.tm_min = mm;             // 分
            watch.setDateTime(timeinfo.tm_year + 1900, timeinfo.tm_mon + 1,
                              timeinfo.tm_mday, timeinfo.tm_hour, timeinfo.tm_min, 30);
            watch.getDateTime(&timeinfo);
            yyear = timeinfo.tm_year + 1900;  // 年
            mmonth = timeinfo.tm_mon + 1;     // 月
            dday = timeinfo.tm_mday;          // 日
            // dayWeek[timeinfo.tm_wday];// 曜日
            hh = timeinfo.tm_hour;  // 時
            mm = timeinfo.tm_min;   // 分
            ss = timeinfo.tm_sec;   // 秒
            Serial.printf("手入力時刻再読込 %d/%d/%d %d:%d\n", yyear, mmonth, dday, hh, mm);
            Mcase = 0;                    // 時計画面へ
            watch.setWaveform(0, waveT);  // エフェクト効果値を設定
            watch.run();                  // エフェクトを再生
          }
          break;
        default:  // それ以外
          break;
      }
    }
  } while (Mcase == 2);  // Mcaseが変わるまでループ
}


//-------------------------------------------------// 3 未作成
void app3() {                              // Mcase=3 未作成
  lcd.setTextColor(TFT_WHITE, TFT_BLACK);  // (字,地)
  lcd.print("menu 3");                     // 表示
  delay(1000);                             // 1秒待つ
  do {
    delay(100);  // 0.1秒待つ
    int16_t touchX[1], touchY[1];
    if (digitalRead(SENSOR_IRQ) == LOW) {
      watch.setWaveform(0, waveT);  // エフェクト効果値を設定
      watch.run();                  // エフェクトを再生
      Mcase = 10;                   // メニューへ
    }
    while (digitalRead(SENSOR_IRQ) == LOW) {  // 指を離すまで待つ
      delay(100);                             // 0.1秒待つ
    }
  } while (Mcase == 3);  // Mcaseが変わるまでループ
}


//-------------------------------------------------// 4 wifiで時刻
void apptimeAutoSet() {             // Mcase=4 wifiで時刻合わせ
  lcd.fillScreen(TFT_BLACK);        // 全画面黒 改頁
  uint8_t i, j, di1 = 75, di2 = 7;  // di1:枠の大きさ,di2:枠間
  for (i = 0; i < 3; i++) {         // x方向3回
    for (j = 0; j < 3; j++) {       // y方向3回
      lcd.drawRoundRect((di1 + di2) * i, (di1 + di2) * j,
                        di1, di1, 5, TFT_GREEN);  // 角丸枠(x,y,w,h,r,色)
    }
  }
  lcd.setFont(&fonts::lgfxJapanGothic_20);  // 固定幅ゴシック体 8,12,16,20,24,28,32,36,40
  lcd.setTextColor(TFT_CYAN, TFT_BLACK);    // (字,地)

  lcd.setCursor(0 + 12, 27);  // カーソル位置
  lcd.print("Japan");         // 表示 1行1列目

  lcd.setCursor(82 + 12, 27);  // カーソル位置
  lcd.print("China");          // 表示 1行2列目

  lcd.setCursor(164 + 12, 27 - 14);  // カーソル位置
  lcd.print("Spain");                // 表示 1行3列目上
  lcd.setCursor(164 + 20, 27 + 14);  // カーソル位置
  lcd.print("(夏)");                 // 表示 1行3列目下

  lcd.setCursor(0 + 5, 82 + 27 - 10);       // カーソル位置
  lcd.setFont(&fonts::lgfxJapanGothic_16);  // 固定幅ゴシック体 8,12,16,20,24,28,32,36,40
  lcd.print("Portugal");                    // 表示 2行1列目上 20でサイズ=0.9は字が消える
  lcd.setFont(&fonts::lgfxJapanGothic_20);  // 固定幅ゴシック体 8,12,16,20,24,28,32,36,40
  lcd.setCursor(0 + 20, 82 + 27 + 14);      // カーソル位置
  lcd.print("(夏)");                        // 表示 2行1列目下

  lcd.setCursor(82 + 7, 82 + 27 - 20);   // カーソル位置
  lcd.print("Canada");                   // 表示 2行2列目上
  lcd.setCursor(82 + 2, 82 + 27);        // カーソル位置
  lcd.print("Toronto");                  // 表示 2行2列目中
  lcd.setCursor(82 + 20, 82 + 27 + 20);  // カーソル位置
  lcd.print("(夏)");                     // 表示 2行2列目下
  lcd.setCursor(164 + 17, 82 + 27);      // カーソル位置
  lcd.print("Thai");                     // 表示 2行3列目

  lcd.setCursor(0 + 17, 164 + 27);  // カーソル位置
  lcd.print("予備");                // 表示 3行1列目

  lcd.setCursor(82 + 17, 164 + 27);  // カーソル位置
  lcd.print("予備");                 // 表示 3行2列目

  lcd.setCursor(164 + 17, 164 + 27);        // カーソル位置
  lcd.print("予備");                        // 表示 3行3列目
  lcd.setFont(&fonts::lgfxJapanGothic_28);  // 固定幅ゴシック体 8,12,16,20,24,28,32,36,40
  do {
    touch3x3();            // 3x3タッチ入力関数へ
  } while (Mtouch == 10);  // Mtouchが変わるまでループ

  int8_t gmt;        // GMTオフセット
  switch (Mtouch) {  // タッチした場所のGMTオフセットを得る
    case 1:          // Japan
      gmt = 9;       // +9H
      break;
    case 2:     // China
      gmt = 8;  // +8H
      break;
    case 3:     // Spain(夏)
      gmt = 2;  // +2H
      break;
    case 4:     // Portugal(夏)
      gmt = 1;  // +1H
      break;
    case 5:      // Canada Toronto(夏)
      gmt = -4;  // -4H
      break;
    case 6:      // タイ
      gmt = -2;  // -2H
      break;
    case 7:     // 空き
    case 8:     // 空き
    case 9:     // 空き
      gmt = 9;  // +9H Japanとする
      break;
  }

  lcd.setTextSize(0.8);                                       // 文字サイズの倍数
  setWifi();                                                  // wifi設定関数へ
  lcd.print("NTPに接続中");                                   // 表示
  configTime(gmt * 60 * 60, 0, ntp1, ntp2, ntp3);             // (GMTとの差(秒),夏時間(秒),NTPサーバ)
  while (sntp_get_sync_status() == SNTP_SYNC_STATUS_RESET) {  // RTC時刻がNTP時刻に合うまで待機
    lcd.print(".");                                           // 進捗表示
    delay(1000);
  }
  lcd.println();
  struct tm timeInfo;                            // 構造体タグ名tmを変数timeinfoとする
  if (!getLocalTime(&timeInfo)) {                // 時刻を取得できなかったら
    lcd.println("時間取得失敗\n3秒後に再起動");  // 表示
    delay(3000);                                 // 3秒待つ
    esp_restart();                               // ESP32をリスタート
    while (1) {};                                // ここへは来ない
  }
  lcd.print("wifiを切断  ");  // 表示
  WiFi.disconnect(true);      // WiFiネットワークから切断

  yyear = timeInfo.tm_year + 1900;  // 年
  mmonth = timeInfo.tm_mon + 1;     // 月
  dday = timeInfo.tm_mday;          // 日
  hh = timeInfo.tm_hour;            // 時
  mm = timeInfo.tm_min;             // 分
  ss = timeInfo.tm_sec;             // 秒
  lcd.println("同期完");            // 表示
  lcd.printf("%d/%d/%d %d:%d:%d\n",
              yyear, mmonth, dday, hh, mm, ss);  // 年月日時:分:秒表示
  lcd.setTextSize(1);                           // 文字サイズの倍数
  delay(3000);                                  // 表示確認のため3秒待つ
  Mcase = 0;                                    // 時計画面へ
}


//-------------------------------------------------// 5
void app5() {                              // Mcase=5 未作成
  lcd.setTextColor(TFT_WHITE, TFT_BLACK);  // (字,地)
  lcd.print("menu 5");                     // 表示
  delay(1000);                             // 1秒待つ
  do {
    delay(100);  // 0.1秒待つ
    int16_t touchX[1], touchY[1];
    if (digitalRead(SENSOR_IRQ) == LOW) {
      watch.setWaveform(0, waveT);  // エフェクト効果値を設定
      watch.run();                  // エフェクトを再生
      Mcase = 10;                   // メニューへ
    }
    while (digitalRead(SENSOR_IRQ) == LOW) {  // 指を離すまで待つ
      delay(100);                             // 0.1秒待つ
    }
  } while (Mcase == 5);  // Mcaseが変わるまでループ
}


//-------------------------------------------------// 6 バックライト輝度
void appBright() {                         // Mcase=6 バックライト輝度調整
  lcd.setCursor(20, 0);                    // カーソル位置
  lcd.setTextColor(TFT_GREEN, TFT_BLACK);  // (字,地)
  lcd.setTextSize(0.9);                    // 文字サイズの倍数
  lcd.print("バックライト輝度");           // 表示 タイトル
  lcd.setTextSize(1.6);                    // 文字サイズの倍数
  lcd.setCursor(30, 165);                  // カーソル位置 タッチ表示用
  lcd.print("+ SET -");                  // 表示
  lcd.setTextSize(1);                      // 文字サイズの倍数
  timeSetNo = 4;                           // 時から始める
  uint8_t brightX = 5, brightY;
  do {
    delay(100);                                 // ループ0.1秒待つ
    int16_t touchX[1], touchY[1];               // タッチ変数
    if (digitalRead(SENSOR_IRQ) == LOW) {       // 割り込みがあったら
      watch.getPoint(touchX, touchY, 1);        // タッチx,y取得
      x = touchX[0];                            // タッチx値
      y = touchY[0];                            // タッチy値
      while (digitalRead(SENSOR_IRQ) == LOW) {  // 指を離すまで待つ
        delay(100);                             // 0.1秒待つ
      }
      //delay(100);                        // 0.1秒待つ
      if (x < 80) {                      // 下段左側なら
        brightX++;                       // +1
        if (brightX > 10) brightX = 10;  // ~10とする
      } else if (x < 160) {              // 下段中央なら
        Mcase = 0;                       // 時計画面へ
        watch.setWaveform(0, waveT);     // エフェクト効果値を設定
        watch.run();                     // エフェクトを再生
      } else {                           // 下段右側なら
        brightX--;                       // -は1down
        if (brightX < 1) brightX = 1;    // 1~とする
      }
    }
    brightY = 2 * brightX * brightX - 1;       // 明るさ=2*x^2-1 x=1-10でy=1-199
    lcd.setBrightness(brightY);                // バックライト輝度 0-255(実際は256通りの輝度ではない)1でも薄く点灯
    lcd.fillRect(0, 30, 240, 100, TFT_WHITE);  // 四角塗りつぶし(x,y,w,h,色)
    lcd.setCursor(105, 130);                   // カーソル位置
    lcd.printf("%3d", brightY);                // 輝度数値表示
  } while (Mcase == 6);                        // Mcaseが変わるまでループ
}

//-------------------------------------------------// 7
void app7() {                              // Mcase=7 未作成
  lcd.setTextColor(TFT_WHITE, TFT_BLACK);  // (字,地)
  lcd.print("menu 7");                     // 表示
  do {
    delay(100);  // ループ0.1秒待つ
    int16_t touchX[1], touchY[1];
    if (digitalRead(SENSOR_IRQ) == LOW) {
      watch.setWaveform(0, waveT);  // エフェクト効果値を設定
      watch.run();                  // エフェクトを再生
      Mcase = 10;                   // メニューへ
    }
    while (digitalRead(SENSOR_IRQ) == LOW) {  // 指を離すまで待つ
      delay(100);                             // 0.1秒待つ
    }
  } while (Mcase == 7);  // Mcaseが変わるまでループ
}


//-------------------------------------------------// 8
void app8() {                              // Mcase=8 未作成
  lcd.setTextColor(TFT_WHITE, TFT_BLACK);  // (字,地)
  lcd.print("menu 8");                     // 表示
  do {
    delay(100);  // ループ0.1秒待つ
    int16_t touchX[1], touchY[1];
    if (digitalRead(SENSOR_IRQ) == LOW) {
      watch.setWaveform(0, waveT);  // エフェクト効果値を設定
      watch.run();                  // エフェクトを再生
      Mcase = 10;                   // メニューへ
    }
    while (digitalRead(SENSOR_IRQ) == LOW) {  // 指を離すまで待つ
      delay(100);                             // 0.1秒待つ
    }
  } while (Mcase == 8);  // Mcaseが変わるまでループ
}


//-------------------------------------------------// 9 時計画面へ
void appReturn() {  // Mcase=9 時計画面へ戻る
  Mcase = 0;        // 時計画面へ
}


//-------------------------------------------------//
void setup() {
  Serial.begin(115200);
  SetFont();                                                        // Font等初期化関数へ
  watch.configAccelerometer();                                      // 加速度センサーの設定 Default 4G,200HZ
  watch.enableAccelerometer();                                      // 加速度センサーを有効に
  watch.enableFeature(SensorBMA423::FEATURE_WAKEUP, true);          // 加速度センサーのダブルクリックトリガーを有効に
  watch.enableWakeupIRQ();                                          // 加速度センサーのダブルクリックによるトリガー割込みを有効に
  watch.configInterrupt();                                          // 割込みを有効に
  touch.begin(Wire, FT6X36_SLAVE_ADDRESS, SENSOR_SDA, SENSOR_SCL);  // FT6X36センサーの初期化
  pinMode(SENSOR_IRQ, INPUT);
  touch.interruptTrigger();  // 割り込みトリガ開始

  rtc.begin(Wire, PCF8563_SLAVE_ADDRESS, RTC_SDA, RTC_SCL);  // PCF8563(RTC)の初期化
  pinMode(RTC_IRQ, INPUT_PULLUP);

  sntp_set_time_sync_notification_cb(timeAvailable);  // NTPの時刻修正実行時にコールバックする関数を設定
  if (!FFat.begin(true)) {                            // FFatの初期化ができなければ
    Serial.println("FFatマウント失敗");               // エラー表示
  }
  flashRead();  // flashのデータ読込関数へ almH,almM
}


void loop() {
  switch (Mcase) {  // 表示状態
    case 0:
      appWatch();  // 0.時計へ
      break;
    case 1:
      appalarmSet();  // 1.アラーム時分設定
      break;
    case 2:
      apptimeSet();  // 2.時刻合わせ
      break;
    case 3:
      app3();  // 3.未作成
      break;
    case 4:
      apptimeAutoSet();  // 4.wifiで時刻合わせ
      break;
    case 5:
      app5();  // 5.未作成
      break;
    case 6:
      appBright();  // 6.バックライト輝度調整
      break;
    case 7:
      app7();  // 7.未作成
      break;
    case 8:
      app8();  // 8.未作成
      break;
    case 9:
      appReturn();  // 9.時計画面へ戻る
      break;
    case 10:
      appMenu();  // 10.メニュー画面へ
      break;
    default:  // それ以外
      break;
  }
  delay(100);                 // 100mS待つ
  lcd.fillScreen(TFT_BLACK);  // 全画面黒
}
* flash memory(3.1Mbyte)のうち、スケッチが53%使用。RAM(327kbyte)のうち、global変数が14%使用、local変数で279kbyte使用可能。(1000byte=1kbyteで計算)