09_M5PaperS3でTxtReader2
09_M5PaperS3でTxtReader2
説明
前回(08_M5PaperS3でTxtReader)の修正・追加版です。表示文字
SD内のtxtファイルの中身を表示します。UFT-8のマルチバイト文字に対応していますが、表示フォントは、lgfxJapanGothic_32 横幅32px か、
lgfxJapanGothic_36 横幅36px
なので、常用漢字+αで4639文字。半角カナ、→の矢印、①等はありません。詳しくは、前回を参照してください。
ファイル名
SDライブラリは、8.3形式かと思いまいたが、そのままで日本語もロングネームも読めました。SDのルートにある50ファイルまでを表示します。前のファイル頁は"File▲"、次のファイル頁は"File▼"をタッチし、9ファイルごとに表示します。ファイル名をタッチするとファイル内容を表示します。
時刻・バッテリー容量
起動画面の1行目に表示します。両数値は随時更新しています。メニュー2
1.時刻設定:設定後RTCに保存します。2.スマホシャッター:スマホのカメラアプリのシャッターを動作させます。
作成した関数
・String setBuf(int32_t byteP)bytePの位置から文章を1頁読込み、その文字列bufを返します。
printFirst()とloop()から呼ばれ、1頁以上読み込んだらbufを返します。
・int setView(String buf)
1頁分を表示セットします。
printFirst()とloop()から呼ばれ、表示後、処理Byte数を返します。
・printFirst()
メニュー内容を設定し1頁目を表示します。
setup()とloop()から呼ばれ、メニュー内を設定し1頁目を表示し、戻ります。
・printMenu()
起動画面(メニュー)を表示します。
setup()とloop()から呼ばれ、表示後、ファイル名かmenu2をタッチしたら戻ります。
・printMenu2()
メニュー2を表示します。
setup()から呼ばれ、各関数へ移動します。メニュー2から戻るには再起動します。
・setTime()
時刻を手動設定します。
メニュー2から呼ばれ、時刻を手動設定し、"RTC保存"後、戻るには再起動します。
・shutter()
スマホシャッター
メニュー2から呼ばれ、画面タッチで動作し、終了は再起動します。
・SetFont()
Font等初期化します。
setup()から呼ばれ、設定後戻ります。
* 再起動は横のボタンを押します。
各Ver
Arduino IDE v2.3.4ボードマネージャ
・esp32 by Espressif Systems は v3.0.7以下の事。v3.0.7
・M5Stack by M5Stack official v2.1.1→v2.1.3
M5Stsck内にM5PaperS3が出てきました
ライブラリ
・M5Unified by M5Stack は v0.2.2以上の事
v0.2.5にすると動作しなくなったのでv0.2.2へ戻しました。
・M5GFX by M5Stack は v0.2.2以上の事
v0.2.6にすると動作しなくなったのでv0.2.3へ戻しました。
"06.ATOM Liteでスマホの写真シャッター"を参照し、"ESP32-BLE-Keyboard"をインストールします。
ボードの設定
Arduino-IDE > ツールにて・ボード > M5Stack > M5PaperS3
・ポート : "COM□"
・その他はDefault
使い方
SDのファイル
・ルートにある事・日本語・ロングネーム可
・50ファイル以下(変数より)
の制約があります。
表示は、ファイル名の拡張子も含め、全角9.5文字(半角19文字)まで ファイルサイズの表示は、999kB以下(表示桁数より)です。
起動するとメニュー画面が開きます。
メニュー画面
・品質・速度 : 高品質 / 高速 (タッチで変更)・画面向き : 縦向き / 横向き (タッチで変更)
・フォントサイズ : 32px / 36px (タッチで変更)
・行間 : 5px / 10px / 15px (タッチで変更)
・ファイル名
"File▲"/"File▼"は前/次のファイル名を表示します。
タッチでメニュー画面を終了し、ファイルの中身を表示します。
ファイル名の表示
例 SDのルートに以下の13ファイルがある場合・C++ 独学入門サイト【C言語からステップアップするカリキュラム】.txt
・SDライブラリの使用方法.txt
・M5Canvasについて.txt
・M5GFX API.txt
・M5Unified API.txt
・M5Unifiedへの移植ポイント.txt
・Open-Meteo.txt
・室内快適湿度40~60%.txt
・time.txt
・シンフリーサーバー残量.txt
・すぐやる事.txt
・Xfreeの終了.txt
・FatFsブログ.txt
文章表示
ファイル名選択後、文章を表示します。・1行目にファイル名、設定値(速度・品質/文字サイズ/行間)、頁番号を表示します。
・次の頁に移動は、画面右側タッチ
・前の頁に移動は、画面左側タッチ
ただし、画面下側20%の部分のタッチは無視します。
(親指の付け根で触ってしまうので)
時刻合わせ(メニュー2)
年月日時分を設定し、"RTC保存"をタッチしたタイミングでRTCに書込みます。書込み後は再起動します。中断は保存せず再起動します。秒は30秒に固定です。スマホシャッター(メニュー2)
スマホとBluetoothの接続をします。1.M5PaperS3のスマホシャッターを起動
2.スマホ > 設定 > 接続設定 > 新しいデバイスとペア設定 > BLE-Shutter1 > "ペア設定する"
3.スマホに"HD Camera"をインストール
(既存のカメラではシャッターが動作しなかったので)
4.スマホのカメラ起動
5.M5PaperS3の画面をタッチでシャッターが動作します。
6.終了は再起動します。
次回
追加するかもしれない項目・しおり
・設定頁までジャンプ
・レート変換電卓
・スワイプ
・世界天気予報
スケッチ
// 09_M5PaperS3_BookReader2.ino 960x540
// 数分放置で画面が黒くなるのはリフレッシュ必要 画面黒→画面白で時間延ばす
#include <SD.h> // SDを使用
File myFile; // Fileのインスタンスを作成
#include <epdiy.h> // 電子ペーパードライバー
#include <M5Unified.h> // M5共通ライブラリ
#include <BleKeyboard.h> // BLEキーボードを使用
BleKeyboard bleKeyboard("BLE-Shutter1"); // Bluetooth名を設定
#define SD_SPI_CS_PIN GPIO_NUM_47 // SDのpin
#define SD_SPI_SCK_PIN GPIO_NUM_39 // SDのpin
#define SD_SPI_MOSI_PIN GPIO_NUM_38 // SDのpin
#define SD_SPI_MISO_PIN GPIO_NUM_40 // SDのpin
String buf; // 表示文章のbuf
int16_t fontS = 32; // 初期fontサイズ (32/36)
int16_t gyoukan = 10; // 初期行間px (5/10/15)
bool edpQ = false; // (true)高品質 / (false)高速
bool gamenTate = true; // 画面(true)縦 / (false)横
int32_t pageN, pageB[1000]; // 頁番号,頁最初のByte値
int32_t goPageN; // ジャンプ先頁番号
lgfx::touch_point_t tp[1]; // タッチポイント
int16_t menuDY = 70; // メニュー画面y刻みpx
String fName, fList[50][2]; // ファイルリスト[n][(0)fName,(1)fByte]
int8_t fPage = 0; // ファイル名選択頁(9ファイル/頁)
bool noPrint = false; // setViewでprintしないの義(する)
//bool txtEnd; // 文章終わり
bool goMenu2 = false; // 最初はメニュー2へ行かない
int16_t yy = 2025, mm = 02, dd = 28, hhh = 23, mmm = 59; // 年月日時分の初期値
static constexpr const char* const wd[7] = { "日", "月", "火", "水", "木", "金", "土" };
void SetFont() { // Font等初期化関数
M5.Display.startWrite(); // バッファを効率的に使用開始
M5.Display.setEpdMode(epd_mode_t::epd_fastest); // 描画速 (低品質)
//M5.Display.setEpdMode(epd_mode_t::epd_quality);// 高品質 (描画遅)
M5.Display.setRotation(0); // 縦長
//M5.Display.setRotation(1);// 横長
M5.Display.setFont(&fonts::lgfxJapanGothic_32); // 固定幅ゴシック体 8,12,16,20,24,28,32,36,40
//M5.Display.setFont(&fonts::lgfxJapanGothic_36);// 固定幅ゴシック体 8,12,16,20,24,28,32,36,40
M5.Display.fillScreen(TFT_WHITE); // 全画面白
M5.Display.setTextColor(TFT_BLACK, TFT_WHITE); // (字,地)
M5.Display.setTextSize(1); // 文字サイズの倍数 1-7
M5.Display.setTextWrap(false, false); // 自動折返し(x,y)
M5.Display.setCursor(0, 0); // カーソル位置
M5.Display.setAutoDisplay(false); // 即表示しない
}
String setBuf(int32_t byteP) { // bytePの位置から読込みそのbufを返す
myFile = SD.open("/" + fName, FILE_READ); // 読込ファイルを開く
myFile.seek(byteP); // 読取開始のByte位置
int32_t i = 0; // 読取Byte数リセット
buf = ""; // bufリセット
while (myFile.available()) { // 読取れるByteがあれば
buf += char(myFile.read()); // 読取文字
i++; // 読取Byte数1加算
if (i > ((540 / fontS) * (960 / fontS) * 3)) break;
// 行間0,全3Byte文字として1頁以上読込んだら抜ける
}
myFile.close(); // ファイルを閉じる
return buf; // 1頁以上の文字を返す
}
int setView(String buf) { // 表示セット関数
int16_t tabX = 2; // 左余白
int16_t viewX = tabX, viewY = 0; // 表示x,y位置 リセット
int16_t i = 0; // Byte位置 リセット
int16_t bufL = buf.length(); // bufの全Byte数 \0は含まず
if (noPrint == false) { // printするなら
M5.Display.fillScreen(TFT_BLACK); // 全画面黒 しないと段々黒くなる
// -------------------------------- セットを表示
M5.Display.display(); // セットを表示
vTaskDelay(1); // 1tickの間タスクをブロック
M5.Display.fillScreen(TFT_WHITE); // 全画面白
M5.Display.display(); // セットを表示
vTaskDelay(1); // 1tickの間タスクをブロック
}
// -------------------------------- 1行目のタイトルセット
M5.Display.setCursor(viewX, 1); // カーソル位置
if (noPrint == false) { // printするなら
M5.Display.print(fName); // ファイル名セット
if (edpQ) { // 高品質画面なら
M5.Display.print(" (高品質/"); // 表示セット
} else { // 高速画面なら
M5.Display.print(" (高速/"); // 表示セット
}
M5.Display.printf("%d/", fontS); // フォントサイズpxセット
M5.Display.printf("%d)", gyoukan); // 行間pxセット
M5.Display.setCursor(440, 1); // カーソル位置
M5.Display.printf("%4d頁", pageN + 1); // 頁番号セット
}
viewY += fontS; // 次の行へ
if (noPrint == false) { // printするなら
M5.Display.drawFastHLine(0, viewY - 1, M5.Display.width(), TFT_BLACK); // 水平線セット
}
while (i < bufL) { // buf終わりまでループ
bool CRon = false; // 改行 リセット
// -------------------------------- 1行セット終わり
if (viewX > (M5.Display.width() / fontS - 1) * fontS) { // 横が1文字分少なく を超えていたら改行
CRon = true; // 改行
viewX = tabX; // 画面の左端
viewY += (fontS + gyoukan); // 行間スペースを含めて移動へ
}
// -------------------------------- 改行
if (buf.charAt(i) == 0x0D) { // CR+LF だったら
CRon = true; // 改行
i += 2; // CR+LF Windows等
viewX = tabX; // 画面の左端
viewY += (fontS + gyoukan); // 行間スペースを含めて移動へ
}
// -------------------------------- LFは無視
if (buf.charAt(i) == 0x0A) { // LFだったら 無いはず
i += 1; // 1Byte進める
viewX = tabX; // 画面の左端
viewY += (fontS + gyoukan); // 行間スペースを含めて移動へ
}
// -------------------------------- 1頁セット終わり
if ((viewY + gyoukan + fontS > M5.Display.height())
&& (CRon == true)) break; // 行が下まで来て改行なら抜ける
// -------------------------------- prit
M5.Display.setCursor(viewX, viewY); // カーソル位置
if (buf.charAt(i) < 0x80) { // 1Byte文字なら
M5.Display.print(buf.substring(i, i + 1)); // 1文字セット
i += 1; // 1Byte進める
viewX += fontS / 2; // 半角
} else if (buf.charAt(i) < 0xC0) { // マルチByteの2Byte目以降
// ここへは来ないはず
} else if (buf.charAt(i) < 0xE0) { // 2Byte文字なら
M5.Display.print(buf.substring(i, i + 2)); // 1文字セット
i += 2; // 2Byte進める
viewX += fontS; // 全角
} else if (buf.charAt(i) < 0xF0) { // 3Byte文字なら
M5.Display.print(buf.substring(i, i + 3)); // 1文字セット
i += 3; // 3Byte進める
viewX += fontS; // 全角
} else if (buf.charAt(i) < 0xF8) { // 4Byte文字なら
M5.Display.print(buf.substring(i, i + 4)); // 1文字セット
i += 4; // 4Byte進める
viewX += fontS; // 全角
} else if (buf.charAt(i) < 0xFC) { // 5Byte文字なら
M5.Display.print(buf.substring(i, i + 5)); // 1文字セット
i += 5; // 5Byte進める
viewX += fontS; // 全角
} else if (buf.charAt(i) < 0xFE) { // 6Byte文字なら
M5.Display.print(buf.substring(i, i + 6)); // 1文字セット
i += 6; // 6Byte進める
viewX += fontS; // 全角
} else {
// ここへは来ないはず
}
}
if (noPrint == false) { // printするなら
// -------------------------------- セットを表示
M5.Display.display(); // セットしたのを表示
vTaskDelay(1); // 1tickの間タスクをブロック
}
return (i); // 処理Byte数を返す
}
void printMenu() { // 起動画面表示
M5.Display.setRotation(0); // 縦長
M5.Display.setFont(&fonts::lgfxJapanGothic_32); // 固定幅ゴシック体 8,12,16,20,24,28,32,36,40
M5.Display.setEpdMode(epd_mode_t::epd_fastest); // 描画速 (低品質)
M5.Display.fillScreen(TFT_WHITE); // 全画面白
M5.Display.setTextColor(TFT_BLACK, TFT_WHITE); // (字,地)
goPageN = 0; // ジャンプ先頁番号=1頁目
int16_t battLevMae = 0;
while (true) { // ファイル名をタッチしたら抜ける
// -------------------------------- メニュー1行目セット h50
M5.Display.fillRect(0, 0, 540, 50, TFT_LIGHTGREY); // 塗りつぶし
M5.Display.setCursor(5, (50 - 32) / 2); // カーソル位置
auto dt = M5.Rtc.getDateTime(); // RTC時間取得
M5.Display.printf("%04d/%02d/%02d(%s)%02d:%02d",
dt.date.year, dt.date.month, dt.date.date,
wd[dt.date.weekDay], dt.time.hours, dt.time.minutes);
int16_t battLev = M5.Power.getBatteryLevel(); // batt残量を取得 0-100
if (abs(battLev - battLevMae) < 2) { // ±1%のちらつきは無視する
battLev = battLevMae; // 前のまま
}
M5.Display.setCursor(540 - 16 * 4 - 15, (50 - 32) / 2); // カーソル位置
M5.Display.printf("%3d%%", battLev); // batt残量を画面セット
battLevMae = battLev; // 今回のを前の%とする
// -------------------------------- メニュー2行目セット
int16_t menuX = 0, menuY = 70; // カーソル変数
M5.Display.setCursor(menuX, menuY); // カーソル位置
if (edpQ == true) { // 高品質画面なら
M5.Display.print(" 品質・速度 : "); // 表示をセット
M5.Display.setTextColor(TFT_WHITE, TFT_BLACK); // (字,地)
M5.Display.print("高品質"); // 表示をセット
M5.Display.setTextColor(TFT_BLACK, TFT_WHITE); // (字,地)
M5.Display.print(" 高速"); // 表示をセット
} else { // 高速画面なら
M5.Display.print(" 品質・速度 : 高品質 "); // 表示をセット
M5.Display.setTextColor(TFT_WHITE, TFT_BLACK); // (字,地)
M5.Display.print("高速"); // 表示をセット
M5.Display.setTextColor(TFT_BLACK, TFT_WHITE); // (字,地)
}
// -------------------------------- メニュー3行目セット
menuY += menuDY; // 次の行へ
M5.Display.setCursor(menuX, menuY); // カーソル位置
if (gamenTate == true) { // 縦画面なら
M5.Display.print(" 画面向き : "); // 表示をセット
M5.Display.setTextColor(TFT_WHITE, TFT_BLACK); // (字,地)
M5.Display.print("縦向き"); // 表示をセット
M5.Display.setTextColor(TFT_BLACK, TFT_WHITE); // (字,地)
M5.Display.print(" 横向き"); // 表示をセット
} else { // 横画面なら
M5.Display.print(" 画面向き : 縦向き "); // 表示をセット
M5.Display.setTextColor(TFT_WHITE, TFT_BLACK); // (字,地)
M5.Display.print("横向き"); // 表示をセット
M5.Display.setTextColor(TFT_BLACK, TFT_WHITE); // (字,地)
}
// -------------------------------- メニュー4行目セット
menuY += menuDY; // 次の行へ
M5.Display.setCursor(menuX, menuY); // カーソル位置
if (fontS == 32) { // fontサイズが32pxなら
M5.Display.print(" Font Size : "); // 表示をセット
M5.Display.setTextColor(TFT_WHITE, TFT_BLACK); // (字,地)
M5.Display.print("32px"); // 表示をセット
M5.Display.setTextColor(TFT_BLACK, TFT_WHITE); // (字,地)
M5.Display.print(" 36px"); // 表示をセット
} else { // fontサイズが36pxなら
M5.Display.print(" Font Size : 32px "); // 表示をセット
M5.Display.setTextColor(TFT_WHITE, TFT_BLACK); // (字,地)
M5.Display.print("36px"); // 表示をセット
M5.Display.setTextColor(TFT_BLACK, TFT_WHITE); // (字,地)
}
// -------------------------------- メニュー5行目セット
menuY += menuDY; // 次の行へ
M5.Display.setCursor(menuX, menuY); // カーソル位置
if (gyoukan == 5) { // 行間が5pxなら
M5.Display.print(" 行間(px) : "); // 行間px
M5.Display.setTextColor(TFT_WHITE, TFT_BLACK); // (字,地)
M5.Display.print("05"); // 表示をセット
M5.Display.setTextColor(TFT_BLACK, TFT_WHITE); // (字,地)
M5.Display.print(" 10 15"); // 表示をセット
} else if (gyoukan == 10) { // 行間が10pxなら
M5.Display.print(" 行間(px) : 05 "); // 表示をセット
M5.Display.setTextColor(TFT_WHITE, TFT_BLACK); // (字,地)
M5.Display.print("10"); // 表示をセット
M5.Display.setTextColor(TFT_BLACK, TFT_WHITE); // (字,地)
M5.Display.print(" 15"); // 表示をセット
} else { // 行間が15pxなら
M5.Display.print(" 行間(px) : 05 10 "); // 表示をセット
M5.Display.setTextColor(TFT_WHITE, TFT_BLACK); // (字,地)
M5.Display.print("15"); // 表示をセット
M5.Display.setTextColor(TFT_BLACK, TFT_WHITE); // (字,地)
}
// -------------------------------- ファイル名セット
menuY += menuDY; // 次の行へ
for (int8_t i = 0; i < 9; i++) { // 9ファイルまで表示
M5.Display.setCursor(menuX, menuY); // カーソル位置
//M5.Display.printf("%3d ", i + 1); // ファイル番号セット
fName = " " + fList[fPage * 9 + i][0] // ファイル名
+ " "; // 20文字空白追加
M5.Display.print(fName); // ファイル名セット
M5.Display.setCursor(menuX + 330, menuY); // カーソル位置
M5.Display.print(" "); // 20文字消す
M5.Display.setCursor(menuX + 330, menuY); // カーソル位置
M5.Display.print(fList[fPage * 9 + i][1]); // ファイルサイズセット
menuY += menuDY; // 次の行へ
}
// -------------------------------- 罫線セット 960x540
for (int8_t i = 1; i < 5; i++) { // 水平線上4本
M5.Display.drawFastHLine(0, menuDY * i + 50, 540, TFT_BLACK);
}
for (int8_t i = 5; i < 13; i++) { // 水平線下8本
M5.Display.drawFastHLine(0, menuDY * i + 50, 540, TFT_BLACK);
}
M5.Display.drawFastHLine(0, menuDY * 13 + 50, 400, TFT_BLACK); // 水平線一番下
M5.Display.drawFastVLine(335, 50, 210, TFT_BLACK); // 垂直線上3行
M5.Display.drawFastVLine(285, 260, 70, TFT_BLACK); // 4行目左
M5.Display.drawFastVLine(420, 260, 70, TFT_BLACK); // 4行目右
M5.Display.drawFastVLine(400, 330, menuDY * 13, TFT_BLACK); // 垂直線下ファイル行
M5.Display.drawRoundRect(3, 53, 534, menuDY * 4 - 6, 30, TFT_BLACK); // 上の丸四角
M5.Display.drawRoundRect(3, menuDY * 4 + 4 + 50, 394, menuDY * 9 - 6, 30, TFT_BLACK); // 下左の丸四角
M5.Display.drawRoundRect(404, menuDY * 5 + 4 + 50, 134, menuDY * 6 - 6, 30, TFT_BLACK); // 下右の丸四角
// -------------------------------- 文字セット
menuY = 300 + 50; // 次の行へ
M5.Display.setCursor(470 - 6 * 8, menuY); // カーソル位置
M5.Display.print("File▲"); // 表示をセット
menuY += menuDY; // 次の行へ
M5.Display.setCursor(470 - 3 * 8, menuY); // カーソル位置
//M5.Display.print("+10"); // 表示をセット
menuY += menuDY; // 次の行へ
M5.Display.setCursor(470 - 2 * 8, menuY); // カーソル位置
//M5.Display.print("+5"); // 表示をセット
menuY += menuDY; // 次の行へ
M5.Display.setCursor(470 - 2 * 8, menuY); // カーソル位置
//M5.Display.print("+1"); // 表示をセット
menuY += menuDY; // 次の行へ
M5.Display.setCursor(430, menuY); // カーソル位置
//M5.Display.printf("%4d頁", goPageN + 1); // 表示をセット
menuY += menuDY; // 次の行へ
M5.Display.setCursor(470 - 2 * 8, menuY); // カーソル位置
//M5.Display.print("-1"); // 表示をセット
menuY += menuDY; // 次の行へ
M5.Display.setCursor(470 - 3 * 8, menuY); // カーソル位置
//M5.Display.print("-10"); // 表示をセット
menuY += menuDY; // 次の行へ
M5.Display.setCursor(470 - 6 * 8, menuY); // カーソル位置
M5.Display.print("File▼"); // 表示をセット
menuY += menuDY; // 次の行へ
M5.Display.setCursor(470 - 6 * 8, menuY); // カーソル位置
M5.Display.print("menu 2"); // 表示をセット
// -------------------------------- セットを表示
M5.Display.display(); // セットを表示
// -------------------------------- タッチ判定
bool drawed = false; // 描画変数=描画していない
goMenu2 = false;
if (M5.Display.getTouchRaw(tp, 1)) { // タッチ情報を取得
drawed = true; // 描画した
if (tp[0].y < menuDY * 1 + 50) { // 1行目なら
if (tp[0].x < 335) { // 左側なら
edpQ = 1; // 高品質
} else { // 右側なら
edpQ = 0; // 高速
}
} else if (tp[0].y < menuDY * 2 + 50) { // 2行目なら
if (tp[0].x < 335) { // 左側なら
gamenTate = 1; // 縦向き
} else { // 右側なら
gamenTate = 0; // 横向き
}
} else if (tp[0].y < menuDY * 3 + 50) { // 3行目なら
if (tp[0].x < 335) { // 左側なら
fontS = 32; // 32px
} else { // 右側なら
fontS = 36; // 36px
}
} else if (tp[0].y < menuDY * 4 + 50) { // 4行目なら
if (tp[0].x < 285) { // 左側なら
gyoukan = 5; // 行間5px
} else if (tp[0].x < 420) { // 中央なら
gyoukan = 10; // 行間10px
} else { // 右側なら
gyoukan = 15; // 行間15px
}
} else if (tp[0].x > 400) { // 5行目以下の右なら
if (tp[0].y < menuDY * 5 + 50) { // 5行目なら
fPage -= 1; // ファイル名表示頁-1
if (fPage < 0) fPage = 0; // 最低0
} else if (tp[0].y < menuDY * 6 + 50) { // 6行目なら
goPageN += 10; // +10
} else if (tp[0].y < menuDY * 7 + 50) { // 7行目なら
goPageN += 5; // +5
} else if (tp[0].y < menuDY * 8 + 50) { // 8行目なら
goPageN++; // +1
} else if (tp[0].y < menuDY * 9 + 50) { // 9行目なら
// 何もしない
} else if (tp[0].y < menuDY * 10 + 50) { // 10行目なら
goPageN--; // -1
} else if (tp[0].y < menuDY * 11 + 50) { // 11行目なら
goPageN -= 10; // -10
} else if (tp[0].y < menuDY * 12 + 50) { // 12行目なら
fPage += 1; // ファイル名表示頁+1
} else {
goMenu2 = true;
break;
}
if (goPageN < 0) goPageN = 0; // 最低0頁とする
if (goPageN > 999) goPageN = 999; // 最高999頁とする
} else if (tp[0].y < menuDY * 5 + 50) { // 5行目のfile1なら
fName = fList[fPage * 9 + 0][0]; // file1,10,19,28,37,...
break; // 戻る
} else if (tp[0].y < menuDY * 6 + 50) { // 6行目のfile2なら
fName = fList[fPage * 9 + 1][0]; // file2
break; // 戻る
} else if (tp[0].y < menuDY * 7 + 50) { // 7行目のfile3なら
fName = fList[fPage * 9 + 2][0]; // file3
break; // 戻る
} else if (tp[0].y < menuDY * 8 + 50) { // 8行目のfile4なら
fName = fList[fPage * 9 + 3][0]; // file4
break; // 戻る
} else if (tp[0].y < menuDY * 9 + 50) { // 9行目のfile5なら
fName = fList[fPage * 9 + 4][0]; // file5
break; // 戻る
} else if (tp[0].y < menuDY * 10 + 50) { // 10行目のfile6なら
fName = fList[fPage * 9 + 5][0]; // file6
break; // 戻る
} else if (tp[0].y < menuDY * 11 + 50) { // 11行目のfile7なら
fName = fList[fPage * 9 + 6][0]; // file7
break; // 戻る
} else if (tp[0].y < menuDY * 12 + 50) { // 12行目のfile8なら
fName = fList[fPage * 9 + 7][0]; // file8
break; // 戻る
} else { // 13行目のfile9なら
fName = fList[fPage * 9 + 8][0]; // file9
break; // 戻る
}
} else if (drawed) { // もし描画していれば
drawed = false; // 描画変数を戻す
}
// -------------------------------- セットを表示
M5.Display.display(); // セットを表示
vTaskDelay(1); // 1tickの間タスクをブロック
}
}
void printMenu2() { // メニュー2表示
M5.Display.setRotation(0); // 縦長
M5.Display.setFont(&fonts::lgfxJapanGothic_32); // 固定幅ゴシック体 8,12,16,20,24,28,32,36,40
M5.Display.setEpdMode(epd_mode_t::epd_fastest); // 描画速 (低品質)
M5.Display.fillScreen(TFT_BLACK); // 全画面黒 しないと段々黒くなる
// -------------------------------- セットを表示
M5.Display.display(); // セットを表示
vTaskDelay(1); // 1tickの間タスクをブロック
M5.Display.fillScreen(TFT_WHITE); // 全画面白
M5.Display.display(); // セットを表示
vTaskDelay(1); // 1tickの間タスクをブロック
M5.Display.setTextColor(TFT_BLACK, TFT_WHITE); // (字,地)
while (true) { // タッチしたら抜ける
// -------------------------------- 罫線
for (int8_t i = 2; i < 13; i++) { // 水平線11本
M5.Display.drawFastHLine(0, menuDY * i, 540, TFT_BLACK);
}
// -------------------------------- 塗りつぶし
M5.Display.fillRect(0, 0, 540, menuDY, TFT_LIGHTGREY);
M5.Display.fillRect(0, menuDY * 13, 540, 960 - menuDY * 13, TFT_LIGHTGREY);
// -------------------------------- 文字セット
int16_t menuX = 0, menuY = 20; // カーソル変数
M5.Display.setCursor((540 - 16 * 14) / 2, menuY); // カーソル位置
M5.Display.print(" [ menu 2 ] "); // 表示をセット
menuY += menuDY; // 次の行へ
M5.Display.setCursor(menuX, menuY); // カーソル位置
M5.Display.print(" 1.時刻手動合わせ"); // 表示をセット
menuY += menuDY; // 次の行へ
M5.Display.setCursor(menuX, menuY); // カーソル位置
M5.Display.print(" 2.スマホシャッター"); // 表示をセット
menuY += menuDY; // 次の行へ
M5.Display.setCursor(menuX, menuY); // カーソル位置
M5.Display.print(" 3.世界天気予報(予定)"); // 表示をセット
menuY += menuDY; // 次の行へ
M5.Display.setCursor(menuX, menuY); // カーソル位置
M5.Display.print(" 4."); // 表示をセット
menuY += menuDY; // 次の行へ
M5.Display.setCursor(menuX, menuY); // カーソル位置
M5.Display.print(" 5."); // 表示をセット
menuY += menuDY; // 次の行へ
M5.Display.setCursor(menuX, menuY); // カーソル位置
M5.Display.print(" 6."); // 表示をセット
menuY += menuDY; // 次の行へ
M5.Display.setCursor(menuX, menuY); // カーソル位置
M5.Display.print(" 7."); // 表示をセット
menuY += menuDY; // 次の行へ
M5.Display.setCursor(menuX, menuY); // カーソル位置
M5.Display.print(" 8."); // 表示をセット
menuY += menuDY; // 次の行へ
M5.Display.setCursor(menuX, menuY); // カーソル位置
M5.Display.print(" 9."); // 表示をセット
menuY += menuDY; // 次の行へ
M5.Display.setCursor(menuX, menuY); // カーソル位置
M5.Display.print(" 10."); // 表示をセット
menuY += menuDY; // 次の行へ
M5.Display.setCursor(menuX, menuY); // カーソル位置
M5.Display.print(" 11."); // 表示をセット
menuY += menuDY; // 次の行へ
M5.Display.setCursor(menuX, menuY); // カーソル位置
M5.Display.print(" 12."); // 表示をセット
// -------------------------------- セットを表示
M5.Display.display();
// -------------------------------- タッチ判定
if (M5.Display.getTouchRaw(tp, 1)) { // タッチ情報を取得
if (tp[0].y < menuDY * 2) { // 2行目なら
setTime(); // (1)時刻合わせ 手動
break;
} else if (tp[0].y < menuDY * 3) { // 3行目なら
shutter(); // (2)スマホシャッター
break;
} else if (tp[0].y < menuDY * 4) { // 4行目なら
// 3.何もしない
} else if (tp[0].y < menuDY * 5) { // 5行目なら
// 4.何もしない
} else if (tp[0].y < menuDY * 6) { // 6行目なら
// 5.何もしない
} else if (tp[0].y < menuDY * 7) { // 7行目なら
// 6.何もしない
} else if (tp[0].y < menuDY * 8) { // 8行目なら
// 7.何もしない
} else if (tp[0].y < menuDY * 9) { // 9行目なら
// 8.何もしない
} else if (tp[0].y < menuDY * 10) { // 10行目なら
// 9.何もしない
} else if (tp[0].y < menuDY * 11) { // 11行目なら
// 10.何もしない
} else if (tp[0].y < menuDY * 12) { // 12行目なら
// 11.何もしない
} else if (tp[0].y < menuDY * 13) { // 13行目なら
// 12.何もしない
} else { // 14行目なら
// 何もしない
}
}
}
}
void setTime() { // メニュー3 時刻手動設定
M5.Display.setRotation(0); // 縦長
M5.Display.setFont(&fonts::lgfxJapanGothic_32); // 固定幅ゴシック体 8,12,16,20,24,28,32,36,40
M5.Display.setEpdMode(epd_mode_t::epd_fastest); // 描画速 (低品質)
M5.Display.fillScreen(TFT_BLACK); // 全画面黒 しないと段々黒くなる
// -------------------------------- セットを表示
M5.Display.display(); // セットを表示
vTaskDelay(1); // 1tickの間タスクをブロック
M5.Display.setTextColor(TFT_BLACK, TFT_WHITE); // (字,地)
auto dt = M5.Rtc.getDateTime();
yy = dt.date.year;
mm = dt.date.month;
dd = dt.date.date;
hhh = dt.time.hours;
mmm = dt.time.minutes;
while (true) { // タッチしたら抜ける
// -------------------------------- メニュー3 枠セット
M5.Display.fillRoundRect(10, 10, 530, 50, 5, TFT_LIGHTGREY); // 丸四角1行分
int16_t x0 = 3, y0 = -3, r = 20; // x,y,r設定
int16_t x2 = 540 / 2 + x0, w = 540 / 2 - x0 * 2, h = menuDY * 3 - y0 * 2; // 式
for (int8_t i = 2; i < 11; i += 4) {
M5.Display.fillRoundRect(x0, menuDY * i + y0, w, h, r, TFT_LIGHTGREY); // 丸四角3行分
M5.Display.fillRoundRect(x2, menuDY * i + y0, w, h, r, TFT_LIGHTGREY); // 丸四角3行分
}
x0 = 10, y0 = 10, r = 16; // x,y,r設定
int16_t x1 = 540 / 4 + x0, x3 = 540 / 4 * 3 + x0; // 式
x2 = 540 / 2 + x0, w = 540 / 4 - x0 * 2, h = menuDY * 1 - y0 * 2; // 式
for (int8_t i = 3; i < 9; i += 4) {
M5.Display.fillRoundRect(x0, menuDY * i + y0, w, h, r, TFT_WHITE); // 丸四角1行分
M5.Display.fillRoundRect(x1, menuDY * i + y0, w, h, r, TFT_WHITE); // 丸四角1行分
M5.Display.fillRoundRect(x2, menuDY * i + y0, w, h, r, TFT_WHITE); // 丸四角1行分
M5.Display.fillRoundRect(x3, menuDY * i + y0, w, h, r, TFT_WHITE); // 丸四角1行分
M5.Display.fillRoundRect(x0, menuDY * (i + 1) + y0, w, h, r, TFT_WHITE); // 丸四角1行分
M5.Display.fillRoundRect(x1, menuDY * (i + 1) + y0, w, h, r, TFT_WHITE); // 丸四角1行分
M5.Display.fillRoundRect(x2, menuDY * (i + 1) + y0, w, h, r, TFT_WHITE); // 丸四角1行分
M5.Display.fillRoundRect(x3, menuDY * (i + 1) + y0, w, h, r, TFT_WHITE); // 丸四角1行分
}
M5.Display.fillRoundRect(x0, menuDY * 11 + y0, w, h, r, TFT_WHITE); // 丸四角1行分
M5.Display.fillRoundRect(x1, menuDY * 11 + y0, w, h, r, TFT_WHITE); // 丸四角1行分
M5.Display.fillRoundRect(x0, menuDY * 12 + y0, w, h, r, TFT_WHITE); // 丸四角1行分
M5.Display.fillRoundRect(x1, menuDY * 12 + y0, w, h, r, TFT_WHITE); // 丸四角1行分
// -------------------------------- メニュー3 文字セット
int16_t menuX04 = (540 / 2 - 16 * 4) / 2, menuX06 = (540 / 2 - 16 * 6) / 2;
int16_t menuX24 = 540 / 2 + menuX04, menuX26 = 540 / 2 + menuX06;
int16_t menuX12 = (540 / 4 - 16 * 2) / 2;
int16_t menuX13 = (540 / 4 - 16 * 3) / 2, menuX22 = 540 / 4 * 1 + menuX12; // 4列時2列目2文字,3文字のカーソル位置
int16_t menuX33 = 540 / 4 * 2 + menuX13, menuX42 = 540 / 4 * 3 + menuX12;
int16_t menuY = 20; // カーソル変数
M5.Display.setCursor((540 - 16 * 20) / 2, menuY); // カーソル位置
M5.Display.print("[ 時刻合わせ 手動 ]"); // 表示をセット
menuY += menuDY; // 次の行へ
menuY += menuDY;
M5.Display.setCursor(menuX06, menuY); // カーソル位置
M5.Display.printf("%04d年", yy); // 表示をセット
M5.Display.setCursor(menuX24, menuY); // カーソル位置
M5.Display.printf("%02d月", mm); // 表示をセット
menuY += menuDY; // 次の行へ
M5.Display.setCursor(menuX12, menuY); // カーソル位置
M5.Display.print("+2"); // 表示をセット
M5.Display.setCursor(menuX22, menuY); // カーソル位置
M5.Display.print("+1"); // 表示をセット
M5.Display.setCursor(menuX33, menuY); // カーソル位置
M5.Display.print("+10"); // 表示をセット
M5.Display.setCursor(menuX42, menuY); // カーソル位置
M5.Display.print("+1"); // 表示をセット
menuY += menuDY; // 次の行へ
M5.Display.setCursor(menuX12, menuY); // カーソル位置
M5.Display.print("-2"); // 表示をセット
M5.Display.setCursor(menuX22, menuY); // カーソル位置
M5.Display.print("-1"); // 表示をセット
M5.Display.setCursor(menuX33, menuY); // カーソル位置
M5.Display.print("-10"); // 表示をセット
M5.Display.setCursor(menuX42, menuY); // カーソル位置
M5.Display.print("-1"); // 表示をセット
menuY += menuDY; // 次の行へ
menuY += menuDY; // 次の行へ
M5.Display.setCursor(menuX04, menuY); // カーソル位置
M5.Display.printf("%02d日", dd); // 表示をセット
M5.Display.setCursor(menuX24, menuY); // カーソル位置
M5.Display.printf("%02d時", hhh); // 表示をセット
menuY += menuDY; // 次の行へ
M5.Display.setCursor(menuX13, menuY); // カーソル位置
M5.Display.print("+10"); // 表示をセット
M5.Display.setCursor(menuX22, menuY); // カーソル位置
M5.Display.print("+1"); // 表示をセット
M5.Display.setCursor(menuX33, menuY); // カーソル位置
M5.Display.print("+10"); // 表示をセット
M5.Display.setCursor(menuX42, menuY); // カーソル位置
M5.Display.print("+1"); // 表示をセット
menuY += menuDY; // 次の行へ
M5.Display.setCursor(menuX13, menuY); // カーソル位置
M5.Display.print("-10"); // 表示をセット
M5.Display.setCursor(menuX22, menuY); // カーソル位置
M5.Display.print("-1"); // 表示をセット
M5.Display.setCursor(menuX33, menuY); // カーソル位置
M5.Display.print("-10"); // 表示をセット
M5.Display.setCursor(menuX42, menuY); // カーソル位置
M5.Display.print("-1"); // 表示をセット
menuY += menuDY; // 次の行へ
menuY += menuDY; // 次の行へ
M5.Display.setCursor(menuX04, menuY); // カーソル位置
M5.Display.printf("%02d分", mmm); // 表示をセット
menuY += menuDY; // 次の行へ
M5.Display.setCursor(menuX13, menuY); // カーソル位置
M5.Display.print("+10"); // 表示をセット
M5.Display.setCursor(menuX22, menuY); // カーソル位置
M5.Display.print("+1"); // 表示をセット
M5.Display.setCursor(menuX26, menuY); // カーソル位置
M5.Display.print("RTC保存"); // 表示をセット
menuY += menuDY; // 次の行へ
M5.Display.setCursor(menuX13, menuY); // カーソル位置
M5.Display.print("-10"); // 表示をセット
M5.Display.setCursor(menuX22, menuY); // カーソル位置
M5.Display.print("-1"); // 表示をセット
menuY += menuDY; // 次の行へ
M5.Display.setCursor(15, menuY); // カーソル位置
M5.Display.print(" 保存で表示=現在時刻となります。"); // 表示をセット
// -------------------------------- セットを表示
M5.Display.display();
// -------------------------------- タッチ判定
bool drawed = false; // 描画変数=描画していない
if (M5.Display.getTouchRaw(tp, 1)) { // タッチ情報を取得
drawed = true; // 描画した
if (tp[0].y < menuDY * 4) { // 4行目なら
if (tp[0].x < 135 * 1) { // 1列目なら
yy += 2; // 年+2
} else if (tp[0].x < 135 * 2) { // 2列目なら
yy++; // 年+1
} else if (tp[0].x < 135 * 3) { // 3列目なら
mm += 10; // 月+10
} else { // 4列目なら
mm++; // 月+1
}
if (mm > 12) mm = 1; // 12月の次は1月
} else if (tp[0].y < menuDY * 6) { // 5行目なら
if (tp[0].x < 135 * 1) { // 1列目なら
yy -= 2; // 年-2
} else if (tp[0].x < 135 * 2) { // 2列目なら
yy--; // 年-1
} else if (tp[0].x < 135 * 3) { // 3列目なら
mm -= 10; // 月-10
} else { // 4列目なら
mm--; // 月-1
}
if (yy < 2025) yy = 2025; // 2025年以降
if (mm < 1) mm = 12; // 1月の次は12月
} else if (tp[0].y < menuDY * 8) { // 8行目なら
if (tp[0].x < 135 * 1) { // 1列目なら
dd += 10; // 日+10
} else if (tp[0].x < 135 * 2) { // 2列目なら
dd++; // 日+1
} else if (tp[0].x < 135 * 3) { // 3列目なら
hhh += 10; // 時+10
} else { // 4列目なら
hhh++; // 時+1
}
if (dd > 31) dd = 1; // 31日の次は1日
if (hhh > 23) hhh = 0; // 23時の次は0時
} else if (tp[0].y < menuDY * 10) { // 9行目なら
if (tp[0].x < 135 * 1) { // 1列目なら
dd -= 10; // 日-10
} else if (tp[0].x < 135 * 2) { // 2列目なら
dd--; // 日-1
} else if (tp[0].x < 135 * 3) { // 3列目なら
hhh -= 10; // 時-10
} else { // 4列目なら
hhh--; // 時-1
}
if (dd < 1) dd = 31; // 1日の次は31日
if (hhh < 0) hhh = 23; // 0時の次は23時
} else if (tp[0].x > 135 * 2) { // 11行以降の右側
M5.Rtc.setDateTime({ { yy, mm, dd }, { hhh, mmm, 30 } });
//break; // menu 2へ
} else if (tp[0].y < menuDY * 12) { // 12行目なら
if (tp[0].x < 135 * 1) { // 1列目なら
mmm += 10; // 分+10
} else { // 2列目なら
mmm++; // 分+1
}
if (mmm > 59) mmm = 0; // 59分の次は0分
} else { // 13行目なら
if (tp[0].x < 135 * 1) { // 1列目なら
mmm -= 10; // 分-10
} else { // 2列目なら
mmm--; // 分-1
}
if (mmm < 0) hhh = 59; // 0分の次は59分
}
// -------------------------------- セットを表示
M5.Display.display();
vTaskDelay(1); // 1tickの間タスクをブロック
}
}
}
void shutter() { // スマホシャッター
bool connectTruePrint = false, connectFalsePrint = false; // 表示してないか
M5.Display.setRotation(0); // 縦長
M5.Display.setFont(&fonts::lgfxJapanGothic_32); // 固定幅ゴシック体 8,12,16,20,24,28,32,36,40
M5.Display.setEpdMode(epd_mode_t::epd_fastest); // 描画速 (低品質)
M5.Display.fillScreen(TFT_BLACK); // 全画面黒 しないと段々黒くなる
// -------------------------------- セットを表示
M5.Display.display(); // セットを表示
vTaskDelay(1); // 1tickの間タスクをブロック
M5.Display.fillScreen(TFT_WHITE); // 全画面黒 しないと段々黒くなる
// -------------------------------- セットを表示
M5.Display.display(); // セットを表示
vTaskDelay(1); // 1tickの間タスクをブロック
M5.Display.setCursor(0, 0); // カーソル位置
M5.Display.setTextColor(TFT_BLACK, TFT_WHITE); // (字,地)
bleKeyboard.begin(); // Bluetoothキーボード開始
#if defined(USE_NIMBLE)
BLEDevice::setSecurityAuth(true, true, true);
#else
BLESecurity* pSecurity = new BLESecurity();
pSecurity->setAuthenticationMode(ESP_LE_AUTH_BOND); // S3では必要
#endif // USE_NIMBLE
M5.Display.println("スケッチ開始 画面touchでshutter"); // セット
M5.Display.display(); // セットを表示
while (1) {
if (bleKeyboard.isConnected()) { // Bluetoothキーボードが接続されていれば
Serial.println("接続中"); // シリアルモニターに表示
if (connectTruePrint == false) { // 接続中が未表示なら
M5.Display.println("接続中"); // セット
M5.Display.display(); // セットを表示
}
connectTruePrint = true; // 接続中は表示済
connectFalsePrint = false; // 未接続は未表示
if (M5.Display.getTouchRaw(tp, 1)) { // タッチ情報を取得
M5.Display.println("シャッター"); // セット
M5.Display.display(); // セットを表示
bleKeyboard.write(KEY_MEDIA_VOLUME_UP); // 音量upキーを送信
delay(500); // 0.5秒待つ
}
} else {
if (connectFalsePrint == false) { // 未接続が未表示なら
M5.Display.println("未接続 (BLE-Shutter1)"); // セット
M5.Display.display(); // セットを表示
}
Serial.println("未接続"); // シリアルモニターに表示
connectFalsePrint = true; // 未接続は表示済
connectTruePrint = false; // 接続中は未表示
}
delay(200); // 0.2秒待つ
}
}
void printFirst() { // メニュー内容を設定し1頁目を表示
// -------------------------------- メニュー内容で設定
if (edpQ) { // 画面の品質設定
M5.Display.setEpdMode(epd_mode_t::epd_quality); // 高品質 (描画遅)
} else {
M5.Display.setEpdMode(epd_mode_t::epd_fastest); // 描画速 (低品質)
}
if (gamenTate) { // 画面向き設定
M5.Display.setRotation(0); // 縦長
} else {
M5.Display.setRotation(1); // 横長
}
if (fontS == 36) { // フォントサイズ設定
M5.Display.setFont(&fonts::lgfxJapanGothic_36); // 固定幅ゴシック体 8,12,16,20,24,28,32,36,40
} else {
M5.Display.setFont(&fonts::lgfxJapanGothic_32); // 固定幅ゴシック体 8,12,16,20,24,28,32,36,40
}
// -------------------------------- 文章表示画面
pageN = 0; // 頁番号0
pageB[pageN] = 0; // 頁番号の最初のバイト値は0
if (goPageN == 0) { // メニューで1頁にセットなら
buf = setBuf(pageB[pageN]); // その頁のbufを得る関数へ
noPrint = false; // printする
pageB[pageN + 1] = setView(buf); // 戻り値(Byte値)は次の頁の開始Byte値
} else { // 飛び先が1(2頁)以上なら
for (pageN = 0; pageN < goPageN - 1; pageN++) { // 表示無しで計算
buf = setBuf(pageB[pageN]); // その頁のbufを得る関数へ
noPrint = true; // printしない
pageB[pageN + 1] = pageB[pageN] + setView(buf);
// 1頁表示し、(開始+処理)のByte数を次頁の開始Byte数にする
}
pageN++; // 次の頁
buf = setBuf(pageB[pageN]); // その頁のbufを得る関数へ
noPrint = false; // printする
pageB[pageN + 1] = pageB[pageN] + setView(buf);
// 1頁表示し、(開始+処理)のByte数を次頁の開始Byte数にする
}
}
void setup() {
auto cfg = M5.config(); // 設定用の構造体を代入
M5.begin(cfg); // 設定した値でデバイスを開始
Serial.begin(115200); // シリアル通信開始
SPI.begin(SD_SPI_SCK_PIN, SD_SPI_MISO_PIN,
SD_SPI_MOSI_PIN, SD_SPI_CS_PIN);
delay(100); // 0.1秒待つ
SetFont(); // Font等初期化関数へ
if (!SD.begin(47)) { // SDライブラリとカードを初期化できなければ
M5.Display.println("失敗 SDライブラリ初期化"); // 表示
while (true) // 無限ループ
;
}
// -------------------------------- ファイル名取得
myFile = SD.open("/"); // ルートのファイルを開く
char buf2[40]; // 作業用
int8_t i = 0; // ファイル数
while (true) { // breakになるまで無限ループ
File entry = myFile.openNextFile(); // ディレクトリ内の次のファイルまたはホルダを取得
if (!entry) break; // 次のファイルまたはフォルダがないなら抜ける
if (!entry.isDirectory()) { // ホルダでなければ
fList[i][0] = String(entry.name()); // ファイル名を取得
Serial.println(fList[i][0]);
if (entry.size() < 1000) { // ファイルサイズが1kB未満なら
sprintf(buf2, "%4d\n", entry.size()); // bufにファイルサイズ(bytes)を
} else { // 1kB以上なら
sprintf(buf2, "%3dk\n", entry.size() / 1000); // Bufにファイルサイズ(kB)を
}
fList[i][1] = String(buf2); // ファイルサイズ(B or kB単位)
i++; // 次のファイル番号
}
entry.close(); // ファイルを閉じる
}
printMenu(); // メニュー表示関数
if (goMenu2) printMenu2();
printFirst(); // メニュー内容を設定し1頁目を表示
}
void loop() {
bool drawed = false; // 描画変数=描画していない
if (M5.Display.getTouch(tp, 1)) { // タッチ情報を取得
if (tp[0].y < M5.Display.height() * 0.8) { // 画面下2割の部分のタッチは無視
drawed = true; // 描画した
if (tp[0].x > M5.Display.width() / 2) { // 次頁タッチなら
pageN++; // 頁番号1加算
buf = setBuf(pageB[pageN]); // その頁のbufを得る関数へ
pageB[pageN + 1] = pageB[pageN] + setView(buf);
// 1頁表示し、(開始+処理)のByte数を次頁の開始Byte数にする
} else if (pageN > 0) { // 前頁タッチで頁番号が1以上なら
pageN--; // 頁番号1減算
buf = setBuf(pageB[pageN]); // その頁のbufを得る関数へ
setView(buf); // 1頁表示関数へ
} else { // 頁番号1で前頁タッチなら
printMenu(); // メニュー表示関数へ
printFirst(); // メニュー内容を設定し1頁目を表示
}
} else if (drawed) { // もし描画していれば
drawed = false; // 描画変数を戻す
}
vTaskDelay(1); // 1tickの間タスクをブロック
}
}
* flash memory(3.1Mbyte)のうち、スケッチが64%使用。RAM(327kbyte)のうち、global変数が16%使用、local変数で274kbyte使用可能。(1000byte=1kbyteで計算)