diff --git a/.github/workflows/build-test.yml b/.github/workflows/build-test.yml
index 47777427..407825ec 100644
--- a/.github/workflows/build-test.yml
+++ b/.github/workflows/build-test.yml
@@ -41,8 +41,14 @@ jobs:
pio run --environment OpenEPaperLink_AP_and_Flasher
pio run --target buildfs --environment OpenEPaperLink_AP_and_Flasher
- - name: Build ESP32_S3_16_8_YELLOW_AP]
+ - name: Build ESP32_S3_16_8_YELLOW_AP
run: |
cd ESP32_AP-Flasher
pio run --environment ESP32_S3_16_8_YELLOW_AP
pio run --target buildfs --environment ESP32_S3_16_8_YELLOW_AP
+
+ - name: OpenEPaperLink_Mini_AP_v4
+ run: |
+ cd ESP32_AP-Flasher
+ pio run --environment OpenEPaperLink_Mini_AP_v4
+ pio run --target buildfs --environment OpenEPaperLink_Mini_AP_v4
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index cfbf9a82..a3a1949f 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -46,24 +46,6 @@ jobs:
# cd /home/runner/work/OpenEPaperLink/OpenEPaperLink/ESP32_AP-Flasher
# python gzip_wwwfiles.py
- - name: Build firmware for Simple_AP
- run: |
- cd ESP32_AP-Flasher
- export PLATFORMIO_BUILD_FLAGS="-D BUILD_VERSION=${{ github.ref_name }} -D SHA=$GITHUB_SHA"
- pio run --environment Simple_AP
- pio run --target buildfs --environment Simple_AP
- mkdir /home/runner/work/OpenEPaperLink/OpenEPaperLink/Simple_AP
- cp ~/.platformio/packages/framework-arduinoespressif32/tools/partitions/boot_app0.bin /home/runner/work/OpenEPaperLink/OpenEPaperLink/Simple_AP/boot_app0.bin
- cp .pio/build/Simple_AP/firmware.bin /home/runner/work/OpenEPaperLink/OpenEPaperLink/Simple_AP/firmware.bin
- cp .pio/build/Simple_AP/bootloader.bin /home/runner/work/OpenEPaperLink/OpenEPaperLink/Simple_AP/bootloader.bin
- cp .pio/build/Simple_AP/partitions.bin /home/runner/work/OpenEPaperLink/OpenEPaperLink/Simple_AP/partitions.bin
- cp .pio/build/Simple_AP/littlefs.bin /home/runner/work/OpenEPaperLink/OpenEPaperLink/Simple_AP/littlefs.bin
- cd /home/runner/work/OpenEPaperLink/OpenEPaperLink/Simple_AP
- esptool.py --chip esp32 merge_bin -o merged-firmware.bin --flash_mode dio --flash_freq 40m --flash_size 4MB 0x1000 bootloader.bin 0x8000 partitions.bin 0xe000 boot_app0.bin 0x10000 firmware.bin 0x290000 littlefs.bin
- cd /home/runner/work/OpenEPaperLink/OpenEPaperLink
- cp Simple_AP/firmware.bin espbinaries/Simple_AP.bin
- cp Simple_AP/merged-firmware.bin espbinaries/Simple_AP_full.bin
-
- name: Build firmware for OpenEPaperLink_Mini_AP
run: |
cd ESP32_AP-Flasher
@@ -176,6 +158,24 @@ jobs:
cp OpenEPaperLink_PoE_AP/firmware.bin espbinaries/OpenEPaperLink_PoE_AP.bin
cp OpenEPaperLink_PoE_AP/merged-firmware.bin espbinaries/OpenEPaperLink_PoE_AP_full.bin
+ - name: Build firmware for OpenEPaperLink_Mini_AP_v4
+ run: |
+ cd ESP32_AP-Flasher
+ export PLATFORMIO_BUILD_FLAGS="-D BUILD_VERSION=${{ github.ref_name }} -D SHA=$GITHUB_SHA"
+ pio run --environment OpenEPaperLink_Mini_AP_v4
+ pio run --target buildfs --environment OpenEPaperLink_Mini_AP_v4
+ mkdir /home/runner/work/OpenEPaperLink/OpenEPaperLink/OpenEPaperLink_Mini_AP_v4
+ cp ~/.platformio/packages/framework-arduinoespressif32/tools/partitions/boot_app0.bin /home/runner/work/OpenEPaperLink/OpenEPaperLink/OpenEPaperLink_Mini_AP_v4/boot_app0.bin
+ cp .pio/build/OpenEPaperLink_Mini_AP_v4/firmware.bin /home/runner/work/OpenEPaperLink/OpenEPaperLink/OpenEPaperLink_Mini_AP_v4/firmware.bin
+ cp .pio/build/OpenEPaperLink_Mini_AP_v4/bootloader.bin /home/runner/work/OpenEPaperLink/OpenEPaperLink/OpenEPaperLink_Mini_AP_v4/bootloader.bin
+ cp .pio/build/OpenEPaperLink_Mini_AP_v4/partitions.bin /home/runner/work/OpenEPaperLink/OpenEPaperLink/OpenEPaperLink_Mini_AP_v4/partitions.bin
+ cp .pio/build/OpenEPaperLink_Mini_AP_v4/littlefs.bin /home/runner/work/OpenEPaperLink/OpenEPaperLink/OpenEPaperLink_Mini_AP_v4/littlefs.bin
+ cd /home/runner/work/OpenEPaperLink/OpenEPaperLink/OpenEPaperLink_Mini_AP_v4
+ esptool.py --chip esp32-s3 merge_bin -o merged-firmware.bin --flash_mode dio --flash_freq 80m --flash_size 16MB 0x0000 bootloader.bin 0x8000 partitions.bin 0xe000 boot_app0.bin 0x10000 firmware.bin 0x00910000 littlefs.bin
+ cd /home/runner/work/OpenEPaperLink/OpenEPaperLink
+ cp OpenEPaperLink_Mini_AP_v4/firmware.bin espbinaries/OpenEPaperLink_Mini_AP_v4.bin
+ cp OpenEPaperLink_Mini_AP_v4/merged-firmware.bin espbinaries/OpenEPaperLink_Mini_AP_v4_full.bin
+
- name: generate release json file
run: |
mkdir jsonfiles
diff --git a/ESP32_AP-Flasher/data/tagtypes/33.json b/ESP32_AP-Flasher/data/tagtypes/33.json
index cd3e8dd6..b98a8546 100644
--- a/ESP32_AP-Flasher/data/tagtypes/33.json
+++ b/ESP32_AP-Flasher/data/tagtypes/33.json
@@ -13,7 +13,7 @@
},
"shortlut": 0,
"options": ["button", "led"],
- "contentids": [22, 23, 1, 2, 3, 4, 5, 7, 8, 9, 10, 11, 16, 17, 18, 19, 20, 27],
+ "contentids": [22, 23, 1, 2, 3, 4, 5, 7, 8, 9, 10, 11, 16, 17, 18, 19, 20, 26, 27],
"usetemplate": 1,
"template": {
"1": {
diff --git a/ESP32_AP-Flasher/data/tagtypes/E1.json b/ESP32_AP-Flasher/data/tagtypes/E1.json
new file mode 100644
index 00000000..7d9238cc
--- /dev/null
+++ b/ESP32_AP-Flasher/data/tagtypes/E1.json
@@ -0,0 +1,29 @@
+{
+ "name": "TFT 160x80",
+ "width": 160,
+ "height": 80,
+ "rotatebuffer": 1,
+ "bpp": 16,
+ "colors": 4,
+ "colortable": {
+ "white": [ 255, 255, 255 ],
+ "black": [ 0, 0, 0 ],
+ "red": [ 255, 0, 0 ],
+ "gray": [ 150, 150, 150 ]
+ },
+ "shortlut": 0,
+ "options": [],
+ "contentids": [ 22, 21 ],
+ "template": {
+ "21": [
+ { "box": [ 0, 0, 160, 80, 1 ] },
+ { "text": [ 1, 3, "OpenEpaperLink AP", "calibrib16.vlw", 2, 0, 0, 1 ] },
+ { "text": [ 1, 28, "IP:", "REFSAN12.vlw", "#888888", 0, 0, 1 ] },
+ { "text": [ 45, 28, "{ap_ip}", "REFSAN12.vlw", 0, 0, 0, 1 ] },
+ { "text": [ 1, 45, "Ch:", "REFSAN12.vlw", "#888888", 0, 0, 1 ] },
+ { "text": [ 45, 45, "{ap_ch}", "REFSAN12.vlw", 0, 0, 0, "1" ] },
+ { "text": [ 1, 62, "Tags:", "REFSAN12.vlw", "#888888", 0, 0, 1 ] },
+ { "text": [ 45, 62, "{ap_tagcount}", "REFSAN12.vlw", 0, 0, 0, "1" ] }
+ ]
+ }
+}
diff --git a/ESP32_AP-Flasher/data/www/content_cards.json.gz b/ESP32_AP-Flasher/data/www/content_cards.json.gz
index 8517eab3..4abf9d0c 100644
Binary files a/ESP32_AP-Flasher/data/www/content_cards.json.gz and b/ESP32_AP-Flasher/data/www/content_cards.json.gz differ
diff --git a/ESP32_AP-Flasher/data/www/flash.js.gz b/ESP32_AP-Flasher/data/www/flash.js.gz
new file mode 100644
index 00000000..b605a095
Binary files /dev/null and b/ESP32_AP-Flasher/data/www/flash.js.gz differ
diff --git a/ESP32_AP-Flasher/data/www/index.html.gz b/ESP32_AP-Flasher/data/www/index.html.gz
index 32b3bbbe..c24f2125 100644
Binary files a/ESP32_AP-Flasher/data/www/index.html.gz and b/ESP32_AP-Flasher/data/www/index.html.gz differ
diff --git a/ESP32_AP-Flasher/data/www/main.css.gz b/ESP32_AP-Flasher/data/www/main.css.gz
index 7b94706c..8204db0e 100644
Binary files a/ESP32_AP-Flasher/data/www/main.css.gz and b/ESP32_AP-Flasher/data/www/main.css.gz differ
diff --git a/ESP32_AP-Flasher/data/www/main.js.gz b/ESP32_AP-Flasher/data/www/main.js.gz
index f17f8da0..345265f1 100644
Binary files a/ESP32_AP-Flasher/data/www/main.js.gz and b/ESP32_AP-Flasher/data/www/main.js.gz differ
diff --git a/ESP32_AP-Flasher/include/flasher.h b/ESP32_AP-Flasher/include/flasher.h
index 963021ac..0164503c 100644
--- a/ESP32_AP-Flasher/include/flasher.h
+++ b/ESP32_AP-Flasher/include/flasher.h
@@ -8,7 +8,7 @@ bool doAPFlash();
bool doAPUpdate(uint8_t type);
void flashCountDown(uint8_t c);
-#ifdef OPENEPAPERLINK_PCB
+#ifdef HAS_EXT_FLASHER
bool extTagConnected();
bool doTagFlash();
#endif
@@ -20,6 +20,7 @@ class flasher {
char md5char[34];
uint8_t tagtype;
uint8_t *infoblock = nullptr;
+ bool includeInfoBlock = false;
// Infoblock structure:
// 0x00-0x0F - Calibration data
@@ -35,7 +36,7 @@ class flasher {
flasher();
~flasher();
bool connectTag(uint8_t port);
- void getFirmwareMD5();
+ bool getFirmwareMD5();
bool getFirmwareMac();
bool findTagByMD5();
bool findTagByType(uint8_t type);
diff --git a/ESP32_AP-Flasher/include/ips_display.h b/ESP32_AP-Flasher/include/ips_display.h
index eb86a2fe..e373e2d1 100644
--- a/ESP32_AP-Flasher/include/ips_display.h
+++ b/ESP32_AP-Flasher/include/ips_display.h
@@ -1,12 +1,13 @@
#include
#include
-#ifdef YELLOW_IPS_AP
+#ifdef HAS_TFT
extern TFT_eSPI tft2;
-extern int32_t tftid;
extern uint8_t YellowSense;
+extern bool tftOverride;
void TFTLog(String text);
+void sendAvail(uint8_t wakeupReason);
#endif
diff --git a/ESP32_AP-Flasher/include/leds.h b/ESP32_AP-Flasher/include/leds.h
index 0f094c11..6f9e0434 100644
--- a/ESP32_AP-Flasher/include/leds.h
+++ b/ESP32_AP-Flasher/include/leds.h
@@ -34,6 +34,8 @@ void shortBlink(CRGB cname);
void showColorPattern(CRGB colorone, CRGB colortwo, CRGB colorthree);
void rgbIdle();
void addFadeColor(CRGB cname);
+
#endif
-void quickBlink(uint8_t repeat);
\ No newline at end of file
+void quickBlink(uint8_t repeat);
+void addFadeMono(uint8_t value);
diff --git a/ESP32_AP-Flasher/include/newproto.h b/ESP32_AP-Flasher/include/newproto.h
index 61dc3c95..bdb71e04 100644
--- a/ESP32_AP-Flasher/include/newproto.h
+++ b/ESP32_AP-Flasher/include/newproto.h
@@ -16,6 +16,7 @@ extern bool checkCRC(void* p, uint8_t len);
extern void processBlockRequest(struct espBlockRequest* br);
extern void prepareCancelPending(const uint8_t dst[8]);
extern void prepareIdleReq(const uint8_t* dst, uint16_t nextCheckin);
+extern void prepareDataAvail(const uint8_t* dst);
extern void prepareDataAvail(uint8_t* data, uint16_t len, uint8_t dataType, const uint8_t* dst);
extern bool prepareDataAvail(String& filename, uint8_t dataType, uint8_t dataTypeArgument, const uint8_t* dst, uint16_t nextCheckin, bool resend = false);
extern void prepareExternalDataAvail(struct pendingData* pending, IPAddress remoteIP);
@@ -41,5 +42,5 @@ uint16_t countQueueItem(const uint8_t* targetMac);
extern PendingItem* getQueueItem(const uint8_t* targetMac);
extern PendingItem* getQueueItem(const uint8_t* targetMac, const uint64_t dataVer);
void checkQueue(const uint8_t* targetMac);
-bool queueDataAvail(struct pendingData* pending);
+bool queueDataAvail(struct pendingData* pending, bool local);
uint8_t* getDataForFile(fs::File& file);
diff --git a/ESP32_AP-Flasher/include/powermgt.h b/ESP32_AP-Flasher/include/powermgt.h
index 958a4522..2fbd68a0 100644
--- a/ESP32_AP-Flasher/include/powermgt.h
+++ b/ESP32_AP-Flasher/include/powermgt.h
@@ -1,5 +1,3 @@
#include
-
-//void doLeds();
void powerControl(bool powerState, uint8_t* pin, uint8_t pincount);
diff --git a/ESP32_AP-Flasher/include/serialap.h b/ESP32_AP-Flasher/include/serialap.h
index 0c44e3ac..635ad1ce 100644
--- a/ESP32_AP-Flasher/include/serialap.h
+++ b/ESP32_AP-Flasher/include/serialap.h
@@ -9,6 +9,7 @@ extern struct espSetChannelPower curChannel;
#define AP_STATE_REQUIRED_POWER_CYCLE 4
#define AP_STATE_FAILED 5
#define AP_STATE_COMING_ONLINE 6
+#define AP_STATE_NORADIO 7
struct APInfoS {
bool isOnline = false;
diff --git a/ESP32_AP-Flasher/include/settings.h b/ESP32_AP-Flasher/include/settings.h
index 6ca8b425..b24c367d 100644
--- a/ESP32_AP-Flasher/include/settings.h
+++ b/ESP32_AP-Flasher/include/settings.h
@@ -1,7 +1,7 @@
#include
#define FLASHER_AP_PORT 0
-#ifdef OPENEPAPERLINK_PCB
+#ifdef HAS_EXT_FLASHER
#define FLASHER_EXT_PORT 1
#define FLASHER_ALTRADIO_PORT 2
#endif
diff --git a/ESP32_AP-Flasher/include/tag_db.h b/ESP32_AP-Flasher/include/tag_db.h
index 8563b4c2..910ad43c 100644
--- a/ESP32_AP-Flasher/include/tag_db.h
+++ b/ESP32_AP-Flasher/include/tag_db.h
@@ -55,7 +55,7 @@ class tagRecord {
struct Config {
uint8_t channel;
char alias[32];
- int16_t led;
+ uint8_t led;
uint8_t tft;
uint8_t language;
uint8_t maxsleep;
diff --git a/ESP32_AP-Flasher/include/usbflasher.h b/ESP32_AP-Flasher/include/usbflasher.h
index 037b5cf3..44aeb248 100644
--- a/ESP32_AP-Flasher/include/usbflasher.h
+++ b/ESP32_AP-Flasher/include/usbflasher.h
@@ -1,4 +1,10 @@
#include
+#define TRANSPORT_USB 0
+#define TRANSPORT_TCP 1
-void usbFlasherTask(void* parameter);
\ No newline at end of file
+#ifdef HAS_USB
+void usbFlasherTask(void* parameter);
+#endif
+void flasherDataHandler(uint8_t* data, size_t len, uint8_t transportType);
+void processFlasherCommand(struct flasherCommand* cmd, uint8_t transportType);
diff --git a/ESP32_AP-Flasher/include/web.h b/ESP32_AP-Flasher/include/web.h
index 68338cda..e243626c 100644
--- a/ESP32_AP-Flasher/include/web.h
+++ b/ESP32_AP-Flasher/include/web.h
@@ -13,6 +13,7 @@ void wsSendTaginfo(const uint8_t *mac, uint8_t syncMode);
void wsSendSysteminfo();
void wsSendAPitem(struct APlist *apitem);
void wsSerial(const String &text);
+void wsSerial(const String &text, const String &color);
uint8_t wsClientCount();
extern AsyncWebSocket ws;
diff --git a/ESP32_AP-Flasher/include/webflasher.h b/ESP32_AP-Flasher/include/webflasher.h
new file mode 100644
index 00000000..f1ccd0f1
--- /dev/null
+++ b/ESP32_AP-Flasher/include/webflasher.h
@@ -0,0 +1,30 @@
+#include
+
+#ifdef HAS_EXT_FLASHER
+
+#include "web.h"
+
+#define WEBFLASH_ENABLE_AUTOFLASH 1
+#define WEBFLASH_ENABLE_USBFLASHER 2
+#define WEBFLASH_FOCUS 3
+#define WEBFLASH_BLUR 4
+
+class Logger : public Print {
+ public:
+ Logger();
+
+ ~Logger();
+
+ size_t write(uint8_t c) override;
+ size_t write(const uint8_t* buffer, size_t size) override;
+ size_t printf(const char* format, ...);
+ size_t println(const char* text);
+
+ private:
+};
+
+void webFlasherTask(void* parameter);
+void handleWSdata(uint8_t* data, size_t len, AsyncWebSocketClient* client);
+void sendDataToClient(const uint8_t* data, size_t len);
+
+#endif
\ No newline at end of file
diff --git a/ESP32_AP-Flasher/include/zbs_interface.h b/ESP32_AP-Flasher/include/zbs_interface.h
index 2bdaafa4..9cebe9f5 100644
--- a/ESP32_AP-Flasher/include/zbs_interface.h
+++ b/ESP32_AP-Flasher/include/zbs_interface.h
@@ -13,7 +13,7 @@ public:
void setSpeed(uint32_t speed);
void set_power(uint8_t state);
void enable_debug();
- void reset();
+ void reset(bool leavepower = true);
void send_byte(uint8_t data);
uint8_t read_byte();
void write_byte(uint8_t cmd, uint8_t addr, uint8_t data);
diff --git a/ESP32_AP-Flasher/platformio.ini b/ESP32_AP-Flasher/platformio.ini
index 927c1b58..da739e78 100644
--- a/ESP32_AP-Flasher/platformio.ini
+++ b/ESP32_AP-Flasher/platformio.ini
@@ -59,6 +59,7 @@ build_flags =
-D BOARD_HAS_PSRAM
-D SAVE_SPACE
-D POWER_NO_SOFT_POWER
+ -D SAVE_SPACE
-D FLASHER_AP_SS=11
-D FLASHER_AP_CLK=9
-D FLASHER_AP_MOSI=10
@@ -71,7 +72,7 @@ build_flags =
-D FLASHER_LED=15
-D FLASHER_RGB_LED=33
build_src_filter =
- +<*>----
+ +<*>-----
board_build.psram_type=qspi_opi
board_upload.maximum_size = 4194304
board_upload.maximum_ram_size = 327680
@@ -107,7 +108,7 @@ build_flags =
-D FLASHER_LED=15
-D FLASHER_RGB_LED=-1
build_src_filter =
- +<*>----
+ +<*>-----
board_build.psram_type=qspi_opi
board_upload.maximum_size = 4194304
board_upload.maximum_ram_size = 327680
@@ -126,7 +127,7 @@ build_unflags =
build_flags =
-std=gnu++17
${env.build_flags}
- -D OPENEPAPERLINK_PCB
+ -D HAS_EXT_FLASHER
-D ARDUINO_USB_MODE=0
-D CONFIG_ESP32S3_SPIRAM_SUPPORT=1
-D CONFIG_SPIRAM_USE_MALLOC=1
@@ -197,8 +198,9 @@ build_flags =
-D FLASHER_AP_TXD=17
-D FLASHER_AP_RXD=16
-D FLASHER_LED=22
+ -D SAVE_SPACE
build_src_filter =
- +<*>----
+ +<*>-----
; ----------------------------------------------------------------------------------------
; !!! this configuration expects an ESP32-S3 16MB Flash 8MB RAM
; ----------------------------------------------------------------------------------------
@@ -215,7 +217,7 @@ lib_deps =
build_flags =
-std=gnu++17
${env.build_flags}
- -D YELLOW_IPS_AP
+ -D HAS_TFT
-D CORE_DEBUG_LEVEL=0
-D ARDUINO_USB_MODE=0
-D CONFIG_ESP32S3_SPIRAM_SUPPORT=1
@@ -254,8 +256,9 @@ build_flags =
-D SERIAL_FLASHER_INTERFACE_UART=1
-D SERIAL_FLASHER_BOOT_HOLD_TIME_MS=50
-D SERIAL_FLASHER_RESET_HOLD_TIME_MS=100
+ -D C6_OTA_FLASHING
build_src_filter =
- +<*>--
+ +<*>---
board_build.flash_mode=qio
board_build.arduino.memory_type = qio_opi
board_build.psram_type=qspi_opi
@@ -302,7 +305,7 @@ build_flags =
-D SERIAL_FLASHER_RESET_HOLD_TIME_MS=100
-D C6_OTA_FLASHING
build_src_filter =
- +<*>--
+ +<*>---
board_build.flash_mode=qio
board_build.arduino.memory_type = qio_opi
board_build.psram_type=qspi_opi
@@ -348,12 +351,102 @@ build_flags =
-D SD_CARD_MOSI=14
-D SD_CARD_SS=12
build_src_filter =
- +<*>---
+ +<*>----
board_build.flash_mode=qio
board_upload.maximum_size = 16777216
board_upload.maximum_ram_size = 327680
board_upload.flash_size = 16MB
; ----------------------------------------------------------------------------------------
+; !!! this configuration expects an ESP32-S3 16MB Flash 8MB RAM
+; ----------------------------------------------------------------------------------------
+[env:OpenEPaperLink_Mini_AP_v4]
+board = esp32-s3-devkitc-1
+board_build.partitions = large_spiffs_16MB.csv
+monitor_dtr = 0
+monitor_rts = 0
+build_unflags =
+ -std=gnu++11
+ -D CONFIG_MBEDTLS_INTERNAL_MEM_ALLOC=y
+ -D ILI9341_DRIVER
+lib_deps =
+ ${env.lib_deps}
+build_flags =
+ -std=gnu++17
+ ${env.build_flags}
+ -D HAS_TFT
+ -D HAS_EXT_FLASHER
+ -D CORE_DEBUG_LEVEL=1
+ -D ARDUINO_USB_CDC_ON_BOOT=1
+ -D CONFIG_ESP32S3_SPIRAM_SUPPORT=1
+ -D CONFIG_SPIRAM_USE_MALLOC=1
+ -D POWER_NO_SOFT_POWER
+ -D BOARD_HAS_PSRAM
+ -D POWER_RAMPING
+ -D POWER_HIGH_SIDE_DRIVER
+ -D CONFIG_MBEDTLS_EXTERNAL_MEM_ALLOC=y
+ -D FLASHER_AP_SS=-1
+ -D FLASHER_AP_CLK=-1
+ -D FLASHER_AP_MOSI=-1
+ -D FLASHER_AP_MISO=-1
+ -D FLASHER_AP_RESET=47
+ -D FLASHER_AP_POWER={-1}
+ -D FLASHER_AP_TEST=-1
+ -D FLASHER_AP_TXD=17
+ -D FLASHER_AP_RXD=18
+ -D FLASHER_DEBUG_TXD=15
+ -D FLASHER_DEBUG_RXD=7
+ -D FLASHER_DEBUG_PROG=21
+ -D FLASHER_LED=16
+ -D FLASHER_EXT_SS=40
+ -D FLASHER_EXT_CLK=41
+ -D FLASHER_EXT_MOSI=2
+ -D FLASHER_EXT_MISO=42
+ -D FLASHER_EXT_RESET=5
+ -D FLASHER_EXT_POWER={6}
+ -D FLASHER_EXT_TXD=38
+ -D FLASHER_EXT_RXD=39
+ -D FLASHER_EXT_TEST=4
+ -D FLASHER_ALT_SS=-1
+ -D FLASHER_ALT_CLK=-1
+ -D FLASHER_ALT_MOSI=-1
+ -D FLASHER_ALT_MISO=-1
+ -D FLASHER_ALT_RESET=-1
+ -D FLASHER_ALT_POWER={-1}
+ -D FLASHER_ALT_TXD=-1
+ -D FLASHER_ALT_RXD=-1
+ -D FLASHER_ALT_TEST=-1
+ -D HAS_RGB_LED
+ -D FLASHER_RGB_LED=48
+ -D ST7735_DRIVER
+ -D ST7735_GREENTAB160x80
+ -D TFT_INVERSION_ON
+ -D TFT_WIDTH=80
+ -D TFT_HEIGHT=160
+ -D TFT_MISO=-1
+ -D TFT_MOSI=13
+ -D TFT_SCLK=12
+ -D TFT_CS=10
+ -D TFT_DC=11
+ -D TFT_RST=1
+ -D TFT_RGB_ORDER=TFT_BGR
+ -D USE_HSPI_PORT
+ -D LOAD_FONT2
+ -D LOAD_FONT4
+ -D LOAD_GLCD
+ -D MD5_ENABLED=1
+ -D SERIAL_FLASHER_INTERFACE_UART=1
+ -D SERIAL_FLASHER_BOOT_HOLD_TIME_MS=50
+ -D SERIAL_FLASHER_RESET_HOLD_TIME_MS=100
+ -D C6_OTA_FLASHING
+build_src_filter =
+ +<*>
+board_build.flash_mode=qio
+board_build.arduino.memory_type = qio_opi
+board_build.psram_type=qspi_opi
+board_upload.maximum_size = 16777216
+board_upload.maximum_ram_size = 327680
+board_upload.flash_size = 16MB
+; ----------------------------------------------------------------------------------------
; !!! this configuration expects an SONOFF ZB Bridge-P
; ----------------------------------------------------------------------------------------
;[env:Sonoff_zb_bridge_P_AP]
diff --git a/ESP32_AP-Flasher/src/contentmanager.cpp b/ESP32_AP-Flasher/src/contentmanager.cpp
index cbedcba0..014a75ed 100644
--- a/ESP32_AP-Flasher/src/contentmanager.cpp
+++ b/ESP32_AP-Flasher/src/contentmanager.cpp
@@ -7,6 +7,7 @@
#define CONTENT_BIGCAL
#define CONTENT_NFCLUT
#define CONTENT_DAYAHEAD
+#define CONTENT_TIMESTAMP
#endif
#define CONTENT_CAL
#define CONTENT_BUIENRADAR
@@ -41,6 +42,12 @@
// https://csvjson.com/json_beautifier
+bool needRedraw(uint8_t contentMode, uint8_t wakeupReason) {
+ // contentmode 26, timestamp
+ if ((wakeupReason == WAKEUP_REASON_BUTTON1 || wakeupReason == WAKEUP_REASON_BUTTON2) && contentMode == 26) return true;
+ return false;
+}
+
void contentRunner() {
if (config.runStatus == RUNSTATUS_STOP) return;
@@ -49,11 +56,11 @@ void contentRunner() {
for (tagRecord *taginfo : tagDB) {
if (taginfo->RSSI &&
- (now >= taginfo->nextupdate || taginfo->wakeupReason == WAKEUP_REASON_NFC) &&
+ (now >= taginfo->nextupdate || needRedraw(taginfo->contentMode, taginfo->wakeupReason)) &&
config.runStatus == RUNSTATUS_RUN &&
Storage.freeSpace() > 31000 && !util::isSleeping(config.sleepTime1, config.sleepTime2)) {
drawNew(taginfo->mac, taginfo);
- // taginfo->wakeupReason = 0;
+ taginfo->wakeupReason = 0;
}
if (taginfo->expectedNextCheckin > now - 10 && taginfo->expectedNextCheckin < now + 30 && taginfo->pendingIdle == 0 && taginfo->pendingCount == 0) {
@@ -73,7 +80,7 @@ void contentRunner() {
minutesUntilNextUpdate = (nextWakeTime - now) / 60 - 2;
}
if (minutesUntilNextUpdate > 1 && (wsClientCount() == 0 || config.stopsleep == 0)) {
- taginfo->pendingIdle = minutesUntilNextUpdate;
+ taginfo->pendingIdle = minutesUntilNextUpdate * 60;
if (taginfo->isExternal == false) {
Serial.printf("sleeping for %d more minutes\n", minutesUntilNextUpdate);
prepareIdleReq(taginfo->mac, minutesUntilNextUpdate);
@@ -180,7 +187,7 @@ void drawNew(const uint8_t mac[8], tagRecord *&taginfo) {
char hexmac[17];
mac2hex(mac, hexmac);
String filename = "/temp/" + String(hexmac) + ".raw";
-#ifdef YELLOW_IPS_AP
+#ifdef HAS_TFT
if (isAp) {
filename = "direct";
}
@@ -287,6 +294,11 @@ void drawNew(const uint8_t mac[8], tagRecord *&taginfo) {
} else {
wsErr("Error accessing " + filename);
}
+ } else {
+ // configfilename is empty. Probably the tag needs to redisplay the image after a reboot.
+ Serial.println("Resend static image");
+ // fixme: doesn't work yet
+ // prepareDataAvail(mac);
}
taginfo->nextupdate = 3216153600;
} break;
@@ -508,10 +520,13 @@ void drawNew(const uint8_t mac[8], tagRecord *&taginfo) {
taginfo->nextupdate = 3216153600;
break;
+#ifdef CONTENT_TIMESTAMP
case 26: // timestamp
+ taginfo->nextupdate = 3216153600;
drawTimestamp(filename, cfgobj, taginfo, imageParams);
updateTagImage(filename, mac, 0, taginfo, imageParams);
- taginfo->nextupdate = 3216153600;
+ break;
+#endif
#ifdef CONTENT_DAYAHEAD
case 27: // Day Ahead:
@@ -1459,7 +1474,7 @@ bool getDayAheadFeed(String &filename, JsonObject &cfgobj, tagRecord *&taginfo,
StaticJsonDocument<512> loc;
getTemplate(loc, 27, taginfo->hwType);
- // This is a link to a Google Apps Script script, which fetches (and caches) the tariff from https://transparency.entsoe.eu/
+ // This is a link to a Google Apps Script script, which fetches (and caches) the tariff from https://transparency.entsoe.eu/
// I made it available to provide easy access to the data, but please don't use this link in any projects other than OpenEpaperLink.
String URL = "https://script.google.com/macros/s/AKfycbwMmeGAaPrWzVZrESSpmPmD--O132PzW_acnBsuEottKNATTqCRn6h8zN0Yts7S56ggsg/exec?country=" + cfgobj["country"].as();
@@ -1546,8 +1561,8 @@ bool getDayAheadFeed(String &filename, JsonObject &cfgobj, tagRecord *&taginfo,
spr.fillRect(barX + i * barwidth + 3, 5, barwidth - 6, 10, imageParams.highlightColor);
spr.fillTriangle(barX + i * barwidth, 15,
barX + i * barwidth + barwidth - 1, 15,
- barX + i * barwidth + barwidth / 2, 15 + barwidth, imageParams.highlightColor);
- spr.drawLine(barX + i * barwidth + barwidth / 2, 20 + barwidth, barX + i * barwidth + barwidth / 2, spr.height(), getColor("pink"));
+ barX + i * barwidth + (barwidth - 1) / 2, 15 + barwidth, imageParams.highlightColor);
+ spr.drawLine(barX + i * barwidth + (barwidth - 1) / 2, 20 + barwidth, barX + i * barwidth + (barwidth - 1) / 2, spr.height(), getColor("pink"));
pricenow = price;
}
}
@@ -1702,9 +1717,142 @@ void drawAPinfo(String &filename, JsonObject &cfgobj, tagRecord *&taginfo, imgPa
spr.deleteSprite();
}
+#ifdef CONTENT_TIMESTAMP
void drawTimestamp(String &filename, JsonObject &cfgobj, tagRecord *&taginfo, imgParam &imageParams) {
- // todo
+ Serial.println("make Timestamp");
+ time_t now;
+ time(&now);
+ struct tm timeinfo;
+
+ StaticJsonDocument<512> loc;
+ getTemplate(loc, 1, taginfo->hwType);
+
+ TFT_eSprite spr = TFT_eSprite(&tft);
+ initSprite(spr, imageParams.width, imageParams.height, imageParams);
+
+ if (!cfgobj["#init"]) {
+ Serial.println("init");
+ // init preload images
+
+ char hexmac[17];
+ mac2hex(taginfo->mac, hexmac);
+
+ String filename2 = "/temp/" + String(hexmac) + "-2.raw";
+ drawString(spr, cfgobj["button1"].as(), spr.width() / 2, 40, "calibrib30.vlw", TC_DATUM, TFT_BLACK);
+ drawString(spr, "Well done!", spr.width() / 2, 90, "calibrib30.vlw", TC_DATUM, TFT_BLACK);
+ spr2buffer(spr, filename2, imageParams);
+
+ struct imageDataTypeArgStruct arg = {0};
+ arg.preloadImage = 1;
+ arg.specialType = 17; // button 2
+ arg.lut = 0;
+ prepareDataAvail(filename2, imageParams.dataType, *((uint8_t *)&arg), taginfo->mac, 5 | 0x8000 );
+
+ spr.fillRect(0, 0, spr.width(), spr.height(), TFT_WHITE);
+
+ filename2 = "/temp/" + String(hexmac) + "-3.raw";
+ drawString(spr, cfgobj["button2"].as(), spr.width() / 2, 40, "calibrib30.vlw", TC_DATUM, TFT_BLACK);
+ drawString(spr, "Well done!", spr.width() / 2, 90, "calibrib30.vlw", TC_DATUM, TFT_BLACK);
+ spr2buffer(spr, filename2, imageParams);
+
+ arg.preloadImage = 1;
+ arg.specialType = 16; // button 1
+ arg.lut = 0;
+ prepareDataAvail(filename2, imageParams.dataType, *((uint8_t *)&arg), taginfo->mac, 5 | 0x8000 );
+
+ cfgobj["#init"] = "1";
+ }
+
+ spr.fillRect(0, 0, spr.width(), spr.height(), TFT_WHITE);
+ drawString(spr, cfgobj["title"], spr.width() / 2, 10, "calibrib30.vlw", TC_DATUM, TFT_BLACK);
+ spr.drawLine(0, 40, spr.width(), 40, TFT_BLACK);
+ drawString(spr, cfgobj["button1"], 32, 145, "calibrib16.vlw", TC_DATUM, TFT_BLACK);
+ drawString(spr, cfgobj["button2"], 122, 145, "calibrib16.vlw", TC_DATUM, TFT_BLACK);
+ spr.fillTriangle(27, 160, 37, 160, 32, 165, TFT_BLACK);
+ spr.fillTriangle(127, 160, 117, 160, 122, 165, TFT_BLACK);
+
+ uint8_t mode = cfgobj["mode"].as();
+ switch (taginfo->wakeupReason) {
+ case WAKEUP_REASON_BUTTON2:
+ Serial.println("button 1");
+ cfgobj["last1"] = now;
+ if (mode == 0) {
+ // 1 timestamp
+ cfgobj["last2"] = cfgobj["button1"].as();
+ }
+ break;
+ case WAKEUP_REASON_BUTTON1:
+ Serial.println("button 2");
+ if (mode == 0) {
+ // 1 timestamp
+ cfgobj["last1"] = now;
+ cfgobj["last2"] = cfgobj["button2"].as();
+ } else {
+ cfgobj["last2"] = now;
+ // 2 timestamps
+ }
+ break;
+ }
+
+ char dateString1[40];
+ uint32_t nextaction = cfgobj["nextaction"].as();
+ String dateformat = languageDateFormat[0] + " %H:%M";
+ time_t timestamp = cfgobj["last1"].as();
+ localtime_r(×tamp, &timeinfo);
+ strftime(dateString1, sizeof(dateString1), dateformat.c_str(), &timeinfo);
+ if (timestamp == 0) strcpy(dateString1, "never");
+
+ if (mode == 0) {
+ drawString(spr, "last:", 10, 50, "calibrib16.vlw", TL_DATUM, TFT_BLACK);
+ drawString(spr, dateString1, spr.width() / 2, 50, "bahnschrift30.vlw", TC_DATUM, TFT_BLACK);
+ drawString(spr, cfgobj["last2"].as(), spr.width() / 2, 80, "bahnschrift30.vlw", TC_DATUM, TFT_BLACK);
+
+ if (nextaction > 0 && timestamp > 0) {
+ timestamp += nextaction * 24 * 3600;
+ if (timestamp < taginfo->nextupdate) taginfo->nextupdate = timestamp;
+ localtime_r(×tamp, &timeinfo);
+ strftime(dateString1, sizeof(dateString1), languageDateFormat[0].c_str(), &timeinfo);
+
+ drawString(spr, "next:", 10, 115, "calibrib16.vlw", TL_DATUM, TFT_BLACK);
+ drawString(spr, dateString1, 50, 115, "calibrib16.vlw", TL_DATUM, timestamp < now ? imageParams.highlightColor : TFT_BLACK);
+ }
+ } else {
+ drawString(spr, cfgobj["button1"].as(), 10, 50, "calibrib16.vlw", TL_DATUM, TFT_BLACK);
+ drawString(spr, dateString1, 20, 67, "fonts/bahnschrift20", TL_DATUM, TFT_BLACK);
+
+ if (nextaction > 0 && timestamp > 0) {
+ timestamp += nextaction * 24 * 3600;
+ if (timestamp < taginfo->nextupdate) taginfo->nextupdate = timestamp;
+ localtime_r(×tamp, &timeinfo);
+ strftime(dateString1, sizeof(dateString1), languageDateFormat[0].c_str(), &timeinfo);
+
+ drawString(spr, "next", 200, 50, "calibrib16.vlw", TL_DATUM, TFT_BLACK);
+ drawString(spr, dateString1, 210, 67, "fonts/bahnschrift20", TL_DATUM, timestamp < now ? imageParams.highlightColor : TFT_BLACK);
+ }
+
+ char dateString2[40];
+ time_t timestamp = cfgobj["last2"].as();
+ localtime_r(×tamp, &timeinfo);
+ strftime(dateString2, sizeof(dateString2), dateformat.c_str(), &timeinfo);
+ if (timestamp == 0) strcpy(dateString2, "never");
+
+ drawString(spr, cfgobj["button2"].as(), 10, 90, "calibrib16.vlw", TL_DATUM, TFT_BLACK);
+ drawString(spr, dateString2, 20, 107, "fonts/bahnschrift20", TL_DATUM, TFT_BLACK);
+
+ if (nextaction > 0 && timestamp > 0) {
+ timestamp += nextaction * 24 * 3600;
+ localtime_r(×tamp, &timeinfo);
+ strftime(dateString2, sizeof(dateString2), languageDateFormat[0].c_str(), &timeinfo);
+
+ drawString(spr, "next", 200, 90, "calibrib16.vlw", TL_DATUM, TFT_BLACK);
+ drawString(spr, dateString2, 210, 107, "fonts/bahnschrift20", TL_DATUM, timestamp < now ? imageParams.highlightColor : TFT_BLACK);
+ }
+ }
+
+ spr2buffer(spr, filename, imageParams);
+ spr.deleteSprite();
}
+#endif
bool getJsonTemplateFile(String &filename, String jsonfile, tagRecord *&taginfo, imgParam &imageParams) {
if (jsonfile.c_str()[0] != '/') {
diff --git a/ESP32_AP-Flasher/src/flasher.cpp b/ESP32_AP-Flasher/src/flasher.cpp
index a32072dd..3882dc12 100644
--- a/ESP32_AP-Flasher/src/flasher.cpp
+++ b/ESP32_AP-Flasher/src/flasher.cpp
@@ -5,23 +5,29 @@
#include
#include "LittleFS.h"
-#include "storage.h"
-// #include
-
#include "leds.h"
#include "settings.h"
+#include "storage.h"
#include "time.h"
#include "zbs_interface.h"
+#ifdef HAS_EXT_FLASHER
+#include "webflasher.h"
+#define Seriallog logger
+extern Logger logger;
+#else
+#define Seriallog Serial
+#endif
+
#define FINGERPRINT_FLASH_SIZE 10240
-#ifdef OPENEPAPERLINK_PCB
+#ifdef HAS_EXT_FLASHER
bool extTagConnected() {
// checks if the TEST (P1.0) pin on the ZBS243 will come up high. If it doesn't, there's probably a tag connected.
pinMode(FLASHER_EXT_TEST, INPUT_PULLDOWN);
- vTaskDelay(5 / portTICK_PERIOD_MS);
+ vTaskDelay(10 / portTICK_PERIOD_MS);
pinMode(FLASHER_EXT_TEST, INPUT_PULLUP);
- vTaskDelay(5 / portTICK_PERIOD_MS);
+ vTaskDelay(10 / portTICK_PERIOD_MS);
return !digitalRead(FLASHER_EXT_TEST);
}
#endif
@@ -55,11 +61,10 @@ void dump(uint8_t *a, uint16_t l) {
int8_t powerPinsAP[] = FLASHER_AP_POWER;
int8_t pinsAP[] = {FLASHER_AP_CLK, FLASHER_AP_MISO, FLASHER_AP_MOSI, FLASHER_AP_RESET, FLASHER_AP_RXD, FLASHER_AP_SS, FLASHER_AP_TEST, FLASHER_AP_TXD};
-#ifdef OPENEPAPERLINK_PCB
+#ifdef HAS_EXT_FLASHER
int8_t powerPinsExt[] = FLASHER_EXT_POWER;
int8_t powerPinsAlt[] = FLASHER_ALT_POWER;
uint8_t pinsExt[] = {FLASHER_EXT_CLK, FLASHER_EXT_MISO, FLASHER_EXT_MOSI, FLASHER_EXT_RESET, FLASHER_EXT_RXD, FLASHER_EXT_SS, FLASHER_EXT_TEST, FLASHER_EXT_TXD};
-
#endif
flasher::flasher() {
@@ -90,7 +95,7 @@ bool flasher::connectTag(uint8_t port) {
power_pins = validatePowerPinCount(powerPinsAP, sizeof(powerPinsAP));
result = zbs->begin(FLASHER_AP_SS, FLASHER_AP_CLK, FLASHER_AP_MOSI, FLASHER_AP_MISO, FLASHER_AP_RESET, (uint8_t *)powerPinsAP, power_pins, FLASHER_AP_SPEED);
break;
-#ifdef OPENEPAPERLINK_PCB
+#ifdef HAS_EXT_FLASHER
case 1:
power_pins = validatePowerPinCount(powerPinsExt, sizeof(powerPinsExt));
result = zbs->begin(FLASHER_EXT_SS, FLASHER_EXT_CLK, FLASHER_EXT_MOSI, FLASHER_EXT_MISO, FLASHER_EXT_RESET, (uint8_t *)powerPinsExt, power_pins, FLASHER_AP_SPEED);
@@ -101,18 +106,17 @@ bool flasher::connectTag(uint8_t port) {
break;
#endif
default:
- Serial.printf("Tried to connect to port %d, but this port isn't available. Some dev borked it up, probably Jelmer.\n", port);
return false;
}
if (!result) Serial.printf("I tried connecting to port %d, but I couldn't establish a link to the tag. That's all I know.\n", port);
return result;
}
-void flasher::getFirmwareMD5() {
+bool flasher::getFirmwareMD5() {
uint8_t *buffer = (uint8_t *)malloc(FINGERPRINT_FLASH_SIZE);
if (buffer == nullptr) {
- Serial.print("couldn't malloc bytes for firmware MD5\n");
- return;
+ Seriallog.print("couldn't malloc bytes for firmware MD5\n");
+ return false;
}
zbs->select_flash(0);
@@ -131,8 +135,9 @@ void flasher::getFirmwareMD5() {
for (uint8_t c = 0; c < 16; c++) {
sprintf(md5char + (2 * c), "%02X", md5[c]);
}
- Serial.printf("MD5=%s\n", md5char);
+ Seriallog.printf("MD5=%s\n", md5char);
free(buffer);
+ return true;
}
bool flasher::getInfoBlockMac() {
@@ -140,13 +145,11 @@ bool flasher::getInfoBlockMac() {
for (uint16_t c = 7; c < 8; c--) {
mac[7 - c] = zbs->read_flash(c + 0x10);
}
- Serial.printf("Infopage mac=");
uint16_t macsum = 0;
for (uint8_t c = 0; c < 8; c++) {
macsum += mac[c];
- Serial.printf("%02X", mac[c]);
}
- Serial.printf("\n");
+ Seriallog.printf("Infopage mac=%02X%02X%02X%02X%02X%02X%02X%02X", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5], mac[6], mac[7]);
if (macsum == 0) return false;
if (macsum > 0x5F9) return false;
return true;
@@ -162,7 +165,7 @@ bool flasher::getInfoBlockMD5() {
macsum += md5[c];
sprintf(md5char + (2 * c), "%02X", md5[c]);
}
- Serial.printf("Infoblock MD5=%s\n", md5char);
+ Seriallog.printf("Infoblock MD5=%s\n", md5char);
if (macsum == 0) return false; // invalid mac
if (macsum > 0xF00) return false; // *probably* an invalid mac
return true;
@@ -183,9 +186,9 @@ bool flasher::findTagByMD5() {
const char *jsonmd5 = elem["MD5"];
if (jsonmd5 != nullptr) {
if (strncmp(md5char, jsonmd5, 32) == 0) {
- Serial.print("MD5 Matches > ");
+ Seriallog.print("MD5 Matches > ");
const char *name = elem["name"];
- Serial.println(name);
+ Seriallog.println(name);
mac_suffix = strtoul(elem["mac_suffix"], 0, 16);
mac_format = elem["mac_format"];
mac_offset = elem["mac_offset"];
@@ -197,7 +200,7 @@ bool flasher::findTagByMD5() {
}
Serial.print("Failed to find this tag's current firmware MD5 in the json database. If this tag is already OpenEpaperLink, this is to be expected.\n");
} else {
- Serial.print("Failed to read json file\n");
+ Seriallog.print("Failed to read json file /tag_md5_db.json\n");
}
readfile.close();
return false;
@@ -212,9 +215,9 @@ bool flasher::findTagByType(uint8_t type) {
if (elem["type"] != nullptr) {
uint8_t jtype = elem["type"];
if (jtype == type) {
- Serial.print("Type Matches > ");
+ Seriallog.print("Type Matches > ");
const char *name = elem["name"];
- Serial.println(name);
+ Seriallog.println(name);
const char *jsonmd5 = elem["MD5"];
for (uint8_t c = 0; c < 16; c++) {
@@ -236,9 +239,9 @@ bool flasher::findTagByType(uint8_t type) {
}
}
}
- Serial.print("Failed to find this tag's type in the json database.\n");
+ Seriallog.print("Failed to find this tag's type in the json database.\n");
} else {
- Serial.print("Failed to read json file\n");
+ Seriallog.print("Failed to read json file\n");
}
readfile.close();
return false;
@@ -320,7 +323,7 @@ bool flasher::writeFlash(uint8_t *flashbuffer, uint16_t size) {
if (!zbs->select_flash(0)) return false;
zbs->erase_flash();
if (!zbs->select_flash(0)) return false;
- Serial.printf("Starting flash, size=%d\n", size);
+ Seriallog.printf("Starting flash, size=%d\n", size);
for (uint16_t c = 0; c < size; c++) {
if (flashbuffer[c] == 0xFF) goto flashWriteSuccess;
for (uint8_t i = 0; i < MAX_WRITE_ATTEMPTS; i++) {
@@ -333,11 +336,11 @@ bool flasher::writeFlash(uint8_t *flashbuffer, uint16_t size) {
flashWriteSuccess:
if (c % 256 == 0) {
#ifdef HAS_RGB_LED
- shortBlink(CRGB::Yellow);
+ shortBlink(CRGB::White);
#else
quickBlink(2);
#endif
- Serial.printf("\rNow flashing, %d/%d ", c, size);
+ Seriallog.printf("\rNow flashing, %d/%d ", c, size);
vTaskDelay(1 / portTICK_PERIOD_MS);
}
}
@@ -412,7 +415,7 @@ bool flasher::writeFlashFromPackOffset(fs::File *file, uint16_t length) {
if (!zbs->select_flash(0)) return false;
zbs->erase_flash();
if (!zbs->select_flash(0)) return false;
- Serial.printf("Starting flash, size=%d\n", length);
+ Seriallog.printf("Starting flash, size=%d\n", length);
uint8_t *buf = (uint8_t *)malloc(256);
uint16_t offset = 0;
@@ -425,20 +428,21 @@ bool flasher::writeFlashFromPackOffset(fs::File *file, uint16_t length) {
length = 0;
}
#ifdef HAS_RGB_LED
- shortBlink(CRGB::Yellow);
+ shortBlink(CRGB::White);
#else
quickBlink(2);
#endif
- Serial.printf("\rFlashing, %d bytes left ", length);
+ Seriallog.printf("\r[Flashing %d bytes] ", length);
+
bool res = writeBlock256(offset, buf);
offset += 256;
if (!res) {
- Serial.printf("Failed writing block to tag, probably a hardware failure\n");
+ Seriallog.printf("Failed writing block to tag, probably a hardware failure\n");
return false;
}
vTaskDelay(1 / portTICK_PERIOD_MS);
}
- Serial.printf("\nFlashing done\n");
+ Seriallog.printf("\nFlashing done\n");
return true;
}
@@ -452,8 +456,8 @@ bool flasher::writeFlashFromPack(String filename, uint8_t type) {
uint8_t jtype = elem["type"];
if (jtype == type) {
const char *name = elem["name"];
- Serial.print("Flashing from FW pack: ");
- Serial.println(name);
+ Seriallog.print("Flashing from FW pack: ");
+ Seriallog.println(name);
uint32_t offset = elem["offset"];
uint16_t length = elem["length"];
@@ -464,10 +468,10 @@ bool flasher::writeFlashFromPack(String filename, uint8_t type) {
}
}
}
- Serial.print("Failed to find this tag's type in the FW pack database.\n");
+ Seriallog.print("Failed to find this tag's type in the FW pack database.\n");
} else {
- Serial.println(err.c_str());
- Serial.print("Failed to read json header from FW pack\n");
+ Seriallog.println(err.c_str());
+ Seriallog.print("Failed to read json header from FW pack\n");
}
readfile.close();
return false;
@@ -501,6 +505,7 @@ bool flasher::writeBlock(uint16_t offset, uint8_t *data, uint16_t len, bool info
return true;
}
+#ifndef C6_OTA_FLASHING
uint16_t getAPUpdateVersion(uint8_t type) {
StaticJsonDocument<512> doc;
fs::File readfile = contentFS->open("/AP_FW_Pack.bin", "r");
@@ -625,12 +630,13 @@ bool doAPUpdate(uint8_t type) {
delete f;
return res;
}
+#endif
void flashCountDown(uint8_t c) {
- Serial.printf("\r%d ", c);
+ Seriallog.printf("\r%d ", c);
for (c -= 1; c < 254; c--) {
vTaskDelay(1000 / portTICK_PERIOD_MS);
- Serial.printf("\r%d ", c);
+ Seriallog.printf("\r%d ", c);
}
}
@@ -639,7 +645,7 @@ void flashCountDown(uint8_t c) {
bool doTagFlash() {
class flasher *f = new flasher();
if (!f->connectTag(FLASHER_EXT_PORT)) {
- Serial.printf("Sorry, failed to connect to this tag...\n");
+ Seriallog.printf("Sorry, failed to connect to this tag...\n");
return false;
}
@@ -647,7 +653,7 @@ bool doTagFlash() {
if (f->findTagByMD5()) {
// this tag currently contains original firmware, found its fingerprint
- Serial.printf("Found original firmware tag, recognized its fingerprint (%s)\n", f->md5char);
+ Seriallog.printf("Found original firmware tag, recognized its fingerprint (%s)\n", f->md5char);
f->readInfoBlock();
f->getFirmwareMac();
f->prepareInfoBlock();
@@ -658,7 +664,7 @@ bool doTagFlash() {
// did find an infoblock MD5 that looks valid
if (f->findTagByMD5()) {
// did find the md5 in the database
- Serial.printf("Found an already-flashed tag, recognized its fingerprint (%s)\n", f->md5char);
+ Seriallog.printf("Found an already-flashed tag, recognized its fingerprint (%s)\n", f->md5char);
f->getInfoBlockMac();
f->getInfoBlockType();
f->readInfoBlock();
@@ -666,14 +672,14 @@ bool doTagFlash() {
f->zbs->reset();
} else {
// couldn't find the md5 from the infoblock
- Serial.printf("Found an already-flashed tag, but we couldn't find its fingerprint (%s) in the database\n", f->md5char);
+ Seriallog.printf("Found an already-flashed tag, but we couldn't find its fingerprint (%s) in the database\n", f->md5char);
return false;
}
} else {
// We couldn't recognize the tag from it's fingerprint...
- Serial.printf("Found a tag but didn't recognize its fingerprint\n", f->md5char);
+ Seriallog.printf("Found a tag but didn't recognize its fingerprint\n", f->md5char);
f->backupFlash();
- Serial.printf("Saved this MD5 binary to filesystem\n");
+ Seriallog.printf("Saved this MD5 binary to filesystem\n");
}
delete f;
return false;
diff --git a/ESP32_AP-Flasher/src/ips_display.cpp b/ESP32_AP-Flasher/src/ips_display.cpp
index 380a64bb..dcd7b267 100644
--- a/ESP32_AP-Flasher/src/ips_display.cpp
+++ b/ESP32_AP-Flasher/src/ips_display.cpp
@@ -7,7 +7,7 @@
#include "storage.h"
#include "tag_db.h"
-#ifdef YELLOW_IPS_AP
+#ifdef HAS_TFT
#include "ips_display.h"
@@ -15,15 +15,17 @@
#define TFT_BACKLIGHT 14
TFT_eSPI tft2 = TFT_eSPI();
-int32_t tftid = -1;
uint8_t YellowSense = 0;
bool tftLogscreen = true;
+bool tftOverride = false;
void TFTLog(String text) {
if (tftLogscreen == false) {
tft2.fillScreen(TFT_BLACK);
- tft2.setCursor(0, 5, 2);
+ tft2.setCursor(0, 0, (tft2.width() == 160 ? 1 : 2));
tftLogscreen = true;
+ } else {
+ if (tft2.width() == 160) tft2.setCursor(0, tft2.getCursorY(), 1);
}
if (text.isEmpty()) return;
tft2.setTextColor(TFT_SILVER);
@@ -34,12 +36,18 @@ void TFTLog(String text) {
int httpIndex = text.indexOf("http");
tft2.print(text.substring(0, httpIndex));
tft2.setTextColor(TFT_YELLOW);
- text = text.substring(httpIndex);
+ if (tft2.width() == 160) {
+ tft2.setCursor(0, tft2.getCursorY() + 8, 2);
+ text = text.substring(httpIndex + 7);
+ } else {
+ text = text.substring(httpIndex);
+ }
} else if (text.indexOf(":") != -1) {
int colonIndex = text.indexOf(":");
tft2.setTextColor(TFT_SILVER);
tft2.print(text.substring(0, colonIndex + 1));
tft2.setTextColor(TFT_WHITE);
+ if (tft2.width() == 160) tft2.setCursor(0, tft2.getCursorY() + 8, 2);
text = text.substring(colonIndex + 1);
} else if (text.endsWith("!")) {
tft2.setTextColor(TFT_GREEN);
@@ -64,36 +72,34 @@ void sendAvail(uint8_t wakeupReason) {
memcpy(&eadr.src, mac, 6);
eadr.adr.lastPacketRSSI = WiFi.RSSI();
eadr.adr.currentChannel = config.channel;
- eadr.adr.hwType = 0xE0;
+ eadr.adr.hwType = (tft2.width() == 160 ? 0xE1 : 0xE0);
eadr.adr.wakeupReason = wakeupReason;
eadr.adr.capabilities = 0;
eadr.adr.tagSoftwareVersion = 0;
eadr.adr.customMode = 0;
processDataReq(&eadr, true);
- if (wakeupReason) tftid = findId(eadr.src);
}
void yellow_ap_display_init(void) {
-
pinMode(YELLOW_SENSE, INPUT_PULLDOWN);
vTaskDelay(100 / portTICK_PERIOD_MS);
if (digitalRead(YELLOW_SENSE) == HIGH) YellowSense = 1;
-
pinMode(TFT_BACKLIGHT, OUTPUT);
- digitalWrite(TFT_BACKLIGHT, HIGH);
-
- ledcSetup(6, 5000, 8);
- ledcAttachPin(TFT_BACKLIGHT, 6);
- ledcWrite(6, config.tft);
+ digitalWrite(TFT_BACKLIGHT, LOW);
tft2.init();
tft2.setRotation(YellowSense == 1 ? 1 : 3);
-
tft2.fillScreen(TFT_BLACK);
- tft2.setCursor(10, 5, 2);
+ tft2.setCursor(12, 0, (tft2.width() == 160 ? 1 : 2));
tft2.setTextColor(TFT_WHITE);
- tft2.println("*** Initialising... ***");
tftLogscreen = true;
+
+ ledcSetup(6, 5000, 8);
+ ledcAttachPin(TFT_BACKLIGHT, 6);
+ if (tft2.width() == 160) {
+ GPIO.func_out_sel_cfg[TFT_BACKLIGHT].inv_sel = 1;
+ }
+ ledcWrite(6, config.tft);
}
void yellow_ap_display_loop(void) {
@@ -110,9 +116,16 @@ void yellow_ap_display_loop(void) {
sendAvail(0xFC);
first_run = 1;
}
- if (millis() - last_update >= 1000) {
- tagRecord* tag = tagDB.at(tftid);
- if (tag->pendingCount > 0) {
+ if (millis() - last_update >= 3000) {
+ uint8_t wifimac[8];
+ WiFi.macAddress(wifimac);
+ memset(&wifimac[6], 0, 2);
+ tagRecord* tag = tagRecord::findByMAC(wifimac);
+ if (tag == nullptr) {
+ last_update = millis();
+ return;
+ }
+ if (tag->pendingCount > 0 && tftOverride == false) {
String filename = tag->filename;
fs::File file = contentFS->open(filename);
if (!file) {
@@ -122,7 +135,7 @@ void yellow_ap_display_loop(void) {
}
TFT_eSprite spr = TFT_eSprite(&tft2);
- if (tag->len == tft2.width() * tft2.height() * 2) spr.setColorDepth(16);
+ spr.setColorDepth(16);
if (tag->len == tft2.width() * tft2.height()) spr.setColorDepth(8);
spr.createSprite(tft2.width(), tft2.height());
void* spriteData = spr.getPointer();
diff --git a/ESP32_AP-Flasher/src/leds.cpp b/ESP32_AP-Flasher/src/leds.cpp
index c995e3d6..d4729005 100644
--- a/ESP32_AP-Flasher/src/leds.cpp
+++ b/ESP32_AP-Flasher/src/leds.cpp
@@ -8,6 +8,7 @@
#include "leds.h"
#include "settings.h"
#include "tag_db.h"
+#include "serialap.h"
QueueHandle_t ledQueue;
int maxledbrightness = 255;
@@ -58,7 +59,6 @@ void addFadeColor(CRGB cname) {
}
void shortBlink(CRGB cname) {
-#ifndef YELLOW_IPS_AP
struct ledInstructionRGB* rgb = new struct ledInstructionRGB;
rgb->ledColor = CRGB::Black;
rgb->fadeTime = 0;
@@ -75,7 +75,6 @@ void shortBlink(CRGB cname) {
rgb->fadeTime = 0;
rgb->length = 3;
addToRGBQueue(rgb, false);
-#endif
}
void flushRGBQueue() {
@@ -91,6 +90,8 @@ void showColorPattern(CRGB colorone, CRGB colortwo, CRGB colorthree) {
const int patternLengths[] = {600, 120, 200, 120, 200, 120};
const CRGB patternColors[] = {CRGB::Black, colorone, CRGB::Black, colortwo, CRGB::Black, colorthree};
+ while (xQueueReceive(rgbLedQueue, &rgb, 0) == pdPASS) { }
+
for (int i = 0; i < sizeof(patternLengths) / sizeof(patternLengths[0]); i++) {
rgb = new struct ledInstructionRGB;
rgb->ledColor = patternColors[i];
@@ -131,7 +132,7 @@ void rgbIdleStep() {
void setBrightness(int brightness) {
maxledbrightness = brightness;
-#ifdef YELLOW_IPS_AP
+#ifdef HAS_TFT
ledcWrite(6, config.tft);
#endif
#ifdef HAS_RGB_LED
@@ -140,14 +141,14 @@ void setBrightness(int brightness) {
}
void updateBrightnessFromConfig() {
- if (config.led != 0) {
- int newbrightness = config.led;
- if (newbrightness < 0) newbrightness = 0;
- if (newbrightness != maxledbrightness) {
- setBrightness(newbrightness);
- }
+ int newbrightness = config.led;
+ if (newbrightness != maxledbrightness) {
+ setBrightness(newbrightness);
}
+#ifdef HAS_TFT
ledcWrite(6, config.tft);
+#endif
+ if (apInfo.state == AP_STATE_NORADIO) addFadeMono(config.led);
}
void addToMonoQueue(struct ledInstruction* mono) {
@@ -176,7 +177,7 @@ void showMono(uint8_t brightness) {
void quickBlink(uint8_t repeat) {
for (int i = 0; i < repeat; i++) {
struct ledInstruction* mono = new struct ledInstruction;
-#ifdef YELLOW_IPS_AP
+#ifdef HAS_TFT
mono->value = 255;
#else
mono->value = maxledbrightness;
@@ -222,7 +223,7 @@ void ledTask(void* parameter) {
struct ledInstruction* monoled = nullptr;
addFadeMono(0);
-#ifdef YELLOW_IPS_AP
+#ifdef HAS_TFT
addFadeMono(255);
#else
addFadeMono(maxledbrightness);
diff --git a/ESP32_AP-Flasher/src/main.cpp b/ESP32_AP-Flasher/src/main.cpp
index 2d01252e..fa360274 100644
--- a/ESP32_AP-Flasher/src/main.cpp
+++ b/ESP32_AP-Flasher/src/main.cpp
@@ -13,7 +13,11 @@
#include "tagdata.h"
#include "wifimanager.h"
-#ifdef HAS_USB
+#ifdef HAS_EXT_FLASHER
+#include "webflasher.h"
+#endif
+
+#if defined HAS_USB || defined HAS_EXT_FLASHER
#include "usbflasher.h"
#endif
@@ -28,10 +32,6 @@ util::Timer intervalSysinfo(seconds(5));
util::Timer intervalVars(seconds(10));
util::Timer intervalSaveDB(minutes(5));
-#ifdef OPENEPAPERLINK_PCB
-util::Timer tagConnectTimer(seconds(1));
-#endif
-
SET_LOOP_TASK_STACK_SIZE(16 * 1024);
void delayedStart(void* parameter) {
@@ -47,7 +47,7 @@ void delayedStart(void* parameter) {
void setup() {
Serial.begin(115200);
Serial.print(">\n");
-#ifdef YELLOW_IPS_AP
+#ifdef HAS_TFT
extern void yellow_ap_display_init(void);
yellow_ap_display_init();
#endif
@@ -108,16 +108,12 @@ void setup() {
}
*/
-#ifdef HAS_USB
- // We'll need to start the 'usbflasher' task for boards with a second (USB) port. This can be used as a 'flasher' interface, using a python script on the host
- xTaskCreate(usbFlasherTask, "usbflasher", 10000, NULL, configMAX_PRIORITIES - 10, NULL);
-#endif
-
initAPconfig();
updateLanguageFromConfig();
updateBrightnessFromConfig();
+ config.runStatus = RUNSTATUS_INIT;
init_web();
xTaskCreate(initTime, "init time", 5000, NULL, 2, NULL);
@@ -135,10 +131,18 @@ void setup() {
} else {
cleanupCurrent();
}
- xTaskCreate(APTask, "AP Process", 6000, NULL, 2, NULL);
+ xTaskCreate(APTask, "AP Process", 6000, NULL, 5, NULL);
vTaskDelay(10 / portTICK_PERIOD_MS);
- config.runStatus = RUNSTATUS_INIT;
+#ifdef HAS_USB
+ // We'll need to start the 'usbflasher' task for boards with a second (USB) port. This can be used as a 'flasher' interface, using a python script on the host
+ xTaskCreate(usbFlasherTask, "usbflasher", 10000, NULL, 5, NULL);
+#endif
+
+#ifdef HAS_EXT_FLASHER
+ xTaskCreate(webFlasherTask, "webflasher", 8000, NULL, 3, NULL);
+#endif
+
esp_reset_reason_t resetReason = esp_reset_reason();
if (resetReason == ESP_RST_PANIC) {
Serial.println("Panic! Pausing content generation for 30 seconds");
@@ -164,29 +168,14 @@ void loop() {
if (intervalSaveDB.doRun() && config.runStatus != RUNSTATUS_STOP) {
saveDB("/current/tagDB.json");
}
- if (intervalContentRunner.doRun() && apInfo.state == AP_STATE_ONLINE) {
+ if (intervalContentRunner.doRun() && (apInfo.state == AP_STATE_ONLINE || apInfo.state == AP_STATE_NORADIO)) {
contentRunner();
}
-#ifdef YELLOW_IPS_AP
+#ifdef HAS_TFT
extern void yellow_ap_display_loop(void);
yellow_ap_display_loop();
#endif
-#ifdef OPENEPAPERLINK_PCB
- if (tagConnectTimer.doRun() && extTagConnected()) {
- flashCountDown(3);
-
- pinMode(FLASHER_EXT_TEST, OUTPUT);
- digitalWrite(FLASHER_EXT_TEST, LOW);
-
- doTagFlash();
-
- vTaskDelay(10000 / portTICK_PERIOD_MS);
- pinMode(FLASHER_EXT_TEST, INPUT);
- vTaskDelay(1000 / portTICK_PERIOD_MS);
- }
-#endif
-
vTaskDelay(100 / portTICK_PERIOD_MS);
}
diff --git a/ESP32_AP-Flasher/src/makeimage.cpp b/ESP32_AP-Flasher/src/makeimage.cpp
index 9b2106b6..16db2718 100644
--- a/ESP32_AP-Flasher/src/makeimage.cpp
+++ b/ESP32_AP-Flasher/src/makeimage.cpp
@@ -10,6 +10,10 @@
#include "storage.h"
#include "util.h"
+#ifdef HAS_TFT
+#include "ips_display.h"
+#endif
+
TFT_eSPI tft = TFT_eSPI();
TFT_eSprite spr = TFT_eSprite(&tft);
@@ -276,11 +280,22 @@ void rewriteHeader(File &f_out) {
void spr2buffer(TFT_eSprite &spr, String &fileout, imgParam &imageParams) {
long t = millis();
-#ifdef YELLOW_IPS_AP
+#ifdef HAS_TFT
extern uint8_t YellowSense;
if (fileout == "direct") {
- tft.setRotation(YellowSense == 1 ? 1 : 3);
- spr.pushSprite(0, 0);
+ if (tftOverride == false) {
+ TFT_eSprite spr2 = TFT_eSprite(&tft2);
+ tft2.setRotation(YellowSense == 1 ? 1 : 3);
+ spr2.createSprite(spr.width(), spr.height());
+ spr2.setColorDepth(spr.getColorDepth());
+
+ void *spriteData = spr.getPointer();
+ void *spriteData2 = spr2.getPointer();
+ size_t dataSize = spr.width() * spr.height() * (spr.getColorDepth() / 8);
+ memcpy(spriteData2, spriteData, dataSize);
+
+ spr2.pushSprite(0, 0);
+ }
return;
}
#endif
diff --git a/ESP32_AP-Flasher/src/newproto.cpp b/ESP32_AP-Flasher/src/newproto.cpp
index b6d368d2..3ee89670 100644
--- a/ESP32_AP-Flasher/src/newproto.cpp
+++ b/ESP32_AP-Flasher/src/newproto.cpp
@@ -82,10 +82,38 @@ void prepareIdleReq(const uint8_t* dst, uint16_t nextCheckin) {
pending.attemptsLeft = 10 + config.maxsleep;
Serial.printf(">SDA %02X%02X%02X%02X%02X%02X%02X%02X NOP\n", dst[7], dst[6], dst[5], dst[4], dst[3], dst[2], dst[1], dst[0]);
- queueDataAvail(&pending);
+ queueDataAvail(&pending, true);
}
}
+void prepareDataAvail(const uint8_t* dst) {
+ // resend
+ tagRecord* taginfo = tagRecord::findByMAC(dst);
+ if (taginfo == nullptr) {
+ if (config.lock) return;
+ wsErr("Tag not found, this shouldn't happen.");
+ return;
+ }
+
+ taginfo->pendingCount++;
+ taginfo->pendingIdle = 0;
+
+ struct pendingData pending = {0};
+ memcpy(pending.targetMac, dst, 8);
+ pending.availdatainfo.dataSize = taginfo->len;
+ pending.availdatainfo.dataType = taginfo->dataType;
+ pending.availdatainfo.nextCheckIn = 0;
+ memcpy(&pending.availdatainfo.dataVer, taginfo->md5, sizeof(uint64_t));
+ pending.attemptsLeft = 10;
+
+ queueDataAvail(&pending, !taginfo->isExternal);
+ if (taginfo->isExternal) {
+ udpsync.netSendDataAvail(&pending);
+ }
+
+ wsSendTaginfo(dst, SYNC_TAGSTATUS);
+}
+
void prepareDataAvail(uint8_t* data, uint16_t len, uint8_t dataType, const uint8_t* dst) {
tagRecord* taginfo = tagRecord::findByMAC(dst);
if (taginfo == nullptr) {
@@ -121,23 +149,22 @@ void prepareDataAvail(uint8_t* data, uint16_t len, uint8_t dataType, const uint8
memcpy(pending.targetMac, dst, 8);
pending.availdatainfo.dataSize = len;
pending.availdatainfo.dataType = dataType;
- pending.availdatainfo.nextCheckIn = 0;
+ pending.availdatainfo.nextCheckIn = 5 | 0x8000; // 5 seconds
pending.availdatainfo.dataVer = *((uint64_t*)md5bytes);
pending.attemptsLeft = 10;
+ queueDataAvail(&pending, !taginfo->isExternal);
if (taginfo->isExternal) {
udpsync.netSendDataAvail(&pending);
- } else {
- queueDataAvail(&pending);
}
wsSendTaginfo(dst, SYNC_TAGSTATUS);
}
bool prepareDataAvail(String& filename, uint8_t dataType, uint8_t dataTypeArgument, const uint8_t* dst, uint16_t nextCheckin, bool resend) {
- if (nextCheckin > config.maxsleep) nextCheckin = config.maxsleep;
- if (wsClientCount() && config.stopsleep == 1) nextCheckin = 0;
-#ifdef YELLOW_IPS_AP
+ if ((nextCheckin & 0x8000) == 0 && nextCheckin > config.maxsleep) nextCheckin = config.maxsleep;
+ if ((nextCheckin & 0x8000) == 0 && wsClientCount() && (config.stopsleep == 1)) nextCheckin = 0;
+#ifdef HAS_TFT
if (filename == "direct") {
char dst_path[64];
sprintf(dst_path, "/current/%02X%02X%02X%02X%02X%02X%02X%02X.raw\0", dst[7], dst[6], dst[5], dst[4], dst[3], dst[2], dst[1], dst[0]);
@@ -196,7 +223,8 @@ bool prepareDataAvail(String& filename, uint8_t dataType, uint8_t dataTypeArgume
time_t now;
time(&now);
- taginfo->pendingIdle = nextCheckin * 60 + 60;
+
+ taginfo->pendingIdle = (nextCheckin & 0x8000) ? (nextCheckin & 0x7FFF) + 5 : (nextCheckin * 60) + 60;
clearPending(taginfo);
} else {
wsLog("firmware upload pending");
@@ -216,9 +244,9 @@ bool prepareDataAvail(String& filename, uint8_t dataType, uint8_t dataTypeArgume
pending.availdatainfo.nextCheckIn = nextCheckin;
pending.attemptsLeft = attempts;
checkMirror(taginfo, &pending);
+ queueDataAvail(&pending, !taginfo->isExternal);
if (taginfo->isExternal == false) {
Serial.printf(">SDA %02X%02X%02X%02X%02X%02X%02X%02X TYPE 0x%02X\n", dst[7], dst[6], dst[5], dst[4], dst[3], dst[2], dst[1], dst[0], pending.availdatainfo.dataType);
- queueDataAvail(&pending);
} else {
udpsync.netSendDataAvail(&pending);
}
@@ -241,10 +269,12 @@ void prepareExternalDataAvail(struct pendingData* pending, IPAddress remoteIP) {
char hexmac[17];
mac2hex(pending->targetMac, hexmac);
String filename = "/current/" + String(hexmac) + "_" + String(millis() % 1000000) + ".pending";
- String imageUrl = "http://" + remoteIP.toString() + "/getdata?mac=" + String(hexmac);
- wsLog("prepareExternalDataAvail GET " + imageUrl);
+ char md5[17];
+ mac2hex(reinterpret_cast(&pending->availdatainfo.dataVer), md5);
+ char imageUrl[80];
+ snprintf(imageUrl, sizeof(imageUrl), "http://%s/getdata?mac=%s&md5=%s", remoteIP.toString().c_str(), hexmac, md5);
+ wsLog("prepareExternalDataAvail GET " + String(imageUrl));
HTTPClient http;
- logLine("http prepareExternalDataAvail " + imageUrl);
http.begin(imageUrl);
int httpCode = http.GET();
if (httpCode == 200) {
@@ -254,9 +284,9 @@ void prepareExternalDataAvail(struct pendingData* pending, IPAddress remoteIP) {
file.close();
xSemaphoreGive(fsMutex);
} else if (httpCode == 404) {
- imageUrl = "http://" + remoteIP.toString() + "/current/" + String(hexmac) + ".raw";
+ snprintf(imageUrl, sizeof(imageUrl), "http://%s/current/%s.raw", remoteIP.toString().c_str(), hexmac);
+ // imageUrl = "http://" + remoteIP.toString() + "/current/" + String(hexmac) + ".raw";
http.end();
- logLine("http prepareExternalDataAvail " + imageUrl);
http.begin(imageUrl);
httpCode = http.GET();
if (httpCode == 200) {
@@ -266,6 +296,9 @@ void prepareExternalDataAvail(struct pendingData* pending, IPAddress remoteIP) {
file.close();
xSemaphoreGive(fsMutex);
}
+ } else {
+ logLine("prepareExternalDataAvail " + String(imageUrl) + " error " + String(httpCode));
+ wsLog("error " + String(httpCode));
}
http.end();
@@ -299,10 +332,13 @@ void prepareExternalDataAvail(struct pendingData* pending, IPAddress remoteIP) {
case DATATYPE_CUSTOM_LUT_OTA: {
char hexmac[17];
mac2hex(pending->targetMac, hexmac);
- String dataUrl = "http://" + remoteIP.toString() + "/getdata?mac=" + String(hexmac);
- wsLog("GET " + dataUrl);
+ char dataUrl[80];
+ char md5[17];
+ mac2hex(reinterpret_cast(&pending->availdatainfo.dataVer), md5);
+ snprintf(dataUrl, sizeof(dataUrl), "http://%s/getdata?mac=%s&md5=%s", remoteIP.toString().c_str(), hexmac, md5);
+ wsLog("GET " + String(dataUrl));
HTTPClient http;
- logLine("http DATATYPE_CUSTOM_LUT_OTA " + dataUrl);
+ logLine("http DATATYPE_CUSTOM_LUT_OTA " + String(dataUrl));
http.begin(dataUrl);
int httpCode = http.GET();
if (httpCode == 200) {
@@ -325,7 +361,7 @@ void prepareExternalDataAvail(struct pendingData* pending, IPAddress remoteIP) {
}
}
checkMirror(taginfo, pending);
- queueDataAvail(pending);
+ queueDataAvail(pending, !taginfo->isExternal);
wsSendTaginfo(pending->targetMac, SYNC_NOSYNC);
}
@@ -365,7 +401,6 @@ void processBlockRequest(struct espBlockRequest* br) {
if (br->blockId >= totalblocks) {
br->blockId = totalblocks - 1;
}
-
uint32_t len = queueItem->len - (BLOCK_DATA_SIZE * br->blockId);
if (len > BLOCK_DATA_SIZE) len = BLOCK_DATA_SIZE;
uint16_t checksum = sendBlock(queueItem->data + (br->blockId * BLOCK_DATA_SIZE), len);
@@ -431,7 +466,7 @@ void processXferComplete(struct espXferComplete* xfc, bool local) {
}
// more in the queue?
- checkQueue(xfc->src);
+ if (local) checkQueue(xfc->src);
wsSendTaginfo(xfc->src, SYNC_TAGSTATUS);
if (local) udpsync.netProcessXferComplete(xfc);
@@ -507,7 +542,7 @@ void processDataReq(struct espAvailDataReq* eadr, bool local, IPAddress remoteIP
taginfo->expectedNextCheckin = 3216153600;
taginfo->pendingIdle = 0;
} else {
- taginfo->expectedNextCheckin = now + 60 * taginfo->pendingIdle;
+ taginfo->expectedNextCheckin = now + taginfo->pendingIdle;
taginfo->pendingIdle = 0;
}
taginfo->lastseen = now;
@@ -537,7 +572,7 @@ void processDataReq(struct espAvailDataReq* eadr, bool local, IPAddress remoteIP
taginfo->temperature = eadr->adr.temperature;
taginfo->batteryMv = eadr->adr.batteryMv;
taginfo->hwType = eadr->adr.hwType;
- taginfo->wakeupReason = eadr->adr.wakeupReason;
+ if (eadr->adr.wakeupReason > 0) taginfo->wakeupReason = eadr->adr.wakeupReason;
taginfo->capabilities = eadr->adr.capabilities;
taginfo->currentChannel = eadr->adr.currentChannel;
taginfo->tagSoftwareVersion = eadr->adr.tagSoftwareVersion;
@@ -618,8 +653,9 @@ bool sendAPSegmentedData(const uint8_t* dst, String data, uint16_t icons, bool i
pending.attemptsLeft = 120;
Serial.printf(">AP Segmented Data %02X%02X%02X%02X%02X%02X%02X%02X\n\0", dst[7], dst[6], dst[5], dst[4], dst[3], dst[2], dst[1], dst[0]);
if (local) {
- return queueDataAvail(&pending);
+ return queueDataAvail(&pending, true);
} else {
+ queueDataAvail(&pending, false);
udpsync.netSendDataAvail(&pending);
return true;
}
@@ -636,8 +672,9 @@ bool showAPSegmentedInfo(const uint8_t* dst, bool local) {
pending.attemptsLeft = 120;
Serial.printf(">SDA %02X%02X%02X%02X%02X%02X%02X%02X\n\0", dst[7], dst[6], dst[5], dst[4], dst[3], dst[2], dst[1], dst[0]);
if (local) {
- return queueDataAvail(&pending);
+ return queueDataAvail(&pending, true);
} else {
+ queueDataAvail(&pending, false);
udpsync.netSendDataAvail(&pending);
return true;
}
@@ -663,8 +700,9 @@ bool sendTagCommand(const uint8_t* dst, uint8_t cmd, bool local, const uint8_t*
}
if (local) {
- return queueDataAvail(&pending);
+ return queueDataAvail(&pending, true);
} else {
+ queueDataAvail(&pending, false);
udpsync.netSendDataAvail(&pending);
return true;
}
@@ -755,7 +793,7 @@ bool checkMirror(struct tagRecord* taginfo, struct pendingData* pending) {
pending2.attemptsLeft = pending->attemptsLeft;
if (taginfo2->isExternal == false) {
- queueDataAvail(&pending2);
+ queueDataAvail(&pending2, true);
} else {
char dst_path[64];
sprintf(dst_path, "/current/%02X%02X%02X%02X%02X%02X%02X%02X_%lu.pending", taginfo2->mac[7], taginfo2->mac[6], taginfo2->mac[5], taginfo2->mac[4], taginfo2->mac[3], taginfo2->mac[2], taginfo2->mac[1], taginfo2->mac[0], millis() % 1000000);
@@ -765,6 +803,7 @@ bool checkMirror(struct tagRecord* taginfo, struct pendingData* pending) {
file.write(taginfo2->data, taginfo2->len);
file.close();
xSemaphoreGive(fsMutex);
+ queueDataAvail(&pending2, false);
udpsync.netSendDataAvail(&pending2);
} else {
xSemaphoreGive(fsMutex);
@@ -850,12 +889,12 @@ void checkQueue(const uint8_t* targetMac) {
if (queueItem == nullptr) {
return;
}
- if (queueCount > 1) queueItem->pendingdata.availdatainfo.nextCheckIn = 0;
+ if (queueCount > 1) queueItem->pendingdata.availdatainfo.nextCheckIn = 5 | 0x8000;
sendDataAvail(&queueItem->pendingdata);
}
}
-bool queueDataAvail(struct pendingData* pending) {
+bool queueDataAvail(struct pendingData* pending, bool local) {
PendingItem newPending;
newPending.pendingdata.availdatainfo = pending->availdatainfo;
newPending.pendingdata.attemptsLeft = pending->attemptsLeft;
@@ -882,7 +921,7 @@ bool queueDataAvail(struct pendingData* pending) {
Serial.println("Reading file " + String(newPending.filename));
file.close();
} else {
- Serial.println("Something's wrong... not found: " + String(newPending.filename));
+ Serial.println("Warning: not found: " + String(newPending.filename));
}
}
newPending.len = taginfo->len;
@@ -891,10 +930,11 @@ bool queueDataAvail(struct pendingData* pending) {
enqueueItem(newPending);
// first in line, send to tag
Serial.printf("queue item added, first in line, total %d elements\n", pendingQueue.size());
- sendDataAvail(pending);
+ if (local) sendDataAvail(pending);
} else {
enqueueItem(newPending);
Serial.printf("queue item added, total %d elements\n", pendingQueue.size());
+ // to do: notify C6 to shorten the checkin time for the current SDA
}
return true;
diff --git a/ESP32_AP-Flasher/src/ota.cpp b/ESP32_AP-Flasher/src/ota.cpp
index d13ee61a..e627e1c1 100644
--- a/ESP32_AP-Flasher/src/ota.cpp
+++ b/ESP32_AP-Flasher/src/ota.cpp
@@ -41,12 +41,16 @@ void handleSysinfoRequest(AsyncWebServerRequest* request) {
doc["psramsize"] = ESP.getPsramSize();
doc["flashsize"] = ESP.getFlashChipSize();
doc["rollback"] = Update.canRollBack();
-#if defined YELLOW_IPS_AP || defined C6_OTA_FLASHING
- doc["C6"] = 1;
+#if defined C6_OTA_FLASHING
+ doc["hasC6"] = 1;
#else
- doc["C6"] = 0;
+ doc["hasC6"] = 0;
+#endif
+#ifdef HAS_EXT_FLASHER
+ doc["hasFlasher"] = 1;
+#else
+ doc["hasFlasher"] = 0;
#endif
-
const size_t bufferSize = measureJson(doc) + 1;
AsyncResponseStream* response = request->beginResponseStream("application/json", bufferSize);
serializeJson(doc, *response);
@@ -266,7 +270,7 @@ void C6firmwareUpdateTask(void* parameter) {
Serial1.begin(115200, SERIAL_8N1, FLASHER_AP_RXD, FLASHER_AP_TXD);
rxSerialStopTask2 = false;
#ifdef FLASHER_DEBUG_RXD
- xTaskCreate(rxSerialTask2, "rxSerialTask2", 1750, NULL, configMAX_PRIORITIES - 4, NULL);
+ xTaskCreate(rxSerialTask2, "rxSerialTask2", 1750, NULL, 2, NULL);
#endif
vTaskDelay(1000 / portTICK_PERIOD_MS);
@@ -285,7 +289,7 @@ void C6firmwareUpdateTask(void* parameter) {
}
void handleUpdateC6(AsyncWebServerRequest* request) {
-#if defined YELLOW_IPS_AP || defined C6_OTA_FLASHING
+#if defined C6_OTA_FLASHING
uint8_t doDownload = 1;
if (request->hasParam("download", true)) {
doDownload = atoi(request->getParam("download", true)->value().c_str());
diff --git a/ESP32_AP-Flasher/src/powermgt.cpp b/ESP32_AP-Flasher/src/powermgt.cpp
index dba55014..6992cf58 100644
--- a/ESP32_AP-Flasher/src/powermgt.cpp
+++ b/ESP32_AP-Flasher/src/powermgt.cpp
@@ -4,7 +4,7 @@
#include "settings.h"
-#ifdef OPENEPAPERLINK_PCB
+#ifdef HAS_EXT_FLASHER
#include "soc/rtc_cntl_reg.h"
#include "soc/soc.h"
#endif
@@ -29,7 +29,7 @@ void simpleAPPower(uint8_t* pin, uint8_t pincount, bool state) {
// when drained if the board applies power, will cause the 3v3 rail to sag enough to reset the ESP32. This is obviously not great. To prevent this from happening,
// we ramp up/down the voltage with PWM. Ramping down really is unnecessary, as the board has a resistor to dump the charge into.
void rampTagPower(uint8_t* pin, bool up) {
-#ifdef OPENEPAPERLINK_PCB
+#ifdef HAS_EXT_FLASHER
WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0);
#endif
if (up) {
@@ -69,7 +69,7 @@ void rampTagPower(uint8_t* pin, bool up) {
ledcDetachPin(pin[0]);
digitalWrite(pin[0], HIGH);
}
-#ifdef OPENEPAPERLINK_PCB
+#ifdef HAS_EXT_FLASHER
WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 1);
#endif
}
diff --git a/ESP32_AP-Flasher/src/serialap.cpp b/ESP32_AP-Flasher/src/serialap.cpp
index fcda1401..f086c726 100644
--- a/ESP32_AP-Flasher/src/serialap.cpp
+++ b/ESP32_AP-Flasher/src/serialap.cpp
@@ -1,8 +1,8 @@
+#include "serialap.h"
+
#include
#include
-#include "serialap.h"
-
#include "commstructs.h"
#include "contentmanager.h"
#include "flasher.h"
@@ -118,7 +118,7 @@ int8_t APpowerPins[] = FLASHER_AP_POWER;
#define AP_RESET_PIN FLASHER_AP_RESET
#define AP_POWER_PIN FLASHER_AP_POWER
#endif
-#ifdef OPENEPAPERLINK_PCB
+#ifdef HAS_EXT_FLASHER
#if (AP_PROCESS_PORT == FLASHER_EXT_PORT)
int8_t APpowerPins[] = FLASHER_EXT_POWER;
#define AP_RESET_PIN FLASHER_EXT_RESET
@@ -172,9 +172,11 @@ void APTagReset() {
// Send data to the AP
uint16_t sendBlock(const void* data, const uint16_t len) {
+ time_t timeCanary = millis();
if (!apInfo.isOnline) return false;
if (!txStart()) return 0;
- for (uint8_t attempt = 0; attempt < 5; attempt++) {
+ // don't retry now, as it collides with communication from the tag
+ for (uint8_t attempt = 0; attempt < 1; attempt++) {
cmdReplyValue = CMD_REPLY_WAIT;
AP_SERIAL_PORT.print(">D>");
if (waitCmdReply()) goto blksend;
@@ -190,32 +192,51 @@ blksend:
bd->checksum = 0;
// calculate checksum
+ const uint8_t* dataBytes = reinterpret_cast(data);
for (uint16_t c = 0; c < len; c++) {
- bd->checksum += ((uint8_t*)data)[c];
+ bd->checksum += dataBytes[c];
}
// send blockData header
- for (uint8_t c = 0; c < sizeof(struct blockData); c++) {
- AP_SERIAL_PORT.write(0xAA ^ blockbuffer[c]);
+ dataBytes = reinterpret_cast(&blockbuffer);
+ const size_t bufferSize = sizeof(struct blockData);
+ uint8_t* modifiedHeader = static_cast(malloc(bufferSize));
+ if (modifiedHeader != nullptr) {
+ for (size_t i = 0; i < bufferSize; i++) {
+ modifiedHeader[i] = 0xAA ^ dataBytes[i];
+ }
+ AP_SERIAL_PORT.write(modifiedHeader, bufferSize);
+ free(modifiedHeader);
}
// send an entire block of data
uint16_t c;
- for (c = 0; c < len; c++) {
- AP_SERIAL_PORT.write(0xAA ^ ((uint8_t*)data)[c]);
+ dataBytes = reinterpret_cast(data);
+ uint8_t* modifiedBuffer = static_cast(malloc(len));
+ if (modifiedBuffer != nullptr) {
+ for (c = 0; c < len; c++) {
+ modifiedBuffer[c] = 0xAA ^ dataBytes[c];
+ }
+ AP_SERIAL_PORT.write(modifiedBuffer, len);
+ free(modifiedBuffer);
}
// fill the rest of the block-length filled with something else (will end up as 0xFF in the buffer)
- for (; c < BLOCK_DATA_SIZE; c++) {
- AP_SERIAL_PORT.write(0x55);
+ const size_t remainingBytes = BLOCK_DATA_SIZE - c;
+ if (remainingBytes > 0) {
+ uint8_t fillBuffer[remainingBytes];
+ memset(fillBuffer, 0x55, remainingBytes);
+ AP_SERIAL_PORT.write(fillBuffer, remainingBytes);
}
// dummy bytes in case some bytes were missed, makes sure the AP gets kicked out of data-loading mode
- for (c = 0; c < 32; c++) {
- AP_SERIAL_PORT.write(0xF5);
- }
+ uint8_t dummyBuffer[32];
+ memset(dummyBuffer, 0xF5, 32);
+ AP_SERIAL_PORT.write(dummyBuffer, 32);
+
if (apInfo.type != ESP32_C6) delay(10);
txEnd();
+ Serial.println("Sendblock complete, " + String(millis() - timeCanary) + "ms");
return bd->checksum;
}
@@ -353,21 +374,21 @@ void rxCmdProcessor(void* parameter) {
case RX_CMD_RQB:
processBlockRequest((struct espBlockRequest*)rxcmd->data);
#ifdef HAS_RGB_LED
- shortBlink(CRGB::Blue);
+ // shortBlink(CRGB::Blue);
#endif
quickBlink(3);
break;
case RX_CMD_ADR:
processDataReq((struct espAvailDataReq*)rxcmd->data, true);
#ifdef HAS_RGB_LED
- shortBlink(CRGB::Aqua);
+ // shortBlink(CRGB::Aqua);
#endif
quickBlink(1);
break;
case RX_CMD_XFC:
processXferComplete((struct espXferComplete*)rxcmd->data, true);
#ifdef HAS_RGB_LED
- shortBlink(CRGB::Purple);
+ // shortBlink(CRGB::Purple);
#endif
break;
case RX_CMD_XTO:
@@ -402,7 +423,9 @@ void rxSerialTask(void* parameter) {
lastchar = AP_SERIAL_PORT.read();
switch (RXState) {
case ZBS_RX_WAIT_HEADER:
+
Serial.write(lastchar);
+
// shift characters in
for (uint8_t c = 0; c < 3; c++) {
cmdbuffer[c] = cmdbuffer[c + 1];
@@ -614,6 +637,8 @@ void rxSerialTask2(void* parameter) {
while (Serial2.available()) {
lastchar = Serial2.read();
charCount++;
+
+ // debug info
Serial.write(lastchar);
}
vTaskDelay(1 / portTICK_PERIOD_MS);
@@ -723,17 +748,34 @@ bool bringAPOnline() {
}
}
+bool checkRadio() {
+ // make a short between FLASHER_AP_TXD and FLASHER_AP_RXD to indicate that no radio is present
+ // e.g. for flasher only, or just to use the S3 to generate images for smaller AP's
+ pinMode(FLASHER_AP_TXD, OUTPUT);
+ pinMode(FLASHER_AP_RXD, INPUT_PULLDOWN);
+ digitalWrite(FLASHER_AP_TXD, LOW);
+ if (digitalRead(FLASHER_AP_RXD) != LOW) return true;
+ digitalWrite(FLASHER_AP_TXD, HIGH);
+ if (digitalRead(FLASHER_AP_RXD) != HIGH) return true;
+ pinMode(FLASHER_AP_TXD, INPUT_PULLDOWN);
+ return false;
+}
+
void APTask(void* parameter) {
- xTaskCreate(rxCmdProcessor, "rxCmdProcessor", 4000, NULL, configMAX_PRIORITIES - 10, NULL);
- xTaskCreate(rxSerialTask, "rxSerialTask", 1750, NULL, configMAX_PRIORITIES - 4, NULL);
-#ifdef FLASHER_DEBUG_RXD
- xTaskCreate(rxSerialTask2, "rxSerialTask2", 1750, NULL, configMAX_PRIORITIES - 4, NULL);
-#endif
+ if (!checkRadio()) {
+ // no radio
+ Serial.println("Working without radio.");
+ addFadeMono(config.led);
+ setAPstate(true, AP_STATE_NORADIO);
+ refreshAllPending();
+ vTaskDelete(NULL);
+ return;
+ }
#if (AP_PROCESS_PORT == FLASHER_AP_PORT)
AP_SERIAL_PORT.begin(115200, SERIAL_8N1, FLASHER_AP_RXD, FLASHER_AP_TXD);
#endif
-#ifdef OPENEPAPERLINK_PCB
+#ifdef HAS_EXT_FLASHER
#if (AP_PROCESS_PORT == FLASHER_EXT_PORT)
AP_SERIAL_PORT.begin(115200, SERIAL_8N1, FLASHER_EXT_RXD, FLASHER_EXT_TXD);
#endif
@@ -742,8 +784,15 @@ void APTask(void* parameter) {
#endif
#endif
+ xTaskCreate(rxCmdProcessor, "rxCmdProcessor", 6000, NULL, 15, NULL);
+ xTaskCreate(rxSerialTask, "rxSerialTask", 1750, NULL, 11, NULL);
+#ifdef FLASHER_DEBUG_RXD
+ xTaskCreate(rxSerialTask2, "rxSerialTask2", 1750, NULL, 2, NULL);
+#endif
+
bringAPOnline();
+#ifndef C6_OTA_FLASHING
if (checkForcedAPFlash() && FLASHER_AP_MOSI != -1) {
if (apInfo.type == SOLUM_SEG_UK && apInfo.isOnline) {
notifySegmentedFlash();
@@ -756,6 +805,7 @@ void APTask(void* parameter) {
checkWaitPowerCycle();
bringAPOnline();
}
+#endif
if (apInfo.isOnline) {
// AP works!
@@ -770,6 +820,7 @@ void APTask(void* parameter) {
}
uint16_t fsversion;
+#ifndef C6_OTA_FLASHING
if (FLASHER_AP_MOSI != -1) {
fsversion = getAPUpdateVersion(apInfo.type);
if ((fsversion) && (apInfo.version != fsversion)) {
@@ -805,6 +856,8 @@ void APTask(void* parameter) {
}
}
}
+#endif
+
refreshAllPending();
} else {
#ifndef FLASH_TIMEOUT
@@ -817,9 +870,10 @@ void APTask(void* parameter) {
#ifdef HAS_RGB_LED
showColorPattern(CRGB::Red, CRGB::Yellow, CRGB::Red);
#endif
- if(apInfo.state != AP_STATE_FLASHING)// In case we are flashing already we do not want to end in a failed AP
+ if (apInfo.state != AP_STATE_FLASHING) // In case we are flashing already we do not want to end in a failed AP
setAPstate(false, AP_STATE_FAILED);
} else {
+#ifndef C6_OTA_FLASHING
// AP unavailable, maybe time to flash?
setAPstate(false, AP_STATE_OFFLINE);
@@ -886,13 +940,14 @@ void APTask(void* parameter) {
flashCountDown(30);
ESP.restart();
}
+#endif
#endif
}
}
uint8_t attempts = 0;
while (1) {
- if (((apInfo.state == AP_STATE_ONLINE)||(apInfo.state == AP_STATE_FAILED)) && (millis() - lastAPActivity > AP_ACTIVITY_MAX_INTERVAL)) {
+ if (((apInfo.state == AP_STATE_ONLINE) || (apInfo.state == AP_STATE_FAILED)) && (millis() - lastAPActivity > AP_ACTIVITY_MAX_INTERVAL)) {
bool reply = sendPing();
if (!reply) {
attempts++;
diff --git a/ESP32_AP-Flasher/src/system.cpp b/ESP32_AP-Flasher/src/system.cpp
index 44d7cb93..95e4732c 100644
--- a/ESP32_AP-Flasher/src/system.cpp
+++ b/ESP32_AP-Flasher/src/system.cpp
@@ -14,11 +14,12 @@ void timeSyncCallback(struct timeval* tv) {
}
void initTime(void* parameter) {
- if (WiFi.status() == WL_CONNECTED) {
- sntp_set_time_sync_notification_cb(timeSyncCallback);
- sntp_set_sync_interval(300 * 1000);
- configTzTime(config.timeZone, "nl.pool.ntp.org", "europe.pool.ntp.org", "time.nist.gov");
+ if (WiFi.status() != WL_CONNECTED) {
+ vTaskDelay(500 / portTICK_PERIOD_MS);
}
+ sntp_set_time_sync_notification_cb(timeSyncCallback);
+ sntp_set_sync_interval(300 * 1000);
+ configTzTime(config.timeZone, "time.cloudflare.com", "pool.ntp.org", "time.nist.gov");
logStartUp();
struct tm timeinfo;
while (millis() < 30000) {
diff --git a/ESP32_AP-Flasher/src/tag_db.cpp b/ESP32_AP-Flasher/src/tag_db.cpp
index c6b9086e..a5234315 100644
--- a/ESP32_AP-Flasher/src/tag_db.cpp
+++ b/ESP32_AP-Flasher/src/tag_db.cpp
@@ -314,22 +314,22 @@ void initAPconfig() {
}
configFile.close();
}
- config.channel = APconfig["channel"] | 0;
+ config.channel = APconfig.containsKey("channel") ? APconfig["channel"] : 0;
if (APconfig["alias"]) strlcpy(config.alias, APconfig["alias"], sizeof(config.alias));
- config.led = APconfig["led"] | 255;
- config.tft = APconfig["tft"] | 255;
- config.language = APconfig["language"] | 0;
- config.maxsleep = APconfig["maxsleep"] | 10;
- config.stopsleep = APconfig["stopsleep"] | 1;
- config.preview = APconfig["preview"] | 1;
- config.lock = APconfig["lock"] | 0;
- config.sleepTime1 = APconfig["sleeptime1"] | 0;
- config.sleepTime2 = APconfig["sleeptime2"] | 0;
+ config.led = APconfig.containsKey("led") ? APconfig["led"] : 255;
+ config.tft = APconfig.containsKey("tft") ? APconfig["tft"] : 255;
+ config.language = APconfig.containsKey("language") ? APconfig["language"] : 0;
+ config.maxsleep = APconfig.containsKey("maxsleep") ? APconfig["maxsleep"] : 10;
+ config.stopsleep = APconfig.containsKey("stopsleep") ? APconfig["stopsleep"] : 1;
+ config.preview = APconfig.containsKey("preview") ? APconfig["preview"] : 1;
+ config.lock = APconfig.containsKey("lock") ? APconfig["lock"] : 0;
+ config.sleepTime1 = APconfig.containsKey("sleeptime1") ? APconfig["sleeptime1"] : 0;
+ config.sleepTime2 = APconfig.containsKey("sleeptime2") ? APconfig["sleeptime2"] : 0;
// default wifi power 8.5 dbM
// see https://github.com/espressif/arduino-esp32/blob/master/libraries/WiFi/src/WiFiGeneric.h#L111
- config.wifiPower = APconfig["wifipower"] | 34;
- config.repo = APconfig["repo"] | "jjwbruijn/OpenEPaperLink";
- config.env = APconfig["env"] | STR(BUILD_ENV_NAME);
+ config.wifiPower = APconfig.containsKey("wifipower") ? APconfig["wifipower"] : 34;
+ config.repo = APconfig.containsKey("repo") ? APconfig["repo"].as() : String("jjwbruijn/OpenEPaperLink");
+ config.env = APconfig.containsKey("env") ? APconfig["env"].as() : String(STR(BUILD_ENV_NAME));
if (APconfig["timezone"]) {
strlcpy(config.timeZone, APconfig["timezone"], sizeof(config.timeZone));
} else {
@@ -425,12 +425,14 @@ bool setVarDB(const std::string& key, const String& value, const bool notify) {
}
String getBaseName(const String& filename) {
- int lastDotIndex = filename.lastIndexOf('.');
- return lastDotIndex != -1 ? filename.substring(0, lastDotIndex) : filename;
+ // int lastDotIndex = filename.lastIndexOf('.');
+ // return lastDotIndex != -1 ? filename.substring(0, lastDotIndex) : filename;
+ return filename.substring(0, 16);
}
void cleanupCurrent() {
// clean unknown previews
+ Serial.println("Cleaning up temporary files");
File dir = contentFS->open("/current");
File file = dir.openNextFile();
while (file) {
@@ -444,9 +446,10 @@ void cleanupCurrent() {
break;
}
}
- if (!found) {
+ if (!found || filename.endsWith(".pending")) {
filename = file.path();
file.close();
+ Serial.println("remove " + filename);
contentFS->remove(filename);
}
}
diff --git a/ESP32_AP-Flasher/src/udp.cpp b/ESP32_AP-Flasher/src/udp.cpp
index 739d38ad..6502f5c9 100644
--- a/ESP32_AP-Flasher/src/udp.cpp
+++ b/ESP32_AP-Flasher/src/udp.cpp
@@ -152,7 +152,7 @@ void UDPcomm::getAPList() {
wsSendAPitem(&APitem);
if (config.alias == 0) {
- xTaskCreate(autoselect, "autoselect", 5000, NULL, configMAX_PRIORITIES - 10, NULL);
+ xTaskCreate(autoselect, "autoselect", 5000, NULL, 2, NULL);
}
uint8_t buffer[sizeof(struct APlist) + 1];
diff --git a/ESP32_AP-Flasher/src/usbflasher.cpp b/ESP32_AP-Flasher/src/usbflasher.cpp
index 3178119a..a90f3239 100644
--- a/ESP32_AP-Flasher/src/usbflasher.cpp
+++ b/ESP32_AP-Flasher/src/usbflasher.cpp
@@ -1,14 +1,26 @@
#include
+#include "usbflasher.h"
+
+#ifdef HAS_USB
+// flashing via 2nd USB port
#include "USB.h"
+USBCDC USBSerial;
+#define cmdSerial USBSerial
+#else
+#define cmdSerial Serial
+#endif
+
+// #include "esp32-hal-tinyusb.h"
#include "flasher.h"
+#include "leds.h"
#include "powermgt.h"
#include "settings.h"
#include "swd.h"
+#include "web.h"
+#include "webflasher.h"
#include "zbs_interface.h"
-USBCDC USBSerial;
-
QueueHandle_t flasherCmdQueue;
uint32_t usbConnectedStartTime = 0;
@@ -21,6 +33,7 @@ bool serialPassthroughState = false;
#define FLASHER_WAIT_DATA 4
#define FLASHER_WAIT_CRCH 5
#define FLASHER_WAIT_CRCL 6
+#define FLASHER_RESET 7
struct flasherCommand {
uint8_t command = 0;
@@ -29,7 +42,7 @@ struct flasherCommand {
};
int8_t powerPins[] = FLASHER_AP_POWER;
-#ifdef OPENEPAPERLINK_PCB
+#ifdef HAS_EXT_FLASHER
int8_t powerPins2[] = FLASHER_EXT_POWER;
int8_t powerPins3[] = FLASHER_ALT_POWER;
#endif
@@ -39,48 +52,48 @@ bool autoFlash(flasher* f) {
if (f->findTagByMD5()) {
// this tag currently contains original firmware, found its fingerprint
- USBSerial.printf("Found original firmware tag, recognized its fingerprint (%s)\n", f->md5char);
+ cmdSerial.printf("Found original firmware tag, recognized its fingerprint (%s)\n", f->md5char);
f->readInfoBlock();
f->getFirmwareMac();
f->prepareInfoBlock();
f->writeInfoBlock();
- USBSerial.printf("Attempting to perform a flash...\n");
+ cmdSerial.printf("Attempting to perform a flash...\n");
if (f->writeFlashFromPack("/Tag_FW_Pack.bin", f->tagtype)) {
- USBSerial.printf("Successfully flashed the tag!\n");
+ cmdSerial.printf("Successfully flashed the tag!\n");
return true;
} else {
- USBSerial.printf("Couldn't flash the tag, for some reason...\n");
+ cmdSerial.printf("Couldn't flash the tag, for some reason...\n");
}
} else if (f->getInfoBlockMD5()) {
// did find an infoblock MD5 that looks valid
if (f->findTagByMD5()) {
// did find the md5 in the database
- USBSerial.printf("Found an already-flashed tag, recognized its fingerprint (%s)\n", f->md5char);
+ cmdSerial.printf("Found an already-flashed tag, recognized its fingerprint (%s)\n", f->md5char);
f->getInfoBlockMac();
f->getInfoBlockType();
f->readInfoBlock();
- USBSerial.printf("Attempting to perform a flash...\n");
+ cmdSerial.printf("Attempting to perform a flash...\n");
if (f->writeFlashFromPack("/Tag_FW_Pack.bin", f->tagtype)) {
- USBSerial.printf("Successfully flashed the tag!\n");
+ cmdSerial.printf("Successfully flashed the tag!\n");
return true;
} else {
- USBSerial.printf("Couldn't flash the tag, for some reason...\n");
+ cmdSerial.printf("Couldn't flash the tag, for some reason...\n");
}
} else {
// couldn't find the md5 from the infoblock
- USBSerial.printf("Found an already-flashed tag, but we couldn't find its fingerprint (%s) in the database\n", f->md5char);
+ cmdSerial.printf("Found an already-flashed tag, but we couldn't find its fingerprint (%s) in the database\n", f->md5char);
return false;
}
} else {
// We couldn't recognize the tag from it's fingerprint...
- USBSerial.printf("Found a tag but didn't recognize its fingerprint\n", f->md5char);
+ cmdSerial.printf("Found a tag but didn't recognize its fingerprint\n", f->md5char);
f->backupFlash();
- USBSerial.printf("Saved this MD5 binary to filesystem\n");
+ cmdSerial.printf("Saved this MD5 binary to filesystem\n");
}
return false;
}
-void sendFlasherAnswer(uint8_t answer_cmd, uint8_t* ans_buff, uint32_t len) {
+void sendFlasherAnswer(uint8_t answer_cmd, uint8_t* ans_buff, uint32_t len, uint8_t transportType) {
uint8_t* answer_buffer = (uint8_t*)calloc(3 + 2 + 2 + len + 2 + 13, 1);
if (answer_buffer == nullptr) return;
uint32_t CRC_value = 0xAB34;
@@ -101,29 +114,42 @@ void sendFlasherAnswer(uint8_t answer_cmd, uint8_t* ans_buff, uint32_t len) {
answer_buffer[3 + 2 + 2 + len] = CRC_value >> 8;
answer_buffer[3 + 2 + 2 + len + 1] = CRC_value;
- USBSerial.write(answer_buffer, 3 + 2 + 2 + len + 2);
+ if (transportType == TRANSPORT_USB) {
+ cmdSerial.write(answer_buffer, 3 + 2 + 2 + len + 2);
+ } else {
+ sendDataToClient(answer_buffer, 3 + 2 + 2 + len + 2);
+ }
// for(uint16_t c = 0; c< 3+2+2+len+2; c++){
//}
free(answer_buffer);
}
-void flasherUartHandler(uint8_t* data, uint8_t len) {
- static struct flasherCommand* cmd;
+void flasherDataHandler(uint8_t* data, size_t len, uint8_t transportType) {
+ static struct flasherCommand* cmd = nullptr;
static uint8_t flasherSerialState = FLASHER_WAIT_A;
static uint32_t flasherCmdDataIndex = 0;
static uint16_t flasherCRC = 0xAB34;
static uint32_t flasherLastCmd = 0;
static uint8_t curLenIndex = 0;
- if ((flasherSerialState != FLASHER_WAIT_A) && (millis() - flasherLastCmd >= 225)) {
- flasherSerialState = FLASHER_WAIT_A;
- // we should probably do something with stale commands containing data (data leak!)
+ if ((flasherSerialState != FLASHER_WAIT_A) && (millis() - flasherLastCmd >= 3000)) {
+ flasherSerialState = FLASHER_RESET;
}
while (len--) {
uint8_t usbbyte = *(data++);
switch (flasherSerialState) {
+ case FLASHER_RESET:
+ if (transportType == TRANSPORT_TCP && cmd != nullptr) {
+ if (cmd->data != nullptr) {
+ free(cmd->data);
+ cmd->data = nullptr;
+ }
+ delete cmd;
+ cmd = nullptr;
+ }
+ flasherSerialState = FLASHER_WAIT_A;
case FLASHER_WAIT_A:
if (usbbyte == 'A') {
flasherSerialState = FLASHER_WAIT_T;
@@ -139,7 +165,7 @@ void flasherUartHandler(uint8_t* data, uint8_t len) {
flasherCRC = 0xAB34;
flasherCmdDataIndex = 0;
} else {
- flasherSerialState = FLASHER_WAIT_A;
+ flasherSerialState = FLASHER_RESET;
}
break;
case FLASHER_WAIT_CMD:
@@ -158,9 +184,11 @@ void flasherUartHandler(uint8_t* data, uint8_t len) {
cmd->data = (uint8_t*)calloc(cmd->len, 1);
if (cmd->data == nullptr) {
delete cmd;
+ cmd = nullptr;
flasherSerialState = FLASHER_WAIT_A;
+ } else {
+ flasherSerialState = FLASHER_WAIT_DATA;
}
- flasherSerialState = FLASHER_WAIT_DATA;
} else {
// 0 len, so skip to CRC immediately
flasherSerialState = FLASHER_WAIT_CRCH;
@@ -181,17 +209,16 @@ void flasherUartHandler(uint8_t* data, uint8_t len) {
case FLASHER_WAIT_CRCL:
flasherCRC -= ((uint16_t)usbbyte);
if (flasherCRC) {
- cmd = nullptr;
- // we should probably delete the cmd and associated data here (data leak)
+ flasherSerialState = FLASHER_RESET;
+ wsSerial("failed CRC");
} else {
- BaseType_t queuestatus = xQueueSend(flasherCmdQueue, &cmd, 0);
- if (queuestatus == pdFALSE) {
- if (cmd->data != nullptr) free(cmd->data);
- delete cmd;
+ if (transportType == TRANSPORT_USB) {
+ BaseType_t queuestatus = xQueueSend(flasherCmdQueue, &cmd, 0);
+ } else {
+ processFlasherCommand(cmd, TRANSPORT_TCP);
}
- cmd = nullptr;
+ flasherSerialState = FLASHER_RESET;
}
- flasherSerialState = FLASHER_WAIT_A;
break;
}
}
@@ -204,6 +231,7 @@ void resetFlasherState() {
serialPassthroughState = false;
}
+#ifdef HAS_USB
static void usbEventCallback(void* arg, esp_event_base_t event_base, int32_t event_id, void* event_data) {
if (event_base == ARDUINO_USB_EVENTS) {
arduino_usb_event_data_t* data = (arduino_usb_event_data_t*)event_data;
@@ -250,11 +278,11 @@ static void usbEventCallback(void* arg, esp_event_base_t event_base, int32_t eve
// Serial.printf("CDC RX [%u]:", data->rx.len);
{
uint8_t buf[data->rx.len];
- size_t len = USBSerial.read(buf, data->rx.len);
+ size_t len = cmdSerial.read(buf, data->rx.len);
if (serialPassthroughState) {
Serial2.write(buf, len);
} else {
- flasherUartHandler(buf, len);
+ flasherDataHandler(buf, len, TRANSPORT_USB);
}
}
break;
@@ -267,6 +295,7 @@ static void usbEventCallback(void* arg, esp_event_base_t event_base, int32_t eve
}
}
}
+#endif
typedef enum {
CMD_GET_VERSION = 1,
@@ -292,51 +321,65 @@ typedef enum {
CMD_COMPLETE = 88,
} ZBS_UART_PROTO;
-uint32_t FLASHER_VERSION = 0x00000030;
+uint32_t FLASHER_VERSION = 0x00000031;
#define CONTROLLER_ZBS243 0
#define CONTROLLER_NRF82511 1
uint8_t selectedController = 0;
uint8_t selectedFlasherPort;
uint32_t currentFlasherOffset;
-flasher* zbsflasherp;
-nrfswd* nrfflasherp;
+flasher* zbsflasherp = nullptr;
+nrfswd* nrfflasherp = nullptr;
-void processFlasherCommand(struct flasherCommand* cmd) {
+void processFlasherCommand(struct flasherCommand* cmd, uint8_t transportType) {
uint8_t* tempbuffer;
uint8_t temp_buff[16];
uint32_t spi_speed = 0;
uint8_t powerPinCount = 1;
static uint32_t curspeed = 0;
+ uint8_t numPowerPins;
+
+#ifdef HAS_RGB_LED
+ shortBlink(CRGB::White);
+#else
+ quickBlink(2);
+#endif
switch (cmd->command) {
case CMD_GET_VERSION:
+ wsSerial("> get version");
temp_buff[0] = FLASHER_VERSION >> 24;
temp_buff[1] = FLASHER_VERSION >> 16;
temp_buff[2] = FLASHER_VERSION >> 8;
temp_buff[3] = FLASHER_VERSION;
- sendFlasherAnswer(cmd->command, temp_buff, 4);
+ sendFlasherAnswer(cmd->command, temp_buff, 4, transportType);
break;
case CMD_RESET_ESP:
- sendFlasherAnswer(cmd->command, NULL, 0);
+ wsSerial("reset");
+ sendFlasherAnswer(cmd->command, NULL, 0, transportType);
delay(100);
ESP.restart();
break;
case CMD_SET_POWER:
+ wsSerial("> power");
switch (selectedFlasherPort) {
case 0:
- powerControl(cmd->data[0], (uint8_t*)powerPins, 1);
+ numPowerPins = sizeof(powerPins);
+ powerControl(cmd->data[0], (uint8_t*)powerPins, numPowerPins);
break;
case 1:
- powerControl(cmd->data[0], (uint8_t*)powerPins2, 1);
+ numPowerPins = sizeof(powerPins2);
+ powerControl(cmd->data[0], (uint8_t*)powerPins2, numPowerPins);
break;
case 2:
- powerControl(cmd->data[0], (uint8_t*)powerPins3, 1);
+ numPowerPins = sizeof(powerPins3);
+ powerControl(cmd->data[0], (uint8_t*)powerPins3, numPowerPins);
break;
}
- sendFlasherAnswer(CMD_SET_POWER, NULL, 0);
+ sendFlasherAnswer(CMD_SET_POWER, NULL, 0, transportType);
break;
case CMD_RESET:
+ wsSerial("> reset tag");
if (zbsflasherp != nullptr) {
zbsflasherp->zbs->reset();
delete zbsflasherp;
@@ -347,124 +390,135 @@ void processFlasherCommand(struct flasherCommand* cmd) {
delete nrfflasherp;
nrfflasherp = nullptr;
}
- sendFlasherAnswer(CMD_RESET, NULL, 0);
+ sendFlasherAnswer(CMD_RESET, NULL, 0, transportType);
break;
case CMD_ERASE_FLASH:
+ wsSerial("> erase flash");
if (selectedController == CONTROLLER_NRF82511) {
} else if (selectedController == CONTROLLER_ZBS243) {
if (zbsflasherp == nullptr) return;
zbsflasherp->zbs->erase_flash();
}
- sendFlasherAnswer(CMD_ERASE_FLASH, NULL, 0);
+ sendFlasherAnswer(CMD_ERASE_FLASH, NULL, 0, transportType);
break;
case CMD_ERASE_INFOPAGE:
+ wsSerial("> erase infopage");
if (selectedController == CONTROLLER_NRF82511) {
nrfflasherp->erase_uicr();
} else if (selectedController == CONTROLLER_ZBS243) {
if (zbsflasherp == nullptr) return;
zbsflasherp->zbs->erase_infoblock();
}
- sendFlasherAnswer(CMD_ERASE_INFOPAGE, NULL, 0);
+ sendFlasherAnswer(CMD_ERASE_INFOPAGE, NULL, 0, transportType);
break;
case CMD_SELECT_PORT:
+ wsSerial("> select port");
selectedFlasherPort = cmd->data[0];
Serial.printf("Port selected = %d\n", cmd->data[0]);
break;
case CMD_SELECT_ZBS243:
+ wsSerial("> connect zbs");
zbsflasherp = new flasher;
temp_buff[0] = zbsflasherp->connectTag(selectedFlasherPort);
- sendFlasherAnswer(CMD_SELECT_ZBS243, temp_buff, 1);
+ sendFlasherAnswer(CMD_SELECT_ZBS243, temp_buff, 1, transportType);
currentFlasherOffset = 0;
selectedController = CONTROLLER_ZBS243;
break;
case CMD_SELECT_NRF82511:
+ wsSerial("> connect nrf");
switch (selectedFlasherPort) {
case 0:
- powerControl(true, (uint8_t*)powerPins, 1);
+ numPowerPins = sizeof(powerPins);
+ powerControl(true, (uint8_t*)powerPins, numPowerPins);
nrfflasherp = new nrfswd(FLASHER_AP_MISO, FLASHER_AP_CLK);
break;
case 1:
- powerControl(true, (uint8_t*)powerPins2, 1);
+ numPowerPins = sizeof(powerPins2);
+ powerControl(true, (uint8_t*)powerPins2, numPowerPins);
nrfflasherp = new nrfswd(FLASHER_EXT_MISO, FLASHER_EXT_CLK);
break;
case 2:
- powerControl(true, (uint8_t*)powerPins3, 1);
+ numPowerPins = sizeof(powerPins3);
+ powerControl(true, (uint8_t*)powerPins3, numPowerPins);
nrfflasherp = new nrfswd(FLASHER_ALT_MISO, FLASHER_ALT_CLK);
break;
}
nrfflasherp->init();
temp_buff[0] = (nrfflasherp->isConnected && !nrfflasherp->isLocked);
- sendFlasherAnswer(CMD_SELECT_NRF82511, temp_buff, 1);
+ sendFlasherAnswer(CMD_SELECT_NRF82511, temp_buff, 1, transportType);
currentFlasherOffset = 0;
selectedController = CONTROLLER_NRF82511;
break;
case CMD_READ_FLASH:
+ wsSerial("> read flash");
uint8_t* bufferp;
uint32_t cur_len;
if (selectedController == CONTROLLER_NRF82511) {
if (nrfflasherp == nullptr) return;
if (currentFlasherOffset >= nrfflasherp->nrf_info.flash_size) {
- sendFlasherAnswer(CMD_COMPLETE, temp_buff, 1);
+ sendFlasherAnswer(CMD_COMPLETE, temp_buff, 1, transportType);
} else {
bufferp = (uint8_t*)malloc(1024);
if (bufferp == nullptr) return;
cur_len = (nrfflasherp->nrf_info.flash_size - currentFlasherOffset >= 1024) ? 1024 : nrfflasherp->nrf_info.flash_size - currentFlasherOffset;
nrfflasherp->nrf_read_bank(currentFlasherOffset, (uint32_t*)bufferp, cur_len);
currentFlasherOffset += cur_len;
- sendFlasherAnswer(CMD_READ_FLASH, bufferp, cur_len);
+ sendFlasherAnswer(CMD_READ_FLASH, bufferp, cur_len, transportType);
if (bufferp != nullptr) free(bufferp);
}
} else if (selectedController == CONTROLLER_ZBS243) {
if (zbsflasherp == nullptr) return;
if (currentFlasherOffset >= 65536) {
- sendFlasherAnswer(CMD_COMPLETE, temp_buff, 1);
+ sendFlasherAnswer(CMD_COMPLETE, temp_buff, 1, transportType);
} else {
bufferp = (uint8_t*)malloc(1024);
if (bufferp == nullptr) return;
cur_len = (65536 - currentFlasherOffset >= 1024) ? 1024 : 65536 - currentFlasherOffset;
zbsflasherp->readBlock(currentFlasherOffset, bufferp, cur_len, false);
currentFlasherOffset += cur_len;
- sendFlasherAnswer(CMD_READ_FLASH, bufferp, cur_len);
+ sendFlasherAnswer(CMD_READ_FLASH, bufferp, cur_len, transportType);
if (bufferp != nullptr) free(bufferp);
}
}
break;
case CMD_READ_INFOPAGE:
+ wsSerial("> read infopage");
uint8_t* ibufferp;
uint32_t icur_len;
if (selectedController == CONTROLLER_NRF82511) {
if (nrfflasherp == nullptr) return;
if (currentFlasherOffset >= 4096) {
- sendFlasherAnswer(CMD_COMPLETE, temp_buff, 1);
+ sendFlasherAnswer(CMD_COMPLETE, temp_buff, 1, transportType);
} else {
ibufferp = (uint8_t*)malloc(1024);
if (ibufferp == nullptr) return;
icur_len = (4096 - currentFlasherOffset >= 256) ? 256 : 4096 - currentFlasherOffset;
nrfflasherp->nrf_read_bank(0x10001000 + currentFlasherOffset, (uint32_t*)ibufferp, icur_len);
currentFlasherOffset += icur_len;
- sendFlasherAnswer(CMD_READ_INFOPAGE, ibufferp, icur_len);
+ sendFlasherAnswer(CMD_READ_INFOPAGE, ibufferp, icur_len, transportType);
if (ibufferp != nullptr) free(ibufferp);
}
} else if (selectedController == CONTROLLER_ZBS243) {
if (zbsflasherp == nullptr) return;
if (currentFlasherOffset >= 1024) {
- sendFlasherAnswer(CMD_COMPLETE, temp_buff, 1);
+ sendFlasherAnswer(CMD_COMPLETE, temp_buff, 1, transportType);
} else {
ibufferp = (uint8_t*)malloc(1024);
if (ibufferp == nullptr) return;
icur_len = (1024 - currentFlasherOffset >= 256) ? 256 : 1024 - currentFlasherOffset;
zbsflasherp->readBlock(currentFlasherOffset, ibufferp, icur_len, true);
currentFlasherOffset += icur_len;
- sendFlasherAnswer(CMD_READ_INFOPAGE, ibufferp, icur_len);
+ sendFlasherAnswer(CMD_READ_INFOPAGE, ibufferp, icur_len, transportType);
if (ibufferp != nullptr) free(ibufferp);
}
}
break;
case CMD_WRITE_FLASH:
+ wsSerial("> write flash");
if (selectedController == CONTROLLER_NRF82511) {
if (nrfflasherp == nullptr) return;
if (currentFlasherOffset >= nrfflasherp->nrf_info.flash_size) {
- sendFlasherAnswer(CMD_COMPLETE, temp_buff, 1);
+ sendFlasherAnswer(CMD_COMPLETE, temp_buff, 1, transportType);
} else {
for (uint32_t c = currentFlasherOffset; c < (currentFlasherOffset + cmd->len);) {
// very ugly and naive way to find out what page we're in, and erase all relevant pages before writing
@@ -479,55 +533,58 @@ void processFlasherCommand(struct flasherCommand* cmd) {
nrfflasherp->nrf_write_bank(currentFlasherOffset, (uint32_t*)cmd->data, cmd->len);
Serial.printf("wrote page to nrf\n");
currentFlasherOffset += cmd->len;
- sendFlasherAnswer(CMD_WRITE_FLASH, NULL, 0);
+ sendFlasherAnswer(CMD_WRITE_FLASH, NULL, 0, transportType);
}
} else if (selectedController == CONTROLLER_ZBS243) {
if (zbsflasherp == nullptr) return;
if (currentFlasherOffset >= 65536) {
- sendFlasherAnswer(CMD_COMPLETE, temp_buff, 1);
+ sendFlasherAnswer(CMD_COMPLETE, temp_buff, 1, transportType);
} else {
zbsflasherp->writeBlock(currentFlasherOffset, cmd->data, cmd->len, false);
currentFlasherOffset += cmd->len;
- sendFlasherAnswer(CMD_WRITE_FLASH, NULL, 0);
+ sendFlasherAnswer(CMD_WRITE_FLASH, NULL, 0, transportType);
}
}
break;
case CMD_WRITE_INFOPAGE:
+ wsSerial("> write infopage");
if (selectedController == CONTROLLER_NRF82511) {
if (nrfflasherp == nullptr) return;
if (currentFlasherOffset >= 4096) {
- sendFlasherAnswer(CMD_COMPLETE, temp_buff, 1);
+ sendFlasherAnswer(CMD_COMPLETE, temp_buff, 1, transportType);
} else {
nrfflasherp->nrf_write_bank(0x10001000 + currentFlasherOffset, (uint32_t*)cmd->data, cmd->len);
Serial.printf("wrote page to nrf\n");
currentFlasherOffset += cmd->len;
- sendFlasherAnswer(CMD_WRITE_INFOPAGE, NULL, 0);
+ sendFlasherAnswer(CMD_WRITE_INFOPAGE, NULL, 0, transportType);
}
} else if (selectedController == CONTROLLER_ZBS243) {
if (zbsflasherp == nullptr) return;
if (currentFlasherOffset >= 1024) {
- sendFlasherAnswer(CMD_COMPLETE, temp_buff, 1);
+ sendFlasherAnswer(CMD_COMPLETE, temp_buff, 1, transportType);
} else {
zbsflasherp->writeBlock(currentFlasherOffset, cmd->data, cmd->len, true);
currentFlasherOffset += cmd->len;
- sendFlasherAnswer(CMD_WRITE_INFOPAGE, NULL, 0);
+ sendFlasherAnswer(CMD_WRITE_INFOPAGE, NULL, 0, transportType);
}
}
break;
case CMD_PASS_THROUGH:
+ wsSerial("> pass through");
Serial2.begin(115200, SERIAL_8N1, FLASHER_EXT_RXD, FLASHER_EXT_TXD);
- USBSerial.println(">>>");
+ cmdSerial.println(">>>");
serialPassthroughState = true;
break;
case CMD_AUTOFLASH:
+ wsSerial("> autoflash");
if (selectedController == CONTROLLER_ZBS243) {
autoFlash(zbsflasherp);
zbsflasherp->zbs->reset();
delete zbsflasherp;
- zbsflasherp = 0;
- USBSerial.write(0x04);
+ zbsflasherp = nullptr;
+ cmdSerial.write(0x04);
} else {
- USBSerial.println("Not yet implemented!");
+ cmdSerial.println("Not yet implemented!");
}
break;
}
@@ -554,41 +611,54 @@ void tagDebugPassthrough() {
uint8_t* buf = (uint8_t*)malloc(len);
Serial2.read(buf, len);
Serial.write(buf, len);
- USBSerial.write(buf, len);
+ cmdSerial.write(buf, len);
free(buf);
}
}
+#ifdef HAS_USB
void usbFlasherTask(void* parameter) {
flasherCmdQueue = xQueueCreate(10, sizeof(struct flasherCommand*));
-#if ARDUINO_USB_MODEflash
-#warning Wrong USB mode is in use, check settings in platformio.ini
+
+#ifndef ARDUINO_USB_MODE
+#error This ESP32 SoC has no Native USB interface
+#elif ARDUINO_USB_MODE == 1
+#warning This sketch should be used when USB is in OTG mode. Wrong USB mode is in use, check settings in platformio.ini
#endif
USB.onEvent(usbEventCallback);
- USBSerial.onEvent(usbEventCallback);
- USBSerial.setTimeout(1000);
+ cmdSerial.onEvent(usbEventCallback);
+ cmdSerial.setTimeout(1000);
+ USB.productName("OpenEpaperLink-flasher");
USB.begin();
- USBSerial.begin();
-
- struct flasherCommand* cmd;
+ cmdSerial.begin();
+ Serial.println("Task started");
+ uint32_t notificationValue;
+ struct flasherCommand* cmd = nullptr;
while (true) {
while (serialPassthroughState) {
tagDebugPassthrough();
vTaskDelay(1 / portTICK_PERIOD_MS);
}
+
BaseType_t queuereceive = xQueueReceive(flasherCmdQueue, &cmd, 1000 / portTICK_PERIOD_MS); // timeout every second to make sure the timeout gets triggered after a while
if (queuereceive == pdTRUE) {
- processFlasherCommand(cmd);
+ processFlasherCommand(cmd, TRANSPORT_USB);
lastCmdTimeStamp = millis();
if (cmd->data != nullptr) {
free(cmd->data);
+ cmd->data = nullptr;
}
delete cmd;
+ cmd = nullptr;
} else {
if (lastCmdTimeStamp) {
if (millis() - lastCmdTimeStamp > USBFLASHER_CONNECTION_TIMEOUT)
- flasherCommandTimeout();
+ flasherCommandTimeout();
}
}
+
+ vTaskDelay(50 / portTICK_PERIOD_MS);
}
-}
\ No newline at end of file
+}
+#endif
+
diff --git a/ESP32_AP-Flasher/src/web.cpp b/ESP32_AP-Flasher/src/web.cpp
index 4536aa2b..9e21ed34 100644
--- a/ESP32_AP-Flasher/src/web.cpp
+++ b/ESP32_AP-Flasher/src/web.cpp
@@ -9,6 +9,8 @@
#include
#include
+#include
+
#include "AsyncJson.h"
#include "LittleFS.h"
#include "SPIFFSEditor.h"
@@ -25,6 +27,10 @@
#include "udp.h"
#include "wifimanager.h"
+#ifdef HAS_EXT_FLASHER
+#include "webflasher.h"
+#endif
+
extern uint8_t data_to_send[];
AsyncWebServer server(80);
@@ -125,17 +131,18 @@ void wsSendSysteminfo() {
uint32_t tagcount = getTagCount(timeoutcount);
char result[40];
if (timeoutcount > 0) {
-#ifdef HAS_RGB_LED
- if (apInfo.state == AP_STATE_ONLINE && apInfo.isOnline == true) rgbIdleColor = CRGB::DarkBlue;
-#endif
- snprintf(result, sizeof(result), "%lu / %lu, %lu timed out", tagcount, tagDB.size(), timeoutcount);
+ snprintf(result, sizeof(result), "%lu/%lu, %lu timeout", tagcount, tagDB.size(), timeoutcount);
} else {
-#ifdef HAS_RGB_LED
- if (apInfo.state == AP_STATE_ONLINE && apInfo.isOnline == true) rgbIdleColor = CRGB::Green;
-#endif
snprintf(result, sizeof(result), "%lu / %lu", tagcount, tagDB.size());
}
setVarDB("ap_tagcount", result);
+#ifdef HAS_RGB_LED
+ if (timeoutcount > 0) {
+ if (apInfo.state == AP_STATE_ONLINE && apInfo.isOnline == true) rgbIdleColor = CRGB::DarkBlue;
+ } else {
+ if (apInfo.state == AP_STATE_ONLINE && apInfo.isOnline == true) rgbIdleColor = CRGB::Green;
+ }
+#endif
tagcounttimer = millis();
}
@@ -201,8 +208,13 @@ void wsSendAPitem(struct APlist *apitem) {
}
void wsSerial(const String &text) {
+ wsSerial(text, String(""));
+}
+
+void wsSerial(const String &text, const String &color) {
StaticJsonDocument<250> doc;
doc["console"] = text;
+ if (!color.isEmpty()) doc["color"] = color;
Serial.println(text);
if (wsMutex) xSemaphoreTake(wsMutex, portMAX_DELAY);
ws.textAll(doc.as());
@@ -274,17 +286,38 @@ void init_web() {
if (hex2mac(dst, mac)) {
tagRecord *taginfo = tagRecord::findByMAC(mac);
if (taginfo != nullptr) {
- if (taginfo->data == nullptr) {
- fs::File file = contentFS->open(taginfo->filename);
- if (!file) {
- request->send(404, "text/plain", "File not found");
+ if (request->hasParam("md5")) {
+ uint8_t md5[8];
+ if (hex2mac(request->getParam("md5")->value(), md5)) {
+ PendingItem *queueItem = getQueueItem(mac, *reinterpret_cast(md5));
+ if (queueItem->data == nullptr) {
+ fs::File file = contentFS->open(queueItem->filename);
+ if (file) {
+ queueItem->data = getDataForFile(file);
+ Serial.println("Reading file " + String(queueItem->filename));
+ file.close();
+ } else {
+ request->send(404, "text/plain", "File not found");
+ return;
+ }
+ }
+ request->send_P(200, "application/octet-stream", queueItem->data, queueItem->len);
return;
}
- taginfo->data = getDataForFile(file);
- file.close();
+ } else {
+ // older version without queue
+ if (taginfo->data == nullptr) {
+ fs::File file = contentFS->open(taginfo->filename);
+ if (!file) {
+ request->send(404, "text/plain", "File not found");
+ return;
+ }
+ taginfo->data = getDataForFile(file);
+ file.close();
+ }
+ request->send_P(200, "application/octet-stream", taginfo->data, taginfo->len);
+ return;
}
- request->send_P(200, "application/octet-stream", taginfo->data, taginfo->len);
- return;
}
}
}
@@ -444,30 +477,38 @@ void init_web() {
udpsync.getAPList();
AsyncResponseStream *response = request->beginResponseStream("application/json");
- File configFile = contentFS->open("/current/apconfig.json", "r");
- if (!configFile) {
- request->send(500, "text/plain", "Error opening apconfig.json file");
- return;
- }
response->print("{");
-#if defined YELLOW_IPS_AP || defined C6_OTA_FLASHING
+#ifdef C6_OTA_FLASHING
response->print("\"C6\": \"1\", ");
#else
response->print("\"C6\": \"1\", ");
#endif
-#if defined SAVE_SPACE
+#ifdef SAVE_SPACE
response->print("\"savespace\": \"1\", ");
#else
response->print("\"savespace\": \"0\", ");
#endif
- configFile.seek(1);
- const size_t bufferSize = 64;
- uint8_t buffer[bufferSize];
- while (configFile.available()) {
- size_t bytesRead = configFile.read(buffer, bufferSize);
- response->write(buffer, bytesRead);
+#ifdef HAS_EXT_FLASHER
+ response->print("\"hasFlasher\": \"1\", ");
+#else
+ response->print("\"hasFlasher\": \"0\", ");
+#endif
+ response->print("\"apstate\": \"" + String(apInfo.state) + "\", ");
+
+ File configFile = contentFS->open("/current/apconfig.json", "r");
+ if (configFile) {
+ configFile.seek(1);
+ const size_t bufferSize = 64;
+ uint8_t buffer[bufferSize];
+ while (configFile.available()) {
+ size_t bytesRead = configFile.read(buffer, bufferSize);
+ response->write(buffer, bytesRead);
+ }
+ configFile.close();
+ } else {
+ response->print("}");
}
- configFile.close();
+
request->send(response);
});
@@ -479,15 +520,17 @@ void init_web() {
aliasValue.toCharArray(config.alias, aliasLength + 1);
config.alias[aliasLength] = '\0';
}
+
+
if (request->hasParam("channel", true)) {
config.channel = static_cast(request->getParam("channel", true)->value().toInt());
}
if (request->hasParam("led", true)) {
- config.led = static_cast(request->getParam("led", true)->value().toInt());
+ config.led = static_cast(request->getParam("led", true)->value().toInt());
updateBrightnessFromConfig();
}
if (request->hasParam("tft", true)) {
- config.tft = static_cast(request->getParam("tft", true)->value().toInt());
+ config.tft = static_cast(request->getParam("tft", true)->value().toInt());
updateBrightnessFromConfig();
}
if (request->hasParam("language", true)) {
@@ -663,6 +706,8 @@ void init_web() {
},
dotagDBUpload);
+ // OTA related calls
+
server.on("/sysinfo", HTTP_GET, handleSysinfoRequest);
server.on("/check_file", HTTP_GET, handleCheckFile);
server.on("/rollback", HTTP_POST, handleRollback);
@@ -677,6 +722,15 @@ void init_web() {
},
handleLittleFSUpload);
+#ifdef HAS_EXT_FLASHER
+
+ // Flasher related calls
+ ws.onEvent([](AsyncWebSocket *server, AsyncWebSocketClient *client, AwsEventType type, void *arg, uint8_t *data, size_t len) {
+ if (type == WS_EVT_DATA) handleWSdata(data, len, client);
+ });
+
+#endif
+
server.onNotFound([](AsyncWebServerRequest *request) {
if (request->url() == "/" || request->url() == "index.htm") {
request->send(200, "text/html", "index.html not found. Did you forget to upload the littlefs partition?");
diff --git a/ESP32_AP-Flasher/src/webflasher.cpp b/ESP32_AP-Flasher/src/webflasher.cpp
new file mode 100644
index 00000000..93a4085e
--- /dev/null
+++ b/ESP32_AP-Flasher/src/webflasher.cpp
@@ -0,0 +1,506 @@
+#include "webflasher.h"
+
+#ifdef HAS_EXT_FLASHER
+
+#include
+#include
+// #include
+// #include
+
+#include "flasher.h"
+#include "ips_display.h"
+#include "settings.h"
+// #include "storage.h"
+#include "powermgt.h"
+#include "swd.h"
+#include "usbflasher.h"
+#include "util.h"
+#include "web.h"
+#include "zbs_interface.h"
+
+#define FLASHMODE_OFF 0
+#define FLASHMODE_AUTO_BACKGROUND 1
+#define FLASHMODE_AUTO_FOCUS 2
+
+#define AUTOFLASH_STEP_IDLE 0
+#define AUTOFLASH_STEP_CONNECT 1
+#define AUTOFLASH_STEP_STARTUP 2
+#define AUTOFLASH_STEP_WAITDISCONNECT 3
+#define AUTOFLASH_STEP_FINISHED 4
+#define AUTOFLASH_STEP_ABORT 5
+#define AUTOFLASH_STEP_COUNTDOWN 6
+#define AUTOFLASH_STEP_COUNTING 8
+#define AUTOFLASH_STEP_WRITE 7
+#define AUTOFLASH_START_USBFLASHER 9
+#define AUTOFLASH_USBFLASHER_RUNNING 10
+#define AUTOFLASH_END_USBFLASHER 11
+
+#define Seriallog logger
+
+uint8_t webFlashMode = FLASHMODE_OFF;
+uint8_t autoFlashStep = AUTOFLASH_STEP_IDLE;
+// TaskHandle_t usbFlasherTaskHandle;
+
+AsyncServer TCPserver(243);
+AsyncClient* connectedClient = NULL;
+
+Logger::Logger() {
+}
+
+Logger::~Logger() {
+}
+
+size_t Logger::write(uint8_t c) {
+ wsSerial(String((char)c));
+ return 1;
+}
+
+size_t Logger::write(const uint8_t* buffer, size_t size) {
+ wsSerial(String((const char*)buffer, size));
+ return size;
+}
+
+size_t Logger::printf(const char* format, ...) {
+ char buffer[256];
+ va_list args;
+ va_start(args, format);
+ size_t len = vsnprintf(buffer, sizeof(buffer), format, args);
+ va_end(args);
+ wsSerial(String(buffer));
+
+#ifdef HAS_TFT
+ if (len >= 6 && strncmp(buffer, "\r[", 2) == 0) {
+ char content[50];
+ strncpy(content, buffer + 2, len - 7);
+ content[len - 7] = '\0';
+ tft2.fillRect(0, 50, tft2.width(), 40, TFT_BLUE);
+ tft2.setCursor(10, 57, 2);
+ tft2.setTextColor(TFT_YELLOW);
+ tft2.print(String(content));
+ }
+#endif
+
+ return len;
+}
+
+size_t Logger::println(const char* text) {
+ size_t len = print(text);
+ print('\n');
+ return len + 1;
+}
+
+Logger logger;
+util::Timer tagConnectTimer(seconds(2));
+extern flasher* zbsflasherp;
+uint8_t errors = 0;
+
+void infoDisplay(String actionName, uint8_t icon) {
+ wsSerial(" " + actionName, "white");
+#ifdef HAS_TFT
+ tftOverride = true;
+ tft2.fillScreen(TFT_PURPLE);
+ tft2.setCursor(0, 0, 4);
+ tft2.setTextColor(TFT_WHITE);
+ tft2.print("Progress:");
+ tft2.setCursor(18, 30, 2);
+ tft2.setTextColor(TFT_WHITE);
+ tft2.print(actionName);
+#endif
+}
+
+bool report(bool result) {
+ if (result) {
+ wsSerial("<✔", "green");
+#ifdef HAS_TFT
+ tft2.setTextColor(TFT_GREEN);
+ tft2.setCursor(5, 30, 2);
+ tft2.print("v");
+ tft2.setCursor(6, 30, 2);
+ tft2.print("v");
+ vTaskDelay(100 / portTICK_PERIOD_MS);
+#endif
+ } else {
+ errors++;
+ autoFlashStep = AUTOFLASH_STEP_ABORT;
+ wsSerial("<✘", "red");
+#ifdef HAS_TFT
+ tft2.setTextColor(TFT_RED);
+ tft2.setCursor(5, 30, 2);
+ tft2.print("x");
+ tft2.setCursor(6, 30, 2);
+ tft2.print("x");
+ vTaskDelay(500 / portTICK_PERIOD_MS);
+#endif
+ }
+ return result;
+}
+
+void onDataReceived(void* arg, AsyncClient* client, void* data, size_t len) {
+ flasherDataHandler((uint8_t*)data, len, TRANSPORT_TCP);
+}
+
+void onClientConnect(void* arg, AsyncClient* client) {
+ Serial.println("New client connected");
+ if (autoFlashStep == AUTOFLASH_USBFLASHER_RUNNING) {
+#ifdef HAS_TFT
+ if (tftOverride == true) {
+ tft2.fillRect(0, 62, tft2.width(), 18, TFT_BLUE);
+ tft2.setCursor(10, 63, 2);
+ tft2.setTextColor(TFT_GREEN);
+ tft2.print("TCP connected");
+ }
+#endif
+ } else {
+ autoFlashStep = AUTOFLASH_START_USBFLASHER;
+ }
+ webFlashMode = FLASHMODE_OFF;
+ if (connectedClient == NULL) {
+ connectedClient = client;
+ client->onData(&onDataReceived, NULL);
+ client->onDisconnect([](void* arg, AsyncClient* c) {
+ Serial.println("Client disconnected");
+
+ if (zbsflasherp != nullptr) {
+ zbsflasherp->zbs->reset();
+ delete zbsflasherp;
+ zbsflasherp = nullptr;
+ }
+
+#ifdef HAS_TFT
+ if (autoFlashStep == AUTOFLASH_USBFLASHER_RUNNING && tftOverride == true) {
+ tft2.fillRect(0, 62, tft2.width(), 18, TFT_BLUE);
+ tft2.setCursor(10, 63, 2);
+ tft2.setTextColor(TFT_YELLOW);
+ tft2.print("TCP disconnected");
+ }
+#endif
+ connectedClient = NULL;
+ });
+ } else {
+ client->close(true);
+ }
+}
+
+void sendDataToClient(const uint8_t* data, size_t len) {
+ if (connectedClient != NULL) {
+ connectedClient->write(reinterpret_cast(data), len);
+ } else {
+ Serial.println("No client connected");
+ }
+}
+
+void webFlasherTask(void* parameter) {
+ TCPserver.begin();
+ TCPserver.onClient(&onClientConnect, NULL);
+
+ static int32_t countDownTimer = 0;
+ while (1) {
+ switch (autoFlashStep) {
+ case AUTOFLASH_STEP_IDLE: {
+ if (webFlashMode != FLASHMODE_OFF && (tagConnectTimer.doRun() || webFlashMode == FLASHMODE_AUTO_FOCUS)) {
+ Serial.println("check pins");
+ if (extTagConnected()) autoFlashStep = AUTOFLASH_STEP_CONNECT;
+ }
+ break;
+ }
+
+ case AUTOFLASH_STEP_CONNECT: {
+ wsSerial("-", "clear");
+ infoDisplay("Connecting", 0);
+ vTaskDelay(2000 / portTICK_PERIOD_MS);
+
+ pinMode(FLASHER_EXT_TEST, OUTPUT);
+ digitalWrite(FLASHER_EXT_TEST, LOW);
+
+ zbsflasherp = new flasher();
+ errors = 0;
+
+ if (!report(zbsflasherp->connectTag(FLASHER_EXT_PORT))) {
+ Seriallog.printf("Sorry, failed to connect to this tag...\n");
+ break;
+
+ } else {
+ infoDisplay("Get firmware md5", 0);
+ report(zbsflasherp->getFirmwareMD5());
+
+ infoDisplay("Find tag", 0);
+ if (zbsflasherp->findTagByMD5()) {
+ // this tag currently contains original firmware, found its fingerprint
+ Seriallog.printf("Found original firmware tag (fingerprint %s)\n", zbsflasherp->md5char);
+
+ infoDisplay("Read info block", 0);
+ report(zbsflasherp->readInfoBlock());
+ if (errors) break;
+
+ infoDisplay("Get firmware mac", 0);
+ report(zbsflasherp->getFirmwareMac());
+ if (errors) break;
+
+ infoDisplay("Prepare info block", 0);
+ report(zbsflasherp->prepareInfoBlock());
+ if (errors) break;
+
+ zbsflasherp->includeInfoBlock = true;
+ autoFlashStep = AUTOFLASH_STEP_COUNTDOWN;
+ break;
+
+ } else if (report(zbsflasherp->getInfoBlockMD5())) {
+ infoDisplay("Find by md5", 0);
+ // did find an infoblock MD5 that looks valid
+ if (report(zbsflasherp->findTagByMD5())) {
+ // did find the md5 in the database
+
+ infoDisplay("Found tag", 0);
+ Seriallog.printf("Found an already-flashed tag (fingerprint %s)\n", zbsflasherp->md5char);
+
+ infoDisplay("Get infoblock mac", 0);
+ report(zbsflasherp->getInfoBlockMac());
+ if (errors) break;
+
+ infoDisplay("Get infoblock type", 0);
+ report(zbsflasherp->getInfoBlockType());
+ if (errors) break;
+
+ infoDisplay("Read infoblock", 0);
+ report(zbsflasherp->readInfoBlock());
+ if (errors) break;
+
+ zbsflasherp->includeInfoBlock = false;
+ autoFlashStep = AUTOFLASH_STEP_COUNTDOWN;
+ break;
+
+ } else {
+ infoDisplay("Unknown fingerprint", 0);
+ report(false);
+ // couldn't find the md5 from the infoblock
+ Seriallog.printf("Found an already-flashed tag, but we couldn't find its fingerprint (%s) in the database\n", zbsflasherp->md5char);
+ break;
+ }
+ } else {
+ // We couldn't recognize the tag from its fingerprint...
+ Seriallog.printf("Found a tag but didn't recognize its fingerprint (%s)\n", zbsflasherp->md5char);
+
+ infoDisplay("Backup firmware", 0);
+ report(zbsflasherp->backupFlash());
+
+ Seriallog.printf("Saved this MD5 binary to filesystem\n");
+
+ infoDisplay("Unknown tag", 0);
+ report(false);
+ break;
+ }
+ }
+
+ zbsflasherp->zbs->reset(false);
+
+ delete zbsflasherp;
+ zbsflasherp = nullptr;
+
+ autoFlashStep = AUTOFLASH_STEP_FINISHED;
+ break;
+ }
+
+ case AUTOFLASH_STEP_COUNTDOWN: {
+ infoDisplay("Final Countdown", 0);
+
+ countDownTimer = millis() + 3000;
+ autoFlashStep = AUTOFLASH_STEP_COUNTING;
+ break;
+ }
+
+ case AUTOFLASH_STEP_COUNTING: {
+ uint32_t timenow = millis();
+ if (timenow >= countDownTimer) {
+ autoFlashStep = AUTOFLASH_STEP_WRITE;
+ timenow = countDownTimer;
+ }
+
+ float timeDifference = countDownTimer - timenow;
+ char timeString[10];
+ snprintf(timeString, sizeof(timeString), "%.1fs", timeDifference / 1000.0);
+
+#ifdef HAS_TFT
+ tftOverride = true;
+ tft2.fillRect(0, 50, tft2.width(), 40, TFT_BLUE);
+ tft2.setCursor(10, 55, 4);
+ tft2.setTextColor(TFT_YELLOW);
+ tft2.print(timeString);
+#endif
+ wsSerial("\r" + String(timeString), "yellow");
+ break;
+ }
+
+ case AUTOFLASH_STEP_WRITE: {
+ if (zbsflasherp->includeInfoBlock) {
+ infoDisplay("Write info block", 0);
+ report(zbsflasherp->writeInfoBlock());
+ if (errors) break;
+ }
+
+ infoDisplay("Write flash", 0);
+ report(zbsflasherp->writeFlashFromPack("/Tag_FW_Pack.bin", zbsflasherp->tagtype));
+ if (errors) break;
+
+ zbsflasherp->zbs->reset(false);
+ delete zbsflasherp;
+ zbsflasherp = nullptr;
+
+ infoDisplay("Write successful", 0);
+ report(true);
+
+ wsSerial("Flashing succeeded", "green");
+#ifdef HAS_TFT
+ tftOverride = true;
+ tft2.fillRect(0, 50, tft2.width(), 40, TFT_DARKGREEN);
+ tft2.setCursor(0, 55, 4);
+ tft2.setTextColor(TFT_WHITE);
+ tft2.print("OK, Done!");
+ vTaskDelay(500 / portTICK_PERIOD_MS);
+#endif
+
+ autoFlashStep = AUTOFLASH_STEP_FINISHED;
+ break;
+ }
+
+ case AUTOFLASH_STEP_ABORT: {
+ wsSerial("Flashing failed", "red");
+#ifdef HAS_TFT
+ tftOverride = true;
+ tft2.fillRect(0, 50, tft2.width(), 40, TFT_RED);
+ tft2.setCursor(0, 55, 4);
+ tft2.setTextColor(TFT_WHITE);
+ tft2.print("Flash failed");
+ vTaskDelay(500 / portTICK_PERIOD_MS);
+#endif
+
+ zbsflasherp->zbs->reset(false);
+ delete zbsflasherp;
+ zbsflasherp = nullptr;
+
+ vTaskDelay(100 / portTICK_PERIOD_MS);
+ pinMode(FLASHER_EXT_TEST, INPUT_PULLDOWN);
+ vTaskDelay(100 / portTICK_PERIOD_MS);
+ autoFlashStep = AUTOFLASH_STEP_WAITDISCONNECT;
+ break;
+ }
+
+ case AUTOFLASH_STEP_FINISHED: {
+ Seriallog.printf("You can safely disconnect the tag");
+
+ vTaskDelay(100 / portTICK_PERIOD_MS);
+ pinMode(FLASHER_EXT_TEST, INPUT_PULLDOWN);
+ vTaskDelay(100 / portTICK_PERIOD_MS);
+ autoFlashStep = AUTOFLASH_STEP_WAITDISCONNECT;
+ break;
+ }
+
+ case AUTOFLASH_STEP_WAITDISCONNECT: {
+ if (!extTagConnected()) autoFlashStep = AUTOFLASH_STEP_STARTUP;
+ vTaskDelay(500 / portTICK_PERIOD_MS);
+ break;
+ }
+
+ case AUTOFLASH_STEP_STARTUP: {
+ int8_t powerPins2[] = FLASHER_EXT_POWER;
+ uint8_t numPowerPins = sizeof(powerPins2);
+ powerControl(false, (uint8_t*)powerPins2, numPowerPins);
+
+#ifdef HAS_TFT
+ tftOverride = true;
+ tft2.fillScreen(TFT_PURPLE);
+ tft2.setCursor(0, 0, 4);
+ tft2.setTextColor(TFT_WHITE);
+ tft2.print("Flasher mode");
+ tft2.setCursor(0, 25, 2);
+ tft2.setTextColor(TFT_WHITE);
+ tft2.print("Ready to connect a tag.");
+ tft2.print("When recognized, it will be flashed automatically");
+ if (webFlashMode == FLASHMODE_AUTO_BACKGROUND) tftOverride = false;
+#endif
+ wsSerial("Ready to connect a tag. It will be flashed automatically", "silver");
+ autoFlashStep = AUTOFLASH_STEP_IDLE;
+ vTaskDelay(100 / portTICK_PERIOD_MS);
+ break;
+ }
+
+ case AUTOFLASH_START_USBFLASHER: {
+#ifdef HAS_TFT
+ tftOverride = true;
+ tft2.fillScreen(TFT_PURPLE);
+ tft2.setCursor(0, 0, 4);
+ tft2.setTextColor(TFT_WHITE);
+ tft2.print("CMD mode");
+ tft2.setCursor(0, 25, 2);
+ tft2.setTextColor(TFT_WHITE);
+ tft2.print("Use OEPL-flasher.py to flash a tag");
+ if (webFlashMode == FLASHMODE_AUTO_BACKGROUND) tftOverride = false;
+#endif
+ autoFlashStep = AUTOFLASH_USBFLASHER_RUNNING;
+ break;
+ }
+
+ case AUTOFLASH_USBFLASHER_RUNNING: {
+ vTaskDelay(500 / portTICK_PERIOD_MS);
+ break;
+ }
+
+ case AUTOFLASH_END_USBFLASHER: {
+ autoFlashStep = AUTOFLASH_STEP_FINISHED;
+ }
+ }
+ vTaskDelay(100 / portTICK_PERIOD_MS);
+ }
+}
+
+void handleWSdata(uint8_t* data, size_t len, AsyncWebSocketClient* client) {
+ StaticJsonDocument<200> doc;
+ DeserializationError error = deserializeJson(doc, (const char*)data);
+ StaticJsonDocument<250> response;
+ response["flashstatus"] = 1;
+
+ if (error) {
+ wsSerial("Failed to parse JSON");
+ return;
+ }
+
+ if (doc.containsKey("flashcmd")) {
+ uint16_t flashcmd = doc["flashcmd"].as();
+ switch (flashcmd) {
+ case WEBFLASH_ENABLE_AUTOFLASH:
+ wsSerial("Switching to autoflash", "yellow");
+ webFlashMode = FLASHMODE_AUTO_FOCUS;
+ autoFlashStep = AUTOFLASH_STEP_STARTUP;
+ break;
+ case WEBFLASH_ENABLE_USBFLASHER:
+ wsSerial("Switching to usbflasher", "yellow");
+ wsSerial("You can now use OEPL-flasher.py to flash your tags", "silver");
+ autoFlashStep = AUTOFLASH_START_USBFLASHER;
+ webFlashMode = FLASHMODE_OFF;
+ break;
+ case WEBFLASH_FOCUS:
+ if (webFlashMode == FLASHMODE_AUTO_BACKGROUND) webFlashMode = FLASHMODE_AUTO_FOCUS;
+ if (webFlashMode == FLASHMODE_OFF) {
+ autoFlashStep = AUTOFLASH_START_USBFLASHER;
+ } else {
+ autoFlashStep = AUTOFLASH_STEP_STARTUP;
+ }
+#ifdef HAS_TFT
+ tftOverride = true;
+#endif
+ break;
+ case WEBFLASH_BLUR:
+ if (webFlashMode == FLASHMODE_AUTO_FOCUS) webFlashMode = FLASHMODE_AUTO_BACKGROUND;
+#ifdef HAS_TFT
+ tftOverride = false;
+ sendAvail(0xFC);
+#endif
+ break;
+ }
+ }
+
+ String jsonResponse;
+ serializeJson(response, jsonResponse);
+ client->text(jsonResponse);
+}
+
+#endif
\ No newline at end of file
diff --git a/ESP32_AP-Flasher/src/wifimanager.cpp b/ESP32_AP-Flasher/src/wifimanager.cpp
index 6441ffc1..7cc365da 100644
--- a/ESP32_AP-Flasher/src/wifimanager.cpp
+++ b/ESP32_AP-Flasher/src/wifimanager.cpp
@@ -37,7 +37,7 @@ WifiManager::WifiManager() {
void WifiManager::terminalLog(String text) {
Serial.println(text);
-#ifdef YELLOW_IPS_AP
+#ifdef HAS_TFT
TFTLog(text);
#endif
}
@@ -67,6 +67,8 @@ void WifiManager::poll() {
}
}
+#ifndef HAS_USB
+ // ap_and_flasher has gpio0 in use as FLASHER_AP_POWER
if (digitalRead(0) == LOW) {
Serial.println("GPIO0 LOW");
long starttime = millis();
@@ -97,6 +99,7 @@ void WifiManager::poll() {
ESP.restart();
}
}
+#endif
pollSerial();
}
@@ -107,12 +110,12 @@ bool WifiManager::connectToWifi() {
_ssid = preferences.getString("ssid", WiFi_SSID());
_pass = preferences.getString("pw", WiFi_psk());
if (_ssid == "") {
- terminalLog("No connection information saved");
+ terminalLog("No connection info saved");
logLine("No connection information saved");
startManagementServer();
return false;
}
- terminalLog("Stored ssid: " + String(_ssid));
+ terminalLog("ssid: " + String(_ssid));
String ip = preferences.getString("ip", "");
String mask = preferences.getString("mask", "");
@@ -166,7 +169,7 @@ bool WifiManager::connectToWifi(String ssid, String pass, bool savewhensuccessfu
WiFi.setSleep(WIFI_PS_MIN_MODEM);
terminalLog("Connecting to WiFi...");
- logLine("Connecting to WiFi...");
+ // logLine("Connecting to WiFi...");
WiFi.persistent(savewhensuccessfull);
WiFi.begin(_ssid.c_str(), _pass.c_str());
_connected = waitForConnection();
@@ -199,7 +202,7 @@ bool WifiManager::waitForConnection() {
WiFi.persistent(true);
IPAddress IP = WiFi.localIP();
terminalLog("Connected!");
- logLine("Connected!");
+ // logLine("Connected!");
_nextReconnectCheck = millis() + _reconnectIntervalCheck;
wifiStatus = CONNECTED;
return true;
@@ -207,7 +210,7 @@ bool WifiManager::waitForConnection() {
void WifiManager::startManagementServer() {
if (!_APstarted) {
- terminalLog("Starting configuration AP, ssid: OpenEPaperLink");
+ terminalLog("Starting config AP, ssid: OpenEPaperLink");
logLine("Starting configuration AP, ssid OpenEPaperLink");
WiFi.disconnect(true, true);
delay(100);
diff --git a/ESP32_AP-Flasher/src/zbs_interface.cpp b/ESP32_AP-Flasher/src/zbs_interface.cpp
index 0e8fd2f6..7eb39c78 100644
--- a/ESP32_AP-Flasher/src/zbs_interface.cpp
+++ b/ESP32_AP-Flasher/src/zbs_interface.cpp
@@ -57,7 +57,7 @@ void ZBS_interface::setSpeed(uint32_t speed) {
}
ZBS_interface::~ZBS_interface() {
- if (spi) delete spi;
+ // if (spi) delete spi;
}
void ZBS_interface::set_power(uint8_t state) {
powerControl(state, _POWER_PIN, _POWER_PINS);
@@ -91,12 +91,14 @@ void ZBS_interface::enable_debug() {
delay(100);
}
-void ZBS_interface::reset() {
+void ZBS_interface::reset(bool leavepower) {
+ /*
if (spi) {
spi->end();
delete spi;
spi = nullptr;
}
+ */
pinMode(_SS_PIN, INPUT);
pinMode(_CLK_PIN, INPUT);
pinMode(_MOSI_PIN, INPUT);
@@ -105,7 +107,7 @@ void ZBS_interface::reset() {
set_power(ZBS_OFF);
delay(500);
digitalWrite(_RESET_PIN, HIGH);
- set_power(ZBS_ON);
+ if (leavepower) set_power(ZBS_ON);
pinMode(_RESET_PIN, INPUT);
}
diff --git a/ESP32_AP-Flasher/wwwroot/content_cards.json b/ESP32_AP-Flasher/wwwroot/content_cards.json
index cdea2301..44f46b29 100644
--- a/ESP32_AP-Flasher/wwwroot/content_cards.json
+++ b/ESP32_AP-Flasher/wwwroot/content_cards.json
@@ -281,6 +281,60 @@
}
]
},
+ {
+ "id": 26,
+ "name": "Time Stamp",
+ "desc": "Displays the last date and time when a button press took place. For example, to keep track of the last time somebody let the dog out, or when you last watered the plants, or change the bed sheets.",
+ "properties": [ "savespace" ],
+ "param": [
+ {
+ "key": "title",
+ "name": "Title",
+ "desc": "Displayed title",
+ "type": "text"
+ },
+ {
+ "key": "button1",
+ "name": "Name for Button 1",
+ "desc": "Text displayed for button 1 (for example, a name or activity)",
+ "type": "text"
+ },
+ {
+ "key": "button2",
+ "name": "Name for Button 2",
+ "desc": "Text displayed for button 2 (for example, a name or activity)",
+ "type": "text"
+ },
+ {
+ "key": "mode",
+ "name": "Working mode",
+ "desc": "With 'keep one time', both buttons reset the same timestamp, recording which button is pressed last. The option for seperate date/time, records two different timestamps, one for each button.",
+ "type": "select",
+ "options": {
+ "0": "keep one timestamp",
+ "1": "seperate timestamp per button"
+ }
+ },
+ {
+ "key": "nextaction",
+ "name": "Frequency (days)",
+ "desc": "When is the next action expected to take place (in days after a timestamp). Leave 0 to disable.",
+ "type": "text"
+ },
+ {
+ "key": "last1",
+ "name": "Timestamp 1",
+ "desc": "The date/time the last event took place",
+ "type": "ro"
+ },
+ {
+ "key": "last2",
+ "name": "Name, or timestamp 2",
+ "desc": "The name of the button that was pressed, or in case of seperate timestamp, the date/time of the second button press",
+ "type": "ro"
+ }
+ ]
+ },
{
"id": 27,
"name": "Dayahead prices",
diff --git a/ESP32_AP-Flasher/wwwroot/flash.js b/ESP32_AP-Flasher/wwwroot/flash.js
new file mode 100644
index 00000000..67eff900
--- /dev/null
+++ b/ESP32_AP-Flasher/wwwroot/flash.js
@@ -0,0 +1,139 @@
+const $ = document.querySelector.bind(document);
+let running = false;
+let buttonState = false;
+
+const WEBFLASH_ENABLE_AUTOFLASH = 1
+const WEBFLASH_ENABLE_USBFLASHER = 2
+const WEBFLASH_FOCUS = 3
+export const WEBFLASH_BLUR = 4
+
+export async function init() {
+ wsCmd(WEBFLASH_FOCUS);
+ checkTagFW();
+}
+
+export function wsCmd(command) {
+ const dataToSend = {
+ flashcmd: command,
+ };
+ const jsonData = JSON.stringify(dataToSend);
+ socket.send(jsonData);
+}
+
+$('#doAutoflash').onclick = function () {
+ if (running) return;
+ disableButtons(true);
+ running = true;
+
+ wsCmd(WEBFLASH_ENABLE_AUTOFLASH);
+
+ running = false;
+ disableButtons(false);
+}
+
+$('#doUSBflash').onclick = function () {
+ if (running) return;
+ disableButtons(true);
+ running = true;
+
+ wsCmd(WEBFLASH_ENABLE_USBFLASHER);
+
+ running = false;
+ disableButtons(false);
+}
+
+export function print(line, color = "white") {
+ const consoleDiv = document.getElementById('flashconsole');
+ if (consoleDiv) {
+ if (color == "clear") {
+ consoleDiv.innerHTML = "";
+ return;
+ }
+
+ const isScrolledToBottom = consoleDiv.scrollHeight - consoleDiv.clientHeight <= consoleDiv.scrollTop;
+ const newLine = document.createElement('div');
+ newLine.style.color = color;
+
+ if (line.startsWith("<")) {
+ const existingLines = consoleDiv.getElementsByTagName('div');
+ let lastLine;
+
+ for (let i = existingLines.length - 1; i >= 0; i--) {
+ const lineText = existingLines[i].textContent;
+ if (lineText.startsWith(" ")) {
+ lastLine = existingLines[i];
+ break;
+ }
+ }
+ if (lastLine) {
+ lastLine.innerHTML = line.substring(1) + lastLine.innerHTML.substring(line.length - 1);
+ lastLine.style.color = color;
+ }
+ } else if (line.startsWith("\r")) {
+ const existingLines = consoleDiv.getElementsByTagName('div');
+ if (existingLines.length > 0) {
+ const lastLine = existingLines[existingLines.length - 1];
+ lastLine.innerHTML = line.substring(1);
+ }
+ } else {
+ newLine.textContent = line;
+ consoleDiv.appendChild(newLine);
+ }
+
+ if (isScrolledToBottom) {
+ consoleDiv.scrollTop = consoleDiv.scrollHeight;
+ }
+ }
+}
+
+function disableButtons(active) {
+ $("#flashtab").querySelectorAll('button').forEach(button => {
+ button.disabled = active;
+ });
+ buttonState = active;
+}
+
+const fetchAndPost = async (url, name, path) => {
+ try {
+ print("updating " + path);
+ const response = await fetch(url);
+ const fileContent = await response.blob();
+
+ const formData = new FormData();
+ formData.append('path', path);
+ formData.append('file', fileContent, name);
+
+ const uploadResponse = await fetch('/littlefs_put', {
+ method: 'POST',
+ body: formData
+ });
+
+ if (!uploadResponse.ok) {
+ print(`${response.status} ${response.body}`, "red");
+ errors++;
+ } else {
+ print(`Firmware file downloaded`, "green");
+ }
+ } catch (error) {
+ print('error: ' + error, "red");
+ errors++;
+ }
+};
+
+async function checkTagFW() {
+ const fwfile = "/Tag_FW_Pack.bin";
+ const url = "/check_file?path=" + encodeURIComponent(fwfile);
+ const response = await fetch(url);
+ if (response.ok) {
+ const data = await response.json();
+ if (data.filesize > 0) {
+ print(`File ${fwfile} found`, "green");
+ } else {
+ print(`File ${fwfile} not found. Downloading...`, "red");
+ await fetchAndPost("https://raw.githubusercontent.com/jjwbruijn/OpenEPaperLink/master/binaries/Tag/Tag_FW_Pack.bin", "Tag_FW_Pack.bin", fwfile);
+ }
+ } else {
+ print(`error checking file ${file.path}: ${response.status}`, "red");
+ errors++;
+ }
+}
diff --git a/ESP32_AP-Flasher/wwwroot/index.html b/ESP32_AP-Flasher/wwwroot/index.html
index 76cc9205..5be2c4e2 100644
--- a/ESP32_AP-Flasher/wwwroot/index.html
+++ b/ESP32_AP-Flasher/wwwroot/index.html
@@ -26,6 +26,7 @@
cell_tower
+ flash_on
settings
text_snippet
@@ -226,6 +227,48 @@
Work in progress...
+
+
Tag flasher
+
+
+
Mode
+
+
Automatic flash
+ With automatic flash, a tag is flashed to the latest firmware as soon as you connect it.
+ It sets the mac automatically, tries to recognize the type, and starts flashing. Currently, Solum M2 tags only.
+
+
Command line
+ Using
OEPL-Flasher.py , you have full control over the flashing of the tag.
+ Use the --ip argument to connect to the flasher.
+
+ Usage:
+
+OEPL-Flasher.py [-h] [-t IP] [-f] [-i] [-eep] [-n] [-z]
+[--pt] [--exit] [{read,write,autoflash,debug}] [filename]
+
+{read,write,autoflash,debug} Command to execute
+filename Filename for read/write commands
+
+options:
+-h, --help show this help message and exit
+-t IP, --ip IP IP Address to use
+-f, --flash Write to the flash
+-i, --infopage Write to the infopage/UICR
+-eep, --eeprom EEPROM operations
+-n, --nrf82511 nRF82511 programming
+-z, --zbs243 ZBS243 programming
+--pt, --passthrough Enters serial passthrough for debug output after flashing
+--exit Exit eeprom loader after sending data
+
+
+
+
+
+
+
+
+
+
Access Point config
@@ -247,7 +290,7 @@
RGB LED brightness
- off
+ off
10%
25%
50%
@@ -443,7 +486,7 @@
-
+
Update ESP32-C6
download latest version
diff --git a/ESP32_AP-Flasher/wwwroot/main.css b/ESP32_AP-Flasher/wwwroot/main.css
index 886761f2..7be7813a 100644
--- a/ESP32_AP-Flasher/wwwroot/main.css
+++ b/ESP32_AP-Flasher/wwwroot/main.css
@@ -198,7 +198,8 @@ label {
#aptab,
#configtab,
-#updatetab {
+#updatetab,
+#flashtab {
padding: 10px;
& p {
@@ -206,7 +207,7 @@ label {
}
}
-#updatetab {
+#updatetab, #flashtab {
&>div {
display: flex;
gap: 2em;
@@ -858,7 +859,7 @@ h4 {
display: none;
}
-.updateCol1 {
+.updateCol1, .flashCol1 {
flex-grow: 1;
}
@@ -873,6 +874,7 @@ h4 {
overflow-y: scroll;
white-space: break-spaces;
flex-grow: 1;
+ max-width: 600px;
}
.console div {
diff --git a/ESP32_AP-Flasher/wwwroot/main.js b/ESP32_AP-Flasher/wwwroot/main.js
index 03a3c2c4..5c539bd3 100644
--- a/ESP32_AP-Flasher/wwwroot/main.js
+++ b/ESP32_AP-Flasher/wwwroot/main.js
@@ -22,11 +22,12 @@ const apstate = [
{ state: "wait for reset", color: "blue" },
{ state: "requires power cycle", color: "purple" },
{ state: "failed", color: "red" },
- { state: "coming online", color: "yellow" }
+ { state: "coming online", color: "yellow" },
+ { state: "AP without radio", color: "green" }
];
const runstate = [
{ state: "⏹︎ stopped" },
- { state: "⏸pause" },
+ { state: "⏸ pause" },
{ state: "" }, // hide running
{ state: "⏳︎ init" }
];
@@ -36,7 +37,7 @@ let isProcessing = false;
let servertimediff = 0;
let paintLoaded = false, paintShow = false;
let cardconfig;
-let otamodule;
+let otamodule, flashmodule;
let socket;
let finishedInitialLoading = false;
let getTagtypeBusy = false;
@@ -53,6 +54,19 @@ window.addEventListener("loadConfig", function () {
this.document.title = data.alias;
}
if (data.C6) {
+ var optionToRemove = $("#apcfgchid").querySelector('option[value="27"]');
+ if (optionToRemove) $("#apcfgchid").removeChild(optionToRemove);
+ $('#c6Option').style.display = 'block';
+ }
+ if (data.hasFlasher) {
+ $('[data-target="flashtab"]').style.display = 'block';
+ }
+ if (data.savespace) {
+ }
+ if (data.apstate) {
+ $("#apstatecolor").style.color = apstate[data.apstate].color;
+ $("#apstate").innerHTML = apstate[data.apstate].state;
+ $('#dashboardStatus').innerHTML = apstate[data.apstate].state;
}
});
});
@@ -86,11 +100,12 @@ window.addEventListener("load", function () {
faviconLink.rel = 'icon';
faviconLink.href = 'favicon.ico';
document.head.appendChild(faviconLink);
- });
+ });
});
/* tabs */
-let activeTab = '';
+let activeTab = '', previousTab = '';
+
function initTabs() {
const tabLinks = document.querySelectorAll(".tablinks");
const tabContents = document.querySelectorAll(".tabcontent");
@@ -204,7 +219,13 @@ function connect() {
populateAPCard(msg.apitem);
}
if (msg.console) {
- if (otamodule && typeof (otamodule.print) === "function") {
+ if (activeTab == 'flashtab' && flashmodule && typeof (flashmodule.print) === "function") {
+ let color = (msg.color ? msg.color : "#c0c0c0");
+ if (msg.console.startsWith("Fail") || msg.console.startsWith("Err")) {
+ color = "red";
+ }
+ flashmodule.print(msg.console, color);
+ } else if (otamodule && typeof (otamodule.print) === "function") {
let color = "#c0c0c0";
if (msg.console.startsWith("Fail") || msg.console.startsWith("Err")) {
color = "red";
@@ -426,7 +447,7 @@ function updatecards() {
let nextcheckin = item.dataset.nextcheckin - ((Date.now() / 1000) - servertimediff);
$('#tag' + tagmac + ' .nextcheckin').innerHTML = "
expected checkin " + displayTime(Math.floor(nextcheckin));
} else {
- $('#tag' + tagmac + ' .nextcheckin').innerHTML = "";
+ // $('#tag' + tagmac + ' .nextcheckin').innerHTML = "";
}
})
@@ -723,7 +744,15 @@ document.addEventListener("loadTab", function (event) {
$('#updateconsole').innerHTML = '';
loadOTA();
break;
+ case 'flashtab':
+ $('#flashconsole').innerHTML = '';
+ loadFlash();
+ break;
}
+ if (previousTab == 'flashtab' && activeTab != 'flashtab' && flashmodule && typeof (flashmodule.wsCmd) === "function") {
+ flashmodule.wsCmd(flashmodule.WEBFLASH_BLUR);
+ }
+ previousTab = activeTab;
});
$('#apcfgsave').onclick = function () {
@@ -771,16 +800,13 @@ $('#uploadButton').onclick = function () {
return response.text();
})
.then(data => {
- console.log('File uploaded successfully: ', data);
alert('TagDB restored. Webpage will reload.');
location.reload();
})
.catch(error => {
- console.error('Error uploading file:', error);
alert('Error uploading file: ' + error);
});
} else {
- console.error('No file selected.');
alert('No file selected');
}
}
@@ -812,16 +838,14 @@ $('#restoreFromLocal').onclick = function () {
return response.text();
})
.then(data => {
- console.log('File uploaded successfully: ', data);
alert('TagDB restored. Webpage will reload.');
location.reload();
})
.catch(error => {
- console.error('Error uploading file:', error);
alert('Error uploading file: ' + error);
});
} else {
- console.log('No data found in localStorage');
+ alert('No data found in localStorage');
}
}
@@ -830,6 +854,11 @@ async function loadOTA() {
otamodule.initUpdate();
}
+async function loadFlash() {
+ flashmodule = await import('./flash.js?v=' + Date.now());
+ flashmodule.init();
+}
+
$('#paintbutton').onclick = function () {
if (paintShow) {
paintShow = false;
@@ -1154,7 +1183,7 @@ function processZlib(data) {
return inflatedBuffer.subarray(headerSize);
} catch (err) {
console.log('zlib: ' + err);
- }
+ }
}
function displayTime(seconds) {
@@ -1656,7 +1685,6 @@ function selectLocation(location) {
document.getElementById('opt#lon').value = location.longitude;
if (document.getElementById('opt#tz')) document.getElementById('opt#tz').value = location.timezone;
$('#georesults').innerHTML = '';
- console.log('Selected location:', location);
}
function debounce(func, delay) {
diff --git a/Tag_Flasher/OEPL-Flasher.py b/Tag_Flasher/OEPL-Flasher.py
index a99aa4db..ed1e343d 100644
--- a/Tag_Flasher/OEPL-Flasher.py
+++ b/Tag_Flasher/OEPL-Flasher.py
@@ -4,6 +4,8 @@ import serial
import time
from intelhex import IntelHex
import os.path
+import socket
+import sys
CMD_GET_VERSION = 1
CMD_RESET_ESP = 2
@@ -33,6 +35,9 @@ CMD_WRITE_FLASH = 83
CMD_AUTOFLASH = 87
CMD_COMPLETE = 88
+TRANSPORT_SER = 0
+TRANSPORT_TCP = 1
+
def read_binary_file(file_path):
with open(file_path, 'rb') as file:
binary_data = file.read()
@@ -55,12 +60,20 @@ def send_cmd(cmd, data):
for x in return_data:
crc_val += x
return_data = b"AT" + return_data + to_byte(crc_val & 0xffff, 2)
- ser.write(return_data)
-
+ if transport == TRANSPORT_TCP:
+ tcp_socket.send(return_data)
+ else:
+ ser.write(return_data)
def wait_for_command():
+ if transport == TRANSPORT_TCP:
+ return wait_for_command_tcp()
+ else:
+ return wait_for_command_ser()
+
+def wait_for_command_ser():
start_time = time.time()
- ser.timeout = 50 # Set the timeout to 1 second
+ ser.timeout = 3 # Set the timeout to 3 seconds
while True:
if ser.in_waiting > 0:
command = ser.read(2) # Read the "AT" prefix
@@ -86,23 +99,50 @@ def wait_for_command():
print("timeout waiting for reply")
return None, None
+def wait_for_command_tcp():
+ start_time = time.time()
+ tcp_socket.settimeout(5)
+ while True:
+ try:
+ if tcp_socket.recv(2) == b"AT":
+ # Read the command byte
+ cmd = int.from_bytes(tcp_socket.recv(1), byteorder='big')
+ data_length = int.from_bytes(
+ tcp_socket.recv(4), byteorder='big') # Read the data length
+ data = tcp_socket.recv(data_length) # Read the data
+ # Read the CRC value
+ crc = int.from_bytes(tcp_socket.recv(2), byteorder='big')
+ # Verify CRC
+ crc_val = 0xAB34
+ for x in to_byte(cmd, 1) + to_byte(data_length, 4) + data:
+ crc_val += x
+ if crc_val & 0xffff == crc:
+ return cmd, data
+ else:
+ print("Invalid CRC. Discarding command. Got " +
+ str(crc_val) + " but was expecting " + str(crc))
+ print("data was:" + str(data))
+ except socket.timeout:
+ if time.time() - start_time > 1:
+ print("timeout waiting for reply")
+ return None, None
def list_available_com_ports():
- ports = serial.tools.list_ports.comports()
- available_ports = [port.device for port in ports]
- print("Specify a serial port to use with -p
")
- print("available COM ports:")
- for port in available_ports:
- print(port)
-
+ available_ports = [port.device for port in serial.tools.list_ports.comports()]
+ print("Available COM ports:", ', '.join(available_ports))
def validate_arguments(args):
- if not (args.nrf82511 or args.zbs243):
- print("Either -nrf82511 or -zbs243 option is required.")
+ if not (args.port or args.ip):
+ print("Either --port or --ip option is required.")
+ list_available_com_ports()
return False
+ if args.command:
+ if not (args.nrf82511 or args.zbs243):
+ print("Either -nrf82511 or -zbs243 option is required.")
+ return False
if not (args.internalap or args.external or args.altradio):
- print("One of -internalap, -external, or -altradio options is required.")
- return False
+ print("Using external port")
+ args.external = True
if args.command in ["read", "write"] and not (args.flash or args.infopage or args.eeprom):
print("One of --flash, --infopage or --eeprom arguments is required for read and write commands.")
return False
@@ -121,7 +161,7 @@ def validate_arguments(args):
return True
-def read_from_serial(port, filename, args):
+def read_from_serial(filename, args):
if args.flash:
print(
f"Reading flash data and saving to file: {filename}.")
@@ -158,7 +198,7 @@ def read_from_serial(port, filename, args):
print("Failed reading block, timeout?")
-def write_to_serial(port, filename, args):
+def write_to_serial(filename, args):
if (args.flash):
print(f"\nErasing flash... ")
send_cmd(CMD_ERASE_FLASH, bytearray([]))
@@ -204,7 +244,7 @@ def write_to_serial(port, filename, args):
print(f'\rSent {i} bytes', end='', flush=True)
elif (cmd == CMD_COMPLETE):
print(
- '\Tried to write more bytes than we have room for! \n', end='', flush=True)
+ 'Tried to write more bytes than we have room for! \n', end='', flush=True)
return
else:
print("Some other error, dunno\n")
@@ -212,23 +252,30 @@ def write_to_serial(port, filename, args):
print('\rAll done writing! ', end='', flush=True)
-def short_passthough(period_time):
+def short_passthrough(period_time):
start_time = time.time()
while time.time() - start_time < period_time:
- data = ser.read()
- if data:
- print(data.decode('utf-8', errors='ignore'), end='')
- if chr(0x04) in data.decode('utf-8', errors='ignore'):
- break
-
+ if transport == TRANSPORT_TCP:
+ try:
+ data = tcp_socket.recv(1)
+ except socket.timeout:
+ pass
+ else:
+ try:
+ data = ser.read(1)
+ except UnicodeDecodeError:
+ pass
+ print(data, end='')
+ if chr(0x04) in data:
+ break
def main():
try:
parser = argparse.ArgumentParser(
description="OpenEPaperLink Flasher for AP/Flasher board")
parser.add_argument("-p", "--port", help="COM port to use")
- parser.add_argument("command", choices=[
- "read", "write", "autoflash", "debug"], help="Command to execute")
+ parser.add_argument("-t", "--ip", help="IP Address to use")
+ parser.add_argument("command", nargs="?", choices=["read", "write", "autoflash", "debug"], help="Command to execute")
parser.add_argument("filename", nargs="?",
help="Filename for read/write commands")
parser.add_argument("-f", "--flash", action="store_true",
@@ -244,7 +291,7 @@ def main():
parser.add_argument("--internalap", action="store_true",
help="Selects the internal accesspoint port")
parser.add_argument("-e", "--external", action="store_true",
- help="Selects the external(side) port")
+ help="Selects the external(side) port (default)")
parser.add_argument("--altradio", action="store_true",
help="Selects the alternate radio port")
parser.add_argument("--pt", "--passthrough", action="store_true",
@@ -254,22 +301,43 @@ def main():
args = parser.parse_args()
if not validate_arguments(args):
+ program_name = os.path.basename(sys.argv[0])
+ print(f"Usage: {program_name} --help")
return
- if not args.port:
- list_available_com_ports()
- return
-
- global ser
- ser = serial.Serial(args.port, baudrate=115200)
- time.sleep(0.1) # Flush serial data
- while (ser.inWaiting() > 0):
- data_str = ser.read(ser.inWaiting())
+ global transport
+ if (args.ip):
+ global tcp_socket
+ ip_address = args.ip
+ port = 243
+ tcp_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ transport = TRANSPORT_TCP
+ try:
+ tcp_socket.connect((ip_address, port))
+ print(f"Connected to {ip_address}:{port}")
+ except Exception as e:
+ print(f"Error: {e}")
+ return
+ else:
+ global ser
+ try:
+ ser = serial.Serial(args.port, baudrate=115200)
+ print(f"Successfully opened serial port {args.port}")
+ except serial.SerialException as e:
+ print(f"Error: {e}")
+ print("Please check if the specified COM port exists or is accessible.")
+ list_available_com_ports()
+ return
+ transport = TRANSPORT_SER
+ time.sleep(0.1) # Flush serial data
+ while (ser.inWaiting() > 0):
+ data_str = ser.read(ser.inWaiting())
+
send_cmd(CMD_GET_VERSION, bytearray([]))
cmd, answer = wait_for_command()
if (cmd == CMD_GET_VERSION):
- print("AP/Flasher version: " +
+ print("Connection with the flasher established. Version: " +
str(answer[0] << 24 | answer[1] << 16 | answer[2] << 8 | answer[3]))
else:
print(
@@ -308,18 +376,19 @@ def main():
print("Connection established to microcontroller")
else:
print("Failed to establish a connection to the microcontroller")
+ send_cmd(CMD_SET_POWER, bytearray([0]))
exit(0)
if args.command == "read":
- read_from_serial(ser, args.filename, args)
+ read_from_serial(args.filename, args)
elif args.command == "write":
- write_to_serial(ser, args.filename, args)
+ write_to_serial(args.filename, args)
elif args.command == "autoflash":
print("Starting automatic tag flash")
send_cmd(CMD_AUTOFLASH, bytearray([]))
- short_passthough(30)
+ short_passthrough(30)
else:
- print("Invalid command!")
+ print("No command")
if(args.eeprom):
send_cmd(CMD_SET_TESTP, bytearray([1]))
@@ -340,21 +409,28 @@ def main():
print(
"---------------------------------------------------------------------------------")
while True:
- try:
- data = ser.read()
- if data:
- print(data.decode('utf-8', errors='ignore'), end='')
- except UnicodeDecodeError:
- print(" ")
- data = ser.read()
- if data:
- print(data.decode('utf-8', errors='ignore'), end='')
- if chr(0x04) in data.decode('utf-8', errors='ignore'):
- break
+ if transport == TRANSPORT_TCP:
+ try:
+ data = tcp_socket.recv(1)
+ except socket.timeout:
+ data = ""
+ pass
+ else:
+ try:
+ data = ser.read(1)
+ except UnicodeDecodeError:
+ data = ""
+ pass
+ print(data, end='')
+ if chr(0x04) in data:
+ break
except KeyboardInterrupt:
print("\nBye!")
- ser.close()
+ if transport == TRANSPORT_TCP:
+ tcp_socket.close()
+ else:
+ ser.close()
exit(1)
diff --git a/binaries/Tag/Tag_FW_Pack.bin b/binaries/Tag/Tag_FW_Pack.bin
index 18288080..7a4bd811 100644
Binary files a/binaries/Tag/Tag_FW_Pack.bin and b/binaries/Tag/Tag_FW_Pack.bin differ
diff --git a/zbs243_Tag_FW/packagebinaries.py b/zbs243_Tag_FW/packagebinaries.py
index 12ebf9cf..74ba03d0 100644
--- a/zbs243_Tag_FW/packagebinaries.py
+++ b/zbs243_Tag_FW/packagebinaries.py
@@ -1,14 +1,12 @@
import os
import json
-version = "0022" # You can set your desired version here.
+version = "26" # You can set your desired version here.
types = {
- 0x00: "SOLUM_154_SSD1619-tag-00-" + version + ".bin",
- 0x01: "SOLUM_29_SSD1619-tag-01-" + version + ".bin",
- 0xF0: "Tag_FW_Segmented_UK.bin",
- 0x02: "SOLUM_42_SSD1619-tag-02-" + version + ".bin",
- 0x11: "SOLUM_29_UC8151-tag-11-" + version + ".bin",
+ 0x00: "SOL_M2_154_SSD_" + version + ".bin",
+ 0x01: "SOL_M2_29_SSD_" + version + ".bin",
+ 0x02: "SOL_M2_42_SSD_" + version + ".bin"
}
binpath = "../binaries/Tag"