mirror of
https://github.com/OpenEPaperLink/OpenEPaperLink.git
synced 2026-03-21 06:06:23 +01:00
tag flasher / timestamp content / bugfixes (#217)
- added webinterface for tag flasher - added tcp transport for communicating with tag flasher (OTG USB also still works) - added content 'timestamp', makes use of preloaded images and buttons on the 2.9" M3 - webinterface is now aware of C6 and flasher capabilities - AP can run without ieee801.15.4 radio (i.e. flasher only) by shorting FLASHER_AP_TXD and FLASHER_AP_RXD - added tcp transport option to OEPL-Flasher.py (serial also still works) - added new environment OpenEPaperLink_Mini_AP_v4 - lots of finetuning and bug fixes
This commit is contained in:
8
.github/workflows/build-test.yml
vendored
8
.github/workflows/build-test.yml
vendored
@@ -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
|
||||
|
||||
36
.github/workflows/release.yml
vendored
36
.github/workflows/release.yml
vendored
@@ -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
|
||||
|
||||
@@ -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": {
|
||||
|
||||
29
ESP32_AP-Flasher/data/tagtypes/E1.json
Normal file
29
ESP32_AP-Flasher/data/tagtypes/E1.json
Normal file
@@ -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" ] }
|
||||
]
|
||||
}
|
||||
}
|
||||
Binary file not shown.
BIN
ESP32_AP-Flasher/data/www/flash.js.gz
Normal file
BIN
ESP32_AP-Flasher/data/www/flash.js.gz
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -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);
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
#include <Arduino.h>
|
||||
#include <TFT_eSPI.h>
|
||||
|
||||
#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
|
||||
|
||||
@@ -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);
|
||||
void quickBlink(uint8_t repeat);
|
||||
void addFadeMono(uint8_t value);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
#include <Arduino.h>
|
||||
|
||||
|
||||
//void doLeds();
|
||||
void powerControl(bool powerState, uint8_t* pin, uint8_t pincount);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#include <Arduino.h>
|
||||
|
||||
#define FLASHER_AP_PORT 0
|
||||
#ifdef OPENEPAPERLINK_PCB
|
||||
#ifdef HAS_EXT_FLASHER
|
||||
#define FLASHER_EXT_PORT 1
|
||||
#define FLASHER_ALTRADIO_PORT 2
|
||||
#endif
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -1,4 +1,10 @@
|
||||
#include <Arduino.h>
|
||||
|
||||
#define TRANSPORT_USB 0
|
||||
#define TRANSPORT_TCP 1
|
||||
|
||||
void usbFlasherTask(void* parameter);
|
||||
#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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
30
ESP32_AP-Flasher/include/webflasher.h
Normal file
30
ESP32_AP-Flasher/include/webflasher.h
Normal file
@@ -0,0 +1,30 @@
|
||||
#include <Arduino.h>
|
||||
|
||||
#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
|
||||
@@ -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);
|
||||
|
||||
@@ -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 =
|
||||
+<*>-<usbflasher.cpp>-<swd.cpp>-<espflasher.cpp>-<ips_display.cpp>
|
||||
+<*>-<usbflasher.cpp>-<swd.cpp>-<espflasher.cpp>-<ips_display.cpp>-<webflasher.cpp>
|
||||
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 =
|
||||
+<*>-<usbflasher.cpp>-<swd.cpp>-<espflasher.cpp>-<ips_display.cpp>
|
||||
+<*>-<usbflasher.cpp>-<swd.cpp>-<espflasher.cpp>-<ips_display.cpp>-<webflasher.cpp>
|
||||
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 =
|
||||
+<*>-<usbflasher.cpp>-<swd.cpp>-<espflasher.cpp>-<ips_display.cpp>
|
||||
+<*>-<usbflasher.cpp>-<swd.cpp>-<espflasher.cpp>-<ips_display.cpp>-<webflasher.cpp>
|
||||
; ----------------------------------------------------------------------------------------
|
||||
; !!! 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 =
|
||||
+<*>-<usbflasher.cpp>-<swd.cpp>
|
||||
+<*>-<usbflasher.cpp>-<swd.cpp>-<webflasher.cpp>
|
||||
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 =
|
||||
+<*>-<usbflasher.cpp>-<swd.cpp>
|
||||
+<*>-<usbflasher.cpp>-<swd.cpp>-<webflasher.cpp>
|
||||
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 =
|
||||
+<*>-<usbflasher.cpp>-<swd.cpp>-<espflasher.cpp>
|
||||
+<*>-<usbflasher.cpp>-<swd.cpp>-<espflasher.cpp>-<webflasher.cpp>
|
||||
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]
|
||||
|
||||
@@ -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<String>();
|
||||
|
||||
@@ -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<String>(), 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<String>(), 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<int>();
|
||||
switch (taginfo->wakeupReason) {
|
||||
case WAKEUP_REASON_BUTTON2:
|
||||
Serial.println("button 1");
|
||||
cfgobj["last1"] = now;
|
||||
if (mode == 0) {
|
||||
// 1 timestamp
|
||||
cfgobj["last2"] = cfgobj["button1"].as<String>();
|
||||
}
|
||||
break;
|
||||
case WAKEUP_REASON_BUTTON1:
|
||||
Serial.println("button 2");
|
||||
if (mode == 0) {
|
||||
// 1 timestamp
|
||||
cfgobj["last1"] = now;
|
||||
cfgobj["last2"] = cfgobj["button2"].as<String>();
|
||||
} else {
|
||||
cfgobj["last2"] = now;
|
||||
// 2 timestamps
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
char dateString1[40];
|
||||
uint32_t nextaction = cfgobj["nextaction"].as<uint32_t>();
|
||||
String dateformat = languageDateFormat[0] + " %H:%M";
|
||||
time_t timestamp = cfgobj["last1"].as<uint32_t>();
|
||||
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<String>(), 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<String>(), 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<uint32_t>();
|
||||
localtime_r(×tamp, &timeinfo);
|
||||
strftime(dateString2, sizeof(dateString2), dateformat.c_str(), &timeinfo);
|
||||
if (timestamp == 0) strcpy(dateString2, "never");
|
||||
|
||||
drawString(spr, cfgobj["button2"].as<String>(), 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] != '/') {
|
||||
|
||||
@@ -5,23 +5,29 @@
|
||||
#include <MD5Builder.h>
|
||||
|
||||
#include "LittleFS.h"
|
||||
#include "storage.h"
|
||||
// #include <FS.h>
|
||||
|
||||
#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;
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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<uint8_t*>(&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<uint8_t*>(&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;
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
#include "serialap.h"
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <HardwareSerial.h>
|
||||
|
||||
#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<const uint8_t*>(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<const uint8_t*>(&blockbuffer);
|
||||
const size_t bufferSize = sizeof(struct blockData);
|
||||
uint8_t* modifiedHeader = static_cast<uint8_t*>(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<const uint8_t*>(data);
|
||||
uint8_t* modifiedBuffer = static_cast<uint8_t*>(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++;
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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>() : String("jjwbruijn/OpenEPaperLink");
|
||||
config.env = APconfig.containsKey("env") ? APconfig["env"].as<String>() : 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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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];
|
||||
|
||||
@@ -1,14 +1,26 @@
|
||||
#include <Arduino.h>
|
||||
|
||||
#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);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
@@ -9,6 +9,8 @@
|
||||
#include <Preferences.h>
|
||||
#include <WiFi.h>
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#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<String>());
|
||||
@@ -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<uint64_t *>(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<uint8_t>(request->getParam("channel", true)->value().toInt());
|
||||
}
|
||||
if (request->hasParam("led", true)) {
|
||||
config.led = static_cast<int16_t>(request->getParam("led", true)->value().toInt());
|
||||
config.led = static_cast<uint8_t>(request->getParam("led", true)->value().toInt());
|
||||
updateBrightnessFromConfig();
|
||||
}
|
||||
if (request->hasParam("tft", true)) {
|
||||
config.tft = static_cast<int16_t>(request->getParam("tft", true)->value().toInt());
|
||||
config.tft = static_cast<uint8_t>(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?");
|
||||
|
||||
506
ESP32_AP-Flasher/src/webflasher.cpp
Normal file
506
ESP32_AP-Flasher/src/webflasher.cpp
Normal file
@@ -0,0 +1,506 @@
|
||||
#include "webflasher.h"
|
||||
|
||||
#ifdef HAS_EXT_FLASHER
|
||||
|
||||
#include <ArduinoJson.h>
|
||||
#include <AsyncTCP.h>
|
||||
// #include <freertos/FreeRTOS.h>
|
||||
// #include <freertos/task.h>
|
||||
|
||||
#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<const char*>(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<int>();
|
||||
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
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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",
|
||||
|
||||
139
ESP32_AP-Flasher/wwwroot/flash.js
Normal file
139
ESP32_AP-Flasher/wwwroot/flash.js
Normal file
@@ -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++;
|
||||
}
|
||||
}
|
||||
@@ -26,6 +26,7 @@
|
||||
<div class="tablinks material-symbols-outlined" data-target="aptab" title="Access Points">cell_tower</div>
|
||||
<!--<div class="tablinks material-symbols-outlined" data-target="templatetab" title="Templates">browse
|
||||
</div>-->
|
||||
<div class="tablinks material-symbols-outlined" data-target="flashtab" title="Tag flasher" style="display:none;">flash_on</div>
|
||||
<div class="tablinks material-symbols-outlined" data-target="configtab" title="Settings">settings</div>
|
||||
<div class="tablinks material-symbols-outlined" data-target="logtab" title="Logging">text_snippet</div>
|
||||
</div>
|
||||
@@ -226,6 +227,48 @@
|
||||
Work in progress...
|
||||
</div>
|
||||
|
||||
<div id="flashtab" class="tabcontent">
|
||||
<h3>Tag flasher</h3>
|
||||
<div>
|
||||
<div class="flashCol1">
|
||||
<h4>Mode</h4>
|
||||
<div style="max-width:400px;">
|
||||
<button class="button" id="doAutoflash">Automatic flash</button><br><br>
|
||||
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.
|
||||
<br><br>
|
||||
<button class="button" id="doUSBflash">Command line</button><br><br>
|
||||
Using <a href="https://github.com/jjwbruijn/OpenEPaperLink/raw/master/Tag_Flasher/OEPL-Flasher.py" target="_blank">OEPL-Flasher.py</a>, you have full control over the flashing of the tag.<br>
|
||||
Use the --ip argument to connect to the flasher.<br>
|
||||
<br>
|
||||
Usage:<br>
|
||||
<pre style="text-wrap: wrap;">
|
||||
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
|
||||
</pre>
|
||||
</div>
|
||||
<!--<h4>Other actions</h4>-->
|
||||
<div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="console" id="flashconsole"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="configtab" class="tabcontent">
|
||||
<h3>Access Point config</h3>
|
||||
<p>
|
||||
@@ -247,7 +290,7 @@
|
||||
<p>
|
||||
<label for="apcfgledbrightness">RGB LED brightness</label>
|
||||
<select id="apcfgledbrightness">
|
||||
<option value="-1">off</option>
|
||||
<option value="0">off</option>
|
||||
<option value="15">10%</option>
|
||||
<option value="31">25%</option>
|
||||
<option value="127" selected>50%</option>
|
||||
@@ -443,7 +486,7 @@
|
||||
</div>
|
||||
</p>
|
||||
<p>
|
||||
<span id="c6Option">
|
||||
<span id="c6Option" style="display:none;">
|
||||
<div id="updateC6Option">
|
||||
<button type="button" id="updateC6Btn">Update ESP32-C6</button>
|
||||
<input type="checkbox" value="1" checked id="c6download">download latest version
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 = "<span>expected checkin</span>" + 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) {
|
||||
|
||||
@@ -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 <PORT>")
|
||||
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)
|
||||
|
||||
|
||||
|
||||
Binary file not shown.
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user