tiro-notes 0.51.61 → 0.51.63
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/node-build/cli.js +0 -309
- package/node-build/package.json +0 -28
- package/node-build/v6 copy.ino +0 -956
- package/node-build/v6.ino +0 -872
- /package/{node-build/README.md → README.md} +0 -0
- /package/{node-build/client → client}/appicons/icons/mac/icon.icns +0 -0
- /package/{node-build/client → client}/appicons/icons/png/1024x1024.png +0 -0
- /package/{node-build/client → client}/appicons/icons/png/128x128.png +0 -0
- /package/{node-build/client → client}/appicons/icons/png/16x16.png +0 -0
- /package/{node-build/client → client}/appicons/icons/png/24x24.png +0 -0
- /package/{node-build/client → client}/appicons/icons/png/256x256.png +0 -0
- /package/{node-build/client → client}/appicons/icons/png/32x32.png +0 -0
- /package/{node-build/client → client}/appicons/icons/png/48x48.png +0 -0
- /package/{node-build/client → client}/appicons/icons/png/512x512.png +0 -0
- /package/{node-build/client → client}/appicons/icons/png/64x64.png +0 -0
- /package/{node-build/client → client}/appicons/icons/win/icon.ico +0 -0
- /package/{node-build/client → client}/appicons/tiro_icon.icns +0 -0
- /package/{node-build/client → client}/appicons/tiro_icon.ico +0 -0
- /package/{node-build/client → client}/appicons/tiro_icon.png +0 -0
- /package/{node-build/client → client}/asset-manifest.json +0 -0
- /package/{node-build/client → client}/custom_icons/bottom-left.png +0 -0
- /package/{node-build/client → client}/custom_icons/bottom-right.png +0 -0
- /package/{node-build/client → client}/custom_icons/bottom.png +0 -0
- /package/{node-build/client → client}/custom_icons/check.svg +0 -0
- /package/{node-build/client → client}/custom_icons/left.png +0 -0
- /package/{node-build/client → client}/custom_icons/line.svg +0 -0
- /package/{node-build/client → client}/custom_icons/right.png +0 -0
- /package/{node-build/client → client}/custom_icons/top-left.png +0 -0
- /package/{node-build/client → client}/custom_icons/top-right.png +0 -0
- /package/{node-build/client → client}/custom_icons/top.png +0 -0
- /package/{node-build/client → client}/custom_icons/uncheck.svg +0 -0
- /package/{node-build/client → client}/custom_icons/view-1.svg +0 -0
- /package/{node-build/client → client}/custom_icons/view-2.svg +0 -0
- /package/{node-build/client → client}/custom_icons/view-3.svg +0 -0
- /package/{node-build/client → client}/custom_icons/view-4.svg +0 -0
- /package/{node-build/client → client}/favicon.png +0 -0
- /package/{node-build/client → client}/icon-192x192.png +0 -0
- /package/{node-build/client → client}/icon-256x256.png +0 -0
- /package/{node-build/client → client}/icon-384x384.png +0 -0
- /package/{node-build/client → client}/icon-512x512.png +0 -0
- /package/{node-build/client → client}/index.html +0 -0
- /package/{node-build/client → client}/manifest.json +0 -0
- /package/{node-build/client → client}/manifest.webmanifest +0 -0
- /package/{node-build/client → client}/service-worker.js +0 -0
- /package/{node-build/client → client}/static/css/12.394917b1.chunk.css +0 -0
- /package/{node-build/client → client}/static/js/0.b890b1cb.chunk.js +0 -0
- /package/{node-build/client → client}/static/js/1.24c0d2eb.chunk.js +0 -0
- /package/{node-build/client → client}/static/js/100.4020f20c.chunk.js +0 -0
- /package/{node-build/client → client}/static/js/101.21c27e18.chunk.js +0 -0
- /package/{node-build/client → client}/static/js/102.e3f94c8e.chunk.js +0 -0
- /package/{node-build/client → client}/static/js/103.18e92661.chunk.js +0 -0
- /package/{node-build/client → client}/static/js/104.cd956379.chunk.js +0 -0
- /package/{node-build/client → client}/static/js/105.1e2e5889.chunk.js +0 -0
- /package/{node-build/client → client}/static/js/106.5c24ba4b.chunk.js +0 -0
- /package/{node-build/client → client}/static/js/107.80b988bf.chunk.js +0 -0
- /package/{node-build/client → client}/static/js/108.fff04134.chunk.js +0 -0
- /package/{node-build/client → client}/static/js/109.ec78f8d9.chunk.js +0 -0
- /package/{node-build/client → client}/static/js/110.44788eb1.chunk.js +0 -0
- /package/{node-build/client → client}/static/js/12.6d6dc9bb.chunk.js +0 -0
- /package/{node-build/client → client}/static/js/12.6d6dc9bb.chunk.js.LICENSE.txt +0 -0
- /package/{node-build/client → client}/static/js/13.9e34d8ba.chunk.js +0 -0
- /package/{node-build/client → client}/static/js/14.a886302f.chunk.js +0 -0
- /package/{node-build/client → client}/static/js/15.01bf8225.chunk.js +0 -0
- /package/{node-build/client → client}/static/js/16.67051f41.chunk.js +0 -0
- /package/{node-build/client → client}/static/js/17.82cdd47c.chunk.js +0 -0
- /package/{node-build/client → client}/static/js/18.ddb404ef.chunk.js +0 -0
- /package/{node-build/client → client}/static/js/19.7dd55448.chunk.js +0 -0
- /package/{node-build/client → client}/static/js/2.b4b18203.chunk.js +0 -0
- /package/{node-build/client → client}/static/js/20.59f5165d.chunk.js +0 -0
- /package/{node-build/client → client}/static/js/21.79c4b0e8.chunk.js +0 -0
- /package/{node-build/client → client}/static/js/22.007e32b0.chunk.js +0 -0
- /package/{node-build/client → client}/static/js/23.f61a9d2c.chunk.js +0 -0
- /package/{node-build/client → client}/static/js/24.e0d7947e.chunk.js +0 -0
- /package/{node-build/client → client}/static/js/25.e8450ff4.chunk.js +0 -0
- /package/{node-build/client → client}/static/js/26.ea917da6.chunk.js +0 -0
- /package/{node-build/client → client}/static/js/27.40c86fd7.chunk.js +0 -0
- /package/{node-build/client → client}/static/js/28.36540711.chunk.js +0 -0
- /package/{node-build/client → client}/static/js/29.90bee6cc.chunk.js +0 -0
- /package/{node-build/client → client}/static/js/3.303b3d58.chunk.js +0 -0
- /package/{node-build/client → client}/static/js/30.41fd826a.chunk.js +0 -0
- /package/{node-build/client → client}/static/js/31.ceec3129.chunk.js +0 -0
- /package/{node-build/client → client}/static/js/32.f164128c.chunk.js +0 -0
- /package/{node-build/client → client}/static/js/33.0aa32cf1.chunk.js +0 -0
- /package/{node-build/client → client}/static/js/34.a7b2b96c.chunk.js +0 -0
- /package/{node-build/client → client}/static/js/35.d6fc00c0.chunk.js +0 -0
- /package/{node-build/client → client}/static/js/36.2c384957.chunk.js +0 -0
- /package/{node-build/client → client}/static/js/37.a4733489.chunk.js +0 -0
- /package/{node-build/client → client}/static/js/38.151ab2a6.chunk.js +0 -0
- /package/{node-build/client → client}/static/js/39.79ebafd7.chunk.js +0 -0
- /package/{node-build/client → client}/static/js/4.5e26b40b.chunk.js +0 -0
- /package/{node-build/client → client}/static/js/40.f3d995a0.chunk.js +0 -0
- /package/{node-build/client → client}/static/js/41.e90a9137.chunk.js +0 -0
- /package/{node-build/client → client}/static/js/42.bd656ca1.chunk.js +0 -0
- /package/{node-build/client → client}/static/js/43.8074805b.chunk.js +0 -0
- /package/{node-build/client → client}/static/js/44.8da981a2.chunk.js +0 -0
- /package/{node-build/client → client}/static/js/45.1a2b3b01.chunk.js +0 -0
- /package/{node-build/client → client}/static/js/46.2e55aca9.chunk.js +0 -0
- /package/{node-build/client → client}/static/js/47.57672172.chunk.js +0 -0
- /package/{node-build/client → client}/static/js/48.3bfd34d7.chunk.js +0 -0
- /package/{node-build/client → client}/static/js/49.c657ccf4.chunk.js +0 -0
- /package/{node-build/client → client}/static/js/5.f7811373.chunk.js +0 -0
- /package/{node-build/client → client}/static/js/50.36421e1d.chunk.js +0 -0
- /package/{node-build/client → client}/static/js/51.cd3bd891.chunk.js +0 -0
- /package/{node-build/client → client}/static/js/52.ddd90678.chunk.js +0 -0
- /package/{node-build/client → client}/static/js/53.8933ff1d.chunk.js +0 -0
- /package/{node-build/client → client}/static/js/54.1d25d19c.chunk.js +0 -0
- /package/{node-build/client → client}/static/js/55.f0195607.chunk.js +0 -0
- /package/{node-build/client → client}/static/js/56.931338e0.chunk.js +0 -0
- /package/{node-build/client → client}/static/js/57.8304da63.chunk.js +0 -0
- /package/{node-build/client → client}/static/js/58.3dbe9d9e.chunk.js +0 -0
- /package/{node-build/client → client}/static/js/59.d8dd3c13.chunk.js +0 -0
- /package/{node-build/client → client}/static/js/6.b8cf2c81.chunk.js +0 -0
- /package/{node-build/client → client}/static/js/60.e8ba53a7.chunk.js +0 -0
- /package/{node-build/client → client}/static/js/61.41f7057e.chunk.js +0 -0
- /package/{node-build/client → client}/static/js/62.8312dcd3.chunk.js +0 -0
- /package/{node-build/client → client}/static/js/63.ceff6f5a.chunk.js +0 -0
- /package/{node-build/client → client}/static/js/64.2b51a3a2.chunk.js +0 -0
- /package/{node-build/client → client}/static/js/65.5b662f09.chunk.js +0 -0
- /package/{node-build/client → client}/static/js/66.aed0e545.chunk.js +0 -0
- /package/{node-build/client → client}/static/js/67.c68e472b.chunk.js +0 -0
- /package/{node-build/client → client}/static/js/68.482d5776.chunk.js +0 -0
- /package/{node-build/client → client}/static/js/69.adf6efab.chunk.js +0 -0
- /package/{node-build/client → client}/static/js/7.5575eef7.chunk.js +0 -0
- /package/{node-build/client → client}/static/js/70.54d4c087.chunk.js +0 -0
- /package/{node-build/client → client}/static/js/71.e02a4ef4.chunk.js +0 -0
- /package/{node-build/client → client}/static/js/72.cdabbbd2.chunk.js +0 -0
- /package/{node-build/client → client}/static/js/73.b58eb627.chunk.js +0 -0
- /package/{node-build/client → client}/static/js/74.a7b0defd.chunk.js +0 -0
- /package/{node-build/client → client}/static/js/75.7ee75f2a.chunk.js +0 -0
- /package/{node-build/client → client}/static/js/76.88cf92c8.chunk.js +0 -0
- /package/{node-build/client → client}/static/js/77.64a32f3b.chunk.js +0 -0
- /package/{node-build/client → client}/static/js/78.e19d46e1.chunk.js +0 -0
- /package/{node-build/client → client}/static/js/79.2daf87c6.chunk.js +0 -0
- /package/{node-build/client → client}/static/js/8.977d1a5d.chunk.js +0 -0
- /package/{node-build/client → client}/static/js/80.2591cada.chunk.js +0 -0
- /package/{node-build/client → client}/static/js/81.4a8cdda6.chunk.js +0 -0
- /package/{node-build/client → client}/static/js/82.285db5b4.chunk.js +0 -0
- /package/{node-build/client → client}/static/js/83.c3ae3b75.chunk.js +0 -0
- /package/{node-build/client → client}/static/js/84.0316e16e.chunk.js +0 -0
- /package/{node-build/client → client}/static/js/85.da21c20c.chunk.js +0 -0
- /package/{node-build/client → client}/static/js/86.54af0d45.chunk.js +0 -0
- /package/{node-build/client → client}/static/js/87.c3e1d4bb.chunk.js +0 -0
- /package/{node-build/client → client}/static/js/88.6f1f304d.chunk.js +0 -0
- /package/{node-build/client → client}/static/js/89.9bf1904b.chunk.js +0 -0
- /package/{node-build/client → client}/static/js/9.3b7d0e8d.chunk.js +0 -0
- /package/{node-build/client → client}/static/js/90.f4f97bc7.chunk.js +0 -0
- /package/{node-build/client → client}/static/js/91.06e2ef0f.chunk.js +0 -0
- /package/{node-build/client → client}/static/js/92.05d93a9c.chunk.js +0 -0
- /package/{node-build/client → client}/static/js/93.84099dca.chunk.js +0 -0
- /package/{node-build/client → client}/static/js/94.c20443c3.chunk.js +0 -0
- /package/{node-build/client → client}/static/js/95.eef2f092.chunk.js +0 -0
- /package/{node-build/client → client}/static/js/96.1f09d3f2.chunk.js +0 -0
- /package/{node-build/client → client}/static/js/97.e7cce996.chunk.js +0 -0
- /package/{node-build/client → client}/static/js/98.ed47f20f.chunk.js +0 -0
- /package/{node-build/client → client}/static/js/99.038f33b0.chunk.js +0 -0
- /package/{node-build/client → client}/static/js/main.608d7586.chunk.js +0 -0
- /package/{node-build/client → client}/static/js/runtime-main.bbeed8c8.js +0 -0
- /package/{node-build/client → client}/static/media/book-solid.87f5b737.svg +0 -0
- /package/{node-build/client → client}/static/media/compact-disc-solid.55b6e50c.svg +0 -0
- /package/{node-build/client → client}/static/media/deco-bg-map.7ca9103b.png +0 -0
- /package/{node-build/client → client}/static/media/fa-brands-400.150de8ea.ttf +0 -0
- /package/{node-build/client → client}/static/media/fa-brands-400.e033a13e.woff2 +0 -0
- /package/{node-build/client → client}/static/media/fa-regular-400.3223dc79.woff2 +0 -0
- /package/{node-build/client → client}/static/media/fa-regular-400.d8747423.ttf +0 -0
- /package/{node-build/client → client}/static/media/fa-solid-900.4a2cd718.ttf +0 -0
- /package/{node-build/client → client}/static/media/fa-solid-900.bb975c96.woff2 +0 -0
- /package/{node-build/client → client}/static/media/fa-v4compatibility.0e3a648b.ttf +0 -0
- /package/{node-build/client → client}/static/media/fa-v4compatibility.68577e40.woff2 +0 -0
- /package/{node-build/client → client}/static/media/file-audio-solid.a76f99f0.svg +0 -0
- /package/{node-build/client → client}/static/media/file-code-solid.62b467ac.svg +0 -0
- /package/{node-build/client → client}/static/media/file-excel-solid.217d73db.svg +0 -0
- /package/{node-build/client → client}/static/media/file-pdf-solid.0a9d75fe.svg +0 -0
- /package/{node-build/client → client}/static/media/file-powerpoint-solid.4d969453.svg +0 -0
- /package/{node-build/client → client}/static/media/file-solid.6415173e.svg +0 -0
- /package/{node-build/client → client}/static/media/file-video-solid.312288cf.svg +0 -0
- /package/{node-build/client → client}/static/media/file-word-solid.9228e1ff.svg +0 -0
- /package/{node-build/client → client}/static/media/file-zipper-solid.7db57917.svg +0 -0
- /package/{node-build/client → client}/static/media/globe-africa-solid.0ad34146.svg +0 -0
- /package/{node-build/client → client}/static/media/link-solid.7b689dbd.svg +0 -0
- /package/{node-build/client → client}/static/media/open-sans-all-400-normal.cb2542fc.woff +0 -0
- /package/{node-build/client → client}/static/media/open-sans-all-700-normal.105e4309.woff +0 -0
- /package/{node-build/client → client}/static/media/open-sans-all-800-normal.86e783c7.woff +0 -0
- /package/{node-build/client → client}/static/media/open-sans-cyrillic-400-normal.5a546777.woff2 +0 -0
- /package/{node-build/client → client}/static/media/open-sans-cyrillic-700-normal.2cf6253f.woff2 +0 -0
- /package/{node-build/client → client}/static/media/open-sans-cyrillic-800-normal.41902933.woff2 +0 -0
- /package/{node-build/client → client}/static/media/open-sans-cyrillic-ext-400-normal.01b11c56.woff2 +0 -0
- /package/{node-build/client → client}/static/media/open-sans-cyrillic-ext-700-normal.00b4dd17.woff2 +0 -0
- /package/{node-build/client → client}/static/media/open-sans-cyrillic-ext-800-normal.2c1c4cc5.woff2 +0 -0
- /package/{node-build/client → client}/static/media/open-sans-greek-400-normal.3064bf7e.woff2 +0 -0
- /package/{node-build/client → client}/static/media/open-sans-greek-700-normal.a0312547.woff2 +0 -0
- /package/{node-build/client → client}/static/media/open-sans-greek-800-normal.d60c6496.woff2 +0 -0
- /package/{node-build/client → client}/static/media/open-sans-greek-ext-400-normal.1ed998a4.woff2 +0 -0
- /package/{node-build/client → client}/static/media/open-sans-greek-ext-700-normal.8850c90c.woff2 +0 -0
- /package/{node-build/client → client}/static/media/open-sans-greek-ext-800-normal.fe3583f2.woff2 +0 -0
- /package/{node-build/client → client}/static/media/open-sans-hebrew-400-normal.daafcec8.woff2 +0 -0
- /package/{node-build/client → client}/static/media/open-sans-hebrew-700-normal.3befb44d.woff2 +0 -0
- /package/{node-build/client → client}/static/media/open-sans-hebrew-800-normal.bfe88fed.woff2 +0 -0
- /package/{node-build/client → client}/static/media/open-sans-latin-400-normal.a1535f45.woff2 +0 -0
- /package/{node-build/client → client}/static/media/open-sans-latin-700-normal.b245bc85.woff2 +0 -0
- /package/{node-build/client → client}/static/media/open-sans-latin-800-normal.185f6b03.woff2 +0 -0
- /package/{node-build/client → client}/static/media/open-sans-latin-ext-400-normal.ccfa20f8.woff2 +0 -0
- /package/{node-build/client → client}/static/media/open-sans-latin-ext-700-normal.64471a17.woff2 +0 -0
- /package/{node-build/client → client}/static/media/open-sans-latin-ext-800-normal.988eae1a.woff2 +0 -0
- /package/{node-build/client → client}/static/media/open-sans-vietnamese-400-normal.473f6b09.woff2 +0 -0
- /package/{node-build/client → client}/static/media/open-sans-vietnamese-700-normal.b4c5968d.woff2 +0 -0
- /package/{node-build/client → client}/static/media/open-sans-vietnamese-800-normal.27b69d87.woff2 +0 -0
- /package/{node-build/client → client}/static/media/search-solid.cc51b702.svg +0 -0
- /package/{node-build/client → client}/tiro-local.crt +0 -0
- /package/{node-build/server → server}/ssl/tiro-local.crt +0 -0
- /package/{node-build/server → server}/ssl/tiro-local.key +0 -0
- /package/{node-build/server → server}/tiro-server.js +0 -0
- /package/{node-build/server → server}/tiro-server.js.LICENSE.txt +0 -0
- /package/{node-build/shared.helpers.build.js → shared.helpers.build.js} +0 -0
package/node-build/v6 copy.ino
DELETED
|
@@ -1,956 +0,0 @@
|
|
|
1
|
-
#include <WiFi.h>
|
|
2
|
-
#include <HTTPClient.h>
|
|
3
|
-
#include <M5Unified.h>
|
|
4
|
-
#include <esp_sleep.h>
|
|
5
|
-
#include <vector>
|
|
6
|
-
#include <string>
|
|
7
|
-
#include <SD.h>
|
|
8
|
-
#include <ArduinoJson.h>
|
|
9
|
-
// if files ending w rec appears >> format fat32 4k size Format the SD card to FAT32 with 4096-byte allocation unit size
|
|
10
|
-
// sudo chmod a+rw /dev/ttyACM0
|
|
11
|
-
// Wi-Fi Configuration
|
|
12
|
-
#define WIFI_SSID "42" // Replace with your SSID
|
|
13
|
-
#define WIFI_PASSWORD "87345678" // Replace with your password
|
|
14
|
-
|
|
15
|
-
// API endpoint (dynamically constructed using gateway IP and port 3023)
|
|
16
|
-
String getApiUrl(String type) {
|
|
17
|
-
IPAddress gateway = WiFi.gatewayIP();
|
|
18
|
-
String endpointWithType = "m5epaper-endpoint&id=" + type;
|
|
19
|
-
String args1 = "custom_backend_api?file=" + endpointWithType + "&token=796a01190502a2a92c38926dafcb10269d6cb3f7a9a6413218468614730d31c0089d8c42b92c53140f597020c6afaca1d9e2c1d8bb2d4fea98a3d90d6a80016ac1341e4275e18587e7a090a91cfdc2c2";
|
|
20
|
-
String url = "http://" + gateway.toString() + ":3023/" + args1;
|
|
21
|
-
return url;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
// Define the main power pin for M5Paper
|
|
25
|
-
#define M5EPD_MAIN_PWR_PIN 21
|
|
26
|
-
// Deep sleep duration (5 minutes in microseconds)
|
|
27
|
-
#define DEEPSLEEPMIN 5
|
|
28
|
-
#define DEEP_SLEEP_DURATION_DELAY (DEEPSLEEPMIN * 60 * 1000 * 1000)
|
|
29
|
-
#define MARGIN 20
|
|
30
|
-
// Maximum retries for API fetch
|
|
31
|
-
#define MAX_RETRIES 3
|
|
32
|
-
#define RETRY_DELAY_MS 2000
|
|
33
|
-
|
|
34
|
-
// Button pins
|
|
35
|
-
#define BUTTON_UP_PIN 37
|
|
36
|
-
#define BUTTON_DOWN_PIN 39
|
|
37
|
-
#define BUTTON_PRESS_PIN 38
|
|
38
|
-
// long press down
|
|
39
|
-
#define BUTTON_LONG_PRESS_DURATION 2000
|
|
40
|
-
|
|
41
|
-
// Page settings
|
|
42
|
-
// const GFXfont* currentFont = &fonts::FreeSansBold12pt7b;
|
|
43
|
-
// const GFXfont* currentFont = &fonts::FreeSans12pt7b;
|
|
44
|
-
|
|
45
|
-
std::vector<String> pages;
|
|
46
|
-
int currentPage = 0;
|
|
47
|
-
String cfont = "sans-bold-12";
|
|
48
|
-
bool shouldWrapText = false;
|
|
49
|
-
int maxCharsPerPage = 1200;
|
|
50
|
-
String currentContentType = "0";
|
|
51
|
-
// Add these global variables
|
|
52
|
-
SemaphoreHandle_t spiBusMutex;
|
|
53
|
-
|
|
54
|
-
///////////////////////////////////////////////////////
|
|
55
|
-
//
|
|
56
|
-
//
|
|
57
|
-
// FUNCTIONS
|
|
58
|
-
//
|
|
59
|
-
//
|
|
60
|
-
|
|
61
|
-
/////////////////////////////////////////////
|
|
62
|
-
//
|
|
63
|
-
// SDCARD
|
|
64
|
-
//
|
|
65
|
-
|
|
66
|
-
#define SD_SPI_SCK_PIN 14
|
|
67
|
-
#define SD_SPI_MISO_PIN 13
|
|
68
|
-
#define SD_SPI_MOSI_PIN 12
|
|
69
|
-
#define SD_SPI_CS_PIN 4
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
// JSON document capacity (adjust based on your data size)
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
// Save a raw string to SD card cache
|
|
76
|
-
bool setCache(const char* cacheId, const String& data) {
|
|
77
|
-
if (xSemaphoreTake(spiBusMutex, portMAX_DELAY) != pdTRUE) {
|
|
78
|
-
return false;
|
|
79
|
-
}
|
|
80
|
-
char filename[32];
|
|
81
|
-
snprintf(filename, sizeof(filename), "/cache-%s.txt", cacheId);
|
|
82
|
-
|
|
83
|
-
// Check if SD card is initialized first
|
|
84
|
-
if (!SD.cardType()) {
|
|
85
|
-
M5.Display.printf("!!! setCache >> SD card not initialized\n");
|
|
86
|
-
delay(3000);
|
|
87
|
-
return false;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
// Open file for write, truncate existing content
|
|
91
|
-
File file = SD.open(filename, FILE_WRITE, true);
|
|
92
|
-
delay(100);
|
|
93
|
-
if (!file) {
|
|
94
|
-
// M5.Display.setCursor(0, 0);
|
|
95
|
-
M5.Display.printf("!!! setCache >> Open %s failed\n", filename);
|
|
96
|
-
delay(1000);
|
|
97
|
-
// initSDcard(); // Try to reinitialize SD card
|
|
98
|
-
return false;
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
size_t bytesWritten = file.print(data);
|
|
102
|
-
delay(100);
|
|
103
|
-
// Ensure all data is flushed before closing
|
|
104
|
-
|
|
105
|
-
// Flush and close with error checking
|
|
106
|
-
delay(100);
|
|
107
|
-
file.flush();
|
|
108
|
-
delay(100);
|
|
109
|
-
file.close();
|
|
110
|
-
|
|
111
|
-
if (bytesWritten != data.length()) {
|
|
112
|
-
M5.Display.setCursor(0, 20);
|
|
113
|
-
M5.Display.printf("!!! setCache >> Partial write: %d/%d bytes", bytesWritten, data.length());
|
|
114
|
-
return false;
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
M5.Display.printf("[OK] setCache >> Saved to %s\n", filename);
|
|
118
|
-
xSemaphoreGive(spiBusMutex);
|
|
119
|
-
return true;
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
// Load a raw string from SD card cache
|
|
123
|
-
String getCache(const char* cacheId) {
|
|
124
|
-
char filename[32];
|
|
125
|
-
snprintf(filename, sizeof(filename), "/cache-%s.txt", cacheId);
|
|
126
|
-
// sleep for 150ms
|
|
127
|
-
delay(150);// sleep for 150ms
|
|
128
|
-
|
|
129
|
-
File file = SD.open(filename, FILE_READ);
|
|
130
|
-
if (!file) {
|
|
131
|
-
M5.Display.printf("!!! getCache >> File %s not found\n", filename);
|
|
132
|
-
return "";
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
String content = "";
|
|
136
|
-
while (file.available()) {
|
|
137
|
-
content += (char)file.read();
|
|
138
|
-
}
|
|
139
|
-
file.close();
|
|
140
|
-
|
|
141
|
-
if (content.length() == 0) {
|
|
142
|
-
M5.Display.println("getCache >> Cache file is empty");
|
|
143
|
-
} else {
|
|
144
|
-
M5.Display.printf("getCache >> SD CARD CACHE > found and loading %d bytes\n", content.length());
|
|
145
|
-
}
|
|
146
|
-
return content;
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
bool deleteCache(const char* cacheId) {
|
|
150
|
-
char filename[32];
|
|
151
|
-
snprintf(filename, sizeof(filename), "/cache-%s.txt", cacheId);
|
|
152
|
-
delay(200);
|
|
153
|
-
|
|
154
|
-
// Check if file exists first
|
|
155
|
-
if (!SD.exists(filename)) {
|
|
156
|
-
M5.Display.printf("!!! deleteCache >> Cache %s not found\n", cacheId);
|
|
157
|
-
return false;
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
// Delete the file
|
|
161
|
-
if (SD.remove(filename)) {
|
|
162
|
-
M5.Display.printf("deleteCache >> Deleted cache: %s\n", cacheId);
|
|
163
|
-
return true;
|
|
164
|
-
} else {
|
|
165
|
-
M5.Display.printf("!!! deleteCache >> Delete %s failed\n", cacheId);
|
|
166
|
-
return false;
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
// Updated init with safer clock speed
|
|
171
|
-
bool initSDcard() {
|
|
172
|
-
// In setup()
|
|
173
|
-
spiBusMutex = xSemaphoreCreateMutex();
|
|
174
|
-
|
|
175
|
-
// Ensure SD card power is stable
|
|
176
|
-
M5.Power.setExtOutput(false); // Enable 5V power for SD card
|
|
177
|
-
delay(200); // Wait for power to stabilize
|
|
178
|
-
M5.Power.setExtOutput(true); // Enable 5V power for SD card
|
|
179
|
-
delay(100); // Wait for power to stabilize
|
|
180
|
-
|
|
181
|
-
SPI.begin(SD_SPI_SCK_PIN, SD_SPI_MISO_PIN, SD_SPI_MOSI_PIN, SD_SPI_CS_PIN);
|
|
182
|
-
|
|
183
|
-
// Use 5MHz clock (M5Paper's recommended speed)
|
|
184
|
-
for (int attempts = 0; attempts < 3; attempts++) {
|
|
185
|
-
if (SD.begin(SD_SPI_CS_PIN, SPI, 5*1000*1000)) {
|
|
186
|
-
M5.Display.println("SD card initialized successfully");
|
|
187
|
-
return true;
|
|
188
|
-
}
|
|
189
|
-
delay(500);
|
|
190
|
-
}
|
|
191
|
-
M5.Display.println("SD card initialization failed after 3 attempts");
|
|
192
|
-
return false;
|
|
193
|
-
}
|
|
194
|
-
// void initSDcard(){
|
|
195
|
-
// SPI.begin(SD_SPI_SCK_PIN, SD_SPI_MISO_PIN, SD_SPI_MOSI_PIN, SD_SPI_CS_PIN);
|
|
196
|
-
// int sd_init_attempts = 0;
|
|
197
|
-
// while (!SD.begin(SD_SPI_CS_PIN, SPI, 25 * 1000 * 1000) && sd_init_attempts < 3) {
|
|
198
|
-
// delay(500);
|
|
199
|
-
// sd_init_attempts++;
|
|
200
|
-
// }
|
|
201
|
-
|
|
202
|
-
// // if (!SD.begin(SD_SPI_CS_PIN, SPI, 5000000)) {
|
|
203
|
-
// // M5.Display.println("!!! SD init failed!");
|
|
204
|
-
// // while (1);
|
|
205
|
-
// // }
|
|
206
|
-
// }
|
|
207
|
-
|
|
208
|
-
/////////////////////////////////////////////
|
|
209
|
-
//
|
|
210
|
-
// JSON
|
|
211
|
-
//
|
|
212
|
-
|
|
213
|
-
String getJsonParam(const String& jsonString, const String& key) {
|
|
214
|
-
DynamicJsonDocument doc(131072); // 128KB buffer
|
|
215
|
-
DeserializationError error = deserializeJson(doc, jsonString);
|
|
216
|
-
if (error) {
|
|
217
|
-
// show first 30 chars of jsonString
|
|
218
|
-
String jsonSnippet = jsonString.substring(0, 30);
|
|
219
|
-
M5.Display.printf("!!! JSON parse error: %s...\n", jsonSnippet.c_str());
|
|
220
|
-
sleep(3000);
|
|
221
|
-
return "";
|
|
222
|
-
}
|
|
223
|
-
if (doc.containsKey(key)) {
|
|
224
|
-
return doc[key].as<String>();
|
|
225
|
-
} else {
|
|
226
|
-
M5.Display.printf("!!! Key %s not found in JSON\n", key.c_str());
|
|
227
|
-
return "";
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
bool setJsonParam(const String& jsonString, const String& key, const String& value) {
|
|
231
|
-
DynamicJsonDocument doc(131072); // 128KB buffer
|
|
232
|
-
DeserializationError error = deserializeJson(doc, jsonString);
|
|
233
|
-
if (error) {
|
|
234
|
-
M5.Display.println("!!!JSON parse error");
|
|
235
|
-
return false;
|
|
236
|
-
}
|
|
237
|
-
doc[key] = value;
|
|
238
|
-
String updatedJson;
|
|
239
|
-
serializeJson(doc, updatedJson);
|
|
240
|
-
// Save updated JSON back to cache
|
|
241
|
-
return setCache("api_response", updatedJson);
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
/////////////////////////////////////////////
|
|
245
|
-
//
|
|
246
|
-
// WIFI AND CONNECTIVITY
|
|
247
|
-
//
|
|
248
|
-
void disconnectWifi() {
|
|
249
|
-
// Disconnect WiFi
|
|
250
|
-
WiFi.disconnect(true);
|
|
251
|
-
WiFi.mode(WIFI_OFF);
|
|
252
|
-
log("wifi disconnected");
|
|
253
|
-
}
|
|
254
|
-
void connectWiFi() {
|
|
255
|
-
M5.Display.print("WiFi: ");
|
|
256
|
-
WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
|
|
257
|
-
|
|
258
|
-
int attempts = 0;
|
|
259
|
-
while (WiFi.status() != WL_CONNECTED && attempts < 10) {
|
|
260
|
-
M5.Display.print(".");
|
|
261
|
-
delay(500);
|
|
262
|
-
attempts++;
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
if (WiFi.status() == WL_CONNECTED) {
|
|
266
|
-
M5.Display.println("\nConnected");
|
|
267
|
-
M5.Display.printf("IP: %s\n", WiFi.localIP().toString().c_str());
|
|
268
|
-
} else {
|
|
269
|
-
M5.Display.println("\nFailed to connect");
|
|
270
|
-
}
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
String fetchApiContent(String contentType) {
|
|
274
|
-
String payload = "";
|
|
275
|
-
bool success = false;
|
|
276
|
-
int retryCount = 0;
|
|
277
|
-
while (retryCount < MAX_RETRIES && !success) {
|
|
278
|
-
M5.Display.setCursor(0, M5.Display.getCursorY());
|
|
279
|
-
if (WiFi.status() == WL_CONNECTED) {
|
|
280
|
-
HTTPClient http;
|
|
281
|
-
String apiUrl = getApiUrl(contentType);
|
|
282
|
-
M5.Display.printf("Fetching API content from: %s...\n", apiUrl.substring(0, 30).c_str());
|
|
283
|
-
http.begin(apiUrl);
|
|
284
|
-
http.setTimeout(10000);
|
|
285
|
-
int httpCode = http.GET();
|
|
286
|
-
if (httpCode > 0) {
|
|
287
|
-
if (httpCode == HTTP_CODE_OK) {
|
|
288
|
-
payload = http.getString();
|
|
289
|
-
success = true;
|
|
290
|
-
} else {
|
|
291
|
-
M5.Display.printf("HTTP Error: %d (Retry %d/%d)\n",
|
|
292
|
-
httpCode, retryCount + 1, MAX_RETRIES);
|
|
293
|
-
}
|
|
294
|
-
} else {
|
|
295
|
-
M5.Display.printf("HTTP GET failed: %s (Retry %d/%d)\n",
|
|
296
|
-
http.errorToString(httpCode).c_str(),
|
|
297
|
-
retryCount + 1, MAX_RETRIES);
|
|
298
|
-
}
|
|
299
|
-
http.end();
|
|
300
|
-
} else {
|
|
301
|
-
M5.Display.println("WiFi not connected!");
|
|
302
|
-
}
|
|
303
|
-
if (!success) {
|
|
304
|
-
retryCount++;
|
|
305
|
-
if (retryCount < MAX_RETRIES) {
|
|
306
|
-
delay(RETRY_DELAY_MS);
|
|
307
|
-
}
|
|
308
|
-
}
|
|
309
|
-
}
|
|
310
|
-
if (success) {
|
|
311
|
-
int lineCount = 0;
|
|
312
|
-
int lastPos = 0;
|
|
313
|
-
int currentPos = 0;
|
|
314
|
-
while (lineCount < 4 && currentPos != -1) {
|
|
315
|
-
currentPos = payload.indexOf('\n', lastPos);
|
|
316
|
-
if (currentPos != -1) {
|
|
317
|
-
lastPos = currentPos + 1;
|
|
318
|
-
lineCount++;
|
|
319
|
-
}
|
|
320
|
-
}
|
|
321
|
-
if (lastPos < payload.length()) {
|
|
322
|
-
String remainingContent = payload.substring(lastPos);
|
|
323
|
-
remainingContent = remainingContent;
|
|
324
|
-
remainingContent.replace("\n", "\n ");
|
|
325
|
-
remainingContent = " " + remainingContent;
|
|
326
|
-
return remainingContent;
|
|
327
|
-
}
|
|
328
|
-
}
|
|
329
|
-
return "";
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
void splitContentIntoPages(String content) {
|
|
333
|
-
pages.clear();
|
|
334
|
-
currentPage = 0;
|
|
335
|
-
|
|
336
|
-
int startPos = 0;
|
|
337
|
-
while (startPos < content.length()) {
|
|
338
|
-
int endPos = startPos + maxCharsPerPage;
|
|
339
|
-
if (endPos >= content.length()) {
|
|
340
|
-
pages.push_back(content.substring(startPos));
|
|
341
|
-
break;
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
// Find the last space before maxCharsPerPage to avoid word breaks
|
|
345
|
-
int lastSpace = content.lastIndexOf(' ', endPos);
|
|
346
|
-
if (lastSpace > startPos) {
|
|
347
|
-
endPos = lastSpace;
|
|
348
|
-
}
|
|
349
|
-
|
|
350
|
-
pages.push_back(content.substring(startPos, endPos));
|
|
351
|
-
startPos = endPos + 1;
|
|
352
|
-
}
|
|
353
|
-
}
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
////////////////////////////////////
|
|
358
|
-
//
|
|
359
|
-
// Draw wrapped text
|
|
360
|
-
|
|
361
|
-
// Splits text into wrapped lines (returns vector of lines)
|
|
362
|
-
std::vector<String> wrapText(const String& text, int maxWidth) {
|
|
363
|
-
std::vector<String> lines;
|
|
364
|
-
const lgfx::v1::IFont* font = M5.Display.getFont();
|
|
365
|
-
int currentLineWidth = 0;
|
|
366
|
-
String currentLine = "";
|
|
367
|
-
String currentWord = "";
|
|
368
|
-
|
|
369
|
-
for (char c : text) {
|
|
370
|
-
if (c == ' ' || c == '\n') {
|
|
371
|
-
// Process current word
|
|
372
|
-
int wordWidth = M5.Display.textWidth(currentWord.c_str(), font);
|
|
373
|
-
if (currentLineWidth + wordWidth > maxWidth) {
|
|
374
|
-
lines.push_back(currentLine); // Add previous line
|
|
375
|
-
currentLine = currentWord; // Start new line with current word
|
|
376
|
-
currentLineWidth = wordWidth;
|
|
377
|
-
} else {
|
|
378
|
-
currentLine += currentWord; // Add word to current line
|
|
379
|
-
currentLineWidth += wordWidth;
|
|
380
|
-
}
|
|
381
|
-
|
|
382
|
-
// Process space or newline
|
|
383
|
-
if (c == ' ') {
|
|
384
|
-
int spaceWidth = M5.Display.textWidth(" ", font);
|
|
385
|
-
if (currentLineWidth + spaceWidth > maxWidth) {
|
|
386
|
-
lines.push_back(currentLine);
|
|
387
|
-
currentLine = "";
|
|
388
|
-
currentLineWidth = 0;
|
|
389
|
-
} else {
|
|
390
|
-
currentLine += " ";
|
|
391
|
-
currentLineWidth += spaceWidth;
|
|
392
|
-
}
|
|
393
|
-
} else { // Newline
|
|
394
|
-
lines.push_back(currentLine);
|
|
395
|
-
currentLine = "";
|
|
396
|
-
currentLineWidth = 0;
|
|
397
|
-
}
|
|
398
|
-
currentWord = "";
|
|
399
|
-
} else {
|
|
400
|
-
currentWord += c; // Build current word
|
|
401
|
-
}
|
|
402
|
-
}
|
|
403
|
-
|
|
404
|
-
// Add remaining word/line
|
|
405
|
-
if (currentWord.length() > 0) {
|
|
406
|
-
int wordWidth = M5.Display.textWidth(currentWord.c_str(), font);
|
|
407
|
-
if (currentLineWidth + wordWidth > maxWidth) {
|
|
408
|
-
lines.push_back(currentLine);
|
|
409
|
-
currentLine = currentWord;
|
|
410
|
-
} else {
|
|
411
|
-
currentLine += currentWord;
|
|
412
|
-
}
|
|
413
|
-
lines.push_back(currentLine);
|
|
414
|
-
}
|
|
415
|
-
|
|
416
|
-
return lines;
|
|
417
|
-
}
|
|
418
|
-
|
|
419
|
-
// Prints wrapped lines (one per line)
|
|
420
|
-
void drawWrappedText2(const String& text, int x, int y, int maxWidth) {
|
|
421
|
-
std::vector<String> lines = wrapText(text, maxWidth);
|
|
422
|
-
int lineHeight = M5.Display.fontHeight(M5.Display.getFont());
|
|
423
|
-
int currentY = y;
|
|
424
|
-
|
|
425
|
-
for (const String& line : lines) {
|
|
426
|
-
M5.Display.setCursor(x, currentY);
|
|
427
|
-
M5.Display.print(line); // Print entire line at once
|
|
428
|
-
currentY += lineHeight;
|
|
429
|
-
}
|
|
430
|
-
}
|
|
431
|
-
|
|
432
|
-
// // const GFXfont* currentFont = &fonts::FreeSansBold12pt7b;
|
|
433
|
-
// const GFXfont* currentFont = &fonts::FreeSans12pt7b;
|
|
434
|
-
//
|
|
435
|
-
|
|
436
|
-
void displayCurrentPage() {
|
|
437
|
-
M5.Display.clear();
|
|
438
|
-
M5.Display.setCursor(0, 0);
|
|
439
|
-
|
|
440
|
-
//
|
|
441
|
-
// FONTS MANAGEMENT
|
|
442
|
-
//
|
|
443
|
-
bool showFooter = true;
|
|
444
|
-
if (cfont == "utf8-24") {
|
|
445
|
-
M5.Display.setFont(&fonts::lgfxJapanMinchoP_24);
|
|
446
|
-
shouldWrapText = true;
|
|
447
|
-
}
|
|
448
|
-
// 12 32 40
|
|
449
|
-
if (cfont == "utf8-12") {
|
|
450
|
-
M5.Display.setFont(&fonts::lgfxJapanMinchoP_12);
|
|
451
|
-
shouldWrapText = true;
|
|
452
|
-
}
|
|
453
|
-
if (cfont == "utf8-32") {
|
|
454
|
-
M5.Display.setFont(&fonts::lgfxJapanMinchoP_32);
|
|
455
|
-
shouldWrapText = true;
|
|
456
|
-
showFooter = false;
|
|
457
|
-
}
|
|
458
|
-
if (cfont == "utf8-40") {
|
|
459
|
-
M5.Display.setFont(&fonts::lgfxJapanMinchoP_40);
|
|
460
|
-
shouldWrapText = true;
|
|
461
|
-
showFooter = false;
|
|
462
|
-
}
|
|
463
|
-
|
|
464
|
-
if (cfont == "sans-12") {
|
|
465
|
-
M5.Display.setFont(&fonts::FreeSans12pt7b);
|
|
466
|
-
shouldWrapText = true;
|
|
467
|
-
}
|
|
468
|
-
if (cfont == "sans-bold-12") {
|
|
469
|
-
M5.Display.setFont(&fonts::FreeSansBold12pt7b);
|
|
470
|
-
shouldWrapText = true;
|
|
471
|
-
}
|
|
472
|
-
if (cfont == "sans-bold-24") {
|
|
473
|
-
M5.Display.setFont(&fonts::FreeSansBold24pt7b);
|
|
474
|
-
shouldWrapText = true;
|
|
475
|
-
showFooter = false;
|
|
476
|
-
}
|
|
477
|
-
if (cfont == "sans-24") {
|
|
478
|
-
M5.Display.setFont(&fonts::FreeSans24pt7b);
|
|
479
|
-
shouldWrapText = true;
|
|
480
|
-
showFooter = false;
|
|
481
|
-
}
|
|
482
|
-
// 18
|
|
483
|
-
if (cfont == "sans-bold-18") {
|
|
484
|
-
M5.Display.setFont(&fonts::FreeSansBold18pt7b);
|
|
485
|
-
shouldWrapText = true;
|
|
486
|
-
showFooter = false;
|
|
487
|
-
}
|
|
488
|
-
if (cfont == "sans-18") {
|
|
489
|
-
M5.Display.setFont(&fonts::FreeSans18pt7b);
|
|
490
|
-
shouldWrapText = true;
|
|
491
|
-
}
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
M5.Display.setTextWrap(true);
|
|
495
|
-
if (pages.size() > 0) {
|
|
496
|
-
if (shouldWrapText) {
|
|
497
|
-
drawWrappedText2(pages[currentPage], MARGIN, MARGIN, M5.Display.width() - (MARGIN*2));
|
|
498
|
-
} else {
|
|
499
|
-
M5.Display.print(pages[currentPage]);
|
|
500
|
-
}
|
|
501
|
-
if (showFooter == true) {
|
|
502
|
-
M5.Display.setCursor(0, M5.Display.height() - MARGIN - 20);
|
|
503
|
-
M5.Display.printf("Page %d/%d | batt %d | %s", currentPage + 1, pages.size(), M5.Power.getBatteryLevel(), currentContentType.c_str());
|
|
504
|
-
}
|
|
505
|
-
} else {
|
|
506
|
-
M5.Display.println("No content available");
|
|
507
|
-
}
|
|
508
|
-
M5.Display.display();
|
|
509
|
-
}
|
|
510
|
-
|
|
511
|
-
void goToPage(int page, bool updatePage) {
|
|
512
|
-
if (page >= 0 && page < pages.size()) {
|
|
513
|
-
currentPage = page;
|
|
514
|
-
if (updatePage == true) {
|
|
515
|
-
// setCache("epaper-page-content", String(page));
|
|
516
|
-
setCache((String("epaper-page-") + currentContentType).c_str(), String(page));
|
|
517
|
-
}
|
|
518
|
-
// setCache((String("epaper-page-") + contentType).c_str(), String(page));
|
|
519
|
-
displayCurrentPage();
|
|
520
|
-
}
|
|
521
|
-
}
|
|
522
|
-
|
|
523
|
-
void nextPage() {
|
|
524
|
-
goToPage(currentPage + 1, true);
|
|
525
|
-
}
|
|
526
|
-
|
|
527
|
-
void previousPage() {
|
|
528
|
-
goToPage(currentPage - 1, true);
|
|
529
|
-
}
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
String ensureUTF8(const String& input) {
|
|
533
|
-
String utf8Str;
|
|
534
|
-
utf8Str.reserve(input.length() * 2); // Preallocate space
|
|
535
|
-
for (char c : input) {
|
|
536
|
-
uint8_t byte = static_cast<uint8_t>(c);
|
|
537
|
-
if (byte < 0x80) {
|
|
538
|
-
utf8Str += c; // ASCII
|
|
539
|
-
} else {
|
|
540
|
-
// Convert Latin-1 (ISO-8859-1) to UTF-8
|
|
541
|
-
utf8Str += static_cast<char>(0xC2); // Leading byte for 0x80-0xBF
|
|
542
|
-
utf8Str += static_cast<char>(byte);
|
|
543
|
-
}
|
|
544
|
-
}
|
|
545
|
-
return utf8Str;
|
|
546
|
-
}
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
///////////////////////////////////////////////////////
|
|
559
|
-
//
|
|
560
|
-
//
|
|
561
|
-
// EXECUTION SCENARIOS
|
|
562
|
-
//
|
|
563
|
-
//
|
|
564
|
-
|
|
565
|
-
bool SCENARIO_tryfetch(String contentType, bool toggleWifi) {
|
|
566
|
-
// M5.Display.setFont(&fonts::FreeSans12pt7b);
|
|
567
|
-
if(fetchContentWifi(contentType, toggleWifi)) {
|
|
568
|
-
return true;
|
|
569
|
-
} else {
|
|
570
|
-
log("Second attempt failed. coming back to menu");
|
|
571
|
-
SCENARIO_toggleMenu();
|
|
572
|
-
delay(2000);
|
|
573
|
-
return false;
|
|
574
|
-
}
|
|
575
|
-
// if (fetchContentWifi(contentType, toggleWifi)) {
|
|
576
|
-
// return true;
|
|
577
|
-
// } else {
|
|
578
|
-
// // sleep 3 seconds
|
|
579
|
-
// M5.Display.setCursor(0, 0);
|
|
580
|
-
// M5.Display.clear();
|
|
581
|
-
// M5.Display.println("Failed to fetch content, retrying...");
|
|
582
|
-
// delay(2000);
|
|
583
|
-
// if (fetchContentWifi(contentType, toggleWifi)) {
|
|
584
|
-
// return true;
|
|
585
|
-
// } else {
|
|
586
|
-
// M5.Display.println("Second attempt failed. coming back to menu");
|
|
587
|
-
// delay(2000);
|
|
588
|
-
// SCENARIO_toggleMenu();
|
|
589
|
-
|
|
590
|
-
// }
|
|
591
|
-
|
|
592
|
-
// }
|
|
593
|
-
}
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
bool fetchApi_andSave(String contentType){
|
|
597
|
-
bool answer = false;
|
|
598
|
-
// Fetch and prepare content
|
|
599
|
-
String content = fetchApiContent(contentType);
|
|
600
|
-
if (content.length() > 0) {
|
|
601
|
-
String contentSub = content.substring(0, 100); // Get first 100 characters
|
|
602
|
-
M5.Display.println("Content fetched, saving to cache..." + contentSub);
|
|
603
|
-
delay(100);
|
|
604
|
-
bool res1 = setCache((String("epaper-display-") + contentType).c_str(), content);
|
|
605
|
-
String result = getJsonParam(content, "result");
|
|
606
|
-
int page = getJsonParam(result, "page").toInt();
|
|
607
|
-
if (page != 0) {
|
|
608
|
-
setCache((String("epaper-page-") + contentType).c_str(), String(page));
|
|
609
|
-
}
|
|
610
|
-
String font = getJsonParam(result, "font");
|
|
611
|
-
setCache((String("epaper-font-") + contentType).c_str(), font);
|
|
612
|
-
int maxCharsPerPage = getJsonParam(result, "maxCharsPerPage").toInt();
|
|
613
|
-
setCache((String("epaper-maxCharsPerPage-") + contentType).c_str(), String(maxCharsPerPage));
|
|
614
|
-
M5.Display.setCursor(0, 0);
|
|
615
|
-
M5.Display.clear();
|
|
616
|
-
if (res1) {
|
|
617
|
-
answer = true;
|
|
618
|
-
M5.Display.println("content saved successfully, Loading it... " + String(answer));
|
|
619
|
-
} else {
|
|
620
|
-
M5.Display.println("Failed to save content");
|
|
621
|
-
delay(100);
|
|
622
|
-
}
|
|
623
|
-
delay(100);
|
|
624
|
-
} else {
|
|
625
|
-
M5.Display.println("No content available");
|
|
626
|
-
M5.Display.display();
|
|
627
|
-
}
|
|
628
|
-
return answer;
|
|
629
|
-
}
|
|
630
|
-
|
|
631
|
-
bool fetchContentWifi(String contentType, bool toggleWifi) {
|
|
632
|
-
|
|
633
|
-
bool answer = false;
|
|
634
|
-
if (toggleWifi) { connectWiFi(); }
|
|
635
|
-
bool answer1 = fetchApi_andSave(contentType);
|
|
636
|
-
answer = answer1;
|
|
637
|
-
if (toggleWifi) { disconnectWifi(); }
|
|
638
|
-
return answer;
|
|
639
|
-
}
|
|
640
|
-
|
|
641
|
-
void cacheFoundSd_displayIt(String contentType){
|
|
642
|
-
// String cachedJson = getCache("epaper-display-"+contentType);
|
|
643
|
-
String cachedJson = getCache((String("epaper-display-") + contentType).c_str());
|
|
644
|
-
if (cachedJson.length() > 0) {
|
|
645
|
-
String result = getJsonParam(cachedJson, "result");
|
|
646
|
-
String content = getJsonParam(result, "content");
|
|
647
|
-
|
|
648
|
-
M5.Display.println("Displaying cached content");
|
|
649
|
-
splitContentIntoPages(content);
|
|
650
|
-
// get page
|
|
651
|
-
// int page = getCache("epaper-page-"+contentType).toInt();
|
|
652
|
-
int page = getCache((String("epaper-page-") + contentType).c_str()).toInt();
|
|
653
|
-
// get font
|
|
654
|
-
cfont = getCache((String("epaper-font-") + contentType).c_str());
|
|
655
|
-
// get maxCharsPerPage
|
|
656
|
-
maxCharsPerPage = getCache((String("epaper-maxCharsPerPage-") + contentType).c_str()).toInt();
|
|
657
|
-
// skip page update
|
|
658
|
-
goToPage(page, false);
|
|
659
|
-
} else {
|
|
660
|
-
M5.Display.println("Cached content is empty");
|
|
661
|
-
}
|
|
662
|
-
}
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
void SCENARIO_fetchAll() {
|
|
666
|
-
connectWiFi();
|
|
667
|
-
for (int i = 0; i <= 10; i++) {
|
|
668
|
-
SCENARIO_tryfetch(String(i), false);
|
|
669
|
-
}
|
|
670
|
-
disconnectWifi();
|
|
671
|
-
SCENARIO_toggleMenu();
|
|
672
|
-
}
|
|
673
|
-
|
|
674
|
-
void SCENARIO_removeAllCache() {
|
|
675
|
-
for (int i = 0; i <= 10; i++) {
|
|
676
|
-
removeCacheContent(String(i));
|
|
677
|
-
}
|
|
678
|
-
log("All cache cleared");
|
|
679
|
-
SCENARIO_toggleMenu();
|
|
680
|
-
|
|
681
|
-
}
|
|
682
|
-
|
|
683
|
-
void log(String message) {
|
|
684
|
-
// bottom
|
|
685
|
-
int pheight = M5.Display.getCursorY();
|
|
686
|
-
M5.Display.setCursor(0, 0);
|
|
687
|
-
M5.Display.println("___________________________________________");
|
|
688
|
-
M5.Display.setCursor(0, 0);
|
|
689
|
-
M5.Display.println(">> " + message);
|
|
690
|
-
}
|
|
691
|
-
|
|
692
|
-
void removeCacheContent(String contentType) {
|
|
693
|
-
deleteCache((String("epaper-display-") + contentType).c_str());
|
|
694
|
-
// deleteCache((String("epaper-page-") + contentType).c_str());
|
|
695
|
-
// deleteCache((String("epaper-font-") + contentType).c_str());
|
|
696
|
-
// deleteCache((String("epaper-maxCharsPerPage-") + contentType).c_str());
|
|
697
|
-
}
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
///////////////////////////////////////////////////////
|
|
718
|
-
//
|
|
719
|
-
//
|
|
720
|
-
// TOGGLE MENU SCENARIO
|
|
721
|
-
//
|
|
722
|
-
//
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
bool isMenuVisible = false;
|
|
726
|
-
int topBarHeight = 50;
|
|
727
|
-
int menuItemHeight = 50;
|
|
728
|
-
int paddingMenuTop = 100;
|
|
729
|
-
|
|
730
|
-
void p (String message){
|
|
731
|
-
M5.Display.println(message);
|
|
732
|
-
}
|
|
733
|
-
|
|
734
|
-
void SCENARIO_toggleMenu() {
|
|
735
|
-
if (isMenuVisible) {
|
|
736
|
-
isMenuVisible = false;
|
|
737
|
-
displayCurrentPage();
|
|
738
|
-
} else {
|
|
739
|
-
SCENARIO_showMenu();
|
|
740
|
-
}
|
|
741
|
-
}
|
|
742
|
-
|
|
743
|
-
struct MenuOption {
|
|
744
|
-
String label;
|
|
745
|
-
void (*action)();
|
|
746
|
-
};
|
|
747
|
-
MenuOption menuOptions[] = {
|
|
748
|
-
{"content 1", []() { SCENARIO_displayContent_else_fetch("1"); }},
|
|
749
|
-
{"content 2", []() { SCENARIO_displayContent_else_fetch("2"); }},
|
|
750
|
-
{"content 3", []() { SCENARIO_displayContent_else_fetch("3"); }},
|
|
751
|
-
{"content 4", []() { SCENARIO_displayContent_else_fetch("4"); }},
|
|
752
|
-
{"content 5", []() { SCENARIO_displayContent_else_fetch("5"); }},
|
|
753
|
-
{"content 6", []() { SCENARIO_displayContent_else_fetch("6"); }},
|
|
754
|
-
{"content 7", []() { SCENARIO_displayContent_else_fetch("7"); }},
|
|
755
|
-
{"content 8", []() { SCENARIO_displayContent_else_fetch("8"); }},
|
|
756
|
-
{"content 9", []() { SCENARIO_displayContent_else_fetch("9"); }},
|
|
757
|
-
{"content 10", []() { SCENARIO_displayContent_else_fetch("10"); }},
|
|
758
|
-
{"fetch all", []() { SCENARIO_fetchAll(); }},
|
|
759
|
-
|
|
760
|
-
{"delete all", []() { SCENARIO_removeAllCache(); }}
|
|
761
|
-
};
|
|
762
|
-
void SCENARIO_showMenu() {
|
|
763
|
-
|
|
764
|
-
// clean display and every 100 height, show option
|
|
765
|
-
isMenuVisible = true;
|
|
766
|
-
M5.Display.clear();
|
|
767
|
-
|
|
768
|
-
for (int i = 0; i < sizeof(menuOptions) / sizeof(MenuOption); i++) {
|
|
769
|
-
M5.Display.setCursor(50, topBarHeight + paddingMenuTop + (i * menuItemHeight));
|
|
770
|
-
M5.Display.printf("%d. %s\n", i + 1, menuOptions[i].label.c_str());
|
|
771
|
-
}
|
|
772
|
-
|
|
773
|
-
M5.Display.display();
|
|
774
|
-
}
|
|
775
|
-
|
|
776
|
-
void SCENARIO_loop_menuTouch(){
|
|
777
|
-
if (!isMenuVisible) return;
|
|
778
|
-
|
|
779
|
-
auto touch = M5.Touch.getDetail();
|
|
780
|
-
if (touch.isPressed() &&
|
|
781
|
-
touch.y >= topBarHeight
|
|
782
|
-
) {
|
|
783
|
-
int touchY = touch.y;
|
|
784
|
-
int optionIndex = (touchY - topBarHeight - paddingMenuTop) / menuItemHeight; // Assuming each option is 50px high
|
|
785
|
-
if (optionIndex >= 0 && optionIndex < sizeof(menuOptions) / sizeof(MenuOption)) {
|
|
786
|
-
M5.Display.setCursor(0, 0);
|
|
787
|
-
M5.Display.println("[Touch detected in menu] =>" + menuOptions[optionIndex].label);
|
|
788
|
-
menuOptions[optionIndex].action();
|
|
789
|
-
isMenuVisible = false;
|
|
790
|
-
// displayCurrentPage();
|
|
791
|
-
}
|
|
792
|
-
}
|
|
793
|
-
|
|
794
|
-
}
|
|
795
|
-
|
|
796
|
-
void SCENARIO_displayContent_else_fetch(String contentType) {
|
|
797
|
-
currentContentType = contentType;
|
|
798
|
-
if (getCache((String("epaper-display-") + contentType).c_str()).length() == 0) {
|
|
799
|
-
SCENARIO_tryfetch(contentType, true);
|
|
800
|
-
cacheFoundSd_displayIt(contentType);
|
|
801
|
-
} else {
|
|
802
|
-
cacheFoundSd_displayIt(contentType);
|
|
803
|
-
}
|
|
804
|
-
}
|
|
805
|
-
|
|
806
|
-
///////////////////////////////////////////////////////
|
|
807
|
-
//
|
|
808
|
-
//
|
|
809
|
-
// EXECUTION LOOP
|
|
810
|
-
//
|
|
811
|
-
//
|
|
812
|
-
// start time for deep sleep delay
|
|
813
|
-
unsigned long deepSleepStartTime;
|
|
814
|
-
|
|
815
|
-
void setup() {
|
|
816
|
-
M5.begin();
|
|
817
|
-
deepSleepStartTime = millis();
|
|
818
|
-
M5.Display.setRotation(0);
|
|
819
|
-
// init font
|
|
820
|
-
M5.Display.setFont(&fonts::FreeSans12pt7b);
|
|
821
|
-
// M5.Display.setEpdMode(epd_fast);
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
// POWER > low energey mode test
|
|
825
|
-
M5.Display.setEpdMode(epd_text);
|
|
826
|
-
M5.Display.println("APP STARTED");
|
|
827
|
-
// Initialize buttons
|
|
828
|
-
pinMode(BUTTON_UP_PIN, INPUT_PULLUP);
|
|
829
|
-
pinMode(BUTTON_DOWN_PIN, INPUT_PULLUP);
|
|
830
|
-
pinMode(BUTTON_PRESS_PIN, INPUT_PULLUP);
|
|
831
|
-
|
|
832
|
-
initSDcard();
|
|
833
|
-
|
|
834
|
-
// POWER > SHUTDOWN WIFI AND OTHER AT SETUP
|
|
835
|
-
WiFi.mode(WIFI_OFF);
|
|
836
|
-
btStop();
|
|
837
|
-
// other sensors
|
|
838
|
-
// M5.SHT30.powerOff();
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
// SCENARIO_displayContent_else_fetch("0");
|
|
843
|
-
SCENARIO_toggleMenu();
|
|
844
|
-
}
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
// add currentTimerMs
|
|
850
|
-
bool canTriggerTouch = true;
|
|
851
|
-
int currentTimerMs = 0;
|
|
852
|
-
|
|
853
|
-
unsigned long previousMillis = 0;
|
|
854
|
-
const long interval = 100;
|
|
855
|
-
|
|
856
|
-
void loop() {
|
|
857
|
-
// currentTimerMs += 100;
|
|
858
|
-
unsigned long currentMillis = millis();
|
|
859
|
-
|
|
860
|
-
// POWER > NON BLOCKING CPU WAIT
|
|
861
|
-
if (currentMillis - previousMillis >= interval) {
|
|
862
|
-
previousMillis = currentMillis;
|
|
863
|
-
currentTimerMs += interval;
|
|
864
|
-
|
|
865
|
-
// Check buttons
|
|
866
|
-
if (digitalRead(BUTTON_DOWN_PIN) == LOW) {
|
|
867
|
-
nextPage();
|
|
868
|
-
delay(200); // Debounce
|
|
869
|
-
currentTimerMs = 0;
|
|
870
|
-
}
|
|
871
|
-
else if (digitalRead(BUTTON_UP_PIN) == LOW) {
|
|
872
|
-
previousPage();
|
|
873
|
-
delay(200); // Debounce
|
|
874
|
-
currentTimerMs = 0;
|
|
875
|
-
}
|
|
876
|
-
else if (digitalRead(BUTTON_PRESS_PIN) == LOW) {
|
|
877
|
-
startDeepSleep();
|
|
878
|
-
currentTimerMs = 0;
|
|
879
|
-
|
|
880
|
-
}
|
|
881
|
-
// In your loop() function
|
|
882
|
-
M5.update(); // Required for touch detection
|
|
883
|
-
|
|
884
|
-
auto touch = M5.Touch.getDetail();
|
|
885
|
-
int cornerWidth = 50; // Width of the touch area
|
|
886
|
-
int screenWidth = M5.Display.width();
|
|
887
|
-
int screenHeight = M5.Display.height();
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
SCENARIO_loop_menuTouch();
|
|
891
|
-
|
|
892
|
-
handleTouchZone(0, 0, cornerWidth, topBarHeight, SCENARIO_toggleMenu, false);
|
|
893
|
-
|
|
894
|
-
// if more than deep sleep delay, deep sleep,
|
|
895
|
-
if (currentTimerMs > ( 6 * 60 * 1000 )) {
|
|
896
|
-
M5.Display.println("[Deep sleep] triggered after " + String(currentTimerMs) + " ms");
|
|
897
|
-
startDeepSleep();
|
|
898
|
-
}
|
|
899
|
-
// reenable touch detection after 1 minute
|
|
900
|
-
if (currentTimerMs > (1*60*1000)) {
|
|
901
|
-
canTriggerTouch = true;
|
|
902
|
-
}
|
|
903
|
-
}
|
|
904
|
-
// delay(100);
|
|
905
|
-
}
|
|
906
|
-
|
|
907
|
-
void handleTouchZone(int x1, int y1, int x2, int y2, void (*action)(), bool delayAfter) {
|
|
908
|
-
auto touch = M5.Touch.getDetail();
|
|
909
|
-
if (touch.isPressed() &&
|
|
910
|
-
touch.x >= x1 && touch.x <= x2 &&
|
|
911
|
-
touch.y >= y1 && touch.y <= y2 &&
|
|
912
|
-
canTriggerTouch
|
|
913
|
-
) {
|
|
914
|
-
if (delayAfter) {
|
|
915
|
-
canTriggerTouch = false;
|
|
916
|
-
}
|
|
917
|
-
M5.Display.setCursor(0, 0);
|
|
918
|
-
currentTimerMs = 0;
|
|
919
|
-
M5.Display.println("[Touch detected in zone]");
|
|
920
|
-
action();
|
|
921
|
-
}
|
|
922
|
-
}
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
void startDeepSleep(){
|
|
927
|
-
|
|
928
|
-
// UNCOMMENT THAT LINE FOR DEEPSLEEP SCREEN
|
|
929
|
-
// DISPLAY deepsleep_screen
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
// OLD WAY
|
|
933
|
-
M5.Display.setFont(&fonts::FreeSans12pt7b);
|
|
934
|
-
shouldWrapText = true;
|
|
935
|
-
M5.Display.setCursor(M5.Display.width() - 200, M5.Display.height() - MARGIN - 20);
|
|
936
|
-
M5.Display.println("[deep sleep]");
|
|
937
|
-
//M5.Display.update();
|
|
938
|
-
delay(1000);
|
|
939
|
-
|
|
940
|
-
// Configure GPIO hold to keep main power active during deep sleep
|
|
941
|
-
gpio_hold_en((gpio_num_t)M5EPD_MAIN_PWR_PIN);
|
|
942
|
-
gpio_deep_sleep_hold_en();
|
|
943
|
-
|
|
944
|
-
// POWER Put e-ink display into low-power mode >> APPARENTLY CRITICAL as not managed by deep sleep
|
|
945
|
-
// Reconfigure pins for deep sleep wake-up
|
|
946
|
-
pinMode(BUTTON_PRESS_PIN, INPUT); // Remove pull-up
|
|
947
|
-
pinMode(BUTTON_UP_PIN, INPUT); // Remove pull-up
|
|
948
|
-
pinMode(BUTTON_DOWN_PIN, INPUT); // Remove pull-up
|
|
949
|
-
M5.Display.sleep();
|
|
950
|
-
WiFi.mode(WIFI_OFF);
|
|
951
|
-
btStop();
|
|
952
|
-
|
|
953
|
-
// Enable button wake-up >> STILL DOES NOT WORK
|
|
954
|
-
esp_sleep_enable_ext0_wakeup(GPIO_NUM_37, LOW);
|
|
955
|
-
M5.Power.powerOff();
|
|
956
|
-
}
|