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 @@ + @@ -226,6 +227,48 @@ Work in progress... +
+

Tag flasher

+
+
+

Mode

+
+

+ 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. +

+

+ 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 @@

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"