73 Commits
2.71 ... 2.81

Author SHA1 Message Date
Nic Limper
e102f8e4e9 stability improvements in C6 flashing 2025-03-27 21:10:10 +01:00
Nic Limper
0fb0c6f74d more robust extended sleep time and 'no updates between' handling 2025-03-27 01:03:04 +01:00
Nic Limper
5dfd0e4582 Update release.yml 2025-03-27 00:59:22 +01:00
Frank Kunz
f311239c9c Add new AP hardware support OpenEPaperLink_ESP32-PoE-ISO_AP (#431)
* Fix filesystem mutex handling

This fixes the re-initialization of the filesystem mutex when the dyn storage
module is reinitialized. Using xSemaphoreCreateMutex would cause a memory leak
when the begin function is called multiple times and a semaphore leakage could
be caused by the re-initialization of the global fsMutex variable while the
semaphore is taken.

The fsMutex should not be taken while the logLine function is called as this
would cause a nested take of the fsMutex, which causes a deadlock.

Signed-off-by: Frank Kunz <mailinglists@kunz-im-inter.net>

* Fix hard coded littlefs in json upload

Signed-off-by: Frank Kunz <mailinglists@kunz-im-inter.net>

* Add new AP hardware support OpenEPaperLink_ESP32-PoE-ISO_AP

This is based on Olimex ESP32-PoE-ISO
https://www.olimex.com/Products/IoT/ESP32/ESP32-POE-ISO/open-source-hardware

It has a SD Card slot that is used to store all filesystem data on SD.
Use the prepare_sdcard.sh script to copy all needed data to an empty
SD card that is formatted with FAT filesystem. The AP firmware will format
the SD if an unformatted or from formatted card is used. This can be used
to intially prepare an empty SD card for usage.

For tag communication a ESP32-C6-WROOM-1(U) is used with the following
connection scheme:

ESP32-PoE-ISO  |  ESP32-C6-WROOM-1
---------------+------------------
  GPIO5        |    EN
  GPIO13       |    GPIO9
  GPIO36       |    GPIO3
  GPIO4        |    GPIO2
  GPIO33       |    GPIO24
  GPIO32       |    GPIO25
               |    GPIO8 pullup 5.1k

Signed-off-by: Frank Kunz <mailinglists@kunz-im-inter.net>

* Avoid error message log print when parsers.json is missing

Signed-off-by: Frank Kunz <mailinglists@kunz-im-inter.net>

* Workaround for IEEE802.15.4 modem stuck issue

The ESP32-C6 esp-idf based modem firmware can run into a case where it
does not receive data anymore from the tags. This can be detected when
it starts to print

"receive buffer full, drop the current frame"

and does not recover from that. In such a case a modem reset is triggered.

Signed-off-by: Frank Kunz <mailinglists@kunz-im-inter.net>

* Add OpenEPaperLink_ESP32-PoE-ISO_AP to release build

Signed-off-by: Frank Kunz <mailinglists@kunz-im-inter.net>

* Add OpenEPaperLink_ESP32-PoE-ISO_AP to condidional build

Signed-off-by: Frank Kunz <mailinglists@kunz-im-inter.net>

* Add Ethernet support

The ethernet support allows to make the network/internet connection
via LAN cable instead of WiFi. LAN is preferred, if a LAN cable is
connected and a valid IP configuration via DHCP can be obtained, WiFi
is switched off. If the LAN cable is disconnected, a fall back to
WiFi is done.

Use those defines in platform.ini for PHY settings:
ETHERNET_PHY_POWER: IO pin where the PHY can be switched of/on, can be
                    -1 if not used.
ETHERNET_CLK_MODE: PHY clock mode, see eth_clock_mode_t in ETH.h
ETHERNET_PHY_MDC: PHY MDC pin
ETHERNET_PHY_MDIO: PHY MDIO pin
ETHERNET_PHY_TYPE: PHY type, see eth_phy_type_t in ETH.h

Limitations:
- only DHCP is supported, no static IP configuration for LAN so far.
- If GPIO0 is used for one of the ETHERNET_CLK_MODE modes, then GPIO0
  cannot be used to clear the WiFi configuration.

Signed-off-by: Frank Kunz <mailinglists@kunz-im-inter.net>

---------

Signed-off-by: Frank Kunz <mailinglists@kunz-im-inter.net>
Co-authored-by: Frank Kunz <mailinglists@kunz-im-inter.net>
2025-03-27 00:48:05 +01:00
Skip Hansen
c3e729744a Added E5.json for Elcrow Advanced 2.8" AP. 2025-03-25 12:56:11 -07:00
Skip Hansen
447611ba4a Added support for Elcrow Advanced 2.8" AP. 2025-03-25 10:02:39 -07:00
Skip Hansen
6637405358 Added optional (compile time) debug logging to C6 & H2. 2025-03-25 08:12:53 -07:00
Skip Hansen
32c74ba5b4 Added support for Elecrow C6 wireless module. 2025-03-25 08:09:26 -07:00
Skip Hansen
177f93844c Display SN rather than MAC for wider range of Chroma tags. 2025-03-23 11:23:43 -07:00
Skip Hansen
d76d110f39 Repurpose unused type 0x81 for Chroma Aeon 74. 2025-03-23 07:16:42 -07:00
atc1441
1584f35624 Improved 4inchAP 2025-03-23 13:55:41 +01:00
atc1441
ac0c3ccfc9 Added Touch support for the 4inchAP 2025-03-22 23:28:53 +01:00
atc1441
3810fbf68c Added 5.81 BWR 2025-03-20 22:41:55 +01:00
Nic Limper
20b4f728e4 Update 2E.json 2025-03-20 17:07:42 +01:00
Nic Limper
047230de25 weather forecast enhancements 2025-03-20 13:43:21 +01:00
Nic Limper
107764c6be fixed y-axis labels on dayahead prices (https://github.com/OpenEPaperLink/OpenEPaperLink/issues/446) 2025-03-19 23:18:30 +01:00
Nic Limper
0819b19db2 follow redirects on getimgurl 2025-03-19 22:55:46 +01:00
Nic Limper
4aedce7839 sync gzipped files; update some tagtypes 2025-03-19 22:02:16 +01:00
Nic Limper
2d486d7c66 update to arduinojson 7.3.0, change ESPAsyncWebServer fork, Arduino 3.x compatibility, enabled comments within json files (#438)
* updated arduinojson to version 7.3.0, enabled comments within json files

* update ESPAsyncWebServer/AsyncTCP to the ESP32Async fork

ESP32Async/ESPAsyncWebServer and ESP32Async/AsyncTCP are much better maintained and it looks like a lot of bugs are fixed compared to the ESPHome version we used before.

* Arduino 3.x compatibility (while maintaining 2.x compatibility)
2025-03-19 21:48:06 +01:00
atc1441
ba8a5c6990 Merge branch 'master' of https://github.com/OpenEPaperLink/OpenEPaperLink 2025-03-17 13:51:05 +01:00
atc1441
1ae015c65f Update 2E.json 2025-03-17 13:50:48 +01:00
Nic Limper
92681aa4c5 Update 2E.json
update version number
2025-03-17 13:50:32 +01:00
Steel
004438cee9 Update 2E.json (#448)
Added weather forecast
2025-03-17 13:49:20 +01:00
atc1441
b7546cf6d4 Create 47.json 2025-03-11 20:32:46 +01:00
atc1441
cce5f56d67 Bug fix of LilyGo init 2025-03-02 16:09:00 +01:00
Nic Limper
eb58b7fc02 Update get_dayahead.js 2025-02-25 12:03:35 +01:00
atc1441
8f0362455a Create 5A.json 2025-02-21 18:19:55 +01:00
Skip Hansen
79fe05581c Add note of required version for CC1110 support to README. 2025-02-21 07:42:24 -08:00
atc1441
658b3b8635 Merge branch 'master' of https://github.com/OpenEPaperLink/OpenEPaperLink 2025-02-19 19:59:08 +01:00
atc1441
77da5964bf Update 63.json 2025-02-19 19:58:40 +01:00
Wheeze_NL
0232725711 Comment out defined ports (#426)
Without defined ports, platformio will autodetect the ports.
Best for most usecases.
2025-02-18 06:18:46 -08:00
Nic Limper
ff7f397705 added missing characters in vlw font 2025-02-16 15:41:32 +01:00
Nic Limper
d8fa96b20e support for multiple folder levels 2025-02-11 20:00:06 +01:00
Nic Limper
15a9728f45 Update get_dayahead.js 2025-02-11 09:59:04 +01:00
Skip Hansen
6c9439822b Nail ArduinoJson to version 7.1.0 to fix S2 Tag flasher build.
Thanks to discord user xRaZx.
2025-02-01 07:07:37 -08:00
Skip Hansen
4b667d0039 Coprocessor OTA changes (#425)
OTA changes to support C6/H2 OTA updates from configured repo.
2025-01-25 14:11:39 -08:00
Skip Hansen
19bbba5202 Added OEPL_TLSR_AP.bin from discord. 2025-01-16 06:59:35 -08:00
Skip Hansen
ad76d122e5 Added chroma29_8151_full_0013.bin & .hex. 2025-01-12 13:57:56 -08:00
Skip Hansen
5ec69153b5 Updated chroma29_8151_ota_0013.bin to fix screen orientation. 2025-01-12 11:05:47 -08:00
Nic Limper
45427148f6 {mac} placeholder in json template content card 2025-01-12 14:12:52 +01:00
Skip Hansen
c5fb16836f Added Chroma29 rev 'C' to tagotaversions.json. 2025-01-11 07:37:43 -08:00
Skip Hansen
63b6f911b6 Added Chroma29 firmware for Rev 'C' hardware. 2025-01-10 14:06:31 -08:00
Nic Limper
dec9b17655 add Portugal to dayahead prices options 2025-01-09 17:24:16 +01:00
atc1441
b8c4d4420e Added 4inch AP 2025-01-08 20:03:48 +01:00
atc1441
0b064a9cee Update 63.json 2025-01-04 12:38:31 +01:00
atc1441
4d186c81ff Added more options in the direct image upload 2025-01-03 22:43:46 +01:00
Skip Hansen
5cc7869c0f Disable builds for deprecated configurations,
OpenEPaperLink_Mini_AP, OpenEPaperLink_Nano_AP, Simple_AP, and
ESP_THREAD_BORDER_ROUTER.
2025-01-02 08:59:00 -08:00
atc1441
6a8450cbcb Added firmware.json file to be compatible to old download C6 method. while switching to new takes place 2024-12-30 00:08:09 +01:00
Skip Hansen
ca8781f956 Fix typos in firmware_C6.json. 2024-12-25 06:45:18 -08:00
Skip Hansen
311ae1a570 Added processor type to C6/H2 binary filenames. 2024-12-25 06:36:41 -08:00
atc1441
06b2718d7d Update ATC_Cheapo_BLE_OEPL_Watch.bin 2024-12-20 22:41:57 +01:00
atc1441
0f574bc3e8 Update ATC_Cheapo_BLE_OEPL_Watch.bin 2024-12-20 22:26:22 +01:00
Nic Limper
7e49c2a09e some small tweaks 2024-12-17 19:20:17 +01:00
Skip Hansen
23cbadb9f6 Merge branch 'master' of https://github.com/OpenEPaperLink/OpenEPaperLink into oepl_master 2024-12-17 09:28:00 -08:00
Skip Hansen
fce2e43ef7 Update C6 and H2 binaries to ver 001f. 2024-12-17 09:27:09 -08:00
Magnus Erler
4f7a381eed Update 2E.json (#416) 2024-12-17 17:14:18 +01:00
Magnus Erler
6a0f1310e1 Update 41.json (#406) 2024-12-17 16:54:36 +01:00
Skip Hansen
bb36185066 C6 Ver 001f - fix #410 & other C6 improvements
1. Only call esp_ieee802154_receive_handle_done() when ack != NULL.
2. Add 2 millisecond timeout for all SubGhz MISO wait loops to
   prevent watchdog timeouts when/if bad things happen (none observed
   during testing)
3. Make CC1101 detection more robust and less intrusive by testing
   MISO and CSn before trying to read chip version number.
4. Remove useless "error" messages which occur during normal operation.
5. Added missing \r in some log messages for EOL consistency.
2024-12-17 07:17:47 -08:00
mhwlng
be8eac2fc5 added E3.json for GDEM1085Z51 10.85" 1360x480 e-paper (#415)
mainly for use in home assistant (implemented only calendar and AP info screens)
2024-12-17 06:30:40 +01:00
atc1441
e4ecf08e29 Create ATC_Cheapo_BLE_OEPL_Watch.bin 2024-12-16 19:36:42 +01:00
atc1441
99c048a29d Merge branch 'master' of https://github.com/OpenEPaperLink/OpenEPaperLink 2024-12-16 17:33:12 +01:00
atc1441
f49731a240 Create E4.json 2024-12-16 17:33:11 +01:00
Jelmer
375662c69e Added 'Set Mac' datatype/content card 2024-12-15 23:11:19 +01:00
Nic Limper
e246ac578d optional border color and -width in box and rbox json template 2024-12-11 14:12:26 +01:00
Nic Limper
d8dcd498a3 optional colored google calendar events on BWR/BWY displays 2024-12-11 12:49:39 +01:00
Nic Limper
91b01c5fca gzipped wwwfiles 2024-12-10 23:01:28 +01:00
Nic Limper
30812dff49 color related improvements
- added "image" to json commands to insert a jpg image/icon from the flash partition
- added optional center/right alignment to "textbox" json command
- google calendar content: added optional colors, different color per calendarid
- improved ordered dithering, works also with unevenly spaced color tables. This is to be used with graphs etc., not suitable for photos (use floyd steinberg for photos)
- improved flyod steinberg dithering (fix some bugs)
- added preview rendering for 4bpp images
- log tab now scrolls to the top on entering
- added optional perceptual color table to tagtypes (for rendering previews, for example to display darker yellows on screen while keeping the 255,255,0 color to the tag
- drag/dropping in an image to a tag while holding shift key now uses ordered dithering (default is floyd steinberg)
- some tagtype improvements
2024-12-10 23:01:02 +01:00
Jelmer
31a90d1498 Fixed G5 overflow bug 2024-12-09 20:58:31 +01:00
atc1441
2d02da1574 Update 62.json 2024-12-09 14:22:54 +01:00
Nic Limper
33f77b2192 gzipped wwwroot 2024-12-08 20:55:59 +01:00
Nic Limper
e079c30c54 version compare bugfix 2024-12-08 20:48:58 +01:00
Nic Limper
eba9f54454 Update update_actions.json 2024-12-08 20:14:00 +01:00
Nic Limper
a0a39e98cd some polishing before release 2024-12-08 19:46:20 +01:00
139 changed files with 4732 additions and 1385 deletions

View File

@@ -70,16 +70,22 @@ jobs:
#- OpenEPaperLink_AP_and_Flasher
- ESP32_S3_16_8_YELLOW_AP
- OpenEPaperLink_Mini_AP_v4
- OpenEPaperLink_ESP32-PoE-ISO_AP
runs-on: ubuntu-22.04
steps:
- name: Checkout Code
uses: actions/checkout@v4
- uses: ./.github/actions/setup-pio
- name: Build ${{ matrix.environment }}
- name: Build ${{ matrix.environment }} binary
run: |
cd ESP32_AP-Flasher
pio run --environment ${{ matrix.environment }}
- name: Build ${{ matrix.environment }} filesystem
if: ${{ matrix.environment != 'OpenEPaperLink_ESP32-PoE-ISO_AP' }}
run: |
cd ESP32_AP-Flasher
pio run --target buildfs --environment ${{ matrix.environment }}

View File

@@ -5,6 +5,12 @@ on:
tags:
- '*'
env:
INCLUDE_C6_H2: true
INCLUDE_MINI_AP: false
INCLUDE_Nano_AP: false
INCLUDE_S2_Tag_Flasher: false
jobs:
build:
runs-on: ubuntu-22.04
@@ -20,14 +26,14 @@ jobs:
- uses: actions/setup-python@v4
with:
python-version: '3.9'
# - name: Commit zipped files
# - name: Commit zipped files
# run: |
# git config --global user.name 'Bot'
# git config --global user.email "bot@openepaperlink.de"
# git commit -am "Zipped web files"
# git push origin HEAD:master
- name: Install PlatformIO Core
run: pip install --upgrade platformio
@@ -41,37 +47,45 @@ jobs:
run: |
mkdir espbinaries
#- name: esp-idf build
# uses: espressif/esp-idf-ci-action@v1
# with:
# esp_idf_version: latest
# target: esp32c6
# path: 'ARM_Tag_FW/OpenEPaperLink_esp32_C6_AP/'
- name: build ESP32-C6 firmware
if: ${{ env.INCLUDE_C6_H2 == 'true' }}
uses: espressif/esp-idf-ci-action@v1
with:
esp_idf_version: latest
target: esp32c6
path: 'ARM_Tag_FW/OpenEPaperLink_esp32_C6_AP/'
# - name: esp-idf build
# uses: espressif/esp-idf-ci-action@v1
# with:
# esp_idf_version: latest
# target: esp32h2
# path: 'ARM_Tag_FW/OpenEPaperLink_esp32_H2_AP/'
- name: Add C6 files to release
if: ${{ env.INCLUDE_C6_H2 == 'true' }}
run: |
cp /home/runner/work/OpenEPaperLink/OpenEPaperLink/ARM_Tag_FW/OpenEPaperLink_esp32_C6_AP/build/OpenEPaperLink_esp32_C6.bin espbinaries/OpenEPaperLink_esp32_C6.bin
cp /home/runner/work/OpenEPaperLink/OpenEPaperLink/ARM_Tag_FW/OpenEPaperLink_esp32_C6_AP/build/bootloader/bootloader.bin espbinaries/bootloader_C6.bin
cp /home/runner/work/OpenEPaperLink/OpenEPaperLink/ARM_Tag_FW/OpenEPaperLink_esp32_C6_AP/build/partition_table/partition-table.bin espbinaries/partition-table_C6.bin
cp /home/runner/work/OpenEPaperLink/OpenEPaperLink//binaries/ESP32-C6/firmware_C6.json espbinaries
#- name: Add C6 files to release
# run: |
# cp /home/runner/work/OpenEPaperLink/OpenEPaperLink/ARM_Tag_FW/OpenEPaperLink_esp32_C6_AP/build/OpenEPaperLink_esp32_C6.bin espbinaries/OpenEPaperLink_esp32_C6.bin
- name: build ESP32-H2 firmware
if: ${{ env.INCLUDE_C6_H2 == 'true' }}
uses: espressif/esp-idf-ci-action@v1
with:
esp_idf_version: latest
target: esp32h2
path: 'ARM_Tag_FW/OpenEPaperLink_esp32_H2_AP/'
#- name: Add H2 files to release
# run: |
# cd ARM_Tag_FW/OpenEPaperLink_esp32_H2_AP/
# dir build
# esptool.py --chip esp32h2 merge_bin -o merged-firmware.bin --flash_mode dio --flash_size 4MB --flash_freq 48m 0x0 build/bootloader/bootloader.bin 0x8000 build/partition_table/partition-table.bin 0x10000 build/OpenEPaperLink_esp32_H2.bin
# cp merged-firmware.bin ../../espbinaries/OpenEPaperLink_esp32_H2.bin
- name: Add H2 files to release
if: ${{ env.INCLUDE_C6_H2 == 'true' }}
run: |
cp /home/runner/work/OpenEPaperLink/OpenEPaperLink/ARM_Tag_FW/OpenEPaperLink_esp32_H2_AP/build/OpenEPaperLink_esp32_H2.bin espbinaries/OpenEPaperLink_esp32_H2.bin
cp /home/runner/work/OpenEPaperLink/OpenEPaperLink/ARM_Tag_FW/OpenEPaperLink_esp32_H2_AP/build/bootloader/bootloader.bin espbinaries/bootloader_H2.bin
cp /home/runner/work/OpenEPaperLink/OpenEPaperLink/ARM_Tag_FW/OpenEPaperLink_esp32_H2_AP/build/partition_table/partition-table.bin espbinaries/partition-table_H2.bin
cp /home/runner/work/OpenEPaperLink/OpenEPaperLink//binaries/ESP32-H2/firmware_H2.json espbinaries
# - name: Zip web files
# run: |
# run: |
# cd /home/runner/work/OpenEPaperLink/OpenEPaperLink/ESP32_AP-Flasher
# python gzip_wwwfiles.py
- name: Build firmware for OpenEPaperLink_Mini_AP
if: ${{ env.INCLUDE_MINI_AP == 'true' }}
run: |
cd ESP32_AP-Flasher
export PLATFORMIO_BUILD_FLAGS="-D BUILD_VERSION=${{ github.ref_name }} -D SHA=$GITHUB_SHA"
@@ -88,8 +102,9 @@ jobs:
cd /home/runner/work/OpenEPaperLink/OpenEPaperLink
cp OpenEPaperLink_Mini_AP/firmware.bin espbinaries/OpenEPaperLink_Mini_AP.bin
cp OpenEPaperLink_Mini_AP/merged-firmware.bin espbinaries/OpenEPaperLink_Mini_AP_full.bin
- name: Build firmware for OpenEPaperLink_Nano_AP
if: ${{ env.INCLUDE_Nano_AP == 'true' }}
run: |
cd ESP32_AP-Flasher
export PLATFORMIO_BUILD_FLAGS="-D BUILD_VERSION=${{ github.ref_name }} -D SHA=$GITHUB_SHA"
@@ -106,11 +121,10 @@ jobs:
cd /home/runner/work/OpenEPaperLink/OpenEPaperLink
cp OpenEPaperLink_Nano_AP/firmware.bin espbinaries/OpenEPaperLink_Nano_AP.bin
cp OpenEPaperLink_Nano_AP/merged-firmware.bin espbinaries/OpenEPaperLink_Nano_AP_full.bin
# - name: move files for big APs
# run: |
# cp -a binaries/ESP32-C6/. ESP32_AP-Flasher/data/
- name: Build firmware for OpenEPaperLink_AP_and_Flasher
run: |
cd ESP32_AP-Flasher
@@ -128,7 +142,6 @@ jobs:
cd /home/runner/work/OpenEPaperLink/OpenEPaperLink
cp OpenEPaperLink_AP_and_Flasher/firmware.bin espbinaries/OpenEPaperLink_AP_and_Flasher.bin
cp OpenEPaperLink_AP_and_Flasher/merged-firmware.bin espbinaries/OpenEPaperLink_AP_and_Flasher_full.bin
- name: Build firmware for ESP32_S3_16_8_YELLOW_AP
run: |
cd ESP32_AP-Flasher
@@ -200,7 +213,7 @@ jobs:
cd /home/runner/work/OpenEPaperLink/OpenEPaperLink
cp OpenEPaperLink_Nano_TLSR/firmware.bin espbinaries/OpenEPaperLink_Nano_TLSR.bin
cp OpenEPaperLink_Nano_TLSR/merged-firmware.bin espbinaries/OpenEPaperLink_Nano_TLSR_full.bin
- name: Build firmware for OpenEPaperLink_PoE_AP
run: |
cd ESP32_AP-Flasher
@@ -254,7 +267,29 @@ jobs:
cd /home/runner/work/OpenEPaperLink/OpenEPaperLink
cp BLE_ONLY_AP/firmware.bin espbinaries/BLE_ONLY_AP.bin
cp BLE_ONLY_AP/merged-firmware.bin espbinaries/BLE_ONLY_AP_full.bin
- name: Build firmware for OpenEPaperLink_ESP32-PoE-ISO_AP
run: |
cd ESP32_AP-Flasher
export PLATFORMIO_BUILD_FLAGS="-D BUILD_VERSION=${{ github.ref_name }} -D SHA=$GITHUB_SHA"
pio run --environment OpenEPaperLink_ESP32-PoE-ISO_AP
mv data data.bak
mkdir data
pio run --target buildfs --environment OpenEPaperLink_ESP32-PoE-ISO_AP
rmdir data
mv data.bak data
mkdir /home/runner/work/OpenEPaperLink/OpenEPaperLink/OpenEPaperLink_ESP32-PoE-ISO_AP
cp ~/.platformio/packages/framework-arduinoespressif32/tools/partitions/boot_app0.bin /home/runner/work/OpenEPaperLink/OpenEPaperLink/OpenEPaperLink_ESP32-PoE-ISO_AP/boot_app0.bin
cp .pio/build/OpenEPaperLink_ESP32-PoE-ISO_AP/firmware.bin /home/runner/work/OpenEPaperLink/OpenEPaperLink/OpenEPaperLink_ESP32-PoE-ISO_AP/firmware.bin
cp .pio/build/OpenEPaperLink_ESP32-PoE-ISO_AP/bootloader.bin /home/runner/work/OpenEPaperLink/OpenEPaperLink/OpenEPaperLink_ESP32-PoE-ISO_AP/bootloader.bin
cp .pio/build/OpenEPaperLink_ESP32-PoE-ISO_AP/partitions.bin /home/runner/work/OpenEPaperLink/OpenEPaperLink/OpenEPaperLink_ESP32-PoE-ISO_AP/partitions.bin
cp .pio/build/OpenEPaperLink_ESP32-PoE-ISO_AP/littlefs.bin /home/runner/work/OpenEPaperLink/OpenEPaperLink/OpenEPaperLink_ESP32-PoE-ISO_AP/littlefs.bin
cd /home/runner/work/OpenEPaperLink/OpenEPaperLink/OpenEPaperLink_ESP32-PoE-ISO_AP
esptool.py --chip esp32 merge_bin -o merged-firmware.bin --flash_mode qio --flash_freq 80m --flash_size 4MB 0x0000 bootloader.bin 0x8000 partitions.bin 0xD000 boot_app0.bin 0x10000 firmware.bin 0x3D0000 littlefs.bin
cd /home/runner/work/OpenEPaperLink/OpenEPaperLink
cp OpenEPaperLink_ESP32-PoE-ISO_AP/firmware.bin espbinaries/OpenEPaperLink_ESP32-PoE-ISO_AP.bin
cp OpenEPaperLink_ESP32-PoE-ISO_AP/merged-firmware.bin espbinaries/OpenEPaperLink_ESP32-PoE-ISO_AP_full.bin
- name: generate release json file
run: |
mkdir jsonfiles
@@ -268,10 +303,11 @@ jobs:
tag: ${{ github.ref }}
file_glob: true
overwrite: true
# this is down here intentionally to be able to modify the binary folder before adding it to the Tag_Flasher later (ota binaries can be removed)
- name: Build firmware for Tag_Flasher
if: ${{ env.INCLUDE_S2_Tag_Flasher == 'true' }}
run: |
cd Tag_Flasher/ESP32_Flasher
export PLATFORMIO_BUILD_FLAGS="-D BUILD_VERSION=${{ github.ref_name }} -D SHA=$GITHUB_SHA"
@@ -288,7 +324,6 @@ jobs:
cd /home/runner/work/OpenEPaperLink/OpenEPaperLink
cp S2_Tag_Flasher/firmware.bin espbinaries/S2_Tag_Flasher.bin
cp S2_Tag_Flasher/merged-firmware.bin espbinaries/S2_Tag_Flasher_full.bin
- name: Add esp bins to release
uses: svenstaro/upload-release-action@v2
with:

View File

@@ -1,4 +1,4 @@
menu "OEPL Hardware config"
menu "OEPL config"
choice OEPL_HARDWARE_PROFILE
prompt "Hardware profile"
@@ -16,6 +16,12 @@ menu "OEPL Hardware config"
config OEPL_HARDWARE_PROFILE_LILYGO
bool "LILYGO-AP"
config OEPL_HARDWARE_PROFILE_4inch
bool "4inchAP"
config OEPL_HARDWARE_PROFILE_ELECROW_C6
bool "ELECROW-C6-AP"
endchoice
config OEPL_HARDWARE_UART_TX
@@ -28,6 +34,16 @@ menu "OEPL Hardware config"
int "GPIO - UART RX"
default 2
config OEPL_HARDWARE_UART_TX
depends on OEPL_HARDWARE_PROFILE_4inch
int "GPIO - UART TX"
default 16
config OEPL_HARDWARE_UART_RX
depends on OEPL_HARDWARE_PROFILE_4inch
int "GPIO - UART RX"
default 17
config OEPL_SUBGIG_SUPPORT
bool "Enable SubGhz Support"
default "n"
@@ -102,6 +118,16 @@ menu "OEPL Hardware config"
USE SPI3_HOST. This is also called VSPI_HOST
endchoice
endmenu
config OEPL_DEBUG_PRINT
bool "Enable OEPL Debug logging"
default "n"
config OEPL_VERBOSE_DEBUG
depends on OEPL_DEBUG_PRINT
bool "Enable OEPL Verbose Debug logging"
default "n"
endmenu

View File

@@ -13,24 +13,16 @@
#include "radio.h"
#include "proto.h"
#include "utils.h"
#include "second_uart.h"
#include "cc1101_radio.h"
#include "logging.h"
#include "SubGigRadio.h"
void DumpHex(void *AdrIn,int Len);
bool CC1101_QuickCheck(void);
#define LOGE(format, ... ) \
printf("%s#%d: " format,__FUNCTION__,__LINE__,## __VA_ARGS__)
#if 0
#define LOG(format, ... ) printf("%s: " format,__FUNCTION__,## __VA_ARGS__)
#define LOG_RAW(format, ... ) printf(format,## __VA_ARGS__)
#define LOG_HEX(x,y) DumpHex(x,y)
#else
#define LOG(format, ... )
#define LOG_RAW(format, ... )
#define LOG_HEX(x,y)
#endif
#define wait_Miso(level) CC1101_WaitMISO(__FUNCTION__,__LINE__,level)
// SPI Stuff
#if CONFIG_SPI2_HOST
@@ -39,6 +31,9 @@ void DumpHex(void *AdrIn,int Len);
#define HOST_ID SPI3_HOST
#endif
// Wait for up to 2 milliseconds for MISO to go low
#define MISO_WAIT_TIMEOUT 2
// Address Config = No address check
// Base Frequency = xxx.xxx
// CRC Enable = false
@@ -247,10 +242,10 @@ SubGigErr SubGig_radio_init(uint8_t ch)
SubGigErr Ret = SUBGIG_ERR_NONE;
do {
gpio_reset_pin(CONFIG_CSN_GPIO);
gpio_set_direction(CONFIG_CSN_GPIO, GPIO_MODE_OUTPUT);
gpio_set_level(CONFIG_CSN_GPIO, 1);
if(!CC1101_QuickCheck()) {
Ret = SUBGIG_CC1101_NOT_FOUND;
break;
}
spi_bus_config_t buscfg = {
.sclk_io_num = CONFIG_SCK_GPIO,
.mosi_io_num = CONFIG_MOSI_GPIO,
@@ -297,6 +292,7 @@ SubGigErr SubGig_radio_init(uint8_t ch)
}
// Check Chip ID
if(!CC1101_Present()) {
LOGE("CC1101 not detected\n");
Ret = SUBGIG_CC1101_NOT_FOUND;
break;
}
@@ -314,7 +310,7 @@ SubGigErr SubGig_radio_init(uint8_t ch)
} while(false);
if(ErrLine != 0) {
LOG("%s#%d: failed %d\n",__FUNCTION__,ErrLine,Err);
LOGA("%s#%d: failed %d\n",__FUNCTION__,ErrLine,Err);
if(Err == 0) {
Ret = ESP_FAIL;
}
@@ -453,8 +449,6 @@ int8_t SubGig_commsRxUnencrypted(uint8_t *data)
if(RxBytes >= 2) {
// NB: RxBytes includes the CRC, deduct it
Ret = (uint8_t) RxBytes - 2;
LOG("Received %d byte subgig frame:\n",Ret);
LOG_HEX(data,Ret);
}
}
} while(false);
@@ -477,7 +471,7 @@ int CheckSubGigState()
}
if(Err != SUBGIG_ERR_NONE) {
LOG("CheckSubGigState: returing %d\n",Err);
LOGE("Returning %d\n",Err);
}
return Err;
@@ -558,5 +552,96 @@ void DumpHex(void *AdrIn,int Len)
LOG_RAW("\n");
}
}
// Quick and hopefully safe check if a CC1101 is present.
// Only the CSN and MISO GPIOs are configured for this test.
// If they are and there's a CC1101 then MISO should go low when
// CSN is low
bool CC1101_QuickCheck()
{
// Init CSn and MISO
esp_err_t Err = ESP_OK;
bool Ret = false;
int Line = 0;
int MisoLevel;
do {
Err = gpio_reset_pin(CONFIG_MISO_GPIO);
if(Err != ESP_OK) {
Line = __LINE__;
break;
}
Err = gpio_set_direction(CONFIG_MISO_GPIO,GPIO_MODE_INPUT);
if(Err != ESP_OK) {
Line = __LINE__;
break;
}
Err = gpio_set_pull_mode(CONFIG_MISO_GPIO,GPIO_PULLUP_ONLY);
if(Err != ESP_OK) {
Line = __LINE__;
break;
}
Err = gpio_reset_pin(CONFIG_CSN_GPIO);
if(Err != ESP_OK) {
Line = __LINE__;
break;
}
Err = gpio_set_direction(CONFIG_CSN_GPIO,GPIO_MODE_OUTPUT);
if(Err != ESP_OK) {
Line = __LINE__;
break;
}
Err = gpio_set_level(CONFIG_CSN_GPIO,1);
if(Err != ESP_OK) {
Line = __LINE__;
break;
}
// The CC1101 is not selected and MISO has a pullup so it should be high
if(wait_Miso(1) != 1) {
LOGA("Error: SubGhz MISO stuck low\n");
break;
}
// Select the CC1101
Err = gpio_set_level(CONFIG_CSN_GPIO,0);
if(Err != ESP_OK) {
Line = __LINE__;
break;
}
MisoLevel = wait_Miso(0);
// Deselect the CC1101
Err = gpio_set_level(CONFIG_CSN_GPIO,1);
if(Err != ESP_OK) {
Line = __LINE__;
break;
}
if(MisoLevel == 0) {
Ret = true;
}
} while(false);
if(Line != 0) {
LOGA("%s#%d: gpio call failed (0x%x)\n",__FUNCTION__,__LINE__,Err);
}
if(Ret) {
// Disable pullup, it's no longer needed
gpio_set_pull_mode(CONFIG_MISO_GPIO,GPIO_FLOATING);
}
else {
// CC1101 not present, deinit MISO and CSn GPIOs
LOGE("CC1101 not detected\n");
gpio_reset_pin(CONFIG_MISO_GPIO);
gpio_reset_pin(CONFIG_CSN_GPIO);
}
return Ret;
}
#endif // CONFIG_OEPL_SUBGIG_SUPPORT

View File

@@ -34,34 +34,12 @@
#include <stdbool.h>
#include <driver/spi_master.h>
#include "proto.h"
#include "utils.h"
#include "second_uart.h"
#include "cc1101_radio.h"
#include "logging.h"
#include "radio.h"
#define ENABLE_LOGGING 0
// LOGA - generic logging, always enabled
#define LOGA(format, ... ) printf(format,## __VA_ARGS__)
// LOGE - error logging, always enabled
#define LOGE(format, ... ) printf("%s: " format,__FUNCTION__,## __VA_ARGS__)
#if ENABLE_LOGGING
#define LOG(format, ... ) printf("%s: " format,__FUNCTION__,## __VA_ARGS__)
#define LOG_RAW(format, ... ) printf(format,## __VA_ARGS__)
#else
#define LOG(format, ... )
#define LOG_RAW(format, ... )
#endif
#define ENABLE_VERBOSE_LOGGING 0
#if ENABLE_VERBOSE_LOGGING
#define LOGV(format, ... ) printf("%s: " format,__FUNCTION__,## __VA_ARGS__)
#define LOGV_RAW(format, ... ) printf(format,## __VA_ARGS__)
#else
#define LOGV(format, ... )
#define LOGB_RAW(format, ... )
#endif
#include <string.h>
#include "freertos/FreeRTOS.h"
@@ -197,7 +175,7 @@ uint8_t CC1101_readReg(uint8_t regAddr, uint8_t regType);
void CC1101_writeReg(uint8_t regAddr, uint8_t value);
void CC1101_setTxState(void);
void setIdleState(void);
static void setIdleState(void);
spi_device_handle_t gSpiHndl;
@@ -288,7 +266,7 @@ static uint8_t gRfState;
#define cc1101_Select() gpio_set_level(CONFIG_CSN_GPIO, LOW)
#define cc1101_Deselect() gpio_set_level(CONFIG_CSN_GPIO, HIGH)
#define wait_Miso() while(gpio_get_level(CONFIG_MISO_GPIO)>0)
#define wait_Miso() CC1101_WaitMISO(__FUNCTION__,__LINE__,0)
#define getGDO0state() gpio_get_level(CONFIG_GDO0_GPIO)
#define wait_GDO0_high() while(!getGDO0state())
#define wait_GDO0_low() while(getGDO0state())
@@ -633,14 +611,13 @@ int CC1101_Rx(uint8_t *RxBuf,size_t RxBufLen,uint8_t *pRssi,uint8_t *pLqi)
// Any data waiting to be read and no overflow?
do {
if(rxBytes & CC1101_RXFIFO_OVERFLOW_MASK) {
LOGE("RxFifo overflow\n");
// This occurs occasionally due to random noise, so do don't log
Ret = -2;
break;
}
if(rxBytes < 2) {
// should have at least 2 bytes, packet len and one byte of data
LOGE("Internal error, rxBytes = %d\n",rxBytes);
Ret = -2;
break;
}
@@ -786,5 +763,24 @@ void CC1101_logState()
}
}
// Wait for up to 2 milliseconds for MISO to go low
#define MISO_WAIT_TIMEOUT 2
int CC1101_WaitMISO(const char *Func,int Line,int level)
{
uint32_t Start = getMillis();
int MisoLevel;
while((MisoLevel = gpio_get_level(CONFIG_MISO_GPIO)) != level) {
if((getMillis() - Start) >= MISO_WAIT_TIMEOUT) {
LOGA("%s#%d: timeout waiting for MISO to go %s\n",
Func,Line,level ? "high" : "low");
break;
}
}
return MisoLevel;
}
#endif // CONFIG_OEPL_SUBGIG_SUPPORT

View File

@@ -29,6 +29,14 @@
#ifndef __CC1101_RADIO_H_
#define __CC1101_RADIO_H_
// Log to all
#define LOGA(format, ... ) \
uart_printf(format "\r",## __VA_ARGS__)
// Error log to all
#define LOGE(format, ... ) \
uart_printf("%s#%d: " format "\r",__FUNCTION__,__LINE__,## __VA_ARGS__)
/**
* CC1101 configuration registers
*/
@@ -113,6 +121,7 @@ void CC1101_DumpRegs(void);
void CC1101_reset(void);
void CC1101_logState(void);
void CC1101_setRxState(void);
int CC1101_WaitMISO(const char *Func,int Line,int level);
#endif // __CC1101_RADIO_H_

View File

@@ -0,0 +1,23 @@
#pragma once
#if CONFIG_OEPL_DEBUG_PRINT
#define LOG(format, ... ) printf("%s: " format,__FUNCTION__,## __VA_ARGS__)
#define LOG_RAW(format, ... ) printf(format,## __VA_ARGS__)
void DumpHex(void *AdrIn,int Len);
#define LOG_HEX(x,y) DumpHex(x,y)
#else
#define LOG(format, ... )
#define LOG_RAW(format, ... )
#define LOG_HEX(x,y)
#endif
#if CONFIG_OEPL_VERBOSE_DEBUG
#define LOGV(format, ... ) LOG(format,## __VA_ARGS__)
#define LOGV_RAW(format, ... ) LOG_RAW(format,## __VA_ARGS__)
#define LOGV_HEX(x,y) LOG_HEX(x,y)
#else
#define LOGV(format, ... )
#define LOGV_RAW(format, ... )
#define LOGV_HEX(x,y)
#endif

View File

@@ -28,6 +28,7 @@
#include <stdbool.h>
#include <stdint.h>
#include <string.h>
#include "logging.h"
#include "SubGigRadio.h"
@@ -35,6 +36,30 @@ static const char *TAG = "MAIN";
const uint8_t channelList[6] = {11, 15, 20, 25, 26, 27};
#if CONFIG_OEPL_VERBOSE_DEBUG
const struct {
uint8_t Type;
const char *Name;
} gPktTypeLookupTbl[] = {
{PKT_TAG_RETURN_DATA, "TAG_RETURN_DATA"},
{PKT_TAG_RETURN_DATA_ACK, "TAG_RETURN_DATA_ACK"},
{PKT_AVAIL_DATA_SHORTREQ, "AVAIL_DATA_SHORTREQ"},
{PKT_AVAIL_DATA_REQ, "AVAIL_DATA_REQ"},
{PKT_AVAIL_DATA_INFO, "AVAIL_DATA_INFO"},
{PKT_BLOCK_PARTIAL_REQUEST, "BLOCK_PARTIAL_REQUEST"},
{PKT_BLOCK_REQUEST_ACK, "BLOCK_REQUEST_ACK"},
{PKT_BLOCK_REQUEST, "BLOCK_REQUEST"},
{PKT_BLOCK_PART, "BLOCK_PART"},
{PKT_XFER_COMPLETE, "XFER_COMPLETE"},
{PKT_XFER_COMPLETE_ACK, "XFER_COMPLETE_ACK"},
{PKT_CANCEL_XFER, "CANCEL_XFER"},
{PKT_PING, "PING"},
{PKT_PONG, "PONG"},
{0,NULL} // End of table
};
#endif
#define DATATYPE_NOUPDATE 0
#define HW_TYPE 0xC6
@@ -44,7 +69,7 @@ const uint8_t channelList[6] = {11, 15, 20, 25, 26, 27};
struct pendingData pendingDataArr[MAX_PENDING_MACS];
// VERSION GOES HERE!
uint16_t version = 0x001e;
uint16_t version = 0x001f;
#define RAW_PKT_PADDING 2
@@ -474,7 +499,7 @@ void processBlockRequest(const uint8_t *buffer, uint8_t forceBlockDownload) {
lastBlockRequest = getMillis();
} else {
// we're talking to another mac, let this mac know we can't accomodate another request right now
pr("BUSY!\n");
pr("BUSY!\n\r");
sendCancelXfer(rxHeader->src);
return;
}
@@ -496,9 +521,9 @@ void processBlockRequest(const uint8_t *buffer, uint8_t forceBlockDownload) {
if (forceBlockDownload) {
if ((getMillis() - nextBlockAttempt) > 380) {
requestDataDownload = true;
pr("FORCED\n");
pr("FORCED\n\r");
} else {
pr("IGNORED\n");
pr("IGNORED\n\r");
}
}
}
@@ -647,7 +672,7 @@ void sendPart(uint8_t partNo) {
}
void sendBlockData() {
if (getBlockDataLength() == 0) {
pr("Invalid block request received, 0 parts..\n");
pr("Invalid block request received, 0 parts..\n\r");
requestedData.requestedParts[0] |= 0x01;
}
@@ -660,7 +685,7 @@ void sendBlockData() {
pr(".");
}
}
pr("\n");
pr("\n\r");
uint8_t partNo = 0;
while (partNo < BLOCK_MAX_PARTS) {
@@ -766,8 +791,24 @@ void app_main(void) {
int8_t ret = commsRxUnencrypted(radiorxbuffer);
if (ret > 1) {
led_flash(0);
uint8_t PktType = getPacketType(radiorxbuffer);
#if CONFIG_OEPL_VERBOSE_DEBUG
LOGV_RAW("Received %d byte ",ret);
for(uint8_t i = 0; gPktTypeLookupTbl[i].Name != NULL; i++) {
if(gPktTypeLookupTbl[i].Type == PktType) {
LOGV_RAW("%s",gPktTypeLookupTbl[i].Name);
break;
}
if(gPktTypeLookupTbl[i].Name == NULL) {
LOGV_RAW("undefined (0x%02x)",PktType);
}
}
LOGV_RAW(" packet:\n");
LOGV_HEX(radiorxbuffer,ret);
#endif
// received a packet, lets see what it is
switch (getPacketType(radiorxbuffer)) {
switch (PktType) {
case PKT_AVAIL_DATA_REQ:
if (ret == 28) {
// old version of the AvailDataReq struct, set all the new fields to zero, so it will pass the CRC

View File

@@ -54,7 +54,11 @@ void esp_ieee802154_transmit_failed(const uint8_t *frame, esp_ieee802154_tx_erro
void esp_ieee802154_transmit_done(const uint8_t *frame, const uint8_t *ack, esp_ieee802154_frame_info_t *ack_frame_info) {
isInTransmit = 0;
ESP_EARLY_LOGI(TAG, "TX %d", frame[0]);
esp_ieee802154_receive_handle_done(ack);
if(ack != NULL) {
if(esp_ieee802154_receive_handle_done(ack)) {
ESP_EARLY_LOGI(TAG, "esp_ieee802154_receive_handle_done() failed");
}
}
}
static bool zigbee_is_enabled = false;
void radio_init(uint8_t ch) {

View File

@@ -21,8 +21,8 @@
#include "soc/uart_struct.h"
#ifdef CONFIG_IDF_TARGET_ESP32C6
#include "soc/lp_uart_reg.h"
static const char *TAG = "SECOND_UART";
#endif
static const char *TAG = "SECOND_UART";
#include "second_uart.h"
@@ -45,6 +45,8 @@ void init_second_uart() {
.flow_ctrl = UART_HW_FLOWCTRL_DISABLE,
.source_clk = UART_SCLK_DEFAULT,
};
ESP_LOGI(TAG, "HARDWARE_UART_TX %d, CONFIG_OEPL_HARDWARE_UART_RX %d",
CONFIG_OEPL_HARDWARE_UART_TX,CONFIG_OEPL_HARDWARE_UART_RX);
ESP_ERROR_CHECK(uart_driver_install(1, BUF_SIZE * 2, BUF_SIZE * 2, 20, &uart0_queue, 0));
ESP_ERROR_CHECK(uart_param_config(1, &uart_config));
ESP_ERROR_CHECK(uart_set_pin(1, CONFIG_OEPL_HARDWARE_UART_TX, CONFIG_OEPL_HARDWARE_UART_RX, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE));

View File

@@ -21,6 +21,9 @@ void uart_printf(const char *format, ...);
#elif defined(CONFIG_OEPL_HARDWARE_PROFILE_POE_AP)
#define CONFIG_OEPL_HARDWARE_UART_TX 5
#define CONFIG_OEPL_HARDWARE_UART_RX 18
#elif defined(CONFIG_OEPL_HARDWARE_PROFILE_ELECROW_C6)
#define CONFIG_OEPL_HARDWARE_UART_TX 0
#define CONFIG_OEPL_HARDWARE_UART_RX 1
#elif defined(CONFIG_OEPL_HARDWARE_PROFILE_CUSTOM)
#if !defined(CONFIG_OEPL_HARDWARE_UART_TX) || !defined(CONFIG_OEPL_HARDWARE_UART_RX)
#error "No UART TX / RX pins defined. Please check menuconfig"

View File

@@ -7,4 +7,4 @@ CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y
CONFIG_ESPTOOLPY_HEADER_FLASHSIZE_UPDATE=y
CONFIG_PARTITION_TABLE_CUSTOM=y
CONFIG_OEPL_SUBGIG_SUPPORT=y
CONFIG_IEEE802154_RX_BUFFER_SIZE=100

View File

@@ -1,14 +1,8 @@
menu "OEPL Hardware config"
menu "OEPL config"
choice OEPL_HARDWARE_PROFILE
prompt "Hardware profile"
default OEPL_HARDWARE_PROFILE_DEFAULT
config OEPL_HARDWARE_PROFILE_DEFAULT
bool "Default"
config OEPL_HARDWARE_PROFILE_POE_AP
bool "PoE-AP"
default OEPL_HARDWARE_PROFILE_LILYGO
config OEPL_HARDWARE_PROFILE_CUSTOM
bool "Custom"
@@ -102,6 +96,16 @@ menu "OEPL Hardware config"
USE SPI3_HOST. This is also called VSPI_HOST
endchoice
endmenu
config OEPL_DEBUG_PRINT
bool "Enable OEPL Debug logging"
default "n"
config OEPL_VERBOSE_DEBUG
depends on OEPL_DEBUG_PRINT
bool "Enable OEPL Verbose Debug logging"
default "n"
endmenu

Binary file not shown.

View File

@@ -10,54 +10,6 @@
"/www/painter.js",
"/www/setup.html",
"/www/setup.js",
"/www/upload-demo.html",
"/fonts/weathericons30.vlw",
"/fonts/weathericons70.vlw",
"/fonts/weathericons78.vlw",
"/fonts/calibrib120.vlw",
"/fonts/calibrib150.vlw",
"/fonts/calibrib50.vlw",
"/fonts/calibrib60.vlw",
"/fonts/BellCent10.vlw",
"/tagtypes/00.json",
"/tagtypes/01.json",
"/tagtypes/02.json",
"/tagtypes/05.json",
"/tagtypes/11.json",
"/tagtypes/21.json",
"/tagtypes/22.json",
"/tagtypes/26.json",
"/tagtypes/27.json",
"/tagtypes/2E.json",
"/tagtypes/2F.json",
"/tagtypes/30.json",
"/tagtypes/31.json",
"/tagtypes/32.json",
"/tagtypes/33.json",
"/tagtypes/34.json",
"/tagtypes/35.json",
"/tagtypes/36.json",
"/tagtypes/40.json",
"/tagtypes/41.json",
"/tagtypes/42.json",
"/tagtypes/43.json",
"/tagtypes/55.json",
"/tagtypes/60.json",
"/tagtypes/61.json",
"/tagtypes/62.json",
"/tagtypes/80.json",
"/tagtypes/81.json",
"/tagtypes/82.json",
"/tagtypes/83.json",
"/tagtypes/B0.json",
"/tagtypes/B1.json",
"/tagtypes/B2.json",
"/tagtypes/B3.json",
"/tagtypes/B5.json",
"/tagtypes/BD.json",
"/tagtypes/BE.json",
"/tagtypes/E0.json",
"/tagtypes/E1.json",
"/tagtypes/F0.json"
"/www/upload-demo.html"
]
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,7 @@
# Name, Type, SubType, Offset, Size, Flags
nvs, data, nvs, 0x9000, 0x5000,
otadata, data, ota, 0xe000, 0x2000,
app0, app, ota_0, 0x10000, 0x1E0000,
app1, app, ota_1, 0x1F0000,0x1E0000,
spiffs, data, spiffs, 0x3D0000,0x20000,
coredump, data, coredump,0x3F0000,0x10000,
1 # Name Type SubType Offset Size Flags
2 nvs data nvs 0x9000 0x5000
3 otadata data ota 0xe000 0x2000
4 app0 app ota_0 0x10000 0x1E0000
5 app1 app ota_1 0x1F0000 0x1E0000
6 spiffs data spiffs 0x3D0000 0x20000
7 coredump data coredump 0x3F0000 0x10000

View File

@@ -4,9 +4,9 @@
class SPIFFSEditor: public AsyncWebHandler {
private:
fs::FS _fs;
mutable fs::FS _fs;
String _username;
String _password;
String _password;
bool _authenticated;
uint32_t _startTime;
public:
@@ -15,10 +15,10 @@ class SPIFFSEditor: public AsyncWebHandler {
#else
SPIFFSEditor(const String& username=String(), const String& password=String(), const fs::FS& fs=SPIFFS);
#endif
virtual bool canHandle(AsyncWebServerRequest *request) override final;
virtual bool canHandle(AsyncWebServerRequest* request) const override final;
virtual void handleRequest(AsyncWebServerRequest *request) override final;
virtual void handleUpload(AsyncWebServerRequest *request, const String& filename, size_t index, uint8_t *data, size_t len, bool final) override final;
virtual bool isRequestHandlerTrivial() override final {return false;}
virtual bool isRequestHandlerTrivial() const override final {return false;}
virtual String listFilesRecursively(String path, bool recursive = false);
};

View File

@@ -19,7 +19,7 @@ void checkVars();
void drawNew(const uint8_t mac[8], tagRecord *&taginfo);
bool updateTagImage(String &filename, const uint8_t *dst, uint16_t nextCheckin, tagRecord *&taginfo, imgParam &imageParams);
void drawString(TFT_eSprite &spr, String content, int16_t posx, int16_t posy, String font, byte align = 0, uint16_t color = TFT_BLACK, uint16_t size = 30, uint16_t bgcolor = TFT_WHITE);
void drawTextBox(TFT_eSprite &spr, String &content, int16_t &posx, int16_t &posy, int16_t boxwidth, int16_t boxheight, String font, uint16_t color = TFT_BLACK, uint16_t bgcolor = TFT_WHITE, float lineheight = 1);
void drawTextBox(TFT_eSprite &spr, String &content, int16_t &posx, int16_t &posy, int16_t boxwidth, int16_t boxheight, String font, uint16_t color = TFT_BLACK, uint16_t bgcolor = TFT_WHITE, float lineheight = 1, byte align = TL_DATUM);
void initSprite(TFT_eSprite &spr, int w, int h, imgParam &imageParams);
void drawDate(String &filename, tagRecord *&taginfo, imgParam &imageParams);
void drawNumber(String &filename, int32_t count, int32_t thresholdred, tagRecord *&taginfo, imgParam &imageParams);

View File

@@ -1,4 +1,20 @@
#include <Arduino.h>
#include <LittleFS.h>
bool doC6flash(uint8_t doDownload);
#if defined HAS_H2
#define SHORT_CHIP_NAME "H2"
#define OTA_BIN_DIR "ESP32-H2"
#define ESP_CHIP_TYPE ESP32H2_CHIP
#elif defined HAS_TSLR
#define SHORT_CHIP_NAME "TSLR"
#elif defined HAS_ELECROW_C6
#define SHORT_CHIP_NAME "ELECROW_C6"
#define OTA_BIN_DIR "ESP32-C6"
#define ESP_CHIP_TYPE ESP32C6_CHIP
#elif defined C6_OTA_FLASHING
#define SHORT_CHIP_NAME "C6"
#define OTA_BIN_DIR "ESP32-C6"
#define ESP_CHIP_TYPE ESP32C6_CHIP
#endif
bool FlashC6_H2(const char *Url);

View File

@@ -93,6 +93,46 @@ extern Arduino_RGB_Display *gfx;
#endif
#ifdef HAS_4inch_TPANEL
#define LV_ATTRIBUTE_TICK_INC IRAM_ATTR
#define TOUCH_MODULES_CST_MUTUAL
// esp32-4848S040
#define LCD_WIDTH 480
#define LCD_HEIGHT 480
#define LCD_VSYNC 17
#define LCD_HSYNC 16
#define LCD_PCLK 21
#define LCD_R0 11
#define LCD_R1 12
#define LCD_R2 13
#define LCD_R3 14
#define LCD_R4 0
#define LCD_G0 8
#define LCD_G1 20
#define LCD_G2 3
#define LCD_G3 46
#define LCD_G4 9
#define LCD_G5 10
#define LCD_B0 4
#define LCD_B1 5
#define LCD_B2 6
#define LCD_B3 7
#define LCD_B4 15
#define LCD_BL 38
#define LCD_DE 18
#define SPI_LCD_CS 39
#define SPI_LCD_SCLK 48
#define SPI_LCD_MOSI 47
#include "Arduino_GFX_Library.h"
extern Arduino_RGB_Display *gfx;
#endif
#ifdef HAS_TFT
extern TFT_eSPI tft2;

View File

@@ -26,6 +26,7 @@ const uint8_t PROGMEM gamma8[] = {
void ledTask(void* parameter);
void setBrightness(int brightness);
void updateBrightnessFromConfig();
void ledcSet(uint8_t channel, uint8_t brightness);
#ifdef HAS_RGB_LED
extern CRGB rgbIdleColor;
@@ -34,7 +35,6 @@ void shortBlink(CRGB cname);
void showColorPattern(CRGB colorone, CRGB colortwo, CRGB colorthree);
void rgbIdle();
void addFadeColor(CRGB cname);
#endif
void quickBlink(uint8_t repeat);

View File

@@ -17,7 +17,6 @@ struct imgParam {
bool hasRed;
uint8_t dataType;
uint8_t dither;
// bool grayLut = false;
uint8_t bufferbpp = 8;
uint8_t rotate = 0;
uint16_t highlightColor = 2;

View File

@@ -26,6 +26,7 @@ extern void processDataReq(struct espAvailDataReq* adr, bool local, IPAddress re
extern void processTagReturnData(struct espTagReturnData* trd, uint8_t len, bool local);
extern bool sendTagCommand(const uint8_t* dst, uint8_t cmd, bool local, const uint8_t* payload = nullptr);
bool sendTagMac(const uint8_t* dst, const uint64_t newmac, bool local);
extern bool sendAPSegmentedData(const uint8_t* dst, String data, uint16_t icons, bool inverted, bool local);
extern bool showAPSegmentedInfo(const uint8_t* dst, bool local);
extern void updateTaginfoitem(struct TagInfo* taginfoitem, IPAddress remoteIP);

View File

@@ -12,8 +12,8 @@ extern struct espSetChannelPower curChannel;
#define AP_STATE_NORADIO 7
struct APInfoS {
bool isOnline = false;
uint8_t state = AP_STATE_OFFLINE;
volatile bool isOnline = false;
volatile uint8_t state = AP_STATE_OFFLINE;
uint8_t type;
uint16_t version = 0;
uint8_t channel;
@@ -29,6 +29,17 @@ struct APInfoS {
extern struct APInfoS apInfo;
enum ApSerialState {
SERIAL_STATE_NONE,
SERIAL_STATE_INITIALIZED,
SERIAL_STATE_STARTING,
SERIAL_STATE_RUNNING,
SERIAL_STATE_STOP,
SERIAL_STATE_STOPPED
};
extern volatile ApSerialState gSerialTaskState;
void APTask(void* parameter);
bool sendCancelPending(struct pendingData* pending);
@@ -38,5 +49,5 @@ void APEnterEarlyReset();
bool sendChannelPower(struct espSetChannelPower* scp);
void rxSerialTask2(void* parameter);
void APTagReset();
bool bringAPOnline();
bool bringAPOnline(uint8_t newState = AP_STATE_ONLINE);
void setAPstate(bool isOnline, uint8_t state);

View File

@@ -4,6 +4,8 @@
#include "FS.h"
#ifdef HAS_SDCARD
#ifndef SD_CARD_SDMMC
#ifndef SD_CARD_SS
#error SD_CARD_SS UNDEFINED
#endif
@@ -18,6 +20,8 @@
#ifndef SD_CARD_MOSI
#define SD_CARD_MOSI 23
#endif
#endif
#endif
@@ -36,7 +40,9 @@ class DynStorage {
extern SemaphoreHandle_t fsMutex;
extern DynStorage Storage;
extern fs::FS *contentFS;
#ifndef SD_CARD_ONLY
extern void copyFile(File in, File out);
#endif
#endif

View File

@@ -15,7 +15,7 @@
#define NO_SUBGHZ_CHANNEL 255
class tagRecord {
public:
tagRecord() : mac{0}, version(0), alias(""), lastseen(0), nextupdate(0), contentMode(0), pendingCount(0), md5{0}, expectedNextCheckin(0), modeConfigJson(""), LQI(0), RSSI(0), temperature(0), batteryMv(0), hwType(0), wakeupReason(0), capabilities(0), lastfullupdate(0), isExternal(false), apIp(IPAddress(0, 0, 0, 0)), pendingIdle(0), hasCustomLUT(false), rotate(0), lut(0), tagSoftwareVersion(0), currentChannel(0), dataType(0), filename(""), data(nullptr), len(0), invert(0), updateCount(0), updateLast(0) {}
tagRecord() : mac{0}, version(0), alias(""), lastseen(0), nextupdate(0), contentMode(0), pendingCount(0), md5{0}, expectedNextCheckin(0), modeConfigJson(""), LQI(0), RSSI(0), temperature(0), batteryMv(0), hwType(0), wakeupReason(0), capabilities(0), lastfullupdate(0), isExternal(false), apIp(IPAddress(0, 0, 0, 0)), pendingIdle(0), rotate(0), lut(0), tagSoftwareVersion(0), currentChannel(0), dataType(0), filename(""), data(nullptr), len(0), invert(0), updateCount(0), updateLast(0) {}
uint8_t mac[8];
uint8_t version;
@@ -38,7 +38,6 @@ class tagRecord {
bool isExternal;
IPAddress apIp;
uint16_t pendingIdle;
bool hasCustomLUT;
uint8_t rotate;
uint8_t lut;
uint16_t tagSoftwareVersion;
@@ -64,7 +63,7 @@ struct Config {
uint8_t language;
uint8_t maxsleep;
uint8_t stopsleep;
uint8_t runStatus;
volatile uint8_t runStatus;
uint8_t preview;
uint8_t nightlyreboot;
uint8_t lock;
@@ -76,6 +75,7 @@ struct Config {
uint8_t discovery;
String repo;
String env;
uint8_t showtimestamp;
};
struct Color {

View File

@@ -16,7 +16,8 @@ enum WifiStatus {
WAIT_CONNECTING,
CONNECTED,
WAIT_RECONNECT,
AP
AP,
ETHERNET
};
class WifiManager {
@@ -41,6 +42,7 @@ class WifiManager {
bool waitForConnection();
void pollSerial();
static void terminalLog(String text);
static String buildHostname(esp_mac_type_t mac_type);
public:
WifiManager();
@@ -53,6 +55,8 @@ class WifiManager {
void startManagementServer();
void poll();
static void WiFiEvent(WiFiEvent_t event);
void initEth();
IPAddress localIP();
};
#endif

View File

@@ -0,0 +1,114 @@
#include "Arduino.h"
#include <Touch_GT911.h>
#include <Wire.h>
Touch_GT911::Touch_GT911(uint8_t _sda, uint8_t _scl, uint16_t _width, uint16_t _height) :
pinSda(_sda), pinScl(_scl), width(_width), height(_height) {
}
TPoint::TPoint(void) {
id = x = y = size = 0;
}
TPoint::TPoint(uint8_t _id, uint16_t _x, uint16_t _y, uint16_t _size) {
id = _id;
x = _x;
y = _y;
size = _size;
}
bool TPoint::operator==(TPoint point) {
return ((point.x == x) && (point.y == y) && (point.size == size));
}
bool TPoint::operator!=(TPoint point) {
return ((point.x != x) || (point.y != y) || (point.size != size));
}
void Touch_GT911::begin(uint8_t _addr) {
addr = _addr;
Wire.begin(pinSda, pinScl);
}
void Touch_GT911::calculateChecksum() {
uint8_t checksum;
for (uint8_t i=0; i<GT911_CONFIG_SIZE; i++) {
checksum += configBuf[i];
}
checksum = (~checksum) + 1;
configBuf[GT911_CONFIG_CHKSUM - GT911_CONFIG_START] = checksum;
}
void Touch_GT911::reConfig() {
calculateChecksum();
writeByteData(GT911_CONFIG_CHKSUM, configBuf[GT911_CONFIG_CHKSUM-GT911_CONFIG_START]);
writeByteData(GT911_CONFIG_FRESH, 1);
}
void Touch_GT911::setResolution(uint16_t _width, uint16_t _height) {
configBuf[GT911_X_OUTPUT_MAX_LOW - GT911_CONFIG_START] = lowByte(_width);
configBuf[GT911_X_OUTPUT_MAX_HIGH - GT911_CONFIG_START] = highByte(_width);
configBuf[GT911_Y_OUTPUT_MAX_LOW - GT911_CONFIG_START] = lowByte(_height);
configBuf[GT911_Y_OUTPUT_MAX_HIGH - GT911_CONFIG_START] = highByte(_height);
reConfig();
}
void Touch_GT911::read(void) {
uint8_t data[7];
uint8_t id;
uint16_t x, y, size;
uint8_t pointInfo = readByteData(GT911_POINT_INFO);
uint8_t bufferStatus = pointInfo >> 7 & 1;
uint8_t proximityValid = pointInfo >> 5 & 1;
uint8_t haveKey = pointInfo >> 4 & 1;
isLargeDetect = pointInfo >> 6 & 1;
touches = pointInfo & 0xF;
isTouched = touches > 0;
if (bufferStatus == 1 && isTouched) {
for (uint8_t i=0; i<touches; i++) {
readBlockData(data, GT911_POINT_1 + i * 8, 7);
points[i] = readPoint(data);
}
}
writeByteData(GT911_POINT_INFO, 0);
}
TPoint Touch_GT911::readPoint(uint8_t *data) {
uint16_t temp;
uint8_t id = data[0];
uint16_t x = data[1] + (data[2] << 8);
uint16_t y = data[3] + (data[4] << 8);
uint16_t size = data[5] + (data[6] << 8);
x = width - x;
y = height - y;
return TPoint(id, x, y, size);
}
void Touch_GT911::writeByteData(uint16_t reg, uint8_t val) {
Wire.beginTransmission(addr);
Wire.write(highByte(reg));
Wire.write(lowByte(reg));
Wire.write(val);
Wire.endTransmission();
}
uint8_t Touch_GT911::readByteData(uint16_t reg) {
uint8_t x;
Wire.beginTransmission(addr);
Wire.write(highByte(reg));
Wire.write(lowByte(reg));
Wire.endTransmission();
Wire.requestFrom(addr, (uint8_t)1);
x = Wire.read();
return x;
}
void Touch_GT911::writeBlockData(uint16_t reg, uint8_t *val, uint8_t size) {
Wire.beginTransmission(addr);
Wire.write(highByte(reg));
Wire.write(lowByte(reg));
// Wire.write(val, size);
for (uint8_t i=0; i<size; i++) {
Wire.write(val[i]);
}
Wire.endTransmission();
}
void Touch_GT911::readBlockData(uint8_t *buf, uint16_t reg, uint8_t size) {
Wire.beginTransmission(addr);
Wire.write(highByte(reg));
Wire.write(lowByte(reg));
Wire.endTransmission();
Wire.requestFrom(addr, size);
for (uint8_t i=0; i<size; i++) {
buf[i] = Wire.read();
}
}

View File

@@ -0,0 +1,116 @@
#ifndef Touch_GT911_H
#define Touch_GT911_H
#include "Arduino.h"
#include <Wire.h>
#define GT911_ADDR1 (uint8_t)0x5D
#define GT911_ADDR2 (uint8_t)0x14
// Real-time command (Write only)
#define GT911_COMMAND (uint16_t)0x8040
#define GT911_ESD_CHECK (uint16_t)0x8041
#define GT911_COMMAND_CHECK (uint16_t)0x8046
#define GT911_STRETCH_R0 (uint16_t)0x805E
#define GT911_STRETCH_R1 (uint16_t)0x805F
#define GT911_STRETCH_R2 (uint16_t)0x8060
#define GT911_STRETCH_RM (uint16_t)0x8061
#define GT911_DRV_GROUPA_NUM (uint16_t)0x8062
#define GT911_CONFIG_START (uint16_t)0x8047
#define GT911_CONFIG_VERSION (uint16_t)0x8047
#define GT911_X_OUTPUT_MAX_LOW (uint16_t)0x8048
#define GT911_X_OUTPUT_MAX_HIGH (uint16_t)0x8049
#define GT911_Y_OUTPUT_MAX_LOW (uint16_t)0x804A
#define GT911_Y_OUTPUT_MAX_HIGH (uint16_t)0x804B
#define GT911_TOUCH_NUMBER (uint16_t)0x804C
#define GT911_MODULE_SWITCH_1 (uint16_t)0x804D
#define GT911_MODULE_SWITCH_2 (uint16_t)0x804E
#define GT911_SHAKE_COUNT (uint16_t)0x804F
#define GT911_FILTER (uint16_t)0x8050
#define GT911_LARGE_TOUCH (uint16_t)0x8051
#define GT911_NOISE_REDUCTION (uint16_t)0x8052
#define GT911_SCREEN_TOUCH_LEVEL (uint16_t)0x8053
#define GT911_SCREEN_RELEASE_LEVEL (uint16_t)0x8054
#define GT911_LOW_POWER_CONTROL (uint16_t)0x8055
#define GT911_REFRESH_RATE (uint16_t)0x8056
#define GT911_X_THRESHOLD (uint16_t)0x8057
#define GT911_Y_THRESHOLD (uint16_t)0x8058
#define GT911_SPACE_TOP_BOTTOM (uint16_t)0x805B
#define GT911_PANEL_TX_GAIN (uint16_t)0x806B
#define GT911_PANEL_RX_GAIN (uint16_t)0x806C
#define GT911_PANEL_DUMP_SHIFT (uint16_t)0x806D
#define GT911_DRV_FRAME_CONTROL (uint16_t)0x806E
#define GT911_CHARGING_LEVEL_UP (uint16_t)0x806F
#define GT911_MODULE_SWITCH3 (uint16_t)0x8070
#define GT911_GESTURE_DIS (uint16_t)0X8071
#define GT911_GESTURE_LONG_PRESS_TIME (uint16_t)0x8072
#define GT911_X_Y_SLOPE_ADJUST (uint16_t)0X8073
#define GT911_GESTURE_CONTROL (uint16_t)0X8074
#define GT911_GESTURE_SWITCH1 (uint16_t)0X8075
#define GT911_GESTURE_SWITCH2 (uint16_t)0X8076
#define GT911_GESTURE_REFRESH_RATE (uint16_t)0x8077
#define GT911_GESTURE_TOUCH_LEVEL (uint16_t)0x8078
#define GT911_NEWGREENWAKEUPLEVEL (uint16_t)0x8079
#define GT911_FREQ_HOPPING_START (uint16_t)0x807A
#define GT911_CONFIG_CHKSUM (uint16_t)0X80FF
#define GT911_CONFIG_FRESH (uint16_t)0X8100
#define GT911_CONFIG_SIZE (uint16_t)0xFF-0x46
// Coordinate information
#define GT911_PRODUCT_ID (uint16_t)0X8140
#define GT911_FIRMWARE_VERSION (uint16_t)0X8140
#define GT911_RESOLUTION (uint16_t)0X8140
#define GT911_VENDOR_ID (uint16_t)0X8140
#define GT911_IMFORMATION (uint16_t)0X8140
#define GT911_POINT_INFO (uint16_t)0X814E
#define GT911_POINT_1 (uint16_t)0X814F
#define GT911_POINT_2 (uint16_t)0X8157
#define GT911_POINT_3 (uint16_t)0X815F
#define GT911_POINT_4 (uint16_t)0X8167
#define GT911_POINT_5 (uint16_t)0X816F
#define GT911_POINTS_REG {GT911_POINT_1, GT911_POINT_2, GT911_POINT_3, GT911_POINT_4, GT911_POINT_5}
class TPoint {
public:
TPoint(void);
TPoint(uint8_t id, uint16_t x, uint16_t y, uint16_t size);
bool operator==(TPoint);
bool operator!=(TPoint);
uint8_t id;
uint16_t x;
uint16_t y;
uint8_t size;
};
class Touch_GT911 {
public:
Touch_GT911(uint8_t _sda, uint8_t _scl,uint16_t _width, uint16_t _height);
void begin(uint8_t _addr=GT911_ADDR1);
void setRotation(uint8_t rot);
void setResolution(uint16_t _width, uint16_t _height);
uint8_t getGesture(void);
void read(void);
uint8_t isLargeDetect;
uint8_t touches = 0;
bool isTouched = false;
TPoint points[5];
private:
void calculateChecksum();
void reConfig();
TPoint readPoint(uint8_t *data);
void writeByteData(uint16_t reg, uint8_t val);
uint8_t readByteData(uint16_t reg);
void writeBlockData(uint16_t reg, uint8_t *val, uint8_t size);
void readBlockData(uint8_t *buf, uint16_t reg, uint8_t size);
uint8_t addr;
uint8_t pinSda;
uint8_t pinScl;
uint16_t width;
uint16_t height;
uint8_t configBuf[GT911_CONFIG_SIZE];
};
#endif // Touch_GT911_H

View File

@@ -1,21 +1,20 @@
; Try to not create new configs if possible, if in doubt, please ask
; OTA on the official repo will only be published for mass-produced APs, feel free to experiment on your own fork
[env]
platform = espressif32
; Choose below between platformio/espressif32 (Arduino 2.x) and pioarduino (Arduino 3.x)
platform = platformio/espressif32@^6.10.0
;platform = https://github.com/pioarduino/platform-espressif32/releases/download/stable/platform-espressif32.zip
framework = arduino
lib_deps =
https://github.com/esphome/ESPAsyncWebServer
https://github.com/esphome/AsyncTCP
bblanchon/ArduinoJson@^6.19.4
lib_deps =
ESP32Async/AsyncTCP
ESP32Async/ESPAsyncWebServer
bblanchon/ArduinoJson
bodmer/TFT_eSPI
https://github.com/Bodmer/TJpg_Decoder.git
https://github.com/nlimper/shoddyxml2
https://github.com/nlimper/shoddyxml2#d19a4e5
https://github.com/nlimper/QRCodeGenerator
fastled/FastLED@3.7.8
fastled/FastLED@3.9.13
https://github.com/MajenkoLibraries/SoftSPI
platform_packages =
platformio/framework-arduinoespressif32 @ 3.20014.231204
board_build.filesystem = littlefs
monitor_filters = esp32_exception_decoder
monitor_speed = 115200
@@ -30,82 +29,9 @@ build_flags =
-D DISABLE_ALL_LIBRARY_WARNINGS
-D ILI9341_DRIVER
-D SMOOTH_FONT
upload_port = COM26
monitor_port = COM26
; ----------------------------------------------------------------------------------------
; !!! this configuration expects the Mini_AP
; ----------------------------------------------------------------------------------------
[env:OpenEPaperLink_Mini_AP]
platform = https://github.com/platformio/platform-espressif32.git
board=lolin_s2_mini
board_build.partitions = default.csv
build_unflags =
-std=gnu++11
-D CONFIG_MBEDTLS_INTERNAL_MEM_ALLOC=y
build_flags =
-std=gnu++17
${env.build_flags}
-D OPENEPAPERLINK_MINI_AP_PCB
-D ARDUINO_USB_MODE=0
-D CONFIG_SPIRAM_USE_MALLOC=1
-D CONFIG_MBEDTLS_EXTERNAL_MEM_ALLOC=y
-D HAS_RGB_LED
-D BOARD_HAS_PSRAM
-D SAVE_SPACE
-D POWER_NO_SOFT_POWER
-D FLASHER_AP_SS=11
-D FLASHER_AP_CLK=9
-D FLASHER_AP_MOSI=10
-D FLASHER_AP_MISO=8
-D FLASHER_AP_RESET=13
-D FLASHER_AP_POWER={-1} ;this board has no soft power control
-D FLASHER_AP_TXD=7
-D FLASHER_AP_RXD=6
-D FLASHER_AP_TEST=12
-D FLASHER_LED=15
-D FLASHER_RGB_LED=33
build_src_filter =
+<*>-<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
board_upload.flash_size = 4MB
; ----------------------------------------------------------------------------------------
; !!! this configuration expects the Nano_AP
; ----------------------------------------------------------------------------------------
[env:OpenEPaperLink_Nano_AP]
platform = https://github.com/platformio/platform-espressif32.git
board=lolin_s2_mini
board_build.partitions = default.csv
build_unflags =
-std=gnu++11
-D CONFIG_MBEDTLS_INTERNAL_MEM_ALLOC=y
build_flags =
-std=gnu++17
${env.build_flags}
-D OPENEPAPERLINK_NANO_AP_PCB
-D ARDUINO_USB_MODE=0
-D CONFIG_SPIRAM_USE_MALLOC=1
-D CONFIG_MBEDTLS_EXTERNAL_MEM_ALLOC=y
-D BOARD_HAS_PSRAM
-D SAVE_SPACE
-D FLASHER_AP_SS=38
-D FLASHER_AP_CLK=40
-D FLASHER_AP_MOSI=39
-D FLASHER_AP_MISO=33
-D FLASHER_AP_RESET=37
-D FLASHER_AP_POWER={16,17,18,21}
-D FLASHER_AP_TXD=35
-D FLASHER_AP_RXD=34
-D FLASHER_AP_TEST=36
-D FLASHER_LED=15
-D FLASHER_RGB_LED=-1
build_src_filter =
+<*>-<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
board_upload.flash_size = 4MB
-D LOAD_FONT2
-D LOAD_GLCD
-D ARDUINOJSON_ENABLE_COMMENTS=1
; ----------------------------------------------------------------------------------------
; !!! this configuration expects the 16MB Flash / 8MB Ram version of the ESP32-S3-DevkitC1
; ----------------------------------------------------------------------------------------
@@ -117,7 +43,7 @@ build_unflags =
-std=gnu++11
-D ARDUINO_USB_MODE=1
-D CONFIG_MBEDTLS_INTERNAL_MEM_ALLOC=y
build_flags =
build_flags =
-std=gnu++17
${env.build_flags}
-D HAS_EXT_FLASHER
@@ -136,7 +62,7 @@ build_flags =
-D FLASHER_AP_MOSI=7
-D FLASHER_AP_MISO=6
-D FLASHER_AP_RESET=15
-D FLASHER_AP_POWER={0}
-D FLASHER_AP_POWER={0}
-D FLASHER_AP_TXD=16
-D FLASHER_AP_RXD=18
-D FLASHER_AP_TEST=17
@@ -160,7 +86,7 @@ build_flags =
-D FLASHER_ALT_TEST=13
-D FLASHER_LED=21
-D FLASHER_RGB_LED=48
build_src_filter =
build_src_filter =
+<*>-<espflasher.cpp>
board_build.flash_mode=qio
board_build.arduino.memory_type = qio_opi
@@ -169,32 +95,6 @@ board_upload.maximum_size = 16777216
board_upload.maximum_ram_size = 327680
board_upload.flash_size = 16MB
; ----------------------------------------------------------------------------------------
; !!! this configuration expects an esp32 (depricated)
; ----------------------------------------------------------------------------------------
[env:Simple_AP]
board = esp32dev
board_build.partitions = default.csv
build_unflags =
-std=gnu++11
build_flags =
-std=gnu++17
${env.build_flags}
-D CORE_DEBUG_LEVEL=0
-D SIMPLE_AP
-D SAVE_SPACE
-D FLASHER_AP_SS=5
-D FLASHER_AP_CLK=18
-D FLASHER_AP_MOSI=23
-D FLASHER_AP_MISO=19
-D FLASHER_AP_RESET=2
-D FLASHER_AP_POWER={13,15}
-D FLASHER_AP_TEST=-1
-D FLASHER_AP_TXD=17
-D FLASHER_AP_RXD=16
-D FLASHER_LED=22
build_src_filter =
+<*>-<usbflasher.cpp>-<swd.cpp>-<espflasher.cpp>-<ips_display.cpp>-<webflasher.cpp>
; ----------------------------------------------------------------------------------------
; !!! this configuration expects an ESP32-S3 16MB Flash 8MB RAM
; ----------------------------------------------------------------------------------------
[env:ESP32_S3_16_8_YELLOW_AP]
@@ -202,12 +102,12 @@ board = esp32-s3-devkitc-1
board_build.partitions = large_spiffs_16MB.csv
build_unflags =
-std=gnu++11
-D ARDUINO_USB_MODE=1
-D CONFIG_MBEDTLS_INTERNAL_MEM_ALLOC=y
-D ILI9341_DRIVER
-D ARDUINO_USB_MODE=1
-D CONFIG_MBEDTLS_INTERNAL_MEM_ALLOC=y
-D ILI9341_DRIVER
lib_deps =
${env.lib_deps}
build_flags =
${env.lib_deps}
build_flags =
-std=gnu++17
${env.build_flags}
-D HAS_TFT
@@ -245,14 +145,13 @@ build_flags =
-D TFT_RST=1
-D TFT_RGB_ORDER=TFT_BGR
-D USE_HSPI_PORT
-D LOAD_FONT2
-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
-D HAS_SUBGHZ
build_src_filter =
build_src_filter =
+<*>-<usbflasher.cpp>-<swd.cpp>-<webflasher.cpp>
board_build.flash_mode=qio
board_build.arduino.memory_type = qio_opi
@@ -270,45 +169,52 @@ monitor_dtr = 0
monitor_rts = 0
build_unflags =
-std=gnu++11
-D CONFIG_MBEDTLS_INTERNAL_MEM_ALLOC=y
;-D ILI9341_DRIVER
-D CONFIG_MBEDTLS_INTERNAL_MEM_ALLOC=y
;-D ILI9341_DRIVER
lib_deps = ${env.lib_deps}
lib_extra_dirs = lib2/Arduino_GFX-1.3.7
build_flags =
build_flags =
-std=gnu++17
${env.build_flags}
-D HAS_TFT
-D HAS_LILYGO_TPANEL
-D CORE_DEBUG_LEVEL=1
-D ARDUINO_USB_CDC_ON_BOOT=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 CONFIG_MBEDTLS_EXTERNAL_MEM_ALLOC=y
-D HAS_BLE_WRITER
-D FLASHER_AP_SS=-1
-D FLASHER_AP_SS=-1
-D FLASHER_AP_CLK=-1
-D FLASHER_AP_MOSI=-1
-D FLASHER_AP_MISO=-1
-D FLASHER_AP_RESET=34
-D FLASHER_AP_POWER={-1}
-D FLASHER_AP_TEST=-1
; NB: FLASHER_DEBUG_TXD and FLASHER_DEBUG_RXD use the same pins as
; FLASHER_AP_TXD and FLASHER_AP_RXD but the naming convention is different
-D FLASHER_DEBUG_SHARED
-D FLASHER_DEBUG_PORT=1
-D FLASHER_AP_TXD=48
-D FLASHER_DEBUG_RXD=48
-D FLASHER_AP_RXD=47
-D FLASHER_DEBUG_TXD=43
-D FLASHER_DEBUG_RXD=44
-D FLASHER_DEBUG_TXD=47
;
-D FLASHER_DEBUG_PROG=33
-D FLASHER_LED=-1
-D TFT_HEIGHT=480
-D TFT_HW_TYPE=226
-D USE_HSPI_PORT
-D LOAD_FONT2
-D MD5_ENABLED=1
-D SERIAL_FLASHER_INTERFACE_UART=1
-D SERIAL_FLASHER_BOOT_HOLD_TIME_MS=200
-D SERIAL_FLASHER_RESET_HOLD_TIME_MS=200
-D HAS_H2
-D C6_OTA_FLASHING
-D HAS_SUBGHZ
build_src_filter =
build_src_filter =
+<*>-<usbflasher.cpp>-<swd.cpp>-<webflasher.cpp>
board_build.flash_mode=qio
board_build.arduino.memory_type = qio_qspi ;Enable external PSRAM
@@ -319,20 +225,83 @@ board_upload.flash_size = 16MB
; ----------------------------------------------------------------------------------------
; !!! this configuration expects an ESP32-S3 16MB Flash 8MB RAM
; ----------------------------------------------------------------------------------------
[env:ESP32_S3_16_8_4inch_AP]
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
lib_deps = ${env.lib_deps}
lib_extra_dirs = lib2/Arduino_GFX-1.3.7
lib2/gt911-touch
build_flags =
-std=gnu++17
${env.build_flags}
-D HAS_TFT
-D HAS_4inch_TPANEL
-D HAS_GT911_TOUCH
-D CORE_DEBUG_LEVEL=1
-D CONFIG_ESP32S3_SPIRAM_SUPPORT=1
-D CONFIG_SPIRAM_USE_MALLOC=1
-D POWER_NO_SOFT_POWER
-D BOARD_HAS_PSRAM
-D CONFIG_MBEDTLS_EXTERNAL_MEM_ALLOC=y
-D HAS_BLE_WRITER
-D FLASHER_AP_SS=-1
-D FLASHER_AP_CLK=-1
-D FLASHER_AP_MOSI=-1
-D FLASHER_AP_MISO=-1
-D FLASHER_AP_RESET=1
-D FLASHER_AP_POWER={-1}
-D FLASHER_AP_TEST=-1
; NB: FLASHER_DEBUG_TXD and FLASHER_DEBUG_RXD use the same pins as
; FLASHER_AP_TXD and FLASHER_AP_RXD but the naming convention is different
-D FLASHER_DEBUG_SHARED
-D FLASHER_DEBUG_PORT=1
-D FLASHER_AP_TXD=40
-D FLASHER_AP_RXD=44
-D FLASHER_DEBUG_TXD=44
-D FLASHER_DEBUG_RXD=40
-D FLASHER_DEBUG_PROG=2
-D FLASHER_LED=-1
; In this case we only got one TX Pin and no RX so lets only use that.
-D UART_LOGGING_TX_ONLY_PIN=43
-D TFT_HEIGHT=480
-D TFT_HW_TYPE=226
-D USE_HSPI_PORT
-D MD5_ENABLED=1
-D SERIAL_FLASHER_INTERFACE_UART=1
-D SERIAL_FLASHER_BOOT_HOLD_TIME_MS=200
-D SERIAL_FLASHER_RESET_HOLD_TIME_MS=200
-D C6_OTA_FLASHING
-D HAS_SUBGHZ
build_src_filter =
+<*>-<usbflasher.cpp>-<swd.cpp>-<webflasher.cpp>
board_build.flash_mode=qio
board_build.arduino.memory_type = qio_opi ;Enable external PSRAM
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 ESP32-S3 16MB Flash 8MB RAM
; ----------------------------------------------------------------------------------------
[env:ESP32_S3_C6_BIG_AP]
board = esp32-s3-devkitc-1
board_build.partitions = large_spiffs_16MB.csv
build_unflags =
-std=gnu++11
-D CONFIG_MBEDTLS_INTERNAL_MEM_ALLOC=y
-D CONFIG_MBEDTLS_INTERNAL_MEM_ALLOC=y
lib_deps =
${env.lib_deps}
${env.lib_deps}
build_flags =
-std=gnu++17
${env.build_flags}
-D HAS_BLE_WRITER
-D CORE_DEBUG_LEVEL=0
-D ARDUINO_USB_CDC_ON_BOOT
-D ARDUINO_USB_CDC_ON_BOOT
-D CONFIG_ESP32S3_SPIRAM_SUPPORT=1
-D CONFIG_SPIRAM_USE_MALLOC=1
-D POWER_NO_SOFT_POWER
@@ -357,7 +326,7 @@ build_flags =
-D SERIAL_FLASHER_BOOT_HOLD_TIME_MS=50
-D SERIAL_FLASHER_RESET_HOLD_TIME_MS=100
-D C6_OTA_FLASHING
build_src_filter =
build_src_filter =
+<*>-<usbflasher.cpp>-<swd.cpp>-<webflasher.cpp>
board_build.flash_mode=qio
board_build.arduino.memory_type = qio_opi
@@ -373,15 +342,15 @@ board = esp32-s3-devkitc-1
board_build.partitions = large_spiffs_16MB.csv
build_unflags =
-std=gnu++11
-D CONFIG_MBEDTLS_INTERNAL_MEM_ALLOC=y
-D CONFIG_MBEDTLS_INTERNAL_MEM_ALLOC=y
lib_deps =
${env.lib_deps}
${env.lib_deps}
build_flags =
-std=gnu++17
${env.build_flags}
-D HAS_BLE_WRITER
-D CORE_DEBUG_LEVEL=0
-D ARDUINO_USB_CDC_ON_BOOT
-D ARDUINO_USB_CDC_ON_BOOT
-D CONFIG_ESP32S3_SPIRAM_SUPPORT=1
-D CONFIG_SPIRAM_USE_MALLOC=1
-D POWER_NO_SOFT_POWER
@@ -406,7 +375,7 @@ build_flags =
-D SERIAL_FLASHER_RESET_HOLD_TIME_MS=100
-D C6_OTA_FLASHING
-D HAS_SUBGHZ
build_src_filter =
build_src_filter =
+<*>-<usbflasher.cpp>-<swd.cpp>-<webflasher.cpp>
board_build.flash_mode=qio
board_build.arduino.memory_type = qio_opi
@@ -422,15 +391,15 @@ board = esp32-s3-devkitc-1
board_build.partitions = large_spiffs_16MB.csv
build_unflags =
-std=gnu++11
-D CONFIG_MBEDTLS_INTERNAL_MEM_ALLOC=y
-D CONFIG_MBEDTLS_INTERNAL_MEM_ALLOC=y
lib_deps =
${env.lib_deps}
${env.lib_deps}
build_flags =
-std=gnu++17
${env.build_flags}
-D HAS_BLE_WRITER
-D CORE_DEBUG_LEVEL=0
-D ARDUINO_USB_CDC_ON_BOOT
-D ARDUINO_USB_CDC_ON_BOOT
-D CONFIG_ESP32S3_SPIRAM_SUPPORT=1
-D CONFIG_SPIRAM_USE_MALLOC=1
-D POWER_NO_SOFT_POWER
@@ -457,7 +426,7 @@ build_flags =
-D SERIAL_FLASHER_RESET_HOLD_TIME_MS=100
-D C6_OTA_FLASHING
-D HAS_SUBGHZ
build_src_filter =
build_src_filter =
+<*>-<usbflasher.cpp>-<swd.cpp>-<webflasher.cpp>
board_build.flash_mode=qio
board_build.arduino.memory_type = qio_opi
@@ -474,16 +443,16 @@ board = esp32-s3-devkitc-1
board_build.partitions = large_spiffs_16MB.csv
build_unflags =
-std=gnu++11
-D CONFIG_MBEDTLS_INTERNAL_MEM_ALLOC=y
-D CONFIG_MBEDTLS_INTERNAL_MEM_ALLOC=y
-D ILI9341_DRIVER
lib_deps =
${env.lib_deps}
${env.lib_deps}
build_flags =
-std=gnu++17
${env.build_flags}
-D HAS_TFT
-D CORE_DEBUG_LEVEL=1
-D ARDUINO_USB_CDC_ON_BOOT=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
@@ -518,15 +487,12 @@ build_flags =
-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=200
-D SERIAL_FLASHER_RESET_HOLD_TIME_MS=200
-D C6_OTA_FLASHING
-D HAS_SUBGHZ
-D HAS_SUBGHZ
build_src_filter =
+<*>-<usbflasher.cpp>-<swd.cpp>-<webflasher.cpp>
board_build.flash_mode=qio
@@ -543,16 +509,16 @@ board = esp32-s3-devkitc-1
board_build.partitions = large_spiffs_16MB.csv
build_unflags =
-std=gnu++11
-D CONFIG_MBEDTLS_INTERNAL_MEM_ALLOC=y
-D CONFIG_MBEDTLS_INTERNAL_MEM_ALLOC=y
-D ILI9341_DRIVER
lib_deps =
${env.lib_deps}
${env.lib_deps}
build_flags =
-std=gnu++17
${env.build_flags}
-D HAS_TFT
-D CORE_DEBUG_LEVEL=1
-D ARDUINO_USB_CDC_ON_BOOT=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
@@ -587,15 +553,12 @@ build_flags =
-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=200
-D SERIAL_FLASHER_RESET_HOLD_TIME_MS=200
-D C6_OTA_FLASHING
-D HAS_SUBGHZ
-D HAS_SUBGHZ
build_src_filter =
+<*>-<usbflasher.cpp>-<swd.cpp>-<webflasher.cpp>
board_build.flash_mode=qio
@@ -612,10 +575,10 @@ board = esp32-s3-devkitc-1
board_build.partitions = large_spiffs_16MB.csv
build_unflags =
-std=gnu++11
-D ARDUINO_USB_MODE=1
-D CONFIG_MBEDTLS_INTERNAL_MEM_ALLOC=y
-D ARDUINO_USB_MODE=1
-D CONFIG_MBEDTLS_INTERNAL_MEM_ALLOC=y
lib_deps =
${env.lib_deps}
${env.lib_deps}
build_flags =
-std=gnu++17
${env.build_flags}
@@ -643,7 +606,7 @@ build_flags =
-D HAS_RGB_LED
-D FLASHER_RGB_LED=48
-D BLE_ONLY
build_src_filter =
build_src_filter =
+<*>-<usbflasher.cpp>-<swd.cpp>-<webflasher.cpp>-<espflasher.cpp>
board_build.flash_mode=qio
board_build.arduino.memory_type = qio_opi
@@ -662,7 +625,7 @@ build_unflags =
-D CONFIG_MBEDTLS_INTERNAL_MEM_ALLOC=y
-std=gnu++11
lib_deps =
${env.lib_deps}
${env.lib_deps}
build_flags =
-std=gnu++17
${env.build_flags}
@@ -689,7 +652,7 @@ build_flags =
-D SD_CARD_MISO=36
-D SD_CARD_MOSI=14
-D SD_CARD_SS=12
build_src_filter =
build_src_filter =
+<*>-<usbflasher.cpp>-<swd.cpp>-<espflasher.cpp>-<webflasher.cpp>
board_build.flash_mode=qio
board_upload.maximum_size = 16777216
@@ -705,17 +668,17 @@ monitor_dtr = 0
monitor_rts = 0
build_unflags =
-std=gnu++11
-D CONFIG_MBEDTLS_INTERNAL_MEM_ALLOC=y
-D CONFIG_MBEDTLS_INTERNAL_MEM_ALLOC=y
-D ILI9341_DRIVER
lib_deps =
${env.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 ARDUINO_USB_CDC_ON_BOOT=1
-D CONFIG_ESP32S3_SPIRAM_SUPPORT=1
-D CONFIG_SPIRAM_USE_MALLOC=1
-D POWER_NO_SOFT_POWER
@@ -770,15 +733,12 @@ build_flags =
-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=200
-D SERIAL_FLASHER_RESET_HOLD_TIME_MS=200
-D C6_OTA_FLASHING
-D HAS_SUBGHZ
-D HAS_SUBGHZ
build_src_filter =
+<*>
board_build.flash_mode=qio
@@ -787,41 +747,60 @@ 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 ESP32-S3 4MB Flash 2MB RAM aka The Official Espressif Zigbee Gateway PCB
; !!! this configuration expects an ESP32-S3 16MB Flash 8MB RAM
; ----------------------------------------------------------------------------------------
[env:ESP_THREAD_BORDER_ROUTER]
[env:ESP32_S3_16_8_ELECROW_ADV_2_8]
board = esp32-s3-devkitc-1
board_build.partitions = default.csv
board_build.partitions = large_spiffs_16MB.csv
build_unflags =
-std=gnu++11
-D CONFIG_MBEDTLS_INTERNAL_MEM_ALLOC=y
-D ARDUINO_USB_MODE=1
-D CONFIG_MBEDTLS_INTERNAL_MEM_ALLOC=y
-D ILI9341_DRIVER
lib_deps =
${env.lib_deps}
${env.lib_deps}
build_flags =
-std=gnu++17
${env.build_flags}
;-D HAS_BLE_WRITER
-D HAS_TFT
-D HAS_ELECROW_ADV_2_8
-D HAS_ELECROW_C6
-D CORE_DEBUG_LEVEL=0
-D ARDUINO_USB_CDC_ON_BOOT
-D ARDUINO_USB_MODE=0
-D CONFIG_ESP32S3_SPIRAM_SUPPORT=1
-D CONFIG_SPIRAM_USE_MALLOC=1
-D POWER_NO_SOFT_POWER
-D BOARD_HAS_PSRAM
-D CONFIG_MBEDTLS_EXTERNAL_MEM_ALLOC=y
-D HAS_BLE_WRITER
-D FLASHER_AP_SS=-1
-D FLASHER_AP_CLK=-1
-D FLASHER_AP_MOSI=-1
-D FLASHER_AP_MISO=-1
-D FLASHER_AP_RESET=7
-D FLASHER_AP_RESET=46
-D FLASHER_AP_POWER={-1}
-D FLASHER_AP_TEST=-1
-D FLASHER_AP_TXD=4
-D FLASHER_AP_RXD=5
-D FLASHER_DEBUG_TXD=17
-D FLASHER_DEBUG_RXD=18
-D FLASHER_DEBUG_PROG=8
-D FLASHER_AP_TXD=9
-D FLASHER_AP_RXD=10
-D FLASHER_DEBUG_TXD=2
-D FLASHER_DEBUG_RXD=1
-D FLASHER_DEBUG_PROG=3
-D FLASHER_LED=-1
-D ST7789_DRIVER
-D TFT_WIDTH=240
-D TFT_HEIGHT=320
-D TFT_HW_TYPE=229
-D TFT_MISO=-1
-D TFT_MOSI=39
-D TFT_SCLK=42
-D TFT_CS=40
-D TFT_DC=41
-D TFT_RST=-1
-D TFT_RGB_ORDER=TFT_BGR
-D USE_HSPI_PORT
-D LOAD_FONT2
-D MD5_ENABLED=1
-D SERIAL_FLASHER_INTERFACE_UART=1
-D SERIAL_FLASHER_BOOT_HOLD_TIME_MS=50
@@ -829,7 +808,65 @@ build_flags =
-D C6_OTA_FLASHING
build_src_filter =
+<*>-<usbflasher.cpp>-<swd.cpp>-<webflasher.cpp>
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 ESP32-S3 4MB Flash 8MB RAM
; Hardware is https://www.olimex.com/Products/IoT/ESP32/ESP32-POE-ISO/open-source-hardware
; with ESP32-C6-WROOM-1U modem
; all data is stored on SDCARD only
; ----------------------------------------------------------------------------------------
[env:OpenEPaperLink_ESP32-PoE-ISO_AP]
board=esp32-poe-iso
board_build.partitions = esp32_sdcard_only.csv
build_unflags =
-D CONFIG_MBEDTLS_INTERNAL_MEM_ALLOC=y
-std=gnu++11
lib_deps =
${env.lib_deps}
build_flags =
-std=gnu++17
${env.build_flags}
-D OPENEPAPERLINK_MINI_AP_PCB
-D CONFIG_SPIRAM_USE_MALLOC=1
-D CONFIG_MBEDTLS_EXTERNAL_MEM_ALLOC=y
-D BOARD_HAS_PSRAM
-mfix-esp32-psram-cache-issue
-D HAS_SDCARD
-D SD_CARD_SDMMC
-D SD_CARD_ONLY
-D POWER_NO_SOFT_POWER
-D FLASHER_AP_SS=-1
-D FLASHER_AP_CLK=-1
-D FLASHER_AP_MOSI=-1
-D FLASHER_AP_MISO=-1
-D FLASHER_AP_RESET=5
-D FLASHER_AP_POWER={-1} ;this board has no soft power control
-D FLASHER_AP_TXD=4
-D FLASHER_AP_RXD=36
-D FLASHER_AP_TEST=-1
-D FLASHER_LED=-1
-D FLASHER_DEBUG_TXD=32
-D FLASHER_DEBUG_RXD=33
-D FLASHER_DEBUG_PROG=13
-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
-D ETHERNET_PHY_POWER=12
-D ETHERNET_CLK_MODE=ETH_CLOCK_GPIO0_OUT
-D ETHERNET_PHY_MDC=ETH_PHY_MDC
-D ETHERNET_PHY_MDIO=ETH_PHY_MDIO
-D ETHERNET_PHY_TYPE=ETH_PHY_TYPE
build_src_filter =
+<*>-<usbflasher.cpp>-<swd.cpp>
board_build.flash_mode=qio
board_upload.maximum_size = 4194304
board_upload.maximum_ram_size = 327680
board_upload.flash_size = 4MB
@@ -841,7 +878,7 @@ board_upload.flash_size = 4MB
;board_build.partitions = default.csv
;build_unflags =
; -std=gnu++11
;build_flags =
;build_flags =
; -std=gnu++17
; ${env.build_flags}
; -D CORE_DEBUG_LEVEL=0
@@ -858,7 +895,7 @@ board_upload.flash_size = 4MB
; -D FLASHER_AP_TXD=19
; -D FLASHER_AP_RXD=23
; -D FLASHER_LED=2
;build_src_filter =
;build_src_filter =
; +<*>-<usbflasher.cpp>-<swd.cpp>-<espflasher.cpp>
;board_build.psram_type=qspi_opi
;board_upload.maximum_size = 4194304
@@ -874,7 +911,7 @@ board_upload.flash_size = 4MB
;build_unflags =
; -std=gnu++11
; -D CONFIG_MBEDTLS_INTERNAL_MEM_ALLOC=y
;build_flags =
;build_flags =
; -std=gnu++17
; ${env.build_flags}
; -D OPENEPAPERLINK_MINI_AP_PCB
@@ -894,7 +931,7 @@ board_upload.flash_size = 4MB
; -D FLASHER_AP_TEST=-1
; -D FLASHER_LED=2
; -D FLASHER_RGB_LED=-1
;build_src_filter =
;build_src_filter =
; +<*>-<usbflasher.cpp>-<swd.cpp>-<espflasher.cpp>
;board_build.psram_type=qspi_opi
;board_upload.maximum_size = 4194304
@@ -910,7 +947,7 @@ board_upload.flash_size = 4MB
; -std=gnu++11
; -D ARDUINO_USB_MODE=1
; -D CONFIG_MBEDTLS_INTERNAL_MEM_ALLOC=y
;build_flags =
;build_flags =
; -std=gnu++17
; ${env.build_flags}
; -D OutdoorAP
@@ -933,7 +970,7 @@ board_upload.flash_size = 4MB
; -D FLASHER_AP_RXD=18
; -D FLASHER_LED=21
; -D FLASHER_RGB_LED=38
;build_src_filter =
;build_src_filter =
; +<*>-<usbflasher.cpp>-<swd.cpp>-<espflasher.cpp>
;board_build.flash_mode=opi
;board_build.arduino.memory_type = opi_opi
@@ -941,7 +978,7 @@ board_upload.flash_size = 4MB
;board_upload.maximum_size = 16777216
;board_upload.maximum_ram_size = 327680
;board_upload.flash_size = 32MB
#upload_flags = --no-stub
;#upload_flags = --no-stub
; ----------------------------------------------------------------------------------------
; !!! this configuration expects an wemos_d1_mini32
; ----------------------------------------------------------------------------------------
@@ -950,7 +987,7 @@ board_upload.flash_size = 4MB
;board_build.partitions = default.csv
;build_unflags =
; -std=gnu++11
;build_flags =
;build_flags =
; -std=gnu++17
; ${env.build_flags}
; -D CORE_DEBUG_LEVEL=0
@@ -965,7 +1002,7 @@ board_upload.flash_size = 4MB
; -D FLASHER_AP_TXD=16
; -D FLASHER_AP_RXD=17
; -D FLASHER_LED=22
;build_src_filter =
;build_src_filter =
; +<*>-<usbflasher.cpp>-<swd.cpp>-<espflasher.cpp>
; ----------------------------------------------------------------------------------------
; !!! this configuration expects an m5stack esp32
@@ -976,7 +1013,7 @@ board_upload.flash_size = 4MB
;board_build.partitions = esp32_sdcard.csv
;build_unflags =
; -std=gnu++11
;build_flags =
;build_flags =
; -std=gnu++17
; ${env.build_flags}
; -D CORE_DEBUG_LEVEL=0
@@ -1002,7 +1039,7 @@ board_upload.flash_size = 4MB
; -D DISABLE_ALL_LIBRARY_WARNINGS
; -D ILI9341_DRIVER
; -D SMOOTH_FONT
;build_src_filter =
;build_src_filter =
; +<*>-<usbflasher.cpp>-<swd.cpp>-<espflasher.cpp>
; ----------------------------------------------------------------------------------------
; !!! this configuration expects the Nano_C6
@@ -1014,7 +1051,7 @@ board_upload.flash_size = 4MB
;build_unflags =
; -std=gnu++11
; -D CONFIG_MBEDTLS_INTERNAL_MEM_ALLOC=y
;build_flags =
;build_flags =
; -std=gnu++17
; ${env.build_flags}
; -D OPENEPAPERLINK_NANO_AP_PCB
@@ -1038,9 +1075,167 @@ board_upload.flash_size = 4MB
; -D SERIAL_FLASHER_INTERFACE_UART=1
; -D SERIAL_FLASHER_BOOT_HOLD_TIME_MS=50
; -D SERIAL_FLASHER_RESET_HOLD_TIME_MS=100
;build_src_filter =
;build_src_filter =
; +<*>-<usbflasher.cpp>-<swd.cpp>-<espflasher.cpp>
;board_build.psram_type=qspi_opi
;board_upload.maximum_size = 4194304
;board_upload.maximum_ram_size = 327680
;board_upload.flash_size = 4MB
; ----------------------------------------------------------------------------------------
; !!! Release 2.75 was the last release that supported ESP32-S2 based APs or ESP32-S3 APs
; !!! with less than 16Mb flash and 8MB RAM.
; ----------------------------------------------------------------------------------------
; ----------------------------------------------------------------------------------------
; !!! this configuration expects the Mini_AP (deprecated)
; ----------------------------------------------------------------------------------------
;[env:OpenEPaperLink_Mini_AP]
;platform = https://github.com/platformio/platform-espressif32.git
;board=lolin_s2_mini
;board_build.partitions = default.csv
;build_unflags =
; -std=gnu++11
; -D CONFIG_MBEDTLS_INTERNAL_MEM_ALLOC=y
;build_flags =
; -std=gnu++17
; ${env.build_flags}
; -D OPENEPAPERLINK_MINI_AP_PCB
; -D ARDUINO_USB_MODE=0
; -D CONFIG_SPIRAM_USE_MALLOC=1
; -D CONFIG_MBEDTLS_EXTERNAL_MEM_ALLOC=y
; -D HAS_RGB_LED
; -D BOARD_HAS_PSRAM
; -D SAVE_SPACE
; -D POWER_NO_SOFT_POWER
; -D FLASHER_AP_SS=11
; -D FLASHER_AP_CLK=9
; -D FLASHER_AP_MOSI=10
; -D FLASHER_AP_MISO=8
; -D FLASHER_AP_RESET=13
; -D FLASHER_AP_POWER={-1} ;this board has no soft power control
; -D FLASHER_AP_TXD=7
; -D FLASHER_AP_RXD=6
; -D FLASHER_AP_TEST=12
; -D FLASHER_LED=15
; -D FLASHER_RGB_LED=33
;build_src_filter =
; +<*>-<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
;board_upload.flash_size = 4MB
; ----------------------------------------------------------------------------------------
; !!! this configuration expects the Nano_AP
; ----------------------------------------------------------------------------------------
;[env:OpenEPaperLink_Nano_AP]
;platform = https://github.com/platformio/platform-espressif32.git
;board=lolin_s2_mini
;board_build.partitions = default.csv
;build_unflags =
; -std=gnu++11
; -D CONFIG_MBEDTLS_INTERNAL_MEM_ALLOC=y
;build_flags =
; -std=gnu++17
; ${env.build_flags}
; -D OPENEPAPERLINK_NANO_AP_PCB
; -D ARDUINO_USB_MODE=0
; -D CONFIG_SPIRAM_USE_MALLOC=1
; -D CONFIG_MBEDTLS_EXTERNAL_MEM_ALLOC=y
; -D BOARD_HAS_PSRAM
; -D SAVE_SPACE
; -D FLASHER_AP_SS=38
; -D FLASHER_AP_CLK=40
; -D FLASHER_AP_MOSI=39
; -D FLASHER_AP_MISO=33
; -D FLASHER_AP_RESET=37
; -D FLASHER_AP_POWER={16,17,18,21}
; -D FLASHER_AP_TXD=35
; -D FLASHER_AP_RXD=34
; -D FLASHER_AP_TEST=36
; -D FLASHER_LED=15
; -D FLASHER_RGB_LED=-1
;build_src_filter =
; +<*>-<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
;board_upload.flash_size = 4MB
;; ----------------------------------------------------------------------------------------
;; !!! this configuration expects an esp32 (deprecated)
;; ----------------------------------------------------------------------------------------
;[env:Simple_AP]
;board = esp32dev
;board_build.partitions = default.csv
;build_unflags =
; -std=gnu++11
;build_flags =
; -std=gnu++17
; ${env.build_flags}
; -D CORE_DEBUG_LEVEL=0
; -D SIMPLE_AP
; -D SAVE_SPACE
; -D FLASHER_AP_SS=5
; -D FLASHER_AP_CLK=18
; -D FLASHER_AP_MOSI=23
; -D FLASHER_AP_MISO=19
; -D FLASHER_AP_RESET=2
; -D FLASHER_AP_POWER={13,15}
; -D FLASHER_AP_TEST=-1
; -D FLASHER_AP_TXD=17
; -D FLASHER_AP_RXD=16
; -D FLASHER_LED=22
;build_src_filter =
; +<*>-<usbflasher.cpp>-<swd.cpp>-<espflasher.cpp>-<ips_display.cpp>-<webflasher.cpp>
;
;; ----------------------------------------------------------------------------------------
;; !!! this configuration expects an ESP32-S3 4MB Flash 2MB RAM aka The Official Espressif Zigbee Gateway PCB (deprecated)
;; ----------------------------------------------------------------------------------------
;[env:ESP_THREAD_BORDER_ROUTER]
;board = esp32-s3-devkitc-1
;board_build.partitions = default.csv
;build_unflags =
; -std=gnu++11
; -D CONFIG_MBEDTLS_INTERNAL_MEM_ALLOC=y
;lib_deps =
; ${env.lib_deps}
;build_flags =
; -std=gnu++17
; ${env.build_flags}
; ;-D HAS_BLE_WRITER
; -D CORE_DEBUG_LEVEL=0
; -D ARDUINO_USB_CDC_ON_BOOT
; -D CONFIG_ESP32S3_SPIRAM_SUPPORT=1
; -D CONFIG_SPIRAM_USE_MALLOC=1
; -D POWER_NO_SOFT_POWER
; -D BOARD_HAS_PSRAM
; -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=7
; -D FLASHER_AP_POWER={-1}
; -D FLASHER_AP_TEST=-1
; -D FLASHER_AP_TXD=4
; -D FLASHER_AP_RXD=5
; -D FLASHER_DEBUG_TXD=17
; -D FLASHER_DEBUG_RXD=18
; -D FLASHER_DEBUG_PROG=8
; -D FLASHER_LED=-1
; -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 =
; +<*>-<usbflasher.cpp>-<swd.cpp>-<webflasher.cpp>
;board_build.psram_type=qspi_opi
;board_upload.maximum_size = 4194304
;board_upload.maximum_ram_size = 327680
;board_upload.flash_size = 4MB
;

View File

@@ -0,0 +1,23 @@
#!/usr/bin/bash
if [[ -d $1 ]]; then
rm -rf $1/*
cp -r data/* $1
rm $1/www/*
cp -r wwwroot/* $1/www/
cp ../binaries/ESP32-C6/firmware.json $1
for f in bootloader partition-table OpenEPaperLink_esp32_C6
do
if [[ -e ../ARM_Tag_FW/OpenEPaperLink_esp32_C6_AP/build/bootloader/${f}.bin ]]; then
cp ../ARM_Tag_FW/OpenEPaperLink_esp32_C6_AP/build/bootloader/${f}.bin $1
else
cp ../binaries/ESP32-C6/${f}.bin $1
fi
done
mkdir $1/current
echo "[[]]" > $1/current/tagDB.json
echo "OK"
else
echo "$1 is not a directory"
exit 1
fi

View File

@@ -8,7 +8,7 @@ SPIFFSEditor::SPIFFSEditor(const fs::FS &fs, const String &username, const Strin
: _fs(fs), _username(username), _password(password), _authenticated(false), _startTime(0) {
}
bool SPIFFSEditor::canHandle(AsyncWebServerRequest *request) {
bool SPIFFSEditor::canHandle(AsyncWebServerRequest *request) const {
if (request->url().equalsIgnoreCase("/edit")) {
if (request->method() == HTTP_GET) {
if (request->hasParam("list")) {
@@ -34,7 +34,6 @@ bool SPIFFSEditor::canHandle(AsyncWebServerRequest *request) {
return false;
}
}
request->addInterestingHeader("If-Modified-Since");
return true;
} else if (request->method() == HTTP_POST || request->method() == HTTP_DELETE || request->method() == HTTP_PUT) {
return true;
@@ -91,7 +90,7 @@ void SPIFFSEditor::handleRequest(AsyncWebServerRequest *request) {
if (request->header("If-Modified-Since").equals(buildTime)) {
request->send(304);
} else {
AsyncWebServerResponse *response = request->beginResponse(_fs, "/www/edit.html");
AsyncWebServerResponse *response = request->beginResponse(_fs, "/www/edit.html", "text/html");
response->addHeader("Last-Modified", buildTime);
request->send(response);
}

View File

@@ -95,7 +95,12 @@ bool BLE_filter_add_device(BLEAdvertisedDevice advertisedDevice) {
for (int i = 0; i < advertisedDevice.getManufacturerData().length(); i++)
Serial.printf("%02X", manuData[i]);
Serial.printf("\r\n");
#if ESP_ARDUINO_VERSION_MAJOR == 2
memcpy(&manuData, (uint8_t*)advertisedDevice.getManufacturerData().data(), manuDatalen);
#else
// [Nic] suggested fix for arduino 3.x by copilot, but I cannot test it
memcpy(&manuData, (uint8_t*)advertisedDevice.getManufacturerData().c_str(), manuDatalen);
#endif
if (manuDatalen == 7 && manuData[0] == 0x53 && manuData[1] == 0x50) { // Lets check for a Gicisky E-Paper display
struct espAvailDataReq theAdvData;

View File

@@ -21,6 +21,7 @@
#ifdef CONTENT_RSS
#include <rssClass.h>
#endif
#include <TJpg_Decoder.h>
#include <time.h>
#include <map>
@@ -53,17 +54,22 @@ void contentRunner() {
time_t now;
time(&now);
uint8_t wifimac[8];
WiFi.macAddress(wifimac);
memset(&wifimac[6], 0, 2);
for (tagRecord *taginfo : tagDB) {
const bool isAp = memcmp(taginfo->mac, wifimac, 8) == 0;
if (taginfo->RSSI &&
(now >= taginfo->nextupdate || needRedraw(taginfo->contentMode, taginfo->wakeupReason)) &&
config.runStatus == RUNSTATUS_RUN &&
Storage.freeSpace() > 31000 && !util::isSleeping(config.sleepTime1, config.sleepTime2)) {
config.runStatus == RUNSTATUS_RUN && (taginfo->expectedNextCheckin < now + 300 || isAp) &&
Storage.freeSpace() > 31000 && !util::isSleeping(config.sleepTime1, config.sleepTime2)) {
drawNew(taginfo->mac, taginfo);
taginfo->wakeupReason = 0;
}
if (taginfo->expectedNextCheckin > now - 10 && taginfo->expectedNextCheckin < now + 30 && taginfo->pendingIdle == 0 && taginfo->pendingCount == 0) {
if (taginfo->expectedNextCheckin > now - 10 && taginfo->expectedNextCheckin < now + 30 && taginfo->pendingIdle == 0 && taginfo->pendingCount == 0 && !isAp) {
int32_t minutesUntilNextUpdate = (taginfo->nextupdate - now) / 60;
if (minutesUntilNextUpdate > config.maxsleep) {
minutesUntilNextUpdate = config.maxsleep;
@@ -81,6 +87,7 @@ void contentRunner() {
}
if (minutesUntilNextUpdate > 1 && (wsClientCount() == 0 || config.stopsleep == 0)) {
taginfo->pendingIdle = minutesUntilNextUpdate * 60;
taginfo->expectedNextCheckin = now + taginfo->pendingIdle;
if (taginfo->isExternal == false) {
prepareIdleReq(taginfo->mac, minutesUntilNextUpdate);
}
@@ -92,7 +99,7 @@ void contentRunner() {
}
void checkVars() {
DynamicJsonDocument cfgobj(500);
JsonDocument cfgobj;
for (tagRecord *tag : tagDB) {
if (tag->contentMode == 19) {
deserializeJson(cfgobj, tag->modeConfigJson);
@@ -168,14 +175,14 @@ void drawNew(const uint8_t mac[8], tagRecord *&taginfo) {
taginfo->contentMode = 21;
taginfo->nextupdate = 0;
} else if (contentFS->exists("/tag_defaults.json")) {
DynamicJsonDocument doc(1000);
JsonDocument doc;
fs::File tagDefaults = contentFS->open("/tag_defaults.json", "r");
DeserializationError err = deserializeJson(doc, tagDefaults);
if (!err) {
if (doc.containsKey("contentMode")) {
if (doc["contentMode"].is<uint8_t>()) {
taginfo->contentMode = doc["contentMode"];
}
if (doc.containsKey("modecfgjson")) {
if (doc["modecfgjson"].is<String>()) {
taginfo->modeConfigJson = doc["modecfgjson"].as<String>();
}
}
@@ -192,7 +199,7 @@ void drawNew(const uint8_t mac[8], tagRecord *&taginfo) {
}
#endif
DynamicJsonDocument doc(500);
JsonDocument doc;
deserializeJson(doc, taginfo->modeConfigJson);
JsonObject cfgobj = doc.as<JsonObject>();
char buffer[64];
@@ -212,7 +219,6 @@ void drawNew(const uint8_t mac[8], tagRecord *&taginfo) {
imageParams.hasRed = false;
imageParams.dataType = DATATYPE_IMG_RAW_1BPP;
imageParams.dither = 2;
// if (taginfo->hasCustomLUT && taginfo->lut != 1) imageParams.grayLut = true;
imageParams.invert = taginfo->invert;
imageParams.symbols = 0;
@@ -222,11 +228,15 @@ void drawNew(const uint8_t mac[8], tagRecord *&taginfo) {
} else {
imageParams.zlib = 0;
}
#ifdef SAVE_SPACE
imageParams.g5 = 0;
#else
if (hwdata.g5 != 0 && taginfo->tagSoftwareVersion >= hwdata.g5) {
imageParams.g5 = 1;
} else {
imageParams.g5 = 0;
}
#endif
imageParams.lut = EPD_LUT_NO_REPEATS;
if (taginfo->lut == 2) imageParams.lut = EPD_LUT_FAST_NO_REDS;
@@ -236,10 +246,6 @@ void drawNew(const uint8_t mac[8], tagRecord *&taginfo) {
imageParams.lut = EPD_LUT_DEFAULT;
taginfo->lastfullupdate = now;
}
if (taginfo->hasCustomLUT && taginfo->capabilities & CAPABILITY_SUPPORTS_CUSTOM_LUTS && taginfo->lut != 1) {
Serial.println("using custom LUT");
imageParams.lut = EPD_LUT_OTA;
}
int32_t interval = cfgobj["interval"].as<int>() * 60;
if (interval == -1440 * 60) {
@@ -285,6 +291,9 @@ void drawNew(const uint8_t mac[8], tagRecord *&taginfo) {
if (imageParams.bpp == 3) {
imageParams.dataType = DATATYPE_IMG_RAW_3BPP;
Serial.println("datatype: DATATYPE_IMG_RAW_3BPP");
} else if (imageParams.bpp == 4) {
imageParams.dataType = DATATYPE_IMG_RAW_4BPP;
Serial.println("datatype: DATATYPE_IMG_RAW_4BPP");
} else if (imageParams.zlib) {
imageParams.dataType = DATATYPE_IMG_ZLIB;
Serial.println("datatype: DATATYPE_IMG_ZLIB");
@@ -448,13 +457,6 @@ void drawNew(const uint8_t mac[8], tagRecord *&taginfo) {
taginfo->nextupdate = 3216153600;
prepareNFCReq(mac, cfgobj["url"].as<const char *>());
break;
case 15: // send gray LUT
taginfo->nextupdate = 3216153600;
prepareLUTreq(mac, cfgobj["bytes"]);
taginfo->hasCustomLUT = true;
break;
#endif
#ifdef CONTENT_BUIENRADAR
@@ -490,8 +492,16 @@ void drawNew(const uint8_t mac[8], tagRecord *&taginfo) {
if (!util::isEmptyOrNull(configFilename)) {
String configUrl = cfgobj["url"].as<String>();
if (!util::isEmptyOrNull(configUrl)) {
DynamicJsonDocument json(1000);
JsonDocument json;
Serial.println("Get json url + file");
int index = configUrl.indexOf("{mac}");
if (index != -1) {
char macStr[17];
mac2hex(mac, macStr);
configUrl.replace("{mac}", macStr);
}
if (util::httpGetJson(configUrl, json, 1000)) {
taginfo->nextupdate = now + interval;
if (getJsonTemplateFileExtractVariables(filename, configFilename, json, taginfo, imageParams)) {
@@ -555,6 +565,14 @@ void drawNew(const uint8_t mac[8], tagRecord *&taginfo) {
}
break;
#endif
case 28: // tag command
{
uint64_t newmac;
sscanf(cfgobj["mac"].as<String>().c_str(), "%llx", &newmac);
sendTagMac(mac, newmac, (taginfo->isExternal == false));
taginfo->nextupdate = 3216153600;
break;
}
}
taginfo->modeConfigJson = doc.as<String>();
@@ -571,6 +589,9 @@ bool updateTagImage(String &filename, const uint8_t *dst, uint16_t nextCheckin,
if (imageParams.bpp == 3) {
imageParams.dataType = DATATYPE_IMG_RAW_3BPP;
Serial.println("datatype: DATATYPE_IMG_RAW_3BPP");
} else if (imageParams.bpp == 4) {
imageParams.dataType = DATATYPE_IMG_RAW_4BPP;
Serial.println("datatype: DATATYPE_IMG_RAW_4BPP");
} else if (imageParams.zlib) {
imageParams.dataType = DATATYPE_IMG_ZLIB;
Serial.println("datatype: DATATYPE_IMG_ZLIB");
@@ -693,7 +714,7 @@ void drawString(TFT_eSprite &spr, String content, int16_t posx, int16_t posy, St
}
}
void drawTextBox(TFT_eSprite &spr, String &content, int16_t &posx, int16_t &posy, int16_t boxwidth, int16_t boxheight, String font, uint16_t color, uint16_t bgcolor, float lineheight) {
void drawTextBox(TFT_eSprite &spr, String &content, int16_t &posx, int16_t &posy, int16_t boxwidth, int16_t boxheight, String font, uint16_t color, uint16_t bgcolor, float lineheight, byte align) {
replaceVariables(content);
switch (processFontPath(font)) {
case 2: {
@@ -703,7 +724,7 @@ void drawTextBox(TFT_eSprite &spr, String &content, int16_t &posx, int16_t &posy
case 3: {
// vlw bitmap font
// spr.drawRect(posx, posy, boxwidth, boxheight, TFT_BLACK);
spr.setTextDatum(TL_DATUM);
spr.setTextDatum(align);
if (font != "") spr.loadFont(font.substring(1), *contentFS);
spr.setTextWrap(false, false);
spr.setTextColor(color, bgcolor);
@@ -776,7 +797,7 @@ void drawDate(String &filename, tagRecord *&taginfo, imgParam &imageParams) {
return;
}
StaticJsonDocument<512> loc;
JsonDocument loc;
getTemplate(loc, 1, taginfo->hwType);
TFT_eSprite spr = TFT_eSprite(&tft);
@@ -823,7 +844,7 @@ void drawNumber(String &filename, int32_t count, int32_t thresholdred, tagRecord
TFT_eSprite spr = TFT_eSprite(&tft);
StaticJsonDocument<512> loc;
JsonDocument loc;
getTemplate(loc, 2, taginfo->hwType);
initSprite(spr, imageParams.width, imageParams.height, imageParams);
@@ -862,25 +883,7 @@ const String getWeatherIcon(const uint8_t id, const bool isNight = false) {
return weatherIcons[id];
}
void drawWeather(String &filename, JsonObject &cfgobj, const tagRecord *taginfo, imgParam &imageParams) {
wsLog("get weather");
getLocation(cfgobj);
const String lat = cfgobj["#lat"];
const String lon = cfgobj["#lon"];
const String tz = cfgobj["#tz"];
String units = "";
if (cfgobj["units"] == "1") {
units += "&temperature_unit=fahrenheit&windspeed_unit=mph&precipitation_unit=inch";
}
DynamicJsonDocument doc(1000);
const bool success = util::httpGetJson("https://api.open-meteo.com/v1/forecast?latitude=" + lat + "&longitude=" + lon + "&current_weather=true&windspeed_unit=ms&timezone=" + tz + units, doc, 5000);
if (!success) {
return;
}
void drawWeatherContent(JsonDocument &doc, JsonDocument &loc, TFT_eSprite &spr, JsonObject &cfgobj, imgParam &imageParams, bool isForecast = false) {
const auto &currentWeather = doc["current_weather"];
const double temperature = currentWeather["temperature"].as<double>();
float windspeed = currentWeather["windspeed"].as<float>();
@@ -897,52 +900,62 @@ void drawWeather(String &filename, JsonObject &cfgobj, const tagRecord *taginfo,
windval = int(windspeed);
}
doc.clear();
if (taginfo->hwType == SOLUM_SEG_UK) {
const String weatherText[] = {"sun", "sun", "sun", "CLDY", "CLDY", "FOG", "", "", "FOG", "", "",
"DRZL", "", "DRZL", "", "DRZL", "ice", "ice", "", "", "",
"rain", "", "rain", "", "rain", "ice", "ice", "", "", "",
"SNOW", "", "SNOW", "", "SNOW", "", "SNOW", "", "", "rain",
"rain", "rain", "", "", "SNOW", "SNOW", "", "", "", "",
"", "", "", "", "STRM", "HAIL", "", "", "HAIL"};
if (temperature < -9.9) {
sprintf(imageParams.segments, "%3d^%2d%-4.4s", static_cast<int>(temperature), windval, weatherText[weathercode].c_str());
imageParams.symbols = 0x00;
} else {
sprintf(imageParams.segments, "%3d^%2d%-4.4s", static_cast<int>(temperature * 10), windval, weatherText[weathercode].c_str());
imageParams.symbols = 0x04;
}
return;
if (!isForecast) {
const auto &location = loc["location"];
drawString(spr, cfgobj["location"], location[0], location[1], location[2]);
}
getTemplate(doc, 4, taginfo->hwType);
TFT_eSprite spr = TFT_eSprite(&tft);
tft.setTextWrap(false, false);
initSprite(spr, imageParams.width, imageParams.height, imageParams);
const auto &location = doc["location"];
drawString(spr, cfgobj["location"], location[0], location[1], location[2]);
const auto &wind = doc["wind"];
const auto &wind = isForecast ? loc["currentwind"] : loc["wind"];
drawString(spr, String(windval), wind[0], wind[1], wind[2], TR_DATUM, (beaufort > 4 ? imageParams.highlightColor : TFT_BLACK));
char tmpOutput[5];
dtostrf(temperature, 2, 1, tmpOutput);
const auto &temp = doc["temp"];
drawString(spr, String(tmpOutput), temp[0], temp[1], temp[2], TL_DATUM, (temperature < 0 ? imageParams.highlightColor : TFT_BLACK));
const auto &temp = loc["temp"];
String temperatureStr = String(tmpOutput);
if (temp[3] && temp[3] == 1) {
temperatureStr += (cfgobj["units"] == "1") ? "°" : "°";
}
drawString(spr, temperatureStr, temp[0], temp[1], temp[2], TL_DATUM, (temperature < 0 ? imageParams.highlightColor : TFT_BLACK));
const int iconcolor = (weathercode == 55 || weathercode == 65 || weathercode == 75 || weathercode == 82 || weathercode == 86 || weathercode == 95 || weathercode == 96 || weathercode == 99)
? imageParams.highlightColor
: TFT_BLACK;
const auto &icon = doc["icon"];
const auto &icon = isForecast ? loc["currenticon"] : loc["icon"];
drawString(spr, getWeatherIcon(weathercode, isNight), icon[0], icon[1], "/fonts/weathericons.ttf", icon[3], iconcolor, icon[2]);
const auto &dir = doc["dir"];
const auto &dir = loc["dir"];
drawString(spr, windDirectionIcon(winddirection), dir[0], dir[1], "/fonts/weathericons.ttf", TC_DATUM, TFT_BLACK, dir[2]);
if (weathercode > 10) {
const auto &umbrella = doc["umbrella"];
const auto &umbrella = loc["umbrella"];
drawString(spr, "\uf084", umbrella[0], umbrella[1], "/fonts/weathericons.ttf", TC_DATUM, imageParams.highlightColor, umbrella[2]);
}
}
void drawWeather(String &filename, JsonObject &cfgobj, const tagRecord *taginfo, imgParam &imageParams) {
wsLog("get weather");
getLocation(cfgobj);
const String lat = cfgobj["#lat"];
const String lon = cfgobj["#lon"];
const String tz = cfgobj["#tz"];
String units = "";
if (cfgobj["units"] == "1") {
units += "&temperature_unit=fahrenheit&windspeed_unit=mph&precipitation_unit=inch";
}
JsonDocument doc;
const bool success = util::httpGetJson("https://api.open-meteo.com/v1/forecast?latitude=" + lat + "&longitude=" + lon + "&current_weather=true&windspeed_unit=ms&timezone=" + tz + units, doc, 5000);
if (!success) {
return;
}
JsonDocument loc;
getTemplate(loc, 4, taginfo->hwType);
TFT_eSprite spr = TFT_eSprite(&tft);
tft.setTextWrap(false, false);
initSprite(spr, imageParams.width, imageParams.height, imageParams);
drawWeatherContent(doc, loc, spr, cfgobj, imageParams);
spr2buffer(spr, filename, imageParams);
spr.deleteSprite();
@@ -960,8 +973,8 @@ void drawForecast(String &filename, JsonObject &cfgobj, const tagRecord *taginfo
units += "&temperature_unit=fahrenheit&windspeed_unit=mph&precipitation_unit=inch";
}
DynamicJsonDocument doc(2000);
const bool success = util::httpGetJson("https://api.open-meteo.com/v1/forecast?latitude=" + lat + "&longitude=" + lon + "&daily=weathercode,temperature_2m_max,temperature_2m_min,precipitation_sum,windspeed_10m_max,winddirection_10m_dominant&windspeed_unit=ms&timeformat=unixtime&timezone=" + tz + units, doc, 5000);
JsonDocument doc;
const bool success = util::httpGetJson("https://api.open-meteo.com/v1/forecast?latitude=" + lat + "&longitude=" + lon + "&daily=weathercode,temperature_2m_max,temperature_2m_min,precipitation_sum,windspeed_10m_max,winddirection_10m_dominant&current_weather=true&windspeed_unit=ms&timeformat=unixtime&timezone=" + tz + units, doc, 5000);
if (!success) {
return;
}
@@ -969,10 +982,12 @@ void drawForecast(String &filename, JsonObject &cfgobj, const tagRecord *taginfo
TFT_eSprite spr = TFT_eSprite(&tft);
tft.setTextWrap(false, false);
StaticJsonDocument<512> loc;
JsonDocument loc;
getTemplate(loc, 8, taginfo->hwType);
initSprite(spr, imageParams.width, imageParams.height, imageParams);
if (loc["temp"]) drawWeatherContent(doc, loc, spr, cfgobj, imageParams, true);
const auto &location = loc["location"];
drawString(spr, cfgobj["location"], location[0], location[1], location[2], TL_DATUM, TFT_BLACK);
const auto &daily = doc["daily"];
@@ -1024,7 +1039,7 @@ void drawForecast(String &filename, JsonObject &cfgobj, const tagRecord *taginfo
drawString(spr, String(tmin) + " ", dag * column1 + day[0].as<int>(), day[4], day[2], TR_DATUM, (tmin < 0 ? imageParams.highlightColor : TFT_BLACK));
drawString(spr, String(" ") + String(tmax), dag * column1 + day[0].as<int>(), day[4], day[2], TL_DATUM, (tmax < 0 ? imageParams.highlightColor : TFT_BLACK));
drawString(spr, String(wind), dag * column1 + column1 - 10, day[3], day[2], TR_DATUM, (beaufort > 5 ? imageParams.highlightColor : TFT_BLACK));
drawString(spr, " " + String(wind), dag * column1 + column1 / 2, day[3], day[2], TL_DATUM, (beaufort > 5 ? imageParams.highlightColor : TFT_BLACK));
if (dag > 0) {
for (int i = loc["line"][0]; i < loc["line"][1]; i += 3) {
spr.drawPixel(dag * column1, i, TFT_BLACK);
@@ -1044,6 +1059,7 @@ int getImgURL(String &filename, String URL, time_t fetched, imgParam &imageParam
http.begin(URL);
http.addHeader("If-Modified-Since", formatHttpDate(fetched));
http.addHeader("X-ESL-MAC", MAC);
http.setFollowRedirects(HTTPC_STRICT_FOLLOW_REDIRECTS);
http.setTimeout(5000); // timeout in ms
const int httpCode = http.GET();
if (httpCode == 200) {
@@ -1118,7 +1134,7 @@ bool getRssFeed(String &filename, String URL, String title, tagRecord *&taginfo,
TFT_eSprite spr = TFT_eSprite(&tft);
StaticJsonDocument<512> loc;
JsonDocument loc;
getTemplate(loc, 9, taginfo->hwType);
initSprite(spr, imageParams.width, imageParams.height, imageParams);
// stampTime(spr);
@@ -1192,7 +1208,7 @@ bool getCalFeed(String &filename, JsonObject &cfgobj, tagRecord *&taginfo, imgPa
wsLog("get calendar");
StaticJsonDocument<512> loc;
JsonDocument loc;
getTemplate(loc, 11, taginfo->hwType);
String URL = cfgobj["apps_script_url"].as<String>() + "?days=" + loc["days"].as<String>();
@@ -1215,7 +1231,7 @@ bool getCalFeed(String &filename, JsonObject &cfgobj, tagRecord *&taginfo, imgPa
return false;
}
DynamicJsonDocument doc(5000);
JsonDocument doc;
DeserializationError error = deserializeJson(doc, http.getString());
if (error) {
wsErr(error.c_str());
@@ -1290,6 +1306,13 @@ bool getCalFeed(String &filename, JsonObject &cfgobj, tagRecord *&taginfo, imgPa
int calYOffset = loc["gridparam"][1].as<int>();
int lineHeight = loc["gridparam"][5].as<int>();
uint16_t backgroundLight = getColor("lightgray");
uint16_t backgroundDark = getColor("darkgray");
if (imageParams.hwdata.bpp >= 3) {
backgroundLight = getColor("#BEFAFF");
backgroundDark = getColor("#79FAFF");
}
// drawString(spr, String(timeinfo.tm_mday), calWidth / 2, -calHeight/5, "Signika-SB.ttf", TC_DATUM, imageParams.highlightColor, calHeight * 1.2);
for (int i = 0; i < calDays; i++) {
@@ -1303,9 +1326,9 @@ bool getCalFeed(String &filename, JsonObject &cfgobj, tagRecord *&taginfo, imgPa
drawString(spr, String(languageDaysShort[dayInfo->tm_wday]) + " " + String(dayInfo->tm_mday), colStart + colWidth / 2, calTop, loc["gridparam"][3], TC_DATUM, TFT_BLACK);
if (dayInfo->tm_wday == 0 || dayInfo->tm_wday == 6) {
spr.fillRect(colStart + 1, calTop + calYOffset, colWidth - 1, calHeight - 1, getColor("darkgray"));
spr.fillRect(colStart + 1, calTop + calYOffset, colWidth - 1, calHeight - 1, backgroundDark);
} else {
spr.fillRect(colStart + 1, calTop + calYOffset, colWidth - 1, calHeight - 1, getColor("lightgray"));
spr.fillRect(colStart + 1, calTop + calYOffset, colWidth - 1, calHeight - 1, backgroundLight);
}
}
@@ -1326,6 +1349,7 @@ bool getCalFeed(String &filename, JsonObject &cfgobj, tagRecord *&taginfo, imgPa
const time_t startdatetime = obj["start"];
const time_t enddatetime = obj["end"];
const bool isallday = obj["isallday"];
const int calendarId = obj["calendar"];
if (!isallday) {
localtime_r(&startdatetime, &timeinfo);
@@ -1357,9 +1381,17 @@ bool getCalFeed(String &filename, JsonObject &cfgobj, tagRecord *&taginfo, imgPa
int16_t eventX = colLeft + fulldaystart * colWidth + 3;
int16_t eventY = calTop + calYOffset + (line - 1) * lineHeight + 3;
spr.drawRect(eventX - 2, eventY - 3, colWidth * (fulldayend - fulldaystart) - 1, lineHeight + 1, TFT_BLACK);
spr.fillRect(eventX - 1, eventY - 2, colWidth * (fulldayend - fulldaystart) - 3, lineHeight - 1, TFT_WHITE);
drawTextBox(spr, eventtitle, eventX, eventY, colWidth * (fulldayend - fulldaystart) - 3, 15, loc["gridparam"][4], TFT_BLACK);
uint16_t background = TFT_WHITE;
uint16_t border = TFT_BLACK;
uint16_t textcolor = TFT_BLACK;
if (loc["colors1"].is<JsonArray>() && loc["colors1"].size() > calendarId) {
background = getColor(loc["colors1"][calendarId]);
border = getColor(loc["colors2"][calendarId]);
if (loc["colors3"][calendarId]) textcolor = getColor(loc["colors3"][calendarId]);
}
spr.fillRect(eventX - 1, eventY - 2, colWidth * (fulldayend - fulldaystart) - 3, lineHeight - 1, background);
spr.drawRect(eventX - 2, eventY - 3, colWidth * (fulldayend - fulldaystart) - 1, lineHeight + 1, border);
drawTextBox(spr, eventtitle, eventX, eventY, colWidth * (fulldayend - fulldaystart) - 3, 15, loc["gridparam"][4], textcolor);
block[i] = line;
if (line > maxBlock) maxBlock = line;
@@ -1390,6 +1422,7 @@ bool getCalFeed(String &filename, JsonObject &cfgobj, tagRecord *&taginfo, imgPa
const time_t startdatetime = obj["start"];
const time_t enddatetime = obj["end"];
const bool isallday = obj["isallday"];
const int calendarId = obj["calendar"];
if (!isallday) {
int fulldaystart = constrain((startdatetime - midnightEpoch) / (24 * 3600), 0, calDays);
@@ -1428,16 +1461,27 @@ bool getCalFeed(String &filename, JsonObject &cfgobj, tagRecord *&taginfo, imgPa
block[i] = indent;
int16_t eventX = colLeft + day * colWidth + (indent - 1) * 5;
int16_t eventY = calTop + calYOffset + (starttime * hourHeight / 60);
spr.drawRect(eventX + 1, eventY, colWidth - 1, (duration * hourHeight / 60) + 1, TFT_BLACK);
spr.fillRect(eventX + 2, eventY + 1, colWidth - 3, (duration * hourHeight / 60) - 1, TFT_WHITE);
uint16_t background = TFT_WHITE;
uint16_t border = TFT_BLACK;
uint16_t textcolor = TFT_BLACK;
if (loc["colors1"].is<JsonArray>() && loc["colors1"].size() > calendarId) {
background = getColor(loc["colors1"][calendarId]);
border = getColor(loc["colors2"][calendarId]);
if (loc["colors3"][calendarId]) textcolor = getColor(loc["colors3"][calendarId]);
}
spr.fillRect(eventX + 2, eventY + 1, colWidth - 3, (duration * hourHeight / 60) - 1, background);
spr.drawRect(eventX + 1, eventY, colWidth - 1, (duration * hourHeight / 60) + 1, border);
eventX += 2;
eventY += 2;
if (day == fulldaystart) {
eventtitle = formattedTimeString + " " + String(eventtitle);
drawTextBox(spr, formattedTimeString, eventX, eventY, colWidth - 1, (duration * hourHeight / 60) - 1, loc["gridparam"][4], textcolor, TFT_WHITE, 1);
eventX++;
eventY = calTop + calYOffset + (starttime * hourHeight / 60) + 2;
} else {
eventtitle = obj["title"].as<String>();
}
drawTextBox(spr, eventtitle, eventX, eventY, colWidth - 1, (duration * hourHeight / 60) - 1, loc["gridparam"][4], TFT_BLACK, TFT_WHITE, 1);
drawTextBox(spr, eventtitle, eventX, eventY, colWidth - 1, (duration * hourHeight / 60) - 1, loc["gridparam"][4], textcolor, TFT_WHITE, 1);
}
}
}
@@ -1466,7 +1510,7 @@ uint16_t getPercentileColor(const double *prices, int numPrices, double price, H
const double *boundaries;
int numColors, numBoundaries;
if (hwdata.bpp == 3) {
if (hwdata.bpp == 3 || hwdata.bpp == 4) {
colors = colors3bpp;
boundaries = boundaries3bpp;
numColors = sizeof(colors3bpp) / sizeof(colors3bpp[0]);
@@ -1532,7 +1576,7 @@ YAxisScale calculateYAxisScale(double priceMin, double priceMax, int divisions)
bool getDayAheadFeed(String &filename, JsonObject &cfgobj, tagRecord *&taginfo, imgParam &imageParams) {
wsLog("get dayahead prices");
StaticJsonDocument<512> loc;
JsonDocument 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/
@@ -1557,7 +1601,7 @@ bool getDayAheadFeed(String &filename, JsonObject &cfgobj, tagRecord *&taginfo,
return false;
}
DynamicJsonDocument doc(5000);
JsonDocument doc;
DeserializationError error = deserializeJson(doc, http.getString());
if (error) {
wsErr(error.c_str());
@@ -1578,7 +1622,7 @@ bool getDayAheadFeed(String &filename, JsonObject &cfgobj, tagRecord *&taginfo,
double maxPrice = std::numeric_limits<double>::lowest();
double prices[n];
DynamicJsonDocument doc2(500);
JsonDocument doc2;
JsonArray tariffArray;
std::string tariffString = cfgobj["tariffkwh"].as<std::string>();
if (tariffString.front() == '[') {
@@ -1610,12 +1654,16 @@ bool getDayAheadFeed(String &filename, JsonObject &cfgobj, tagRecord *&taginfo,
minPrice = yAxisScale.min;
uint16_t yAxisX = loc["yaxis"][1].as<int>();
uint16_t yAxisY = loc["yaxis"][3].as<int>() | 9;
uint16_t barBottom = loc["bars"][3].as<int>();
for (double i = minPrice; i <= maxPrice; i += yAxisScale.step) {
int y = mapDouble(i, minPrice, maxPrice, spr.height() - barBottom, spr.height() - barBottom - loc["bars"][2].as<int>());
spr.drawLine(0, y, spr.width(), y, TFT_BLACK);
if (loc["yaxis"][0]) drawString(spr, String(int(i * units)), yAxisX, y - 9, loc["yaxis"][0], TL_DATUM, TFT_BLACK);
if (loc["yaxis"][0]) {
String label = (maxPrice * units < 10) ? String(i * units, 1) : String(int(i * units));
drawString(spr, label, yAxisX, y - yAxisY, loc["yaxis"][0], TL_DATUM, TFT_BLACK);
}
}
uint16_t barwidth = loc["bars"][1].as<int>() / n;
@@ -1685,7 +1733,7 @@ void drawQR(String &filename, String qrcontent, String title, tagRecord *&taginf
// https://github.com/ricmoo/QRCode
qrcode_initText(&qrcode, qrcodeData, version, ECC_MEDIUM, text);
StaticJsonDocument<512> loc;
JsonDocument loc;
getTemplate(loc, 10, taginfo->hwType);
initSprite(spr, imageParams.width, imageParams.height, imageParams);
drawString(spr, title, loc["title"][0], loc["title"][1], loc["title"][2], TC_DATUM, TFT_BLACK, loc["title"][3]);
@@ -1727,7 +1775,7 @@ uint8_t drawBuienradar(String &filename, JsonObject &cfgobj, tagRecord *&taginfo
if (httpCode == 200) {
TFT_eSprite spr = TFT_eSprite(&tft);
StaticJsonDocument<512> loc;
JsonDocument loc;
getTemplate(loc, 16, taginfo->hwType);
initSprite(spr, imageParams.width, imageParams.height, imageParams);
@@ -1801,7 +1849,7 @@ void drawAPinfo(String &filename, JsonObject &cfgobj, tagRecord *&taginfo, imgPa
}
TFT_eSprite spr = TFT_eSprite(&tft);
DynamicJsonDocument loc(2048);
JsonDocument loc;
uint8_t screenCurrentOrientation = 0;
getTemplate(loc, 21, taginfo->hwType);
@@ -1822,7 +1870,7 @@ void drawTimestamp(String &filename, JsonObject &cfgobj, tagRecord *&taginfo, im
time(&now);
struct tm timeinfo;
StaticJsonDocument<512> loc;
JsonDocument loc;
getTemplate(loc, 1, taginfo->hwType);
TFT_eSprite spr = TFT_eSprite(&tft);
@@ -2158,7 +2206,7 @@ void drawJsonStream(Stream &stream, String &filename, tagRecord *&taginfo, imgPa
TFT_eSprite spr = TFT_eSprite(&tft);
initSprite(spr, imageParams.width, imageParams.height, imageParams);
uint8_t screenCurrentOrientation = 0;
DynamicJsonDocument doc(500);
JsonDocument doc;
if (stream.find("[")) {
do {
DeserializationError error = deserializeJson(doc, stream);
@@ -2206,38 +2254,93 @@ void rotateBuffer(uint8_t rotation, uint8_t &currentOrientation, TFT_eSprite &sp
}
}
TFT_eSprite sprDraw = TFT_eSprite(&tft);
bool spr_draw(int16_t x, int16_t y, uint16_t w, uint16_t h, uint16_t *bitmap) {
sprDraw.pushImage(x, y, w, h, bitmap);
return 1;
}
void drawElement(const JsonObject &element, TFT_eSprite &spr, imgParam &imageParams, uint8_t &currentOrientation) {
if (element.containsKey("text")) {
if (element["text"].is<JsonArray>()) {
const JsonArray &textArray = element["text"];
const uint16_t align = textArray[5] | 0;
const uint16_t size = textArray[6] | 0;
const String bgcolorstr = textArray[7].as<String>();
const uint16_t bgcolor = (bgcolorstr.length() > 0) ? getColor(bgcolorstr) : TFT_WHITE;
drawString(spr, textArray[2], textArray[0].as<int>(), textArray[1].as<int>(), textArray[3], align, getColor(textArray[4]), size, bgcolor);
} else if (element.containsKey("textbox")) {
} else if (element["textbox"].is<JsonArray>()) {
// posx, posy, width, height, text, font, color, lineheight, align
const JsonArray &textArray = element["textbox"];
float lineheight = textArray[7].as<float>();
if (lineheight == 0) lineheight = 1;
int16_t posx = textArray[0] | 0;
int16_t posy = textArray[1] | 0;
String text = textArray[4];
drawTextBox(spr, text, posx, posy, textArray[2], textArray[3], textArray[5], getColor(textArray[6]), TFT_WHITE, lineheight);
} else if (element.containsKey("box")) {
const uint16_t align = textArray[8] | 0;
drawTextBox(spr, text, posx, posy, textArray[2], textArray[3], textArray[5], getColor(textArray[6]), TFT_WHITE, lineheight, align);
} else if (element["box"].is<JsonArray>()) {
const JsonArray &boxArray = element["box"];
spr.fillRect(boxArray[0].as<int>(), boxArray[1].as<int>(), boxArray[2].as<int>(), boxArray[3].as<int>(), getColor(boxArray[4]));
} else if (element.containsKey("rbox")) {
if (boxArray.size()>=7) {
for (int i=0; i < boxArray[6].as<int>(); i++) {
spr.drawRect(boxArray[0].as<int>() + i, boxArray[1].as<int>() + i, boxArray[2].as<int>() - 2 * i, boxArray[3].as<int>() - 2 * i, getColor(boxArray[5]));
}
}
} else if (element["rbox"].is<JsonArray>()) {
const JsonArray &rboxArray = element["rbox"];
spr.fillRoundRect(rboxArray[0].as<int>(), rboxArray[1].as<int>(), rboxArray[2].as<int>(), rboxArray[3].as<int>(), rboxArray[4].as<int>(), getColor(rboxArray[5]));
} else if (element.containsKey("line")) {
if (rboxArray.size() >= 8) {
for (int i = 0; i < rboxArray[7].as<int>(); i++) {
spr.drawRoundRect(rboxArray[0].as<int>() + i, rboxArray[1].as<int>() + i, rboxArray[2].as<int>() - 2 * i, rboxArray[3].as<int>() - 2 * i, rboxArray[4].as<int>() - i / 1.41, getColor(rboxArray[6]));
if (i > 0) {
spr.drawRoundRect(rboxArray[0].as<int>() + i - 1, rboxArray[1].as<int>() + i, rboxArray[2].as<int>() - 2 * i + 2, rboxArray[3].as<int>() - 2 * i, rboxArray[4].as<int>() - i / 1.41, getColor(rboxArray[6]));
}
}
}
} else if (element["line"].is<JsonArray>()) {
const JsonArray &lineArray = element["line"];
spr.drawLine(lineArray[0].as<int>(), lineArray[1].as<int>(), lineArray[2].as<int>(), lineArray[3].as<int>(), getColor(lineArray[4]));
} else if (element.containsKey("triangle")) {
} else if (element["triangle"].is<JsonArray>()) {
const JsonArray &lineArray = element["triangle"];
spr.fillTriangle(lineArray[0].as<int>(), lineArray[1].as<int>(), lineArray[2].as<int>(), lineArray[3].as<int>(), lineArray[4].as<int>(), lineArray[5].as<int>(), getColor(lineArray[6]));
} else if (element.containsKey("circle")) {
} else if (element["circle"].is<JsonArray>()) {
const JsonArray &circleArray = element["circle"];
spr.fillCircle(circleArray[0].as<int>(), circleArray[1].as<int>(), circleArray[2].as<int>(), getColor(circleArray[3]));
} else if (element.containsKey("rotate")) {
if (circleArray.size() >= 6) {
for (int i = 0; i < circleArray[5].as<int>(); i++) {
spr.drawCircle(circleArray[0].as<int>(), circleArray[1].as<int>(), circleArray[2].as<int>() - i, getColor(circleArray[4]));
if (i > 0) {
spr.drawCircle(circleArray[0].as<int>(), circleArray[1].as<int>(), circleArray[2].as<int>() - i - 0.5, getColor(circleArray[4]));
}
}
}
} else if (element["image"].is<JsonArray>()) {
const JsonArray &imgArray = element["image"];
TJpgDec.setSwapBytes(true);
TJpgDec.setJpgScale(1);
TJpgDec.setCallback(spr_draw);
uint16_t w = 0, h = 0;
String filename = imgArray[0];
if (filename[0] != '/') {
filename = "/" + filename;
}
TJpgDec.getFsJpgSize(&w, &h, filename, *contentFS);
if (w == 0 && h == 0) {
wsErr("invalid jpg");
return;
}
Serial.println("jpeg conversion " + String(w) + "x" + String(h));
sprDraw.setColorDepth(16);
sprDraw.createSprite(w, h);
if (sprDraw.getPointer() == nullptr) {
wsErr("Failed to create sprite in contentmanager");
} else {
TJpgDec.drawFsJpg(0, 0, filename, *contentFS);
sprDraw.pushToSprite(&spr, imgArray[1].as<int>(), imgArray[2].as<int>());
sprDraw.deleteSprite();
}
} else if (element["rotate"].is<uint8_t>()) {
uint8_t rotation = element["rotate"].as<int>();
rotateBuffer(rotation, currentOrientation, spr, imageParams);
}
@@ -2318,11 +2421,11 @@ void getLocation(JsonObject &cfgobj) {
if (util::isEmptyOrNull(lat) || util::isEmptyOrNull(lon)) {
wsLog("get location");
StaticJsonDocument<80> filter;
JsonDocument filter;
filter["results"][0]["latitude"] = true;
filter["results"][0]["longitude"] = true;
filter["results"][0]["timezone"] = true;
DynamicJsonDocument doc(1000);
JsonDocument doc;
if (util::httpGetJson("https://geocoding-api.open-meteo.com/v1/search?name=" + urlEncode(cfgobj["location"]) + "&count=1", doc, 5000, &filter)) {
cfgobj["#lat"] = doc["results"][0]["latitude"].as<String>();
cfgobj["#lon"] = doc["results"][0]["longitude"].as<String>();
@@ -2353,20 +2456,6 @@ void prepareNFCReq(const uint8_t *dst, const char *url) {
len = 1 + len;
prepareDataAvail(data, len, DATATYPE_NFC_RAW_CONTENT, dst);
}
void prepareLUTreq(const uint8_t *dst, const String &input) {
constexpr const char *delimiters = ", \t";
constexpr const int maxValues = 76;
uint8_t waveform[maxValues];
char *ptr = strtok(const_cast<char *>(input.c_str()), delimiters);
int i = 0;
while (ptr != nullptr && i < maxValues) {
waveform[i++] = static_cast<uint8_t>(strtol(ptr, nullptr, 16));
ptr = strtok(nullptr, delimiters);
}
const size_t waveformLen = sizeof(waveform);
prepareDataAvail(waveform, waveformLen, DATATYPE_CUSTOM_LUT_OTA, dst);
}
#endif
#ifdef CONTENT_TAGCFG
@@ -2389,8 +2478,8 @@ void prepareConfigFile(const uint8_t *dst, const JsonObject &config) {
#endif
void getTemplate(JsonDocument &json, const uint8_t id, const uint8_t hwtype) {
StaticJsonDocument<80> filter;
DynamicJsonDocument doc(2048);
JsonDocument filter;
JsonDocument doc;
const String idstr = String(id);
constexpr const char *templateKey = "template";
@@ -2404,11 +2493,11 @@ void getTemplate(JsonDocument &json, const uint8_t id, const uint8_t hwtype) {
filter["usetemplate"] = true;
const DeserializationError error = deserializeJson(doc, jsonFile, DeserializationOption::Filter(filter));
jsonFile.close();
if (!error && doc.containsKey(templateKey) && doc[templateKey].containsKey(idstr)) {
if (!error && doc[templateKey].is<JsonVariant>() && doc[templateKey][idstr].is<JsonVariant>()) {
json.set(doc[templateKey][idstr]);
return;
}
if (!error && doc.containsKey("usetemplate")) {
if (!error && doc["usetemplate"].is<uint8_t>()) {
getTemplate(json, id, doc["usetemplate"]);
return;
}

View File

@@ -9,6 +9,14 @@
#include "storage.h"
#include "tag_db.h"
#include "web.h"
#include "espflasher.h"
#include "util.h"
#define LOG(format, ... ) Serial.printf(format,## __VA_ARGS__)
#ifndef FLASHER_DEBUG_PORT
#define FLASHER_DEBUG_PORT 2
#endif
esp_loader_error_t connect_to_target(uint32_t higher_transmission_rate) {
esp_loader_connect_args_t connect_config = ESP_LOADER_CONNECT_DEFAULT();
@@ -120,150 +128,197 @@ esp_loader_error_t flash_binary(String &file_path, size_t address) {
bool downloadAndWriteBinary(String &filename, const char *url) {
HTTPClient binaryHttp;
Serial.println(url);
bool Ret = false;
bool bHaveFsMutex = false;
LOG("downloadAndWriteBinary: url %s\n",url);
binaryHttp.begin(url);
binaryHttp.setFollowRedirects(HTTPC_FORCE_FOLLOW_REDIRECTS);
int binaryResponseCode = binaryHttp.GET();
Serial.println(binaryResponseCode);
if (binaryResponseCode == HTTP_CODE_OK) {
do {
int binaryResponseCode = binaryHttp.GET();
if(binaryResponseCode != HTTP_CODE_OK) {
wsSerial("http error " + String(binaryResponseCode) + " fetching " + String(url));
break;
}
int contentLength = binaryHttp.getSize();
Serial.println(contentLength);
LOG("contentLength %d\r\n",contentLength);
if(contentLength < 0) {
wsSerial("Couldn't get contentLength");
break;
}
xSemaphoreTake(fsMutex, portMAX_DELAY);
bHaveFsMutex = true;
File file = contentFS->open(filename, "wb");
if (file) {
wsSerial("downloading " + String(filename));
WiFiClient *stream = binaryHttp.getStreamPtr();
uint8_t buffer[1024];
size_t totalBytesRead = 0;
time_t timeOut = millis() + 5000;
// while (stream->available()) {
while (millis() < timeOut) {
size_t bytesRead = stream->readBytes(buffer, sizeof(buffer));
if(!file) {
wsSerial("file open error " + String(filename));
break;
}
wsSerial("downloading " + String(filename));
WiFiClient *stream = binaryHttp.getStreamPtr();
uint8_t buffer[1024];
size_t totalBytesRead = 0;
// timeout if we don't average at least 1k bytes/second
unsigned long timeOut = millis() + contentLength;
while(stream->connected() && totalBytesRead < contentLength) {
size_t bytesRead;
size_t bytesToRead;
if(stream->available()) {
bytesToRead = min(sizeof(buffer), (size_t) stream->available());
bytesRead = stream->readBytes(buffer, bytesToRead);
if(bytesRead == 0 || millis() > timeOut) {
wsSerial("Download time out");
break;
}
file.write(buffer, bytesRead);
totalBytesRead += bytesRead;
if (totalBytesRead == contentLength) break;
vTaskDelay(1 / portTICK_PERIOD_MS);
} else {
vTaskDelay(10 / portTICK_PERIOD_MS);
}
file.close();
xSemaphoreGive(fsMutex);
binaryHttp.end();
file = contentFS->open(filename, "r");
if (file) {
if (totalBytesRead == contentLength || (contentLength == 0 && file.size() > 0)) {
file.close();
return true;
}
wsSerial("Download failed, " + String(file.size()) + " bytes");
file.close();
}
} else {
xSemaphoreGive(fsMutex);
wsSerial("file open error " + String(filename));
}
} else {
wsSerial("http error " + String(binaryResponseCode) + " fetching " + String(url));
}
file.close();
if(!stream->connected()) {
wsSerial("Connection dropped during transfer");
break;
}
file = contentFS->open(filename, "r");
if(!file) {
wsSerial("file open error " + String(filename));
break;
}
if(file.size() == contentLength) {
Ret = true;
} else {
wsSerial("Download failed, " + String(file.size()) + " bytes");
}
file.close();
} while(false);
binaryHttp.setReuse(false);
binaryHttp.end();
return false;
if(bHaveFsMutex) {
xSemaphoreGive(fsMutex);
}
return Ret;
}
bool doC6flash(uint8_t doDownload) {
String filenameFirmwareLocal = "/firmware.json";
DynamicJsonDocument jsonDoc(1024);
if (doDownload) {
const String githubUrl = "https://raw.githubusercontent.com/" + config.repo + "/master/binaries/ESP32-C6/firmware.json";
if (downloadAndWriteBinary(filenameFirmwareLocal, githubUrl.c_str())) {
File readfile = contentFS->open(filenameFirmwareLocal, "r");
if (!readfile) {
Serial.println("load firmware.json: Failed to open file");
return false;
}
DeserializationError jsonError = deserializeJson(jsonDoc, readfile);
bool FlashC6_H2(const char *RepoUrl) {
String JsonFilename = "/firmware_" SHORT_CHIP_NAME ".json" ;
bool Ret = false;
bool bLoaderInit = false;
bool bDownload = strlen(RepoUrl) > 0;
int retry;
JsonDocument jsonDoc;
if (!jsonError) {
JsonArray jsonArray = jsonDoc.as<JsonArray>();
for (JsonObject obj : jsonArray) {
String filename = "/" + obj["filename"].as<String>();
// String binaryUrl = "https://raw.githubusercontent.com/" + config.repo + "/master/binaries/ESP32-C6" + String(filename);
String binaryUrl = "http://www.openepaperlink.eu/binaries/ESP32-C6" + String(filename);
for (int retry = 0; retry < 10; retry++) {
if (downloadAndWriteBinary(filename, binaryUrl.c_str())) {
break;
}
wsSerial("Retry " + String(retry));
if (retry < 9) {
delay(1000);
} else {
return false;
}
}
}
} else {
wsSerial("json error fetching " + String(githubUrl));
return false;
LOG("%s#%d: ",__FUNCTION__,__LINE__); util::printHeap();
do {
if(bDownload) {
String FileUrl = RepoUrl + JsonFilename;
if(!downloadAndWriteBinary(JsonFilename, FileUrl.c_str())) {
LOG("%s#%d: ",__FUNCTION__,__LINE__); util::printHeap();
break;
}
} else {
return false;
}
} else {
File readfile = contentFS->open(filenameFirmwareLocal, "r");
if (!readfile) {
Serial.println("load local firmware.json: Failed to open file");
return false;
File readfile = contentFS->open(JsonFilename, "r");
if(!readfile) {
wsSerial("load " + JsonFilename + ": Failed to open file");
return true;
}
DeserializationError jsonError = deserializeJson(jsonDoc, readfile);
}
const loader_esp32_config_t config = {
.baud_rate = 115200,
.uart_port = 2,
.uart_rx_pin = FLASHER_DEBUG_TXD,
.uart_tx_pin = FLASHER_DEBUG_RXD,
.reset_trigger_pin = FLASHER_AP_RESET,
.gpio0_trigger_pin = FLASHER_DEBUG_PROG,
};
if(jsonError) {
wsSerial(String("json error parsing") + JsonFilename);
break;
}
if (loader_port_esp32_init(&config) != ESP_LOADER_SUCCESS) {
wsSerial("Serial initialization failed");
loader_port_esp32_deinit();
return false;
}
if(!bDownload) {
Ret = true;
break;
}
if (connect_to_target(115200) == ESP_LOADER_SUCCESS) {
if (esp_loader_get_target() == ESP32C6_CHIP) {
wsSerial("Connected to ESP32-C6");
int maxRetries = 5;
esp_loader_error_t err;
JsonArray jsonArray = jsonDoc.as<JsonArray>();
for(JsonObject obj : jsonArray) {
String filename = "/" + obj["filename"].as<String>();
String binaryUrl = RepoUrl + filename;
JsonArray jsonArray = jsonDoc.as<JsonArray>();
for (JsonObject obj : jsonArray) {
String filename = "/" + obj["filename"].as<String>();
const char *addressStr = obj["address"];
uint32_t address = strtoul(addressStr, NULL, 16);
for (int retry = 0; retry < maxRetries; retry++) {
err = flash_binary(filename, address);
if (err == ESP_LOADER_SUCCESS) break;
Serial.printf("Flash failed with error %d. Retrying...\n", err);
for(retry = 0; retry < 10; retry++) {
if(downloadAndWriteBinary(filename, binaryUrl.c_str())) {
break;
}
wsSerial("Retry " + String(retry));
if(retry < 9) {
delay(1000);
}
if (err != ESP_LOADER_SUCCESS) {
loader_port_esp32_deinit();
return false;
}
}
Serial.println("Done!");
} else {
wsSerial("Connected to wrong ESP32 type");
loader_port_esp32_deinit();
return false;
if(retry == 10) {
break;
}
}
} else {
wsSerial("Connection to the C6 failed");
if(retry < 10) {
Ret = true;
}
} while(false);
if(Ret == true) do {
Ret = false;
const loader_esp32_config_t config = {
.baud_rate = 115200,
.uart_port = FLASHER_DEBUG_PORT,
.uart_rx_pin = FLASHER_DEBUG_TXD,
.uart_tx_pin = FLASHER_DEBUG_RXD,
.reset_trigger_pin = FLASHER_AP_RESET,
.gpio0_trigger_pin = FLASHER_DEBUG_PROG,
};
bLoaderInit = true;
if(loader_port_esp32_init(&config) != ESP_LOADER_SUCCESS) {
wsSerial("Serial initialization failed");
break;
}
if(connect_to_target(115200) != ESP_LOADER_SUCCESS) {
wsSerial("Connection to the " SHORT_CHIP_NAME " failed");
break;
}
if(esp_loader_get_target() != ESP_CHIP_TYPE) {
wsSerial("Connected to wrong ESP32 type");
break;
}
wsSerial("Connected to ESP32-" SHORT_CHIP_NAME);
int maxRetries = 5;
esp_loader_error_t err;
JsonArray jsonArray = jsonDoc.as<JsonArray>();
for(JsonObject obj : jsonArray) {
String filename = "/" + obj["filename"].as<String>();
const char *addressStr = obj["address"];
uint32_t address = strtoul(addressStr, NULL, 16);
for(int retry = 0; retry < maxRetries; retry++) {
err = flash_binary(filename, address);
if(err == ESP_LOADER_SUCCESS) {
Ret = true;
break;
}
Serial.printf("Flash failed with error %d. Retrying...\n", err);
delay(1000);
}
if(err != ESP_LOADER_SUCCESS) {
break;
}
}
Serial.println("Done!");
} while(false);
if(bLoaderInit) {
loader_port_esp32_deinit();
return false;
}
loader_port_esp32_deinit();
return true;
LOG("%s#%d: ",__FUNCTION__,__LINE__); util::printHeap();
return Ret;
}

View File

@@ -10,6 +10,7 @@
#include "storage.h"
#include "time.h"
#include "zbs_interface.h"
#include <WiFi.h>
#ifdef HAS_EXT_FLASHER
#include "webflasher.h"
@@ -178,7 +179,7 @@ bool flasher::getInfoBlockType() {
}
bool flasher::findTagByMD5() {
DynamicJsonDocument doc(3000);
JsonDocument doc;
fs::File readfile = contentFS->open("/tag_md5_db.json", "r");
DeserializationError err = deserializeJson(doc, readfile);
if (!err) {
@@ -207,7 +208,7 @@ bool flasher::findTagByMD5() {
}
bool flasher::findTagByType(uint8_t type) {
DynamicJsonDocument doc(3000);
JsonDocument doc;
fs::File readfile = contentFS->open("/tag_md5_db.json", "r");
DeserializationError err = deserializeJson(doc, readfile);
if (!err) {
@@ -265,7 +266,7 @@ bool flasher::getFirmwareMac() {
void flasher::getMacFromWiFi() {
mac[0] = 0x00;
mac[1] = 0x00;
esp_read_mac(mac + 2, ESP_MAC_WIFI_SOFTAP);
WiFi.softAPmacAddress(mac + 2);
}
bool flasher::backupFlash() {
@@ -447,7 +448,7 @@ bool flasher::writeFlashFromPackOffset(fs::File *file, uint16_t length) {
}
bool flasher::writeFlashFromPack(String filename, uint8_t type) {
DynamicJsonDocument doc(1024);
JsonDocument doc;
fs::File readfile = contentFS->open(filename, "r");
DeserializationError err = deserializeJson(doc, readfile);
if (!err) {
@@ -507,7 +508,7 @@ bool flasher::writeBlock(uint16_t offset, uint8_t *data, uint16_t len, bool info
#ifndef C6_OTA_FLASHING
uint16_t getAPUpdateVersion(uint8_t type) {
StaticJsonDocument<512> doc;
JsonDocument doc;
fs::File readfile = contentFS->open("/AP_FW_Pack.bin", "r");
DeserializationError err = deserializeJson(doc, readfile);
if (!err) {

View File

@@ -12,16 +12,21 @@
#include "ips_display.h"
#define YELLOW_SENSE 8 // sense AP hardware
#ifdef HAS_ELECROW_ADV_2_8
#define TFT_BACKLIGHT 38
#else
#define TFT_BACKLIGHT 14
#endif
TFT_eSPI tft2 = TFT_eSPI();
uint8_t YellowSense = 0;
bool tftLogscreen = true;
bool tftOverride = false;
#ifdef HAS_LILYGO_TPANEL
#if defined HAS_LILYGO_TPANEL || defined HAS_4inch_TPANEL
#if defined HAS_LILYGO_TPANEL
static const uint8_t st7701_type9_init_operations_lilygo[] = {
BEGIN_WRITE,
@@ -41,14 +46,14 @@ static const uint8_t st7701_type9_init_operations_lilygo[] = {
WRITE_C8_D8, 0xCC, 0x10,
WRITE_COMMAND_8, 0xB0, // Positive Voltage Gamma Control
WRITE_COMMAND_8, 0xB0, // Positive Voltage Gamma Control
WRITE_BYTES, 16,
0x00, 0x0F, 0x16, 0x0E,
0x11, 0x07, 0x09, 0x09,
0x08, 0x23, 0x05, 0x11,
0x0F, 0x28, 0x2D, 0x18,
WRITE_COMMAND_8, 0xB1, // Negative Voltage Gamma Control
WRITE_COMMAND_8, 0xB1, // Negative Voltage Gamma Control
WRITE_BYTES, 16,
0x00, 0x0F, 0x16, 0x0E,
0x11, 0x07, 0x09, 0x08,
@@ -142,7 +147,7 @@ static const uint8_t st7701_type9_init_operations_lilygo[] = {
// WRITE_C8_D8, 0xD1, 0x81,//Test
// WRITE_C8_D8, 0xD2, 0x08,//Test
WRITE_COMMAND_8, 0x29, // Display On
WRITE_COMMAND_8, 0x29, // Display On
// WRITE_C8_D8, 0x35, 0x00,//Test
// WRITE_C8_D8, 0xCE, 0x04,//Test
@@ -152,41 +157,201 @@ static const uint8_t st7701_type9_init_operations_lilygo[] = {
// 0xF0, 0xA3, 0xA3, 0x71,
END_WRITE};
Arduino_DataBus *bus = new Arduino_XL9535SWSPI(IIC_SDA /* SDA */, IIC_SCL /* SCL */, -1 /* XL PWD */,
Arduino_DataBus* bus = new Arduino_XL9535SWSPI(IIC_SDA /* SDA */, IIC_SCL /* SCL */, -1 /* XL PWD */,
XL95X5_CS /* XL CS */, XL95X5_SCLK /* XL SCK */, XL95X5_MOSI /* XL MOSI */);
Arduino_ESP32RGBPanel *rgbpanel = new Arduino_ESP32RGBPanel(
-1 /* DE */, LCD_VSYNC /* VSYNC */, LCD_HSYNC /* HSYNC */, LCD_PCLK /* PCLK */,
LCD_B0 /* B0 */, LCD_B1 /* B1 */, LCD_B2 /* B2 */, LCD_B3 /* B3 */, LCD_B4 /* B4 */,
LCD_G0 /* G0 */, LCD_G1 /* G1 */, LCD_G2 /* G2 */, LCD_G3 /* G3 */, LCD_G4 /* G4 */, LCD_G5 /* G5 */,
LCD_R0 /* R0 */, LCD_R1 /* R1 */, LCD_R2 /* R2 */, LCD_R3 /* R3 */, LCD_R4 /* R4 */,
1 /* hsync_polarity */, 20 /* hsync_front_porch */, 2 /* hsync_pulse_width */, 0 /* hsync_back_porch */,
1 /* vsync_polarity */, 30 /* vsync_front_porch */, 8 /* vsync_pulse_width */, 1 /* vsync_back_porch */,
10 /* pclk_active_neg */, 6000000L /* prefer_speed */, true /* useBigEndian */,
0 /* de_idle_high*/, 0 /* pclk_idle_high */);
Arduino_RGB_Display *gfx = new Arduino_RGB_Display(
LCD_WIDTH /* width */, LCD_HEIGHT /* height */, rgbpanel, 0 /* rotation */, true /* auto_flush */,
bus, -1 /* RST */, st7701_type9_init_operations_lilygo, sizeof(st7701_type9_init_operations_lilygo));
Arduino_ESP32RGBPanel* rgbpanel = new Arduino_ESP32RGBPanel(
-1 /* DE */, LCD_VSYNC /* VSYNC */, LCD_HSYNC /* HSYNC */, LCD_PCLK /* PCLK */,
LCD_B0 /* B0 */, LCD_B1 /* B1 */, LCD_B2 /* B2 */, LCD_B3 /* B3 */, LCD_B4 /* B4 */,
LCD_G0 /* G0 */, LCD_G1 /* G1 */, LCD_G2 /* G2 */, LCD_G3 /* G3 */, LCD_G4 /* G4 */, LCD_G5 /* G5 */,
LCD_R0 /* R0 */, LCD_R1 /* R1 */, LCD_R2 /* R2 */, LCD_R3 /* R3 */, LCD_R4 /* R4 */,
1 /* hsync_polarity */, 20 /* hsync_front_porch */, 2 /* hsync_pulse_width */, 0 /* hsync_back_porch */,
1 /* vsync_polarity */, 30 /* vsync_front_porch */, 8 /* vsync_pulse_width */, 1 /* vsync_back_porch */,
10 /* pclk_active_neg */, 6000000L /* prefer_speed */, true /* useBigEndian */,
0 /* de_idle_high*/, 0 /* pclk_idle_high */);
Arduino_RGB_Display* gfx = new Arduino_RGB_Display(
LCD_WIDTH /* width */, LCD_HEIGHT /* height */, rgbpanel, 0 /* rotation */, true /* auto_flush */,
bus, -1 /* RST */, st7701_type9_init_operations_lilygo, sizeof(st7701_type9_init_operations_lilygo));
#else
static const uint8_t st7701_4848S040_init[] = {
BEGIN_WRITE,
WRITE_COMMAND_8, 0xFF,
WRITE_BYTES, 5, 0x77, 0x01, 0x00, 0x00, 0x10,
WRITE_C8_D16, 0xC0, 0x3B, 0x00,
WRITE_C8_D16, 0xC1, 0x0D, 0x02,
WRITE_C8_D16, 0xC2, 0x31, 0x05,
WRITE_C8_D8, 0xCD, 0x00, // 0xCD, 0x08 !!
WRITE_COMMAND_8, 0xB0, // Positive Voltage Gamma Control
WRITE_BYTES, 16,
0x00, 0x11, 0x18, 0x0E, 0x11, 0x06, 0x07, 0x08,
0x07, 0x22, 0x04, 0x12, 0x0F, 0xAA, 0x31, 0x18,
WRITE_COMMAND_8, 0xB1, // Negative Voltage Gamma Control
WRITE_BYTES, 16,
0x00, 0x11, 0x19, 0x0E, 0x12, 0x07, 0x08, 0x08,
0x08, 0x22, 0x04, 0x11, 0x11, 0xA9, 0x32, 0x18,
// PAGE1
WRITE_COMMAND_8, 0xFF,
WRITE_BYTES, 5, 0x77, 0x01, 0x00, 0x00, 0x11,
WRITE_C8_D8, 0xB0, 0x60, // Vop=4.7375v
WRITE_C8_D8, 0xB1, 0x32, // VCOM=32
WRITE_C8_D8, 0xB2, 0x07, // VGH=15v
WRITE_C8_D8, 0xB3, 0x80,
WRITE_C8_D8, 0xB5, 0x49, // VGL=-10.17v
WRITE_C8_D8, 0xB7, 0x85,
WRITE_C8_D8, 0xB8, 0x21, // AVDD=6.6 & AVCL=-4.6
WRITE_C8_D8, 0xC1, 0x78,
WRITE_C8_D8, 0xC2, 0x78,
WRITE_COMMAND_8, 0xE0,
WRITE_BYTES, 3, 0x00, 0x1B, 0x02,
WRITE_COMMAND_8, 0xE1,
WRITE_BYTES, 11,
0x08, 0xA0, 0x00, 0x00,
0x07, 0xA0, 0x00, 0x00,
0x00, 0x44, 0x44,
WRITE_COMMAND_8, 0xE2,
WRITE_BYTES, 12,
0x11, 0x11, 0x44, 0x44,
0xED, 0xA0, 0x00, 0x00,
0xEC, 0xA0, 0x00, 0x00,
WRITE_COMMAND_8, 0xE3,
WRITE_BYTES, 4, 0x00, 0x00, 0x11, 0x11,
WRITE_C8_D16, 0xE4, 0x44, 0x44,
WRITE_COMMAND_8, 0xE5,
WRITE_BYTES, 16,
0x0A, 0xE9, 0xD8, 0xA0, 0x0C, 0xEB, 0xD8, 0xA0,
0x0E, 0xED, 0xD8, 0xA0, 0x10, 0xEF, 0xD8, 0xA0,
WRITE_COMMAND_8, 0xE6,
WRITE_BYTES, 4, 0x00, 0x00, 0x11, 0x11,
WRITE_C8_D16, 0xE7, 0x44, 0x44,
WRITE_COMMAND_8, 0xE8,
WRITE_BYTES, 16,
0x09, 0xE8, 0xD8, 0xA0,
0x0B, 0xEA, 0xD8, 0xA0,
0x0D, 0xEC, 0xD8, 0xA0,
0x0F, 0xEE, 0xD8, 0xA0,
WRITE_COMMAND_8, 0xEB,
WRITE_BYTES, 7, 0x02, 0x00, 0xE4, 0xE4, 0x88, 0x00, 0x40,
WRITE_C8_D16, 0xEC, 0x3C, 0x00,
WRITE_COMMAND_8, 0xED,
WRITE_BYTES, 16,
0xAB, 0x89, 0x76, 0x54, 0x02, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0x20, 0x45, 0x67, 0x98, 0xBA,
//-----------VAP & VAN---------------
WRITE_COMMAND_8, 0xFF,
WRITE_BYTES, 5, 0x77, 0x01, 0x00, 0x00, 0x13,
WRITE_C8_D8, 0xE5, 0xE4,
WRITE_COMMAND_8, 0xFF,
WRITE_BYTES, 5, 0x77, 0x01, 0x00, 0x00, 0x00,
// WRITE_COMMAND_8, 0x21, // 0x20 normal, 0x21 IPS !!
WRITE_C8_D8, 0x3A, 0x50, // 0x70 RGB888, 0x60 RGB666, 0x50 RGB565
WRITE_COMMAND_8, 0x11, // Sleep Out
END_WRITE,
DELAY, 120,
BEGIN_WRITE,
WRITE_COMMAND_8, 0x29, // Display On
END_WRITE};
Arduino_DataBus* bus = new Arduino_SWSPI(
GFX_NOT_DEFINED /* DC */, SPI_LCD_CS /* CS */, SPI_LCD_SCLK /* SCK */, SPI_LCD_MOSI /* MOSI */, GFX_NOT_DEFINED /* MISO */);
Arduino_ESP32RGBPanel* rgbpanel = new Arduino_ESP32RGBPanel(
LCD_DE /* DE */, LCD_VSYNC /* VSYNC */, LCD_HSYNC /* HSYNC */, LCD_PCLK /* PCLK */,
LCD_R0 /* B0 */, LCD_R1 /* B1 */, LCD_R2 /* B2 */, LCD_R3 /* B3 */, LCD_R4 /* B4 */,
LCD_G0 /* G0 */, LCD_G1 /* G1 */, LCD_G2 /* G2 */, LCD_G3 /* G3 */, LCD_G4 /* G4 */, LCD_G5 /* G5 */,
LCD_B0 /* R0 */, LCD_B1 /* R1 */, LCD_B2 /* R2 */, LCD_B3 /* R3 */, LCD_B4 /* R4 */,
1 /* hsync_polarity */, 10 /* hsync_front_porch */, 8 /* hsync_pulse_width */, 50 /* hsync_back_porch */,
1 /* vsync_polarity */, 10 /* vsync_front_porch */, 8 /* vsync_pulse_width */, 20 /* vsync_back_porch */,
10 /* pclk_active_neg */, 6000000L /* prefer_speed */, true);
Arduino_RGB_Display* gfx = new Arduino_RGB_Display(
LCD_WIDTH /* width */, LCD_HEIGHT /* height */, rgbpanel, 0 /* rotation */, true /* auto_flush */,
bus, -1 /* RST */, st7701_4848S040_init, sizeof(st7701_4848S040_init));
#endif
#endif
#if defined HAS_GT911_TOUCH
#include <Wire.h>
#include <Touch_GT911.h>
#define TOUCH_GT911_SCL 45
#define TOUCH_GT911_SDA 19
int touch_last_x = 0, touch_last_y = 0;
uint32_t last_touch_read = 0;
uint8_t is_new_touch_checked = false;
Touch_GT911 ts = Touch_GT911(TOUCH_GT911_SDA, TOUCH_GT911_SCL, max(480, 0), max(480, 0));
void touch_init()
{
ts.begin();
}
void touch_loop()
{
if (millis() - last_touch_read >= 50) {
last_touch_read = millis();
ts.read();
if (ts.isTouched)
{
touch_last_x = map(ts.points[0].x, 480, 0, 0, 480 - 1);
touch_last_y = map(ts.points[0].y, 480, 0, 0, 480 - 1);
Serial.printf("Touch position X: %i Y: %i\r\n", touch_last_x, touch_last_y);
if(is_new_touch_checked == false)
{
is_new_touch_checked = true;
if(touch_last_x <= 240)
sendAvail(WAKEUP_REASON_BUTTON1);
else
sendAvail(WAKEUP_REASON_BUTTON2);
}
}else{
is_new_touch_checked = false;
}
}
}
#else
void touch_init()
{
}
void touch_loop()
{
}
#endif
void TFTLog(String text) {
#ifdef HAS_LILYGO_TPANEL
#if defined HAS_LILYGO_TPANEL || defined HAS_4inch_TPANEL
gfx->setTextSize(2);
gfx->setTextSize(2);
if (tftLogscreen == false) {
gfx->fillScreen(BLACK);
gfx->setCursor(0, 0);
tftLogscreen = true;
}
}
if (text.isEmpty()) return;
gfx->setTextColor(WHITE);
gfx->println(text);
#else
#else
if (tftLogscreen == false) {
tft2.fillScreen(TFT_BLACK);
tft2.setCursor(0, 0, (tft2.width() == 160 ? 1 : 2));
@@ -220,7 +385,7 @@ void TFTLog(String text) {
tft2.setTextColor(TFT_GREEN);
}
tft2.println(text);
#endif
#endif
}
int32_t findId(uint8_t mac[8]) {
@@ -240,11 +405,13 @@ void sendAvail(uint8_t wakeupReason) {
memcpy(&eadr.src, mac, 6);
eadr.adr.lastPacketRSSI = WiFi.RSSI();
eadr.adr.currentChannel = config.channel;
#ifdef HAS_LILYGO_TPANEL
#ifdef TFT_HW_TYPE
eadr.adr.hwType = TFT_HW_TYPE;
#elif defined HAS_LILYGO_TPANEL || defined HAS_4inch_TPANEL
eadr.adr.hwType = 0xE2;
#else
#else
eadr.adr.hwType = (tft2.width() == 160 ? 0xE1 : 0xE0);
#endif
#endif
eadr.adr.wakeupReason = wakeupReason;
eadr.adr.capabilities = 0;
eadr.adr.tagSoftwareVersion = 0;
@@ -253,50 +420,65 @@ void sendAvail(uint8_t wakeupReason) {
}
void yellow_ap_display_init(void) {
#ifdef HAS_LILYGO_TPANEL
tftLogscreen = true;
#if defined HAS_LILYGO_TPANEL || defined HAS_4inch_TPANEL
tftLogscreen = true;
pinMode(LCD_BL, OUTPUT);
digitalWrite(LCD_BL, HIGH);
#if ESP_ARDUINO_VERSION_MAJOR == 2
ledcAttachPin(LCD_BL, 1);
ledcSetup(1, 1000, 8);
ledcWrite(1, config.tft); // brightness
#else
ledcAttachChannel(LCD_BL, 1000, 8, 1);
ledcWriteChannel(1, config.tft);
#endif
ledcWrite(1, config.tft); // brightness
#if defined HAS_LILYGO_TPANEL
Wire.begin(IIC_SDA, IIC_SCL);
#endif
gfx->begin();
gfx->fillScreen(BLACK);
#else
#else
#ifdef HAS_ELECROW_C6
YellowSense = 0;
#else
pinMode(YELLOW_SENSE, INPUT_PULLDOWN);
vTaskDelay(100 / portTICK_PERIOD_MS);
if (digitalRead(YELLOW_SENSE) == HIGH) YellowSense = 1;
#endif
pinMode(TFT_BACKLIGHT, OUTPUT);
digitalWrite(TFT_BACKLIGHT, LOW);
tft2.init();
#ifdef ST7735_NANO_TLSR
#ifdef ST7735_NANO_TLSR
YellowSense = 0;
tft2.setRotation(1);
#else
#else
tft2.setRotation(YellowSense == 1 ? 1 : 3);
#endif
#endif
tft2.fillScreen(TFT_BLACK);
tft2.setCursor(12, 0, (tft2.width() == 160 ? 1 : 2));
tft2.setTextColor(TFT_WHITE);
tftLogscreen = true;
ledcSetup(6, 5000, 8);
ledcAttachPin(TFT_BACKLIGHT, 6);
#if ESP_ARDUINO_VERSION_MAJOR == 2
ledcSetup(1, 5000, 8);
ledcAttachPin(TFT_BACKLIGHT, 1);
ledcWrite(1, config.tft);
if (tft2.width() == 160) {
GPIO.func_out_sel_cfg[TFT_BACKLIGHT].inv_sel = 1;
}
ledcWrite(6, config.tft);
#endif
#else
ledcAttachChannel(TFT_BACKLIGHT, 5000, 8, 1);
ledcWriteChannel(1, config.tft);
if (tft2.width() == 160) ledcOutputInvert(TFT_BACKLIGHT, true);
#endif
#endif
touch_init();
}
void yellow_ap_display_loop(void) {
@@ -338,19 +520,19 @@ void yellow_ap_display_loop(void) {
void* spriteData = spr.getPointer();
size_t bytesRead = file.readBytes((char*)spriteData, spr.width() * spr.height() * 2);
file.close();
#ifdef HAS_LILYGO_TPANEL
long dy = spr.height();
long dx = spr.width();
#if defined HAS_LILYGO_TPANEL || defined HAS_4inch_TPANEL
long dy = spr.height();
long dx = spr.width();
uint16_t* data = static_cast<uint16_t*>(const_cast<void*>(spriteData));
gfx->draw16bitRGBBitmap(0, 0, (uint16_t *)spriteData, dx, dy);
spr.deleteSprite();
#else
gfx->draw16bitRGBBitmap(0, 0, (uint16_t*)spriteData, dx, dy);
spr.deleteSprite();
#else
spr.pushSprite(0, 0);
#endif
#endif
tftLogscreen = false;
struct espXferComplete xfc = {0};
@@ -359,6 +541,7 @@ void yellow_ap_display_loop(void) {
}
last_update = millis();
}
touch_loop();
}
#endif

View File

@@ -29,8 +29,8 @@ void updateLanguageFromConfig() {
return;
}
DynamicJsonDocument doc(1024);
StaticJsonDocument<80> filter;
JsonDocument doc;
JsonDocument filter;
filter[String(currentLanguage)] = true;
const DeserializationError error = deserializeJson(doc, file, DeserializationOption::Filter(filter));
file.close();

View File

@@ -36,6 +36,14 @@ struct ledInstruction {
bool reQueue = false;
};
void ledcSet(uint8_t channel, uint8_t brightness) {
#if ESP_ARDUINO_VERSION_MAJOR == 2
ledcWrite(channel, brightness);
#else
ledcWriteChannel(channel, brightness);
#endif
}
#ifdef HAS_RGB_LED
void addToRGBQueue(struct ledInstructionRGB* rgb, bool requeue) {
@@ -132,13 +140,9 @@ void rgbIdleStep() {
void setBrightness(int brightness) {
maxledbrightness = brightness;
#ifdef HAS_LILYGO_TPANEL
ledcWrite(1, config.tft);
#else
#ifdef HAS_TFT
ledcWrite(6, config.tft);
#endif
#if defined HAS_LILYGO_TPANEL || defined HAS_4inch_TPANEL || HAS_TFT
ledcSet(1, config.tft);
#endif
#ifdef HAS_RGB_LED
FastLED.setBrightness(maxledbrightness);
@@ -150,12 +154,8 @@ void updateBrightnessFromConfig() {
if (newbrightness != maxledbrightness) {
setBrightness(newbrightness);
}
#ifdef HAS_LILYGO_TPANEL
ledcWrite(1, config.tft);
#else
#ifdef HAS_TFT
ledcWrite(6, config.tft);
#endif
#if defined HAS_LILYGO_TPANEL || defined HAS_4inch_TPANEL || HAS_TFT
ledcSet(1, config.tft);
#endif
if (apInfo.state == AP_STATE_NORADIO) addFadeMono(config.led);
}
@@ -176,11 +176,9 @@ void addFadeMono(uint8_t value) {
}
void showMono(uint8_t brightness) {
#ifdef CONFIG_IDF_TARGET_ESP32
ledcWrite(7, 255 - gamma8[brightness]);
#else
ledcWrite(7, gamma8[brightness]);
#endif
if (FLASHER_LED != -1) {
ledcSet(7, gamma8[brightness]);
}
}
void quickBlink(uint8_t repeat) {
@@ -222,11 +220,13 @@ void ledTask(void* parameter) {
ledQueue = xQueueCreate(30, sizeof(struct ledInstruction*));
ledcSetup(7, 5000, 8);
if (FLASHER_LED != -1) {
digitalWrite(FLASHER_LED, HIGH);
pinMode(FLASHER_LED, OUTPUT);
#if ESP_ARDUINO_VERSION_MAJOR == 2
ledcSetup(7, 5000, 8);
ledcAttachPin(FLASHER_LED, 7);
#else
ledcAttachChannel(FLASHER_LED, 1000, 8, 7);
#endif
}
struct ledInstruction* monoled = nullptr;
@@ -297,7 +297,7 @@ void ledTask(void* parameter) {
if (monoled->fadeTime <= 1) {
showMono(monoled->value);
}
}
}
} else {
if (monoled->fadeTime) {
monoled->fadeTime--;
@@ -313,4 +313,4 @@ void ledTask(void* parameter) {
vTaskDelay(1 / portTICK_PERIOD_MS);
}
}
}

View File

@@ -2,6 +2,9 @@
#include <Arduino.h>
#include <WiFi.h>
#include <time.h>
#ifdef ETHERNET_CLK_MODE
#include <ETH.h>
#endif
#include "contentmanager.h"
#include "flasher.h"
@@ -48,7 +51,12 @@ void delayedStart(void* parameter) {
}
void setup() {
#ifdef UART_LOGGING_TX_ONLY_PIN
Serial.begin(115200, SERIAL_8N1, -1, UART_LOGGING_TX_ONLY_PIN);
gpio_set_drive_capability((gpio_num_t)FLASHER_AP_RXD, GPIO_DRIVE_CAP_0);
#else
Serial.begin(115200);
#endif
#if ARDUINO_USB_CDC_ON_BOOT == 1
Serial.setTxTimeoutMs(0); // workaround bug in USB CDC that slows down serial output when no usb connected
#endif
@@ -114,6 +122,7 @@ void setup() {
}
*/
wm.initEth();
initAPconfig();
updateLanguageFromConfig();
@@ -150,7 +159,12 @@ void setup() {
// 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);
#else
#ifdef ETHERNET_CLK_MODE
if (!(ETHERNET_CLK_MODE == ETH_CLOCK_GPIO0_IN || ETHERNET_CLK_MODE == ETH_CLOCK_GPIO0_OUT))
#endif
pinMode(0, INPUT_PULLUP);
#endif
#ifdef HAS_EXT_FLASHER

View File

@@ -16,8 +16,10 @@
#endif
#include "commstructs.h"
#ifndef SAVE_SPACE
#include "g5/Group5.h"
#include "g5/g5enc.inl"
#endif
TFT_eSPI tft = TFT_eSPI();
TFT_eSprite spr = TFT_eSprite(&tft);
@@ -73,14 +75,43 @@ struct Error {
int32_t b;
};
uint32_t colorDistance(Color &c1, Color &c2, Error &e1) {
e1.r = constrain(e1.r, -255, 255);
e1.g = constrain(e1.g, -255, 255);
e1.b = constrain(e1.b, -255, 255);
uint32_t colorDistance(const Color &c1, const Color &c2, const Error &e1) {
int32_t r_diff = c1.r + e1.r - c2.r;
int32_t g_diff = c1.g + e1.g - c2.g;
int32_t b_diff = c1.b + e1.b - c2.b;
return 3 * r_diff * r_diff + 6 * g_diff * g_diff + b_diff * b_diff;
if (abs(c1.r - c1.g) < 20 && abs(c1.b - c1.g) < 20) {
if (abs(c2.r - c2.g) > 20 || abs(c2.b - c2.g) > 20) return 4294967295; // don't select color pixels on black and white
}
return 3 * r_diff * r_diff + 5.47 * g_diff * g_diff + 1.53 * b_diff * b_diff;
}
std::tuple<int, int, float, float> findClosestColors(const Color &pixel, const std::vector<Color> &palette) {
int closestIndex = -1, secondClosestIndex = -1;
float closestDist = std::numeric_limits<float>::max();
float secondClosestDist = std::numeric_limits<float>::max();
for (size_t i = 0; i < palette.size(); ++i) {
float dist = colorDistance(pixel, palette[i], (Error){0, 0, 0});
if (dist < closestDist) {
secondClosestIndex = closestIndex;
secondClosestDist = closestDist;
closestIndex = i;
closestDist = dist;
} else if (dist < secondClosestDist) {
secondClosestIndex = i;
secondClosestDist = dist;
}
}
if (closestIndex != -1 && secondClosestIndex != -1) {
auto rgbValue = [](const Color &color) {
return (color.r << 16) | (color.g << 8) | color.b;
};
if (rgbValue(palette[secondClosestIndex]) > rgbValue(palette[closestIndex])) {
std::swap(closestIndex, secondClosestIndex);
std::swap(closestDist, secondClosestDist);
}
}
return { closestIndex, secondClosestIndex, closestDist, secondClosestDist};
}
void spr2color(TFT_eSprite &spr, imgParam &imageParams, uint8_t *buffer, size_t buffer_size, bool is_red) {
@@ -110,11 +141,6 @@ void spr2color(TFT_eSprite &spr, imgParam &imageParams, uint8_t *buffer, size_t
Error *error_bufferold = new Error[bufw + 4];
Error *error_buffernew = new Error[bufw + 4];
const uint8_t ditherMatrix[4][4] = {
{0, 9, 2, 10},
{12, 5, 14, 6},
{3, 11, 1, 8},
{15, 7, 13, 4}};
size_t bitOffset = 0;
memset(error_bufferold, 0, bufw * sizeof(Error));
@@ -136,27 +162,40 @@ void spr2color(TFT_eSprite &spr, imgParam &imageParams, uint8_t *buffer, size_t
break;
}
if (imageParams.dither == 2) {
// Ordered dithering
uint8_t ditherValue = ditherMatrix[y % 4][x % 4] << (imageParams.bpp == 3 ? 2 : 4);
error_bufferold[x].r = ditherValue - (imageParams.bpp == 3 ? 30 : 120); // * 256 / 16 - 128 + 8
error_bufferold[x].g = ditherValue - (imageParams.bpp == 3 ? 30 : 120);
error_bufferold[x].b = ditherValue - (imageParams.bpp == 3 ? 30 : 120);
}
int best_color_index = 0;
uint32_t best_color_distance = colorDistance(color, palette[0], error_bufferold[x]);
for (int i = 1; i < num_colors; i++) {
if (best_color_distance == 0) break;
uint32_t distance = colorDistance(color, palette[i], error_bufferold[x]);
if (distance < best_color_distance) {
best_color_distance = distance;
best_color_index = i;
if (imageParams.dither == 2) {
// special ordered dithering
auto [c1Index, c2Index, distC1, distC2] = findClosestColors(color, palette);
Color c1 = palette[c1Index];
Color c2 = palette[c2Index];
float weight = distC1 / (distC1 + distC2);
if (weight <= 0.03) {
best_color_index = c1Index;
} else if (weight < 0.30) {
best_color_index = ((y % 2 && ((y / 2 + x) % 2)) ? c2Index : c1Index);
} else if (weight < 0.70) {
best_color_index = ((x + y) % 2 ? c2Index : c1Index);
} else if (weight < 0.97) {
best_color_index = ((y % 2 && ((y / 2 + x) % 2)) % 2 ? c1Index : c2Index);
} else {
best_color_index = c2Index;
}
}
if (imageParams.bpp == 3) {
if (imageParams.dither == 1 || imageParams.dither == 0) {
uint32_t best_color_distance = colorDistance(color, palette[0], error_bufferold[x]);
for (int i = 1; i < num_colors; i++) {
if (best_color_distance == 0) break;
uint32_t distance = colorDistance(color, palette[i], error_bufferold[x]);
if (distance < best_color_distance) {
best_color_distance = distance;
best_color_index = i;
}
}
}
if (imageParams.bpp == 3 || imageParams.bpp == 4) {
size_t byteIndex = bitOffset / 8;
uint8_t bitIndex = bitOffset % 8;
@@ -199,34 +238,44 @@ void spr2color(TFT_eSprite &spr, imgParam &imageParams, uint8_t *buffer, size_t
color.g + error_bufferold[x].g - palette[best_color_index].g,
color.b + error_bufferold[x].b - palette[best_color_index].b};
error_buffernew[x].r += error.r >> 2;
error_buffernew[x].g += error.g >> 2;
error_buffernew[x].b += error.b >> 2;
float scaling_factor = 255.0f / std::max(std::abs(error.r), std::max(std::abs(error.g), std::abs(error.b)));
if (scaling_factor < 1.0f) {
error.r *= scaling_factor;
error.g *= scaling_factor;
error.b *= scaling_factor;
}
error_buffernew[x].r += error.r / 4;
error_buffernew[x].g += error.g / 4;
error_buffernew[x].b += error.b / 4;
if (x > 0) {
error_buffernew[x - 1].r += error.r >> 3;
error_buffernew[x - 1].g += error.g >> 3;
error_buffernew[x - 1].b += error.b >> 3;
error_buffernew[x - 1].r += error.r / 8;
error_buffernew[x - 1].g += error.g / 8;
error_buffernew[x - 1].b += error.b / 8;
}
if (x > 1) {
error_buffernew[x - 2].r += error.r >> 4;
error_buffernew[x - 2].g += error.g >> 4;
error_buffernew[x - 2].b += error.b >> 4;
error_buffernew[x - 2].r += error.r / 16;
error_buffernew[x - 2].g += error.g / 16;
error_buffernew[x - 2].b += error.b / 16;
}
error_buffernew[x + 1].r += error.r >> 3;
error_buffernew[x + 1].g += error.g >> 3;
error_buffernew[x + 1].b += error.b >> 3;
error_bufferold[x + 1].r += error.r >> 2;
error_bufferold[x + 1].g += error.g >> 2;
error_bufferold[x + 1].b += error.b >> 2;
error_buffernew[x + 1].r += error.r / 8;
error_buffernew[x + 1].g += error.g / 8;
error_buffernew[x + 1].b += error.b / 8;
error_buffernew[x + 2].r += error.r >> 4;
error_buffernew[x + 2].g += error.g >> 4;
error_buffernew[x + 2].b += error.b >> 4;
error_bufferold[x + 1].r += error.r / 4;
error_bufferold[x + 1].g += error.g / 4;
error_bufferold[x + 1].b += error.b / 4;
error_bufferold[x + 2].r += error.r >> 3;
error_bufferold[x + 2].g += error.g >> 3;
error_bufferold[x + 2].b += error.b >> 3;
error_buffernew[x + 2].r += error.r / 16;
error_buffernew[x + 2].g += error.g / 16;
error_buffernew[x + 2].b += error.b / 16;
error_bufferold[x + 2].r += error.r / 8;
error_bufferold[x + 2].g += error.g / 8;
error_bufferold[x + 2].b += error.b / 8;
}
}
memcpy(error_bufferold, error_buffernew, bufw * sizeof(Error));
@@ -246,9 +295,9 @@ size_t prepareHeader(uint8_t headerbuf[], uint16_t bufw, uint16_t bufh, imgParam
memcpy(headerbuf + (imageParams.rotatebuffer % 2 == 1 ? 3 : 1), &bufw, sizeof(uint16_t));
memcpy(headerbuf + (imageParams.rotatebuffer % 2 == 1 ? 1 : 3), &bufh, sizeof(uint16_t));
if (imageParams.bpp == 3) {
totalbytes = buffer_size * 3 + headersize;
headerbuf[5] = 3;
if (imageParams.bpp == 3 || imageParams.bpp == 4) {
totalbytes = buffer_size * imageParams.bpp + headersize;
headerbuf[5] = imageParams.bpp;
} else if (imageParams.hasRed && imageParams.bpp > 1) {
totalbytes = buffer_size * 2 + headersize;
headerbuf[5] = 2;
@@ -289,19 +338,21 @@ void rewriteHeader(File &f_out) {
f_out.write(flg);
}
#ifndef SAVE_SPACE
uint8_t *g5Compress(uint16_t width, uint16_t height, uint8_t *buffer, uint16_t buffersize, uint16_t &outBufferSize) {
G5ENCIMAGE g5enc;
int rc;
uint8_t *outbuffer = (uint8_t *)ps_malloc(buffersize + 16384);
uint8_t *outbuffer = (uint8_t *)ps_malloc(buffersize+16384);
if (outbuffer == NULL) {
Serial.println("Failed to allocate the output buffer for the G5 encoder");
return nullptr;
}
rc = g5_encode_init(&g5enc, width, height, outbuffer, buffersize + 16384);
rc = g5_encode_init(&g5enc, width, height, outbuffer, buffersize);
for (int y = 0; y < height && rc == G5_SUCCESS; y++) {
rc = g5_encode_encodeLine(&g5enc, buffer);
buffer += (width / 8);
if (rc != G5_SUCCESS) break;
}
if (rc == G5_ENCODE_COMPLETE) {
outBufferSize = g5_encode_getOutSize(&g5enc);
@@ -312,10 +363,25 @@ uint8_t *g5Compress(uint16_t width, uint16_t height, uint8_t *buffer, uint16_t b
}
return outbuffer;
}
#endif
void doTimestamp(TFT_eSprite *spr) {
time_t now = time(nullptr);
struct tm *timeinfo = localtime(&now);
char buffer[20];
strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M", timeinfo);
// spr->drawRect(spr->width() - 16 * 6 - 4, spr->height() - 10 - 2, 16 * 6 + 3, 11, TFT_BLACK);
spr->drawRect(spr->width() - 16 * 6 - 3, spr->height() - 10 - 1, 16 * 6 + 1, 9, TFT_WHITE);
spr->setTextColor(TFT_BLACK, TFT_WHITE);
spr->setCursor(spr->width() - 16 * 6 - 2, spr->height() - 10, 1);
spr->print(buffer);
}
void spr2buffer(TFT_eSprite &spr, String &fileout, imgParam &imageParams) {
long t = millis();
if (config.showtimestamp) doTimestamp(&spr);
#ifdef HAS_TFT
extern uint8_t YellowSense;
if (fileout == "direct") {
@@ -334,7 +400,7 @@ void spr2buffer(TFT_eSprite &spr, String &fileout, imgParam &imageParams) {
size_t dataSize = spr.width() * spr.height() * (spr.getColorDepth() / 8);
memcpy(spriteData2, spriteData, dataSize);
#ifdef HAS_LILYGO_TPANEL
#if defined HAS_LILYGO_TPANEL || defined HAS_4inch_TPANEL
if (spr.getColorDepth() == 16) {
long dy = spr.height();
long dx = spr.width();
@@ -406,6 +472,7 @@ void spr2buffer(TFT_eSprite &spr, String &fileout, imgParam &imageParams) {
free(comp);
rewriteHeader(f_out);
#ifndef SAVE_SPACE
} else if (imageParams.g5) {
// handling for G5-compressed image data
@@ -434,7 +501,6 @@ void spr2buffer(TFT_eSprite &spr, String &fileout, imgParam &imageParams) {
} else {
height *= 2;
}
}
uint16_t outbufferSize = 0;
uint8_t *outBuffer;
@@ -452,7 +518,6 @@ void spr2buffer(TFT_eSprite &spr, String &fileout, imgParam &imageParams) {
if (outbufferSize > buffer_size) {
printf("That wasn't very useful, falling back to raw\n");
compressionSuccessful = false;
f_out.seek(0);
} else {
f_out.write(outBuffer, outbufferSize);
}
@@ -466,8 +531,10 @@ void spr2buffer(TFT_eSprite &spr, String &fileout, imgParam &imageParams) {
} else {
imageParams.dataType = DATATYPE_IMG_RAW_1BPP;
}
f_out.seek(0);
f_out.write(buffer, buffer_size);
}
#endif
} else {
f_out.write(buffer, buffer_size);
if (imageParams.hasRed && imageParams.bpp > 1) {
@@ -479,9 +546,10 @@ void spr2buffer(TFT_eSprite &spr, String &fileout, imgParam &imageParams) {
free(buffer);
} break;
case 3: {
case 3:
case 4: {
long bufw = spr.width(), bufh = spr.height();
size_t buffer_size = (bufw * bufh) / 8 * 3;
size_t buffer_size = (bufw * bufh) / 8 * imageParams.bpp;
uint8_t *buffer = (uint8_t *)ps_malloc(buffer_size);
if (!buffer) {
Serial.println("Failed to allocate buffer");

View File

@@ -275,7 +275,8 @@ void prepareExternalDataAvail(struct pendingData* pending, IPAddress remoteIP) {
case DATATYPE_IMG_RAW_1BPP:
case DATATYPE_IMG_RAW_2BPP:
case DATATYPE_IMG_G5:
case DATATYPE_IMG_RAW_3BPP: {
case DATATYPE_IMG_RAW_3BPP:
case DATATYPE_IMG_RAW_4BPP: {
char hexmac[17];
mac2hex(pending->targetMac, hexmac);
String filename = "/current/" + String(hexmac) + "_" + String(millis() % 1000000) + ".pending";
@@ -338,8 +339,7 @@ void prepareExternalDataAvail(struct pendingData* pending, IPAddress remoteIP) {
break;
}
case DATATYPE_NFC_RAW_CONTENT:
case DATATYPE_NFC_URL_DIRECT:
case DATATYPE_CUSTOM_LUT_OTA: {
case DATATYPE_NFC_URL_DIRECT: {
char hexmac[17];
mac2hex(pending->targetMac, hexmac);
char dataUrl[80];
@@ -348,7 +348,7 @@ void prepareExternalDataAvail(struct pendingData* pending, IPAddress remoteIP) {
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 " + String(dataUrl));
logLine("http DATATYPE_NFC_* " + String(dataUrl));
http.begin(dataUrl);
int httpCode = http.GET();
if (httpCode == 200) {
@@ -447,9 +447,11 @@ void processXferComplete(struct espXferComplete* xfc, bool local) {
contentFS->remove(dst_path);
}
if (contentFS->exists(queueItem->filename)) {
if (config.preview && (queueItem->pendingdata.availdatainfo.dataType == DATATYPE_IMG_RAW_3BPP || queueItem->pendingdata.availdatainfo.dataType == DATATYPE_IMG_RAW_2BPP || queueItem->pendingdata.availdatainfo.dataType == DATATYPE_IMG_RAW_1BPP || queueItem->pendingdata.availdatainfo.dataType == DATATYPE_IMG_G5 || queueItem->pendingdata.availdatainfo.dataType == DATATYPE_IMG_ZLIB)) {
uint8_t dataType = queueItem->pendingdata.availdatainfo.dataType;
if (config.preview && dataType != DATATYPE_FW_UPDATE && dataType != DATATYPE_NOUPDATE) {
contentFS->rename(queueItem->filename, String(dst_path));
} else {
}
else {
if (queueItem->pendingdata.availdatainfo.dataType != DATATYPE_FW_UPDATE) contentFS->remove(queueItem->filename);
}
}
@@ -505,9 +507,9 @@ void processXferTimeout(struct espXferComplete* xfc, bool local) {
if (taginfo != nullptr) {
taginfo->pendingIdle = 60;
clearPending(taginfo);
while (dequeueItem(xfc->src)) {
};
}
while (dequeueItem(xfc->src)) {
};
checkQueue(xfc->src);
@@ -558,15 +560,14 @@ void processDataReq(struct espAvailDataReq* eadr, bool local, IPAddress remoteIP
taginfo->apIp = IPAddress(0, 0, 0, 0);
}
if (taginfo->pendingIdle == 0) {
taginfo->expectedNextCheckin = now + 60;
if (taginfo->pendingIdle == 0 || countQueueItem(eadr->src) > 0) {
if (taginfo->expectedNextCheckin < now + 60) taginfo->expectedNextCheckin = now + 60;
} else if (taginfo->pendingIdle == 9999) {
taginfo->expectedNextCheckin = 3216153600;
taginfo->pendingIdle = 0;
} else {
taginfo->expectedNextCheckin = now + taginfo->pendingIdle;
taginfo->pendingIdle = 0;
}
taginfo->pendingIdle = 0;
taginfo->lastseen = now;
if (eadr->adr.lastPacketRSSI != 0) {
@@ -605,6 +606,7 @@ void processDataReq(struct espAvailDataReq* eadr, bool local, IPAddress remoteIP
if (local) {
sprintf(buffer, "<ADR %02X%02X%02X%02X%02X%02X%02X%02X\r\n\0", eadr->src[7], eadr->src[6], eadr->src[5], eadr->src[4], eadr->src[3], eadr->src[2], eadr->src[1], eadr->src[0]);
Serial.print(buffer);
checkQueue(eadr->src); // experiemental 3/26/25: redundant check
}
if (local) {
@@ -750,6 +752,35 @@ bool sendTagCommand(const uint8_t* dst, uint8_t cmd, bool local, const uint8_t*
}
}
bool sendTagMac(const uint8_t* dst, const uint64_t newmac, bool local) {
struct pendingData pending = {0};
memcpy(pending.targetMac, dst, 8);
pending.availdatainfo.dataType = DATATYPE_COMMAND_DATA;
pending.availdatainfo.dataTypeArgument = 0x23;
pending.availdatainfo.nextCheckIn = 0;
pending.availdatainfo.dataVer = newmac;
pending.availdatainfo.dataSize = 0;
pending.attemptsLeft = MAX_XFER_ATTEMPTS;
Serial.printf(">Tag %02X%02X%02X%02X%02X%02X%02X%02X Mac set\r\n\0", dst[7], dst[6], dst[5], dst[4], dst[3], dst[2], dst[1], dst[0]);
tagRecord* taginfo = tagRecord::findByMAC(dst);
if (taginfo != nullptr) {
taginfo->pendingCount++;
wsSendTaginfo(taginfo->mac, SYNC_TAGSTATUS);
}
if (local) {
return queueDataAvail(&pending, true);
} else {
queueDataAvail(&pending, false);
udpsync.netSendDataAvail(&pending);
return true;
}
}
void updateTaginfoitem(struct TagInfo* taginfoitem, IPAddress remoteIP) {
tagRecord* taginfo = tagRecord::findByMAC(taginfoitem->mac);
@@ -802,7 +833,7 @@ bool checkMirror(struct tagRecord* taginfo, struct pendingData* pending) {
for (int16_t c = 0; c < tagDB.size(); c++) {
tagRecord* taginfo2 = tagDB.at(c);
if (taginfo2->contentMode == 20 && taginfo2->version == 0) {
DynamicJsonDocument doc(500);
JsonDocument doc;
deserializeJson(doc, taginfo2->modeConfigJson);
JsonObject cfgobj = doc.as<JsonObject>();
uint8_t mac[8] = {0};
@@ -955,20 +986,23 @@ bool queueDataAvail(struct pendingData* pending, bool local) {
taginfo->data = nullptr;
} else {
newPending.data = nullptr;
// optional: read data early, don't wait for block request.
fs::File file = contentFS->open(newPending.filename);
if (file) {
newPending.data = getDataForFile(file);
Serial.println("Reading file " + String(newPending.filename));
file.close();
} else {
Serial.println("Warning: not found: " + String(newPending.filename));
if (pendingQueue.size() < 5) { // maximized to 5 to save some memory
// optional: read data early, don't wait for block request.
fs::File file = contentFS->open(newPending.filename);
if (file) {
newPending.data = getDataForFile(file);
Serial.println("Reading file " + String(newPending.filename));
file.close();
} else {
Serial.println("Warning: not found: " + String(newPending.filename));
}
}
}
newPending.len = taginfo->len;
if ((pending->availdatainfo.dataType == DATATYPE_IMG_RAW_1BPP || pending->availdatainfo.dataType == DATATYPE_IMG_RAW_2BPP || pending->availdatainfo.dataType == DATATYPE_IMG_RAW_3BPP || pending->availdatainfo.dataType == DATATYPE_IMG_ZLIB || pending->availdatainfo.dataType == DATATYPE_IMG_G5) && (pending->availdatainfo.dataTypeArgument & 0xF8) == 0x00) {
uint8_t dataType = pending->availdatainfo.dataType;
if (dataType != DATATYPE_FW_UPDATE && dataType != DATATYPE_NOUPDATE && pending->availdatainfo.dataTypeArgument & 0xF8 == 0x00) {
// in case of an image (no preload), remove already queued images
pendingQueue.erase(std::remove_if(pendingQueue.begin(), pendingQueue.end(),
[pending](const PendingItem& item) {

View File

@@ -7,6 +7,7 @@
#include <MD5Builder.h>
#include <Update.h>
#include "flasher.h"
#include "espflasher.h"
#include "leds.h"
#include "serialap.h"
@@ -15,6 +16,7 @@
#include "util.h"
#include "web.h"
#ifndef BUILD_ENV_NAME
#define BUILD_ENV_NAME unknown
#endif
@@ -30,9 +32,11 @@
#define STR_IMPL(x) #x
#define STR(x) STR_IMPL(x)
#define LOG(format, ... ) Serial.printf(format,## __VA_ARGS__)
void handleSysinfoRequest(AsyncWebServerRequest* request) {
StaticJsonDocument<250> doc;
JsonDocument doc;
doc["alias"] = config.alias;
doc["env"] = STR(BUILD_ENV_NAME);
doc["buildtime"] = STR(BUILD_TIME);
@@ -41,11 +45,20 @@ void handleSysinfoRequest(AsyncWebServerRequest* request) {
doc["psramsize"] = ESP.getPsramSize();
doc["flashsize"] = ESP.getFlashChipSize();
doc["rollback"] = Update.canRollBack();
#if defined C6_OTA_FLASHING
doc["hasC6"] = 1;
#else
doc["ap_version"] = apInfo.version;
doc["hasC6"] = 0;
doc["hasH2"] = 0;
doc["hasTslr"] = 0;
#if defined HAS_H2
doc["hasH2"] = 1;
#elif defined HAS_TSLR
doc["hasTslr"] = 1;
#elif defined C6_OTA_FLASHING
doc["hasC6"] = 1;
#endif
#ifdef HAS_EXT_FLASHER
doc["hasFlasher"] = 1;
#else
@@ -66,7 +79,7 @@ void handleCheckFile(AsyncWebServerRequest* request) {
const String filePath = request->getParam("path")->value();
File file = contentFS->open(filePath, "r");
if (!file) {
StaticJsonDocument<64> doc;
JsonDocument doc;
doc["filesize"] = 0;
doc["md5"] = "";
String jsonResponse;
@@ -85,7 +98,7 @@ void handleCheckFile(AsyncWebServerRequest* request) {
file.close();
StaticJsonDocument<128> doc;
JsonDocument doc;
doc["filesize"] = fileSize;
doc["md5"] = md5Hash;
String jsonResponse;
@@ -135,12 +148,13 @@ void handleLittleFSUpload(AsyncWebServerRequest* request, String filename, size_
file.write(uploadInfo->buffer, uploadInfo->bufferSize);
file.close();
uploadInfo->bufferSize = 0;
xSemaphoreGive(fsMutex);
} else {
xSemaphoreGive(fsMutex);
logLine("Failed to open file for appending: " + uploadfilename);
final = true;
error = true;
}
xSemaphoreGive(fsMutex);
memcpy(uploadInfo->buffer, data, len);
uploadInfo->bufferSize = len;
@@ -153,11 +167,12 @@ void handleLittleFSUpload(AsyncWebServerRequest* request, String filename, size_
if (file) {
file.write(uploadInfo->buffer, uploadInfo->bufferSize);
file.close();
xSemaphoreGive(fsMutex);
} else {
xSemaphoreGive(fsMutex);
logLine("Failed to open file for appending: " + uploadfilename);
error = true;
}
xSemaphoreGive(fsMutex);
request->_tempObject = nullptr;
delete uploadInfo;
}
@@ -289,22 +304,29 @@ void handleRollback(AsyncWebServerRequest* request) {
}
}
#ifdef C6_OTA_FLASHING
void C6firmwareUpdateTask(void* parameter) {
uint8_t doDownload = *((uint8_t*)parameter);
char* urlPtr = reinterpret_cast<char*>(parameter);
LOG("C6firmwareUpdateTask: url '%s'\n", urlPtr);
wsSerial("Stopping AP service");
setAPstate(false, AP_STATE_FLASHING);
gSerialTaskState = SERIAL_STATE_STOP;
config.runStatus = RUNSTATUS_STOP;
setAPstate(false, AP_STATE_FLASHING);
#ifndef FLASHER_DEBUG_SHARED
extern bool rxSerialStopTask2;
rxSerialStopTask2 = true;
#endif
vTaskDelay(500 / portTICK_PERIOD_MS);
Serial1.end();
setAPstate(false, AP_STATE_FLASHING);
wsSerial("C6 flash starting");
wsSerial(SHORT_CHIP_NAME " flash starting");
bool result = doC6flash(doDownload);
bool result = FlashC6_H2(urlPtr);
wsSerial("C6 flash end");
wsSerial(SHORT_CHIP_NAME " flash end");
if (result) {
setAPstate(false, AP_STATE_OFFLINE);
@@ -314,36 +336,65 @@ void C6firmwareUpdateTask(void* parameter) {
wsSerial("starting monitor");
Serial1.begin(115200, SERIAL_8N1, FLASHER_AP_RXD, FLASHER_AP_TXD);
#ifndef FLASHER_DEBUG_SHARED
rxSerialStopTask2 = false;
#ifdef FLASHER_DEBUG_RXD
xTaskCreate(rxSerialTask2, "rxSerialTask2", 1750, NULL, 2, NULL);
xTaskCreate(rxSerialTask2, "rxSerialTask2", 1850, NULL, 2, NULL);
#endif
vTaskDelay(1000 / portTICK_PERIOD_MS);
wsSerial("resetting AP");
APTagReset();
vTaskDelay(1000 / portTICK_PERIOD_MS);
apInfo.version = 0;
wsSerial("bringing AP online");
if (bringAPOnline()) config.runStatus = RUNSTATUS_RUN;
// if (bringAPOnline(AP_STATE_REQUIRED_POWER_CYCLE)) config.runStatus = RUNSTATUS_STOP;
if (bringAPOnline(AP_STATE_ONLINE)) {
config.runStatus = RUNSTATUS_RUN;
setAPstate(true, AP_STATE_ONLINE);
}
wsSerial("Finished!");
} else {
wsSerial("Flashing failed. :-(");
// Wait for version info to arrive
vTaskDelay(500 / portTICK_PERIOD_MS);
if(apInfo.version == 0) {
result = false;
}
}
if (result) {
wsSerial("Finished!");
char buffer[50];
snprintf(buffer,sizeof(buffer),
"ESP32-" SHORT_CHIP_NAME " version is now %04x", apInfo.version);
wsSerial(String(buffer));
}
else if(apInfo.version == 0) {
wsSerial("AP failed to come online. :-(");
}
else {
wsSerial("Flashing failed. :-(");
}
// wsSerial("Reboot system now");
// wsSerial("[reboot]");
free(urlPtr);
vTaskDelay(30000 / portTICK_PERIOD_MS);
vTaskDelete(NULL);
}
#endif
void handleUpdateC6(AsyncWebServerRequest* request) {
#if defined C6_OTA_FLASHING
uint8_t doDownload = 1;
if (request->hasParam("download", true)) {
doDownload = atoi(request->getParam("download", true)->value().c_str());
if (request->hasParam("url",true)) {
const char* urlStr = request->getParam("url", true)->value().c_str();
char* urlCopy = strdup(urlStr);
xTaskCreate(C6firmwareUpdateTask, "OTAUpdateTask", 6400, urlCopy, 10, NULL);
request->send(200, "Ok");
}
xTaskCreate(C6firmwareUpdateTask, "OTAUpdateTask", 6144, &doDownload, 10, NULL);
request->send(200, "Ok");
else {
LOG("Sending bad request");
request->send(400, "Bad request");
}
#elif defined(SHORT_CHIP_NAME)
request->send(400, SHORT_CHIP_NAME " flashing not implemented");
#else
request->send(400, "C6 flashing not implemented");
request->send(400, "C6/H2 flashing not implemented");
#endif
}
@@ -355,7 +406,7 @@ void handleUpdateActions(AsyncWebServerRequest* request) {
request->send(200, "No update actions needed");
return;
}
DynamicJsonDocument doc(1000);
JsonDocument doc;
DeserializationError error = deserializeJson(doc, file);
const JsonArray deleteFiles = doc["deletefile"].as<JsonArray>();
for (const auto& filePath : deleteFiles) {
@@ -367,4 +418,4 @@ void handleUpdateActions(AsyncWebServerRequest* request) {
wsSerial("Cleanup finished");
request->send(200, "Clean up finished");
contentFS->remove("/update_actions.json");
}
}

View File

@@ -3,6 +3,7 @@
#include <Arduino.h>
#include "settings.h"
#include "leds.h"
#ifdef HAS_EXT_FLASHER
#include "soc/rtc_cntl_reg.h"
@@ -34,16 +35,21 @@ void rampTagPower(uint8_t* pin, bool up) {
WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0);
#endif
if (up) {
#if ESP_ARDUINO_VERSION_MAJOR == 2
ledcSetup(0, 50000, 8);
ledcWrite(0, 254);
vTaskDelay(1 / portTICK_PERIOD_MS);
pinMode(pin[0], OUTPUT);
ledcAttachPin(pin[0], 0);
#else
ledcWriteChannel(0, 254);
ledcAttachChannel(pin[0], 50000, 8, 0);
#endif
pinMode(FLASHER_EXT_RESET, OUTPUT);
digitalWrite(FLASHER_EXT_RESET, LOW);
ledcAttachPin(pin[0], 0);
vTaskDelay(10 / portTICK_PERIOD_MS);
for (uint8_t c = 254; c != 0xFF; c--) {
ledcWrite(0, c);
ledcSet(0, c);
delayMicroseconds(700);
}
digitalWrite(pin[0], LOW);
@@ -52,14 +58,14 @@ void rampTagPower(uint8_t* pin, bool up) {
digitalWrite(FLASHER_EXT_RESET, INPUT_PULLUP);
} else {
ledcSetup(0, 50000, 8);
ledcWrite(0, 0);
ledcSet(0, 0);
vTaskDelay(1 / portTICK_PERIOD_MS);
pinMode(pin[0], OUTPUT);
pinMode(FLASHER_EXT_RESET, INPUT_PULLDOWN);
ledcAttachPin(pin[0], 0);
vTaskDelay(10 / portTICK_PERIOD_MS);
for (uint8_t c = 0; c < 0xFF; c++) {
ledcWrite(0, c);
ledcSet(0, c);
if (c > 250) {
vTaskDelay(2 / portTICK_PERIOD_MS);
} else {

View File

@@ -2,6 +2,7 @@
#include <Arduino.h>
#include <HardwareSerial.h>
#include <system.h>
#include "commstructs.h"
#include "contentmanager.h"
@@ -14,6 +15,8 @@
#include "web.h"
#include "zbs_interface.h"
#define LOG(format, ...) printf(format, ##__VA_ARGS__)
QueueHandle_t rxCmdQueue;
SemaphoreHandle_t txActive;
@@ -25,7 +28,9 @@ SemaphoreHandle_t txActive;
volatile uint8_t cmdReplyValue = CMD_REPLY_WAIT;
#define AP_SERIAL_PORT Serial1
#ifndef FLASHER_DEBUG_SHARED
volatile bool rxSerialStopTask2 = false;
#endif
uint8_t channelList[6];
struct espSetChannelPower curChannel = {0, 11, 10};
@@ -42,6 +47,8 @@ struct espSetChannelPower curChannel = {0, 11, 10};
volatile uint32_t lastAPActivity = 0;
struct APInfoS apInfo;
volatile ApSerialState gSerialTaskState;
struct rxCmd {
uint8_t* data;
uint8_t len;
@@ -150,12 +157,28 @@ void setAPstate(bool isOnline, uint8_t state) {
CRGB::Red,
CRGB::YellowGreen};
rgbIdleColor = colorMap[state];
#ifdef BLE_ONLY
rgbIdleColor = CRGB::Green;
#endif
#ifdef BLE_ONLY
rgbIdleColor = CRGB::Green;
#endif
rgbIdlePeriod = (isOnline ? 767 : 255);
if (isOnline) rgbIdle();
#endif
#ifdef FLASHER_DEBUG_SHARED
// Flasher shares port with AP comms
if (state == AP_STATE_FLASHING) {
LOG("Shared COM port, gSerialTaskState %d\n", gSerialTaskState);
gSerialTaskState = SERIAL_STATE_STOP;
for (int i = 0; i < 100; i++) {
vTaskDelay(1 / portTICK_RATE_MS);
if (gSerialTaskState == SERIAL_STATE_STOPPED) {
gSerialTaskState = SERIAL_STATE_NONE;
break;
}
}
LOG("gSerialTaskState %d\n", gSerialTaskState);
}
#endif
wsSendSysteminfo();
}
// Reset the tag
@@ -384,46 +407,49 @@ void rxCmdProcessor(void* parameter) {
txActive = xSemaphoreCreateBinary();
xSemaphoreGive(txActive);
while (1) {
struct rxCmd* rxcmd = nullptr;
BaseType_t q = xQueueReceive(rxCmdQueue, &rxcmd, 10);
if (q == pdTRUE) {
switch (rxcmd->type) {
case RX_CMD_RQB:
processBlockRequest((struct espBlockRequest*)rxcmd->data);
if (apInfo.isOnline) {
struct rxCmd* rxcmd = nullptr;
BaseType_t q = xQueueReceive(rxCmdQueue, &rxcmd, 10);
if (q == pdTRUE) {
switch (rxcmd->type) {
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);
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);
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:
processXferTimeout((struct espXferComplete*)rxcmd->data, true);
break;
case RX_CMD_RSET:
Serial.println("AP did reset, resending pending\r\n");
refreshAllPending();
sendChannelPower(&curChannel);
break;
case RX_CMD_TRD:
// received tag return data
processTagReturnData((struct espTagReturnData*)rxcmd->data, rxcmd->len, true);
break;
break;
case RX_CMD_XTO:
processXferTimeout((struct espXferComplete*)rxcmd->data, true);
break;
case RX_CMD_RSET:
Serial.println("AP did reset, resending pending\r\n");
refreshAllPending();
sendChannelPower(&curChannel);
break;
case RX_CMD_TRD:
// received tag return data
processTagReturnData((struct espTagReturnData*)rxcmd->data, rxcmd->len, true);
break;
}
if (rxcmd->data) free(rxcmd->data);
if (rxcmd) free(rxcmd);
}
if (rxcmd->data) free(rxcmd->data);
if (rxcmd) free(rxcmd);
}
vTaskDelay(1 / portTICK_PERIOD_MS);
}
}
void rxSerialTask(void* parameter) {
@@ -435,7 +461,9 @@ void rxSerialTask(void* parameter) {
static char lastchar = 0;
static uint8_t charindex = 0;
while (1) {
gSerialTaskState = SERIAL_STATE_RUNNING;
LOG("rxSerialTask starting\n");
while (gSerialTaskState == SERIAL_STATE_RUNNING) {
while (AP_SERIAL_PORT.available()) {
lastchar = AP_SERIAL_PORT.read();
switch (RXState) {
@@ -506,8 +534,8 @@ void rxSerialTask(void* parameter) {
packetp = (uint8_t*)calloc(sizeof(struct espBlockRequest) + 8, 1);
memset(cmdbuffer, 0x00, 4);
lastAPActivity = millis();
if (apInfo.isOnline == false)
setAPstate(true, AP_STATE_ONLINE);
// don't set APstate heree, as it interferes with the flashing process
// if (apInfo.isOnline == false && config.runStatus == RUNSTATUS_RUN) setAPstate(true, AP_STATE_ONLINE);
}
if (strncmp(cmdbuffer, "ADR>", 4) == 0) {
RXState = ZBS_RX_WAIT_DATA_REQ;
@@ -516,8 +544,8 @@ void rxSerialTask(void* parameter) {
packetp = (uint8_t*)calloc(sizeof(struct espAvailDataReq) + 8, 1);
memset(cmdbuffer, 0x00, 4);
lastAPActivity = millis();
if (apInfo.isOnline == false)
setAPstate(true, AP_STATE_ONLINE);
// don't set APstate heree, as it interferes with the flashing process
// if (apInfo.isOnline == false && config.runStatus == RUNSTATUS_RUN) setAPstate(true, AP_STATE_ONLINE);
}
if (strncmp(cmdbuffer, "XFC>", 4) == 0) {
RXState = ZBS_RX_WAIT_XFERCOMPLETE;
@@ -540,8 +568,6 @@ void rxSerialTask(void* parameter) {
packetp = (uint8_t*)calloc(sizeof(struct espTagReturnData) + 8, 1);
memset(cmdbuffer, 0x00, 4);
lastAPActivity = millis();
if (apInfo.isOnline == false)
setAPstate(true, AP_STATE_ONLINE);
}
break;
case ZBS_RX_BLOCK_REQUEST:
@@ -666,10 +692,26 @@ void rxSerialTask(void* parameter) {
}
vTaskDelay(1 / portTICK_PERIOD_MS);
} // end of while(1)
AP_SERIAL_PORT.end();
gSerialTaskState = SERIAL_STATE_STOPPED;
LOG("rxSerialTask stopped\n");
vTaskDelete(NULL);
}
#if defined(FLASHER_DEBUG_RXD) && !defined(FLASHER_DEBUG_SHARED)
uint32_t millisDiff(uint32_t m) {
uint32_t ms = millis();
if (ms >= m)
return ms - m;
else
return UINT32_MAX - m + ms + 1;
}
#ifdef FLASHER_DEBUG_RXD
void rxSerialTask2(void* parameter) {
char rxStr[100] = {0};
int rxStrCount = 0;
uint32_t modemResetHoldoff = millis();
char lastchar = 0;
time_t startTime = millis();
int charCount = 0;
@@ -681,6 +723,33 @@ void rxSerialTask2(void* parameter) {
// debug info
Serial.write(lastchar);
rxStr[rxStrCount] = lastchar;
if (lastchar == '\n' || lastchar == '\r') {
if (strncmp(rxStr, "receive buffer full, drop the current frame", 43) == 0 && millisDiff(modemResetHoldoff) > 20000) {
modemResetHoldoff = millis();
vTaskDelay(100 / portTICK_PERIOD_MS);
config.runStatus = RUNSTATUS_STOP;
Serial.println("IEEE802.15.4 modem stuck case detected, resetting...");
APTagReset();
vTaskDelay(1000 / portTICK_PERIOD_MS);
Serial.println("bringing AP online again");
if (bringAPOnline()) {
config.runStatus = RUNSTATUS_RUN;
Serial.println("Finished!");
} else {
Serial.println("Failed!");
}
logLine("IEEE802.15.4 modem reset " + (config.runStatus == RUNSTATUS_RUN) ? ("ok") : ("failed"));
}
rxStrCount = 0;
memset(rxStr, 0, sizeof(rxStr));
} else if (rxStrCount < sizeof(rxStr) - 2) {
rxStrCount++;
} else {
rxStrCount = 0;
memset(rxStr, 0, sizeof(rxStr));
}
}
vTaskDelay(1 / portTICK_PERIOD_MS);
@@ -688,7 +757,7 @@ void rxSerialTask2(void* parameter) {
if (currentTime - startTime >= 1000) {
if (charCount > 6000) {
rxSerialStopTask2 = true;
Serial.println("Serial monitor stopped because of flooding (" + String(charCount) + " characters per second");
Serial.println("Serial monitor stopped because of flooding (" + String(charCount) + " characters per second)");
}
startTime = currentTime;
charCount = 0;
@@ -746,12 +815,36 @@ void segmentedShowIp() {
vTaskDelay(2000 / portTICK_PERIOD_MS);
}
bool bringAPOnline() {
#ifdef BLE_ONLY
bool bringAPOnline(uint8_t newState) {
#ifdef BLE_ONLY
apInfo.state = AP_STATE_NORADIO;
#endif
#endif
if (apInfo.state == AP_STATE_NORADIO) return true;
if (apInfo.state == AP_STATE_FLASHING) return false;
if (gSerialTaskState != SERIAL_STATE_INITIALIZED) {
#ifdef HAS_ELECROW_ADV_2_8
// Set GPIO45 low to connect the wireless interface to the multiplexed pins
pinMode(45, OUTPUT);
digitalWrite(45, LOW);
#endif
#if (AP_PROCESS_PORT == FLASHER_AP_PORT)
AP_SERIAL_PORT.begin(115200, SERIAL_8N1, FLASHER_AP_RXD, FLASHER_AP_TXD);
#elif defined(HAS_EXT_FLASHER)
#if (AP_PROCESS_PORT == FLASHER_EXT_PORT)
AP_SERIAL_PORT.begin(115200, SERIAL_8N1, FLASHER_EXT_RXD, FLASHER_EXT_TXD);
#elif (AP_PROCESS_PORT == FLASHER_ALTRADIO_PORT)
AP_SERIAL_PORT.begin(115200, SERIAL_8N1, FLASHER_AP_RXD, FLASHER_AP_TXD);
#endif
#endif
gSerialTaskState = SERIAL_STATE_INITIALIZED;
}
if (gSerialTaskState != SERIAL_STATE_RUNNING) {
gSerialTaskState = SERIAL_STATE_STARTING;
xTaskCreate(rxSerialTask, "rxSerialTask", 1750, NULL, 11, NULL);
vTaskDelay(500 / portTICK_PERIOD_MS);
}
setAPstate(false, AP_STATE_OFFLINE);
// try without rebooting
AP_SERIAL_PORT.updateBaudRate(115200);
@@ -788,18 +881,18 @@ bool bringAPOnline() {
}
vTaskDelay(200 / portTICK_PERIOD_MS);
setAPstate(true, AP_STATE_ONLINE);
setAPstate(newState == AP_STATE_ONLINE ? true : false, newState);
return true;
}
}
bool checkRadio() {
#ifdef BLE_ONLY
#ifdef BLE_ONLY
return false;
#endif
#ifndef C6_OTA_FLASHING
#endif
#ifndef C6_OTA_FLASHING
return true;
#endif
#endif
// 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);
@@ -823,25 +916,11 @@ void APTask(void* parameter) {
return;
}
#if (AP_PROCESS_PORT == FLASHER_AP_PORT)
AP_SERIAL_PORT.begin(115200, SERIAL_8N1, FLASHER_AP_RXD, FLASHER_AP_TXD);
#endif
#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
#if (AP_PROCESS_PORT == FLASHER_ALTRADIO_PORT)
AP_SERIAL_PORT.begin(115200, SERIAL_8N1, FLASHER_AP_RXD, FLASHER_AP_TXD);
#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
#if defined(FLASHER_DEBUG_RXD) && !defined(FLASHER_DEBUG_SHARED)
xTaskCreate(rxSerialTask2, "rxSerialTask2", 1850, NULL, 2, NULL);
vTaskDelay(500 / portTICK_PERIOD_MS);
#endif
bringAPOnline();
#ifndef C6_OTA_FLASHING
@@ -876,7 +955,7 @@ void APTask(void* parameter) {
if (FLASHER_AP_MOSI != -1) {
fsversion = getAPUpdateVersion(apInfo.type);
if ((fsversion) && (apInfo.version != fsversion)) {
Serial.printf("Firmware version on LittleFS: %04X\r\n", fsversion);
Serial.printf("Firmware version on FS: %04X\r\n", fsversion);
Serial.printf("We're going to try to update the AP's FW in\r\n");
flashCountDown(30);

View File

@@ -2,22 +2,63 @@
#ifdef HAS_SDCARD
#include "FS.h"
#ifdef SD_CARD_SDMMC
#include "SD_MMC.h"
#define SDCARD SD_MMC
#else
#include "SD.h"
#include "SPI.h"
#define SDCARD SD
#endif
#endif
#ifndef SD_CARD_ONLY
#include "LittleFS.h"
#endif
DynStorage::DynStorage() : isInited(0) {}
SemaphoreHandle_t fsMutex;
SemaphoreHandle_t fsMutex = NULL;
#ifndef SD_CARD_ONLY
static void initLittleFS() {
LittleFS.begin();
contentFS = &LittleFS;
}
#endif
#ifdef HAS_SDCARD
static bool sd_init_done = false;
#ifdef SD_CARD_SDMMC
static void initSDCard() {
if(!SD_MMC.begin("/sdcard", true, true, BOARD_MAX_SDMMC_FREQ, 5)){
Serial.println("Card Mount Failed");
return;
}
uint8_t cardType = SD_MMC.cardType();
if(cardType == CARD_NONE){
Serial.println("No SD_MMC card attached");
return;
}
Serial.print("SD_MMC Card Type: ");
if(cardType == CARD_MMC){
Serial.println("MMC");
} else if(cardType == CARD_SD){
Serial.println("SDSC");
} else if(cardType == CARD_SDHC){
Serial.println("SDHC");
} else {
Serial.println("UNKNOWN");
}
uint64_t cardSize = SD_MMC.cardSize() / (1024 * 1024);
Serial.printf("SD_MMC Card Size: %lluMB\n", cardSize);
contentFS = &SD_MMC;
}
#else
static SPIClass* spi;
static void initSDCard() {
@@ -45,15 +86,19 @@ static void initSDCard() {
contentFS = &SD;
}
#endif
#endif
uint64_t DynStorage::freeSpace(){
this->begin();
#ifdef HAS_SDCARD
return SD.totalBytes() - SD.usedBytes();
return SDCARD.totalBytes() - SDCARD.usedBytes();
#endif
#ifndef SD_CARD_ONLY
return LittleFS.totalBytes() - LittleFS.usedBytes();
#endif
}
#ifndef SD_CARD_ONLY
void copyFile(File in, File out) {
Serial.print("Copying ");
Serial.print(in.path());
@@ -127,14 +172,25 @@ void copyIfNeeded(const char* path) {
}
}
#endif
#endif
void DynStorage::begin() {
fsMutex = xSemaphoreCreateMutex();
if(fsMutex == NULL) {
fsMutex = xSemaphoreCreateMutex();
}
#ifndef SD_CARD_ONLY
initLittleFS();
#endif
#ifdef HAS_SDCARD
initSDCard();
if(!sd_init_done) {
xSemaphoreTake(fsMutex, portMAX_DELAY);
initSDCard();
xSemaphoreGive(fsMutex);
sd_init_done = true;
}
#ifndef SD_CARD_ONLY
copyIfNeeded("/index.html");
copyIfNeeded("/fonts");
copyIfNeeded("/www");
@@ -143,6 +199,7 @@ void DynStorage::begin() {
copyIfNeeded("/tag_md5_db.json");
copyIfNeeded("/update_actions.json");
copyIfNeeded("/content_template.json");
#endif
#endif
if (!contentFS->exists("/current")) {
@@ -155,7 +212,17 @@ void DynStorage::begin() {
void DynStorage::end() {
#ifdef HAS_SDCARD
#ifndef SD_CARD_ONLY
initLittleFS();
#endif
#ifdef SD_CARD_SDMMC
#ifndef SD_CARD_ONLY
contentFS = &LittleFS;
#endif
SD_MMC.end();
sd_init_done = false;
#else
#ifndef SD_CARD_ONLY
if (SD_CARD_CLK == FLASHER_AP_CLK ||
SD_CARD_MISO == FLASHER_AP_MISO ||
SD_CARD_MOSI == FLASHER_AP_MOSI) {
@@ -171,7 +238,8 @@ void DynStorage::end() {
contentFS = &LittleFS;
}
#endif
#endif
#endif
}

View File

@@ -14,7 +14,7 @@ void timeSyncCallback(struct timeval* tv) {
}
void initTime(void* parameter) {
if (WiFi.status() != WL_CONNECTED) {
if (!(WiFi.status() == WL_CONNECTED || wm.wifiStatus == ETHERNET)) {
vTaskDelay(500 / portTICK_PERIOD_MS);
}
sntp_set_time_sync_notification_cb(timeSyncCallback);

View File

@@ -67,25 +67,24 @@ bool hex2mac(const String& hexString, uint8_t* mac) {
}
String tagDBtoJson(const uint8_t mac[8], uint8_t startPos) {
DynamicJsonDocument doc(5000);
JsonArray tags = doc.createNestedArray("tags");
JsonDocument doc;
JsonArray tags = doc["tags"].to<JsonArray>();
for (uint32_t c = startPos; c < tagDB.size(); ++c) {
const tagRecord* taginfo = tagDB.at(c);
const bool select = !mac || memcmp(taginfo->mac, mac, 8) == 0;
if (select && taginfo->version == 0) {
JsonObject tag = tags.createNestedObject();
JsonObject tag = tags.add<JsonObject>();
fillNode(tag, taginfo);
if (measureJson(doc) > 5000) {
doc["continu"] = c + 1;
break;
}
if (mac) {
break;
}
}
if (doc.capacity() - doc.memoryUsage() < doc.memoryUsage() / (c + 1) + 500) {
doc["continu"] = c + 1;
break;
}
}
return doc.as<String>();
@@ -126,7 +125,7 @@ void fillNode(JsonObject& tag, const tagRecord* taginfo) {
}
void saveDB(const String& filename) {
DynamicJsonDocument doc(2500);
JsonDocument doc;
const long t = millis();
@@ -138,8 +137,10 @@ void saveDB(const String& filename) {
vTaskDelay(pdMS_TO_TICKS(100));
String backupFilename = filename + ".bak";
if (!contentFS->rename(filename.c_str(), backupFilename.c_str())) {
xSemaphoreGive(fsMutex);
logLine("error renaming tagDB to .bak");
wsErr("error renaming tagDB to .bak");
xSemaphoreTake(fsMutex, portMAX_DELAY);
}
}
@@ -156,7 +157,7 @@ void saveDB(const String& filename) {
doc.clear();
if (taginfo->version == 0) {
JsonObject tag = doc.createNestedObject();
JsonObject tag = doc.add<JsonObject>();
fillNode(tag, taginfo);
if (c > 0) {
file.write(',');
@@ -186,7 +187,7 @@ bool loadDB(const String& filename) {
bool parsing = true;
if (readfile.find("[")) {
DynamicJsonDocument doc(1000);
JsonDocument doc;
while (parsing) {
DeserializationError err = deserializeJson(doc, readfile);
if (!err) {
@@ -210,7 +211,7 @@ bool loadDB(const String& filename) {
taginfo->nextupdate = (uint32_t)tag["nextupdate"];
taginfo->expectedNextCheckin = (uint32_t)tag["nextcheckin"];
if (taginfo->expectedNextCheckin < now) {
taginfo->expectedNextCheckin = now + 1800;
taginfo->expectedNextCheckin = now + 60;
}
taginfo->pendingCount = 0;
taginfo->alias = tag["alias"].as<String>();
@@ -279,11 +280,11 @@ uint32_t getTagCount(uint32_t& timeoutcount, uint32_t& lowbattcount) {
if (!taginfo->isExternal) tagcount++;
const int32_t timeout = now - taginfo->lastseen;
if (taginfo->expectedNextCheckin < 3600) {
// not initialised, timeout if not seen last 10 minutes
if (timeout > 600) timeoutcount++;
} else if (now - taginfo->expectedNextCheckin > 600) {
// expected checkin is behind, timeout if not seen last 10 minutes
if (timeout > 600) timeoutcount++;
// not initialised, timeout if not seen last 5 minutes
if (timeout > config.maxsleep * 60 + 300) timeoutcount++;
} else if (now - static_cast<time_t>(taginfo->expectedNextCheckin) > 600) {
// expected checkin is behind, timeout if not seen last 5 minutes
if (timeout > config.maxsleep * 60 + 300) timeoutcount++;
}
if (taginfo->batteryMv < 2400 && taginfo->batteryMv != 0 && taginfo->batteryMv != 1337) lowbattcount++;
}
@@ -308,7 +309,7 @@ void clearPending(tagRecord* taginfo) {
}
void initAPconfig() {
DynamicJsonDocument APconfig(768);
JsonDocument APconfig;
File configFile = contentFS->open("/current/apconfig.json", "r");
if (configFile) {
DeserializationError error = deserializeJson(APconfig, configFile);
@@ -319,29 +320,30 @@ void initAPconfig() {
}
configFile.close();
}
config.channel = APconfig.containsKey("channel") ? APconfig["channel"] : 0;
config.subghzchannel = APconfig.containsKey("subghzchannel") ? APconfig["subghzchannel"] : 0;
config.channel = APconfig["channel"].is<uint8_t>() ? APconfig["channel"] : 0;
config.subghzchannel = APconfig["subghzchannel"].is<uint8_t>() ? APconfig["subghzchannel"] : 0;
if (APconfig["alias"]) strlcpy(config.alias, APconfig["alias"], sizeof(config.alias));
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.nightlyreboot = APconfig.containsKey("nightlyreboot") ? APconfig["nightlyreboot"] : 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;
config.ble = APconfig.containsKey("ble") ? APconfig["ble"] : 0;
config.discovery = APconfig.containsKey("discovery") ? APconfig["discovery"] : 0;
config.led = APconfig["led"].is<uint8_t>() ? APconfig["led"] : 255;
config.tft = APconfig["tft"].is<uint8_t>() ? APconfig["tft"] : 255;
config.language = APconfig["language"].is<uint8_t>() ? APconfig["language"] : 0;
config.maxsleep = APconfig["maxsleep"].is<uint8_t>() ? APconfig["maxsleep"] : 10;
config.stopsleep = APconfig["stopsleep"].is<uint8_t>() ? APconfig["stopsleep"] : 1;
config.preview = APconfig["preview"].is<uint8_t>() ? APconfig["preview"] : 1;
config.nightlyreboot = APconfig["nightlyreboot"].is<uint8_t>() ? APconfig["nightlyreboot"] : 1;
config.lock = APconfig["lock"].is<uint8_t>() ? APconfig["lock"] : 0;
config.sleepTime1 = APconfig["sleeptime1"].is<uint8_t>() ? APconfig["sleeptime1"] : 0;
config.sleepTime2 = APconfig["sleeptime2"].is<uint8_t>() ? APconfig["sleeptime2"] : 0;
config.ble = APconfig["ble"].is<uint8_t>() ? APconfig["ble"] : 0;
config.discovery = APconfig["discovery"].is<uint8_t>() ? APconfig["discovery"] : 0;
config.showtimestamp = APconfig["showtimestamp"].is<uint8_t>() ? APconfig["showtimestamp"] : 0;
#ifdef BLE_ONLY
config.ble = true;
config.ble = true;
#endif
// default wifi power 8.5 dbM
// see https://github.com/espressif/arduino-esp32/blob/master/libraries/WiFi/src/WiFiGeneric.h#L111
config.wifiPower = APconfig.containsKey("wifipower") ? APconfig["wifipower"] : 34;
config.repo = APconfig.containsKey("repo") ? APconfig["repo"].as<String>() : String("OpenEPaperLink/OpenEPaperLink");
config.env = APconfig.containsKey("env") ? APconfig["env"].as<String>() : String(STR(BUILD_ENV_NAME));
config.wifiPower = APconfig["wifipower"].is<uint8_t>() ? APconfig["wifipower"] : 34;
config.repo = APconfig["repo"].is<String>() ? APconfig["repo"].as<String>() : String("OpenEPaperLink/OpenEPaperLink");
config.env = APconfig["env"].is<String>() ? APconfig["env"].as<String>() : String(STR(BUILD_ENV_NAME));
if (APconfig["timezone"]) {
strlcpy(config.timeZone, APconfig["timezone"], sizeof(config.timeZone));
} else {
@@ -352,7 +354,7 @@ void initAPconfig() {
void saveAPconfig() {
xSemaphoreTake(fsMutex, portMAX_DELAY);
fs::File configFile = contentFS->open("/current/apconfig.json", "w");
DynamicJsonDocument APconfig(500);
JsonDocument APconfig;
APconfig["channel"] = config.channel;
APconfig["subghzchannel"] = config.subghzchannel;
APconfig["alias"] = config.alias;
@@ -372,6 +374,7 @@ void saveAPconfig() {
APconfig["repo"] = config.repo;
APconfig["env"] = config.env;
APconfig["discovery"] = config.discovery;
APconfig["showtimestamp"] = config.showtimestamp;
serializeJsonPretty(APconfig, configFile);
configFile.close();
xSemaphoreGive(fsMutex);
@@ -388,7 +391,7 @@ HwType getHwType(const uint8_t id) {
File jsonFile = contentFS->open(filename, "r");
if (jsonFile) {
StaticJsonDocument<150> filter;
JsonDocument filter;
filter["width"] = true;
filter["height"] = true;
filter["rotatebuffer"] = true;
@@ -398,7 +401,7 @@ HwType getHwType(const uint8_t id) {
filter["g5_compression"] = true;
filter["highlight_color"] = true;
filter["colortable"] = true;
StaticJsonDocument<1000> doc;
JsonDocument doc;
DeserializationError error = deserializeJson(doc, jsonFile, DeserializationOption::Filter(filter));
jsonFile.close();
if (error) {
@@ -412,17 +415,17 @@ HwType getHwType(const uint8_t id) {
hwType.rotatebuffer = doc["rotatebuffer"];
hwType.bpp = doc["bpp"];
hwType.shortlut = doc["shortlut"];
if (doc.containsKey("zlib_compression")) {
if (doc["zlib_compression"].is<const char*>()) {
hwType.zlib = strtol(doc["zlib_compression"], nullptr, 16);
} else {
hwType.zlib = 0;
}
if (doc.containsKey("g5_compression")) {
if (doc["g5_compression"].is<const char*>()) {
hwType.g5 = strtol(doc["g5_compression"], nullptr, 16);
} else {
hwType.g5 = 0;
}
hwType.highlightColor = doc.containsKey("highlight_color") ? doc["highlight_color"].as<uint16_t>() : 2;
hwType.highlightColor = doc["highlight_color"].is<uint16_t>() ? doc["highlight_color"].as<uint16_t>() : 2;
JsonObject colorTable = doc["colortable"];
for (auto kv : colorTable) {
JsonArray color = kv.value();

View File

@@ -10,6 +10,9 @@ std::unordered_map<size_t, TagData::Parser> TagData::parsers = {};
void TagData::loadParsers(const String& filename) {
const long start = millis();
if (!contentFS->exists(filename)) {
return;
}
fs::File file = contentFS->open(filename, "r");
if (!file) {
return;
@@ -17,7 +20,7 @@ void TagData::loadParsers(const String& filename) {
Serial.println("Reading parsers from file");
if (file.find("[")) {
DynamicJsonDocument doc(1000);
JsonDocument doc;
bool parsing = true;
while (parsing) {
DeserializationError err = deserializeJson(doc, file);
@@ -145,4 +148,4 @@ void TagData::parse(const uint8_t src[8], const size_t id, const uint8_t* data,
}
}
#endif
#endif

View File

@@ -9,6 +9,7 @@
#include "serialap.h"
#include "tag_db.h"
#include "web.h"
#include "wifimanager.h"
#define UDPIP IPAddress(239, 10, 0, 1)
#define UDPPORT 16033
@@ -34,7 +35,7 @@ void UDPcomm::init() {
if (config.discovery == 0) {
if (udp.listenMulticast(UDPIP, UDPPORT)) {
udp.onPacket([this](AsyncUDPPacket packet) {
if (packet.remoteIP() != WiFi.localIP()) {
if (packet.remoteIP() != wm.localIP()) {
this->processPacket(packet);
}
});
@@ -42,7 +43,7 @@ void UDPcomm::init() {
} else {
if (udp.listen(UDPPORT)) {
udp.onPacket([this](AsyncUDPPacket packet) {
if (packet.isBroadcast() && packet.remoteIP() != WiFi.localIP()) {
if (packet.isBroadcast() && packet.remoteIP() != wm.localIP()) {
this->processPacket(packet);
}
});
@@ -88,7 +89,7 @@ void UDPcomm::processPacket(AsyncUDPPacket packet) {
}
case PKT_APLIST_REQ: {
APlist APitem;
APitem.src = WiFi.localIP();
APitem.src = wm.localIP();
strcpy(APitem.alias, config.alias);
APitem.channelId = curChannel.channel;
APitem.tagCount = getTagCount();
@@ -154,7 +155,7 @@ void autoselect(void* pvParameters) {
void UDPcomm::getAPList() {
APlist APitem;
APitem.src = WiFi.localIP();
APitem.src = wm.localIP();
strcpy(APitem.alias, config.alias);
APitem.channelId = curChannel.channel;
APitem.tagCount = getTagCount();

View File

@@ -31,8 +31,6 @@
#include "webflasher.h"
#endif
extern uint8_t data_to_send[];
AsyncWebServer server(80);
AsyncWebSocket ws("/ws");
WifiManager wm;
@@ -41,7 +39,7 @@ SemaphoreHandle_t wsMutex;
uint32_t lastssidscan = 0;
void wsLog(const String &text) {
StaticJsonDocument<250> doc;
JsonDocument doc;
doc["logMsg"] = text;
if (wsMutex) xSemaphoreTake(wsMutex, portMAX_DELAY);
ws.textAll(doc.as<String>());
@@ -49,7 +47,7 @@ void wsLog(const String &text) {
}
void wsErr(const String &text) {
StaticJsonDocument<250> doc;
JsonDocument doc;
doc["errMsg"] = text;
if (wsMutex) xSemaphoreTake(wsMutex, portMAX_DELAY);
ws.textAll(doc.as<String>());
@@ -68,8 +66,8 @@ size_t dbSize() {
}
void wsSendSysteminfo() {
DynamicJsonDocument doc(300);
JsonObject sys = doc.createNestedObject("sys");
JsonDocument doc;
JsonObject sys = doc["sys"].to<JsonObject>();
time_t now;
time(&now);
static int freeSpaceLastRun = 0;
@@ -109,7 +107,7 @@ void wsSendSysteminfo() {
strftime(timeBuffer, sizeof(timeBuffer), languageDateFormat[0].c_str(), &timeinfo);
setVarDB("ap_date", timeBuffer);
}
setVarDB("ap_ip", WiFi.localIP().toString());
setVarDB("ap_ip", wm.localIP().toString());
#ifdef HAS_SUBGHZ
String ApChanString = String(apInfo.channel);
@@ -208,8 +206,8 @@ void wsSendTaginfo(const uint8_t *mac, uint8_t syncMode) {
}
void wsSendAPitem(struct APlist *apitem) {
DynamicJsonDocument doc(250);
JsonObject ap = doc.createNestedObject("apitem");
JsonDocument doc;
JsonObject ap = doc["apitem"].to<JsonObject>();
char version_str[6];
sprintf(version_str, "%04X", apitem->version);
@@ -230,7 +228,7 @@ void wsSerial(const String &text) {
}
void wsSerial(const String &text, const String &color) {
StaticJsonDocument<250> doc;
JsonDocument doc;
doc["console"] = text;
if (!color.isEmpty()) doc["color"] = color;
Serial.println(text);
@@ -324,7 +322,7 @@ void init_web() {
return;
}
}
request->send_P(200, "application/octet-stream", queueItem->data, queueItem->len);
request->send(200, "application/octet-stream", queueItem->data, queueItem->len);
return;
}
} else {
@@ -338,7 +336,7 @@ void init_web() {
taginfo->data = getDataForFile(file);
file.close();
}
request->send_P(200, "application/octet-stream", taginfo->data, taginfo->len);
request->send(200, "application/octet-stream", taginfo->data, taginfo->len);
return;
}
}
@@ -516,13 +514,21 @@ void init_web() {
UDPcomm udpsync;
udpsync.getAPList();
AsyncResponseStream *response = request->beginResponseStream("application/json");
String HasC6 = "0";
String HasH2 = "0";
String HasTSLR = "0";
response->print("{");
#ifdef C6_OTA_FLASHING
response->print("\"C6\": \"1\", ");
#else
response->print("\"C6\": \"0\", ");
#ifdef HAS_H2
HasH2 = "1";
#elif defined(HAS_TSLR)
HasTSLR = "1";
#elif defined(C6_OTA_FLASHING)
HasC6 = "1";
#endif
response->print("\"C6\": \"" + HasC6 + "\", ");
response->print("\"H2\": \"" + HasH2 + "\", ");
response->print("\"TLSR\": \"" + HasTSLR + "\", ");
#ifdef SAVE_SPACE
response->print("\"savespace\": \"1\", ");
#else
@@ -628,6 +634,9 @@ void init_web() {
if (request->hasParam("discovery", true)) {
config.discovery = static_cast<uint8_t>(request->getParam("discovery", true)->value().toInt());
}
if (request->hasParam("showtimestamp", true)) {
config.showtimestamp = static_cast<uint8_t>(request->getParam("showtimestamp", true)->value().toInt());
}
if (request->hasParam("repo", true)) {
config.repo = request->getParam("repo", true)->value();
}
@@ -652,7 +661,7 @@ void init_web() {
});
server.on("/set_vars", HTTP_POST, [](AsyncWebServerRequest *request) {
if (request->hasParam("json", true)) {
DynamicJsonDocument jsonDocument(2048);
JsonDocument jsonDocument;
DeserializationError error = deserializeJson(jsonDocument, request->getParam("json", true)->value());
if (error) {
request->send(400, "text/plain", "Failed to parse JSON");
@@ -679,7 +688,7 @@ void init_web() {
server.on("/get_wifi_config", HTTP_GET, [](AsyncWebServerRequest *request) {
Preferences preferences;
AsyncResponseStream *response = request->beginResponseStream("application/json");
StaticJsonDocument<250> doc;
JsonDocument doc;
preferences.begin("wifi", false);
const char *keys[] = {"ssid", "pw", "ip", "mask", "gw", "dns"};
const size_t numKeys = sizeof(keys) / sizeof(keys[0]);
@@ -693,13 +702,13 @@ void init_web() {
server.on("/get_ssid_list", HTTP_GET, [](AsyncWebServerRequest *request) {
AsyncResponseStream *response = request->beginResponseStream("application/json");
DynamicJsonDocument doc(5000);
JsonDocument doc;
doc["scanstatus"] = WiFi.scanComplete();
JsonArray networks = doc.createNestedArray("networks");
JsonArray networks = doc["networks"].to<JsonArray>();
for (int i = 0; i < (WiFi.scanComplete() > 50 ? 50 : WiFi.scanComplete()); ++i) {
if (WiFi.SSID(i) != "") {
JsonObject network = networks.createNestedObject();
JsonObject network = networks.add<JsonObject>();
network["ssid"] = WiFi.SSID(i);
network["ch"] = WiFi.channel(i);
network["rssi"] = WiFi.RSSI(i);
@@ -725,7 +734,7 @@ void init_web() {
const size_t numKeys = sizeof(keys) / sizeof(keys[0]);
for (size_t i = 0; i < numKeys; i++) {
String key = keys[i];
if (jsonObj.containsKey(key)) {
if (jsonObj[key].is<String>()) {
preferences.putString(key.c_str(), jsonObj[key].as<String>());
}
}
@@ -868,10 +877,11 @@ void doImageUpload(AsyncWebServerRequest *request, String filename, size_t index
file.write(uploadInfo->buffer, uploadInfo->bufferSize);
file.close();
uploadInfo->bufferSize = 0;
xSemaphoreGive(fsMutex);
} else {
xSemaphoreGive(fsMutex);
logLine("Failed to open file for appending: " + uploadfilename);
}
xSemaphoreGive(fsMutex);
memcpy(uploadInfo->buffer, data, len);
uploadInfo->bufferSize = len;
@@ -885,10 +895,11 @@ void doImageUpload(AsyncWebServerRequest *request, String filename, size_t index
if (file) {
file.write(uploadInfo->buffer, uploadInfo->bufferSize);
file.close();
xSemaphoreGive(fsMutex);
} else {
xSemaphoreGive(fsMutex);
logLine("Failed to open file for appending: " + uploadfilename);
}
xSemaphoreGive(fsMutex);
request->_tempObject = nullptr;
delete uploadInfo;
}
@@ -903,6 +914,18 @@ void doImageUpload(AsyncWebServerRequest *request, String filename, size_t index
if (request->hasParam("dither", true)) {
dither = request->getParam("dither", true)->value().toInt();
}
if (request->hasParam("alias", true)) {
taginfo->alias = request->getParam("alias", true)->value();
}
if (request->hasParam("rotate", true)) {
taginfo->rotate = atoi(request->getParam("rotate", true)->value().c_str());
}
if (request->hasParam("lut", true)) {
taginfo->lut = atoi(request->getParam("lut", true)->value().c_str());
}
if (request->hasParam("invert", true)) {
taginfo->invert = atoi(request->getParam("invert", true)->value().c_str());
}
uint32_t ttl = 0;
if (request->hasParam("ttl", true)) {
ttl = request->getParam("ttl", true)->value().toInt();
@@ -946,7 +969,7 @@ void doJsonUpload(AsyncWebServerRequest *request) {
uint8_t mac[8];
if (hex2mac(dst, mac)) {
xSemaphoreTake(fsMutex, portMAX_DELAY);
File file = LittleFS.open("/current/" + dst + ".json", "w");
File file = contentFS->open("/current/" + dst + ".json", "w");
if (!file) {
request->send(400, "text/plain", "Failed to create file");
xSemaphoreGive(fsMutex);

View File

@@ -466,9 +466,9 @@ void webFlasherTask(void* parameter) {
}
void handleWSdata(uint8_t* data, size_t len, AsyncWebSocketClient* client) {
StaticJsonDocument<200> doc;
JsonDocument doc;
DeserializationError error = deserializeJson(doc, (const char*)data);
StaticJsonDocument<250> response;
JsonDocument response;
response["flashstatus"] = 1;
if (error) {
@@ -476,7 +476,7 @@ void handleWSdata(uint8_t* data, size_t len, AsyncWebSocketClient* client) {
return;
}
if (doc.containsKey("flashcmd")) {
if (doc["flashcmd"].is<int>()) {
uint16_t flashcmd = doc["flashcmd"].as<int>();
switch (flashcmd) {
case WEBFLASH_ENABLE_AUTOFLASH:

View File

@@ -4,6 +4,8 @@
#include <WiFi.h>
#include <esp_wifi.h>
#include <ETH.h>
#include "newproto.h"
#include "system.h"
#include "tag_db.h"
@@ -16,6 +18,13 @@ uint8_t WifiManager::apClients = 0;
uint8_t x_buffer[100];
uint8_t x_position = 0;
#if defined(ETHERNET_PHY_POWER) && defined(ETHERNET_PHY_MDC) && defined(ETHERNET_PHY_MDIO) && defined(ETHERNET_PHY_TYPE) && defined(ETHERNET_CLK_MODE)
static bool eth_init = false;
static bool eth_connected = false;
static bool eth_ip_ok = false;
static long eth_timeout = 0;
#endif
WifiManager::WifiManager() {
_reconnectIntervalCheck = 5000;
_retryIntervalCheck = 5 * 60000;
@@ -43,6 +52,24 @@ void WifiManager::terminalLog(String text) {
}
void WifiManager::poll() {
#if defined(ETHERNET_PHY_POWER) && defined(ETHERNET_PHY_MDC) && defined(ETHERNET_PHY_MDIO) && defined(ETHERNET_PHY_TYPE) && defined(ETHERNET_CLK_MODE)
if (eth_connected) {
wifiStatus = ETHERNET;
if(!eth_ip_ok && eth_timeout != 0 && millis() - eth_timeout > 2000) {
eth_timeout = 0;
eth_connected = false;
}
} else if(!eth_connected && wifiStatus == ETHERNET) {
wifiStatus = NOINIT;
_APstarted = false;
WiFi.mode(WIFI_STA);
connectToWifi();
}
#endif
if (wifiStatus == AP && millis() > _nextReconnectCheck && _ssid != "") {
if (apClients == 0) {
terminalLog("Attempting to reconnect to WiFi.");
@@ -68,6 +95,10 @@ void WifiManager::poll() {
}
#ifndef HAS_USB
#ifdef ETHERNET_CLK_MODE
if (!(ETHERNET_CLK_MODE == ETH_CLOCK_GPIO0_IN || ETHERNET_CLK_MODE == ETH_CLOCK_GPIO0_OUT)) {
#endif
// ap_and_flasher has gpio0 in use as FLASHER_AP_POWER
if (digitalRead(0) == LOW) {
Serial.println("GPIO0 LOW");
@@ -99,12 +130,37 @@ void WifiManager::poll() {
ESP.restart();
}
}
#ifdef ETHERNET_CLK_MODE
}
#endif
#endif
pollSerial();
}
void WifiManager::initEth() {
#if defined(ETHERNET_PHY_POWER) && defined(ETHERNET_PHY_MDC) && defined(ETHERNET_PHY_MDIO) && defined(ETHERNET_PHY_TYPE) && defined(ETHERNET_CLK_MODE)
if(!eth_init) {
eth_init = true;
ETH.begin(
ETH_PHY_ADDR,
ETHERNET_PHY_POWER,
ETHERNET_PHY_MDC,
ETHERNET_PHY_MDIO,
ETHERNET_PHY_TYPE,
ETHERNET_CLK_MODE,
false);
}
#endif
}
bool WifiManager::connectToWifi() {
#if defined(ETHERNET_PHY_POWER) && defined(ETHERNET_PHY_MDC) && defined(ETHERNET_PHY_MDIO) && defined(ETHERNET_PHY_TYPE) && defined(ETHERNET_CLK_MODE)
if (wifiStatus == ETHERNET || eth_connected)
return true;
#endif
Preferences preferences;
preferences.begin("wifi", false);
_ssid = preferences.getString("ssid", WiFi_SSID());
@@ -136,6 +192,11 @@ bool WifiManager::connectToWifi() {
}
bool WifiManager::connectToWifi(String ssid, String pass, bool savewhensuccessfull) {
#if defined(ETHERNET_PHY_POWER) && defined(ETHERNET_PHY_MDC) && defined(ETHERNET_PHY_MDIO) && defined(ETHERNET_PHY_TYPE) && defined(ETHERNET_CLK_MODE)
if (wifiStatus == ETHERNET)
return true;
#endif
_ssid = ssid;
_pass = pass;
_savewhensuccessfull = savewhensuccessfull;
@@ -145,26 +206,7 @@ bool WifiManager::connectToWifi(String ssid, String pass, bool savewhensuccessfu
delay(100);
WiFi.mode(WIFI_MODE_NULL);
delay(100);
char hostname[32] = "OpenEpaperLink-";
uint8_t mac[6];
esp_read_mac(mac, ESP_MAC_WIFI_STA);
char lastTwoBytes[5];
sprintf(lastTwoBytes, "%02X%02X", mac[4], mac[5]);
strcat(hostname, lastTwoBytes);
if (config.alias[0] != '\0') {
int len = strlen(config.alias);
int j = 0;
for (int i = 0; i < len; i++) {
char c = config.alias[i];
if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || c == '-') {
hostname[j] = c;
j++;
}
}
hostname[j] = '\0';
}
WiFi.setHostname(hostname);
WiFi.setHostname(buildHostname(ESP_MAC_WIFI_STA).c_str());
WiFi.mode(WIFI_STA);
WiFi.setSleep(WIFI_PS_MIN_MODEM);
@@ -177,6 +219,11 @@ bool WifiManager::connectToWifi(String ssid, String pass, bool savewhensuccessfu
}
bool WifiManager::waitForConnection() {
#if defined(ETHERNET_PHY_POWER) && defined(ETHERNET_PHY_MDC) && defined(ETHERNET_PHY_MDIO) && defined(ETHERNET_PHY_TYPE) && defined(ETHERNET_CLK_MODE)
if (wifiStatus == ETHERNET)
return true;
#endif
unsigned long timeout = millis() + _connectionTimeout;
wifiStatus = WAIT_CONNECTING;
@@ -209,7 +256,7 @@ bool WifiManager::waitForConnection() {
}
void WifiManager::startManagementServer() {
if (!_APstarted) {
if (!_APstarted && wifiStatus != ETHERNET) {
terminalLog("Starting config AP, ssid: OpenEPaperLink");
logLine("Starting configuration AP, ssid OpenEPaperLink");
WiFi.disconnect(true, true);
@@ -225,6 +272,37 @@ void WifiManager::startManagementServer() {
}
}
String WifiManager::buildHostname(esp_mac_type_t mac_type) {
char hostname[32] = "OpenEpaperLink-";
uint8_t mac[6];
esp_read_mac(mac, mac_type);
char lastTwoBytes[5];
sprintf(lastTwoBytes, "%02X%02X", mac[4], mac[5]);
strcat(hostname, lastTwoBytes);
if (config.alias[0] != '\0') {
int len = strlen(config.alias);
int j = 0;
for (int i = 0; i < len; i++) {
char c = config.alias[i];
if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || c == '-') {
hostname[j] = c;
j++;
}
}
hostname[j] = '\0';
}
return String(hostname);
}
IPAddress WifiManager::localIP() {
if (wifiStatus == ETHERNET) {
return ETH.localIP();
} else {
return WiFi.localIP();
}
}
String WifiManager::WiFi_SSID() {
wifi_config_t conf;
esp_wifi_get_config(WIFI_IF_STA, &conf);
@@ -296,6 +374,46 @@ void WifiManager::WiFiEvent(WiFiEvent_t event) {
// eventname = "Assigned IP address to client";
break;
#if defined(ETHERNET_PHY_POWER) && defined(ETHERNET_PHY_MDC) && defined(ETHERNET_PHY_MDIO) && defined(ETHERNET_PHY_TYPE) && defined(ETHERNET_CLK_MODE)
case ARDUINO_EVENT_ETH_START:
eventname = "ETH Started";
//set eth hostname here
ETH.setHostname(buildHostname(ESP_MAC_ETH).c_str());
eth_timeout = 0;
break;
case ARDUINO_EVENT_ETH_CONNECTED:
eventname = "ETH Connected";
WiFi.mode(WIFI_MODE_NULL);
WiFi.disconnect();
eth_connected = true;
eth_timeout = millis();
break;
case ARDUINO_EVENT_ETH_GOT_IP:
if (ETH.fullDuplex()) {
eventname = "ETH MAC: " + ETH.macAddress() + ", IPv4: " + ETH.localIP().toString() + ", FULL_DUPLEX, " + ETH.linkSpeed() + "Mbps";
} else {
eventname = "ETH MAC: " + ETH.macAddress() + ", IPv4: " + ETH.localIP().toString() + ", " + ETH.linkSpeed() + "Mbps";
}
eth_ip_ok = true;
init_udp();
eth_timeout = 0;
break;
case ARDUINO_EVENT_ETH_DISCONNECTED:
eventname = "ETH Disconnected";
eth_connected = false;
eth_ip_ok = false;
eth_timeout = 0;
break;
case ARDUINO_EVENT_ETH_STOP:
eventname = "ETH Stopped";
eth_connected = false;
eth_ip_ok = false;
eth_timeout = 0;
break;
#endif
default:
break;
}

View File

@@ -140,7 +140,7 @@
{
"id": 8,
"name": "Weather forecast",
"desc": "Weather forecast for the next five days. Weather data by Open-Meteo.com. Parameters Lat, Lon and Time Zone are filled automatically from the entered location. In case of an ambiguous location, you can alter those manually.",
"desc": "Weather forecast for the next five days. Weather data by Open-Meteo.com. Parameters Lat, Lon and Time Zone are filled automatically from the entered location. In case of an ambiguous location, choose from the drop down list.",
"param": [
{
"key": "location",
@@ -389,6 +389,7 @@
"NO4": "Norway NO4",
"NO5": "Norway NO5",
"PL": "Poland",
"PT": "Portugal",
"RO": "Romania",
"SK": "Slovakia",
"SI": "Slovenia",
@@ -593,21 +594,6 @@
}
]
},
{
"id": 15,
"name": "Send custom LUT",
"desc": "EXPERIMENTAL. Don't use. YOU RISK DAMAGING YOUR SCREEN.",
"capabilities": 4,
"properties": [ "savespace" ],
"param": [
{
"key": "bytes",
"name": "bytes",
"desc": "76 bytes, formatted as 0x00,0x00,...",
"type": "text"
}
]
},
{
"id": 17,
"name": "Send Command",
@@ -621,6 +607,19 @@
}
]
},
{
"id": 28,
"name": "Set Tag Mac",
"desc": "Sets the tags mac-address to a specific value. Must be 16 characters, hexdecimal (8 bytes)",
"param": [
{
"key": "mac",
"name": "MAC",
"desc": "Set Mac address",
"type": "text"
}
]
},
{
"id": 18,
"name": "Set Tag Config",

View File

@@ -1,4 +1,3 @@
<!--This is the plain html source of the hex encoded Editor-Page embedded in SPIFFSEditor.cpp -->
<!DOCTYPE html>
<html lang="en">
@@ -462,26 +461,23 @@
imageData.data[i * 4 + 2] = is16Bit ? (rgb & 0x1F) << 3 : ((rgb & 0x03) << 6) * 1.3;
imageData.data[i * 4 + 3] = 255;
}
} else if (tagTypes[hwtype].bpp == 3) {
} else if ([3, 4].includes(tagTypes[hwtype].bpp)) {
const bpp = tagTypes[hwtype].bpp;
const colorTable = tagTypes[hwtype].colortable;
let pixelIndex = 0;
for (let i = 0; i < data.length; i += 3) {
for (let j = 0; j < 8; j++) {
let bitPos = j * 3;
let bytePos = Math.floor(bitPos / 8);
let bitOffset = bitPos % 8;
let pixelValue = (data[i + bytePos] >> (5 - bitOffset)) & 0x07;
if (bitOffset > 5) {
pixelValue = ((data[i + bytePos] & (0xFF >> bitOffset)) << (bitOffset - 5)) |
(data[i + bytePos + 1] >> (13 - bitOffset));
}
imageData.data[pixelIndex * 4] = colorTable[pixelValue][0];
imageData.data[pixelIndex * 4 + 1] = colorTable[pixelValue][1];
imageData.data[pixelIndex * 4 + 2] = colorTable[pixelValue][2];
imageData.data[pixelIndex * 4 + 3] = 255;
pixelIndex++;
}
let bitOffset = 0;
while (bitOffset < data.length * 8) {
let byteIndex = bitOffset >> 3;
let startBit = bitOffset & 7;
let pixelValue = (data[byteIndex] << 8 | data[byteIndex + 1] || 0) >> (16 - bpp - startBit) & ((1 << bpp) - 1);
let color = colorTable[pixelValue];
imageData.data[pixelIndex * 4] = color[0];
imageData.data[pixelIndex * 4 + 1] = color[1];
imageData.data[pixelIndex * 4 + 2] = color[2];
imageData.data[pixelIndex * 4 + 3] = 255;
pixelIndex++;
bitOffset += bpp;
}
} else {
@@ -629,9 +625,9 @@
leaf.onclick = function (e) {
treeRoot.removeChild(treeRoot.childNodes[0]);
if (name == "..") {
httpGet(treeRoot, "/");
httpGet(treeRoot, path === "/" ? "/" : path.substring(0, path.lastIndexOf('/')) || "/");
} else {
httpGet(treeRoot, "/" + name);
httpGet(treeRoot, path + (path != "/" ? "/" : "") + name);
}
};
leaf.oncontextmenu = function (e) {
@@ -657,9 +653,6 @@
sortByKey(items, 'name');
var list = ce("ul");
parent.appendChild(list);
if (path != "/") {
list.appendChild(createDirLeaf("/", "..", 0));
}
var ll = items.length;
for (var i = 0; i < ll; i++) {
if (items[i].type === "file") {
@@ -669,7 +662,9 @@
list.insertBefore(createDirLeaf(path, items[i].name, items[i].size), list.firstChild);
}
}
if (path != "/") {
list.insertBefore(createDirLeaf(path, "..", 0), list.firstChild);
}
}
function isTextFile(path) {
@@ -866,7 +861,7 @@
editor.loadUrl(vars.file);
};
</script>
<script id='ace' src="https://cdnjs.cloudflare.com/ajax/libs/ace/1.2.6/ace.js" type="text/javascript"
<script id='ace' src="https://cdnjs.cloudflare.com/ajax/libs/ace/1.37.2/ace.js" type="text/javascript"
charset="utf-8"></script>
<script>
if (typeof ace.edit == "undefined") {

View File

@@ -6,9 +6,9 @@
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=0" />
<title>Open EPaper Link Access Point</title>
<script src="main.js" defer></script>
<script src="g5decoder.js?2"></script>
<link rel="stylesheet" href="main.css" type="text/css" />
<script src="main.js?2.74" defer></script>
<script src="g5decoder.js?2.74"></script>
<link rel="stylesheet" href="main.css?2.74" type="text/css" />
<!--<link rel="icon" type="image/vnd.icon" href="favicon.ico">-->
<link rel="stylesheet"
href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@24,400,0,0" />
@@ -182,6 +182,7 @@
<div class="nextcheckin"></div>
<div class="nextupdate"></div>
<div class="corner">
<div class="waitingicon" title="New content is scheduled to be generated (as soon as possible, or shortly before the next expected checkin time)">&#9203;</div>
<div class="pendingicon" title="A new message is waiting for the tag to pick up">&circlearrowright;</div>
<div class="warningicon" title="This tag has not been seen for a long time">&#9888;
</div>
@@ -290,24 +291,24 @@ options:
<option value="27">27</option>
</select>
</p>
<p title="Enable SubGhz support and select channel. This requires an AP that has an optional CC1101 SubGhz radio module attached.">
<label for="apcfgsubgigchid">SubGhz channel</label>
<select id="apcfgsubgigchid">
<option value="0" selected>disabled</option>
<option value="100">100 - 864.000 Mhz (Europe, etc)</option>
<option value="101">101 - 865.006 Mhz (Europe, etc)</option>
<option value="102">102 - 866.014 Mhz (Europe, etc)</option>
<option value="103">103 - 867.020 Mhz (Europe, etc)</option>
<option value="104">104 - 868.027 Mhz (Europe, etc)</option>
<option value="105">105 - 869.034 Mhz (Europe, etc)</option>
<option value="200">200 - 903.000 Mhz (US, etc)</option>
<option value="201">201 - 907.027 Mhz (US, etc)</option>
<option value="202">202 - 911.054 Mhz (US, etc)</option>
<option value="203">203 - 915.083 Mhz (US, etc)</option>
<option value="204">204 - 919.110 Mhz (US, etc)</option>
<option value="205">205 - 923.138 Mhz (US, etc)</option>
</select>
</p>
<p title="Enable SubGhz support and select channel. This requires an AP that has an optional CC1101 SubGhz radio module attached.">
<label for="apcfgsubgigchid">SubGhz channel</label>
<select id="apcfgsubgigchid">
<option value="0" selected>disabled</option>
<option value="100">100 - 864.000 Mhz (Europe, etc)</option>
<option value="101">101 - 865.006 Mhz (Europe, etc)</option>
<option value="102">102 - 866.014 Mhz (Europe, etc)</option>
<option value="103">103 - 867.020 Mhz (Europe, etc)</option>
<option value="104">104 - 868.027 Mhz (Europe, etc)</option>
<option value="105">105 - 869.034 Mhz (Europe, etc)</option>
<option value="200">200 - 903.000 Mhz (US, etc)</option>
<option value="201">201 - 907.027 Mhz (US, etc)</option>
<option value="202">202 - 911.054 Mhz (US, etc)</option>
<option value="203">203 - 915.083 Mhz (US, etc)</option>
<option value="204">204 - 919.110 Mhz (US, etc)</option>
<option value="205">205 - 923.138 Mhz (US, etc)</option>
</select>
</p>
<p title="Enable Bluetooth (BLE) support. Only enable this if you have BLE capable tags. Changing this value requires a reboot to take effect.">
<label for="apcfgble">Bluetooth</label>
<select id="apcfgble">
@@ -479,6 +480,13 @@ options:
<option value="1">Broadcast</option>
</select>
</p>
<p title="Show a timestamp on the screen when the tag is updated">
<label for="apcshowtimestamp">Enable timestamp</label>
<select id="apcshowtimestamp">
<option value="0" selected>no</option>
<option value="1">yes</option>
</select>
</p>
<p>
<input type="button" value="Save" id="apcfgsave"><span id="apcfgmsg"></span>
</p>
@@ -522,7 +530,10 @@ options:
<button id="confirmSelectRepo">Confirm</button><button id="cancelSelectRepo">Cancel</button>
</div>
<h4>Releases</h4>
<div id="releasetable"></div>
<div id="releasetable" class="releasetable"></div>
<h4 id="radio_release_title"></h4>
<div id="radio_releasetable" class="releasetable"></div>
<div id="radio_releasetable1" class="releasetable"></div>
<h4>Other actions</h4>
<div>
<p id="rollbackOption" style="display:none">

View File

@@ -651,6 +651,16 @@ select {
text-align: center;
}
.waitingicon {
display: none;
font-size: 1.2em;
background-color: lightgreen;
color: black;
height: 20px;
width: 20px;
vertical-align: top;
text-align: center;
}
ul.messages {
padding: 5px;
}
@@ -825,37 +835,37 @@ h4 {
font-weight: bold;
}
#releasetable {
.releasetable {
margin: 10px 0px;
}
#releasetable table {
.releasetable table {
border-spacing: 1px;
}
#releasetable th {
.releasetable th {
text-align: left;
background-color: #ffffff;
padding: 1px 5px;
}
#releasetable td {
.releasetable td {
background-color: #ffffff;
padding: 1px 5px;
min-width: 70px;
vertical-align: baseline;
}
#releasetable td:nth-child(2) {
.releasetable td:nth-child(2) {
white-space: nowrap;
}
#releasetable button {
.releasetable button {
padding: 3px 10px;
background-color: #e0e0e0;
}
#releasetable button:hover {
.releasetable button:hover {
background-color: #a0a0a0;
}

View File

@@ -21,7 +21,7 @@ const apstate = [
{ state: "online", color: "green", icon: "check_circle" },
{ state: "flashing", color: "orange", icon: "flash_on" },
{ state: "wait for reset", color: "blue", icon: "hourglass" },
{ state: "AP requires power cycle", color: "purple", icon: "refresh" },
{ state: "AP requires reboot", color: "purple", icon: "refresh" },
{ state: "failed", color: "red", icon: "error" },
{ state: "coming online...", color: "orange", icon: "hourglass" },
{ state: "AP without radio", color: "green", icon: "wifi_off" }
@@ -54,10 +54,9 @@ window.addEventListener("loadConfig", function () {
$(".logo").innerHTML = data.alias;
this.document.title = data.alias;
}
if (data.C6 == 1) {
if (data.C6 == 1 || (data.H2 && data.H2 == 1)) {
var optionToRemove = $("#apcfgchid").querySelector('option[value="27"]');
if (optionToRemove) $("#apcfgchid").removeChild(optionToRemove);
$('#updateC6Option').style.display = 'block';
}
if (data.hasFlasher == 1) {
$('[data-target="flashtab"]').style.display = 'block';
@@ -133,6 +132,7 @@ function initTabs() {
tabLinks.forEach(link => {
link.classList.remove("active");
});
if (targetId == "logtab") document.getElementById(targetId).scrollTop = 0;
document.getElementById(targetId).style.display = "block";
this.classList.add("active");
});
@@ -299,7 +299,7 @@ function processTags(tagArray) {
if (alias.match(/^4467/)) {
let macdigit = Number.parseInt(alias.substr(4, 2), 16) & 0x1f;
let model = String.fromCharCode(macdigit + 65);
if (model == 'J' || model == 'M') {
if (model >= 'A' && model <= 'Z') {
macdigit = Number.parseInt(alias.substr(6, 2), 16) & 0x1f;
model += String.fromCharCode(macdigit + 65);
alias = model + alias.substr(8, 8) + 'x'
@@ -371,11 +371,16 @@ function processTags(tagArray) {
} else {
$('#tag' + tagmac + ' .nextupdate').innerHTML = "";
}
if (element.nextupdate < (Date.now() / 1000) - servertimediff) {
$('#tag' + tagmac + ' .waitingicon').style.display = 'inline-block';
} else {
$('#tag' + tagmac + ' .waitingicon').style.display = 'none';
}
if (element.nextcheckin > 1672531200) {
div.dataset.nextcheckin = element.nextcheckin;
} else {
div.dataset.nextcheckin = element.lastseen + 1800;
div.dataset.nextcheckin = element.lastseen + 60;
}
div.style.opacity = '1';
@@ -461,7 +466,7 @@ function updatecards() {
if (item.dataset.lastseen && item.dataset.lastseen > (Date.now() / 1000) - servertimediff - 30 * 24 * 3600 * 60) {
let idletime = (Date.now() / 1000) - servertimediff - item.dataset.lastseen;
$('#tag' + tagmac + ' .lastseen').innerHTML = "<span>last seen</span>" + displayTime(Math.floor(idletime)) + " ago";
if ((Date.now() / 1000) - servertimediff - 600 > item.dataset.nextcheckin) {
if ((Date.now() / 1000) - servertimediff - apConfig.maxsleep * 60 - 300 > item.dataset.nextcheckin) {
$('#tag' + tagmac + ' .warningicon').style.display = 'inline-block';
$('#tag' + tagmac).classList.remove("tagpending")
$('#tag' + tagmac).style.background = '#e0e0a0';
@@ -793,6 +798,7 @@ document.addEventListener("loadTab", function (event) {
$("#apcnight1").value = data.sleeptime1;
$("#apcnight2").value = data.sleeptime2;
$("#apcdiscovery").value = data.discovery;
$("#apcshowtimestamp").value = data.showtimestamp;
}
})
$('#apcfgmsg').innerHTML = '';
@@ -830,7 +836,8 @@ $('#apcfgsave').onclick = function () {
formData.append('timezone', $('#apctimezone').value);
formData.append('sleeptime1', $('#apcnight1').value);
formData.append('sleeptime2', $('#apcnight2').value);
formData.append('discovery', $('#apcdiscovery').value)
formData.append('discovery', $('#apcdiscovery').value);
formData.append('showtimestamp', $('#apcshowtimestamp').value);
fetch("save_apcfg", {
method: "POST",
body: formData
@@ -1253,28 +1260,24 @@ function drawCanvas(buffer, canvas, hwtype, tagmac, doRotate) {
imageData.data[i * 4 + 3] = 255;
}
} else if (tagTypes[hwtype].bpp == 3) {
} else if ([3, 4].includes(tagTypes[hwtype].bpp)) {
const bpp = tagTypes[hwtype].bpp;
const colorTable = tagTypes[hwtype].colortable;
let pixelIndex = 0;
for (let i = 0; i < data.length; i += 3) {
for (let j = 0; j < 8; j++) {
let bitPos = j * 3;
let bytePos = Math.floor(bitPos / 8);
let bitOffset = bitPos % 8;
let pixelValue = (data[i + bytePos] >> (5 - bitOffset)) & 0x07;
if (bitOffset > 5) {
pixelValue = ((data[i + bytePos] & (0xFF >> bitOffset)) << (bitOffset - 5)) |
(data[i + bytePos + 1] >> (13 - bitOffset));
}
imageData.data[pixelIndex * 4] = colorTable[pixelValue][0];
imageData.data[pixelIndex * 4 + 1] = colorTable[pixelValue][1];
imageData.data[pixelIndex * 4 + 2] = colorTable[pixelValue][2];
imageData.data[pixelIndex * 4 + 3] = 255;
pixelIndex++;
}
}
let bitOffset = 0;
while (bitOffset < data.length * 8) {
let byteIndex = bitOffset >> 3;
let startBit = bitOffset & 7;
let pixelValue = (data[byteIndex] << 8 | data[byteIndex + 1] || 0) >> (16 - bpp - startBit) & ((1 << bpp) - 1);
let color = colorTable[pixelValue];
imageData.data[pixelIndex * 4] = color[0];
imageData.data[pixelIndex * 4 + 1] = color[1];
imageData.data[pixelIndex * 4 + 2] = color[2];
imageData.data[pixelIndex * 4 + 3] = 255;
pixelIndex++;
bitOffset += bpp;
}
} else {
const offsetRed = (data.length >= (canvas.width * canvas.height / 8) * 2) ? canvas.width * canvas.height / 8 : 0;
@@ -1527,7 +1530,7 @@ async function getTagtype(hwtype) {
height: parseInt(jsonData.height),
bpp: parseInt(jsonData.bpp),
rotatebuffer: jsonData.rotatebuffer,
colortable: Object.values(jsonData.colortable),
colortable: Object.values(jsonData.perceptual ?? jsonData.colortable),
contentids: Object.values(jsonData.contentids ?? []),
options: Object.values(jsonData.options ?? []),
zlib: parseInt(jsonData.zlib_compression || "0", 16),
@@ -1571,6 +1574,7 @@ function dropUpload() {
dropZone.addEventListener('drop', (event) => {
event.preventDefault();
const shiftKey = event.shiftKey;
const file = event.dataTransfer.files[0];
const tagCard = event.target.closest('.tagcard');
const mac = tagCard.dataset.mac;
@@ -1604,6 +1608,7 @@ function dropUpload() {
canvas.toBlob(async (blob) => {
const formData = new FormData();
formData.append('mac', mac);
if (shiftKey) formData.append('dither', '2');
formData.append('file', blob, 'image.jpg');
try {
@@ -1790,12 +1795,25 @@ function populateAPInfo(apip) {
})
.then(data => {
if (data.env) {
let gModuleType = "";
if (data.hasC6 == 1) {
gModuleType = "esp32-C6";
}
if (data.hasH2 == 1) {
gModuleType = "esp32-H2";
}
if (data.hasTslr == 1) {
gModuleType = "TSLR";
}
let version = '';
version += `env: ${data.env}<br>`;
version += `build date: ${formatEpoch(data.buildtime)}<br>`;
version += `esp32 version: ${data.buildversion}<br>`;
version += `psram size: ${data.psramsize}<br>`;
version += `flash size: ${data.flashsize}<br>`;
if (gModuleType) {
version += `${gModuleType} version: 0x${parseInt(data.ap_version).toString(16).toUpperCase()}<br>`;
}
$('#ap' + apid + ' .apswversion').innerHTML = version;
}
})
@@ -1906,7 +1924,7 @@ function openPreview(mac, w, h) {
previewWindow.document.body.style.backgroundColor = "#dddddd";
previewWindow.document.body.style.margin = "15px";
previewWindow.document.body.style.overflow = "hidden";
previewWindow.document.body.innerHTML = `<canvas id="preview" style="border:1px solid #888888;"></canvas>`;
previewWindow.document.body.innerHTML = `<canvas id="preview" style="border:1px solid #888888;image-rendering: pixelated;"></canvas>`;
showPreview(previewWindow, element);

View File

@@ -7,8 +7,27 @@ let running = false;
let errors = 0;
let env = '', currentVer = '', currentBuildtime = 0;
let buttonState = false;
let gIsC6 = false;
let gIsH2 = false;
let gModuleType = '';
let gShortName = '';
let gCurrentRfVer = 0;
export async function initUpdate() {
if (apConfig.C6 == 1) {
gIsC6 = true;
gModuleType = "ESP32-C6";
gShortName = "C6";
}
else if(apConfig?.H2 && apConfig.H2 == 1) {
gIsH2 = true;
gModuleType = "ESP32-H2";
gShortName = "H2";
}
else {
gModuleType = "Unknown"
}
$('#radio_release_title').innerHTML = gModuleType + " Firmware";
const response = await fetch("version.txt");
let filesystemversion = await response.text();
@@ -30,7 +49,7 @@ export async function initUpdate() {
$('#selectRepo').style.display = 'inline-block';
$('#repoWarning').style.display = 'none';
const sysinfoPromise = fetch("sysinfo")
const sdata = await fetch("sysinfo")
.then(response => {
if (response.status != 200) {
print("Error fetching sysinfo: " + response.status, "red");
@@ -48,95 +67,170 @@ export async function initUpdate() {
print('Error fetching sysinfo: ' + error, "red");
});
const repoPromise = fetch(repoUrl)
.then(response => response.json())
if (sdata.env) {
print(`current env: ${sdata.env}`);
print(`build date: ${formatEpoch(sdata.buildtime)}`);
print(`esp32 version: ${sdata.buildversion}`);
if(gModuleType != '') {
var hex_ver = '0000' + sdata.ap_version.toString(16);
print(`${gModuleType} version: ${hex_ver.slice(-4)}`);
}
print(`filesystem version: ${filesystemversion}`);
print(`psram size: ${sdata.psramsize}`);
print(`flash size: ${sdata.flashsize}`);
print("--------------------------", "gray");
env = apConfig.env || sdata.env;
if (sdata.env != env) {
print(`Warning: you selected a build environment ${env} which is\ndifferent than the currently used ${sdata.env}.\nOnly update the firmware with a mismatched build environment if\nyou know what you're doing.`, "yellow");
}
currentVer = sdata.buildversion;
currentBuildtime = sdata.buildtime;
gCurrentRfVer = sdata.ap_version;
if (sdata.rollback) $("#rollbackOption").style.display = 'block';
$('#environment').value = env;
}
Promise.all([sysinfoPromise, repoPromise])
.then(([sdata, rdata]) => {
if (sdata.env) {
print(`current env: ${sdata.env}`);
print(`build date: ${formatEpoch(sdata.buildtime)}`);
print(`esp32 version: ${sdata.buildversion}`);
print(`filesystem version: ${filesystemversion}`);
print(`psram size: ${sdata.psramsize}`);
print(`flash size: ${sdata.flashsize}`);
print("--------------------------", "gray");
env = apConfig.env || sdata.env;
if (sdata.env != env) {
print(`Warning: you selected a build environment ${env} which is\ndifferent than the currently used ${sdata.env}.\nOnly update the firmware with a mismatched build environment if\nyou know what you're doing.`, "yellow");
}
currentVer = sdata.buildversion;
currentBuildtime = sdata.buildtime;
if (sdata.rollback) $("#rollbackOption").style.display = 'block';
$('#environment').value = env;
const rdata = await fetch(repoUrl).then(response => response.json())
const JsonName = 'firmware_' + gShortName + '.json';
const releaseDetails = rdata.map(release => {
const assets = release.assets;
const filesJsonAsset = assets.find(asset => asset.name === 'filesystem.json');
const binariesJsonAsset = assets.find(asset => asset.name === 'binaries.json');
const containsEnv = assets.find(asset => asset.name === env + '.bin');
const firmwareAsset = assets.find(asset => asset.name === JsonName);
if (filesJsonAsset && binariesJsonAsset && containsEnv) {
return {
html_url: release.html_url,
tag_name: release.tag_name,
name: release.name,
date: formatDateTime(release.published_at),
author: release.author.login,
file_url: filesJsonAsset.browser_download_url,
bin_url: binariesJsonAsset.browser_download_url,
firmware_url: firmwareAsset?.browser_download_url,
}
};
})
const releaseDetails = rdata.map(release => {
const assets = release.assets;
const filesJsonAsset = assets.find(asset => asset.name === 'filesystem.json');
const binariesJsonAsset = assets.find(asset => asset.name === 'binaries.json');
const containsEnv = assets.find(asset => asset.name === env + '.bin');
if (filesJsonAsset && binariesJsonAsset && containsEnv) {
return {
html_url: release.html_url,
tag_name: release.tag_name,
name: release.name,
date: formatDateTime(release.published_at),
author: release.author.login,
file_url: filesJsonAsset.browser_download_url,
bin_url: binariesJsonAsset.browser_download_url
}
};
});
const easyupdate = $('#easyupdate');
if (releaseDetails.length === 0) {
easyupdate.innerHTML = ("No releases found.");
if (releaseDetails.length === 0) {
easyupdate.innerHTML = ("No releases found.");
} else {
const release = releaseDetails[0];
if (release?.tag_name) {
if (release.tag_name == currentVer) {
easyupdate.innerHTML = `Version ${currentVer}. You are up to date`;
} else if (release.date < formatEpoch(currentBuildtime - 30 * 60)) {
easyupdate.innerHTML = `Your version is newer than the latest release date.<br>Are you the developer? :-)`;
} else {
const release = releaseDetails[0];
if (release?.tag_name) {
if (parseInt(release.tag_name) == parseInt(currentVer)) {
easyupdate.innerHTML = `Version ${currentVer}. You are up to date`;
} else if (release.date < formatEpoch(currentBuildtime - 30 * 60)) {
easyupdate.innerHTML = `Your version is newer than the latest release date.<br>Are you the developer? :-)`;
} else {
easyupdate.innerHTML = `An update from version ${currentVer} to version ${release.tag_name} is available.<button onclick="otamodule.updateAll('${release.bin_url}','${release.file_url}','${release.tag_name}')">Update now!</button>`;
}
}
easyupdate.innerHTML = `An update from version ${currentVer} to version ${release.tag_name} is available.<button onclick="otamodule.updateAll('${release.bin_url}','${release.file_url}','${release.tag_name}')">Update now!</button>`;
}
}
}
const table = document.createElement('table');
const tableHeader = document.createElement('tr');
tableHeader.innerHTML = '<th>Release</th><th>Date</th><th>Name</th><th colspan="2">Update:</th><th>Remark</th>';
table.appendChild(tableHeader);
const table = document.createElement('table');
const tableHeader = document.createElement('tr');
tableHeader.innerHTML = '<th>Release</th><th>Date</th><th>Name</th><th colspan="2"><center>Update</center></th><th>Remark</th>';
table.appendChild(tableHeader);
let rowCounter = 0;
releaseDetails.forEach(release => {
if (rowCounter < 4 && release?.html_url) {
const tableRow = document.createElement('tr');
let tablerow = `<td><a href="${release.html_url}" target="_new">${release.tag_name}</a></td><td>${release.date}</td><td>${release.name}</td><td><button type="button" onclick="otamodule.updateWebpage('${release.file_url}','${release.tag_name}', true)">Filesystem</button></td><td><button type="button" onclick="otamodule.updateESP('${release.bin_url}', true)">ESP32</button></td>`;
if (release.tag_name == currentVer) {
tablerow += "<td>current version</td>";
} else if (release.date < formatEpoch(currentBuildtime)) {
tablerow += "<td>older</td>";
} else {
tablerow += "<td>newer</td>";
}
tableRow.innerHTML = tablerow;
table.appendChild(tableRow);
rowCounter++;
}
});
let rowCounter = 0;
let radioFwCounter = 0;
releaseDetails.forEach(release => {
if (rowCounter < 4 && release?.html_url) {
const tableRow = document.createElement('tr');
let tablerow = `<td><a href="${release.html_url}" target="_new">${release.tag_name}</a></td><td>${release.date}</td><td>${release.name}</td><td><button type="button" onclick="otamodule.updateWebpage('${release.file_url}','${release.tag_name}', true)">Filesystem</button></td><td><button type="button" onclick="otamodule.updateESP('${release.bin_url}', true)">ESP32</button></td>`;
if (release.tag_name == currentVer) {
tablerow += "<td>current version</td>";
} else if (release.date < formatEpoch(currentBuildtime)) {
tablerow += "<td>older</td>";
} else {
tablerow += "<td>newer</td>";
}
tableRow.innerHTML = tablerow;
table.appendChild(tableRow);
rowCounter++;
}
if (release?.firmware_url) {
radioFwCounter++;
}
});
$('#releasetable').innerHTML = "";
$('#releasetable').appendChild(table);
$('#releasetable').innerHTML = "";
$('#releasetable').appendChild(table);
disableButtons(buttonState);
})
.catch(error => {
print('Error fetching releases:' + error, "red");
});
if(radioFwCounter > 0) {
const table1 = document.createElement('table');
const tableHeader1 = document.createElement('tr');
tableHeader1.innerHTML = '<th>Release</th><th>Date</th><th>Name</th><th><center>Update</center></th><th>Version</th><th>Remark</th>';
table1.appendChild(tableHeader1);
rowCounter = 0;
for (const release of releaseDetails) {
if (rowCounter < 4 && release?.firmware_url) {
const tableRow = document.createElement('tr');
var tablerow;
var firmwareVer = "unknown";
var release_url = release.firmware_url;
tablerow = `<td><a href="${release.html_url}" target="_new">${release.tag_name}</a></td><td>${release.date}</td><td>${release.name}</td>`;
tablerow += `<td><button type="button" onclick="otamodule.updateC6H2('${release_url}')">${gModuleType}</button></td>`;
const firmwareUrl = 'http://proxy.openepaperlink.org/proxy.php?url=' + release.firmware_url;
firmwareVer = await fetch(firmwareUrl, { method: 'GET'})
.then(function (response) { return response.json(); })
.then(function (response) {
return response[2]['version']; })
.catch(error => {
print('Error fetching releases:' + error, "red");
});
tablerow += '<td>' + firmwareVer + '</td><td>';
if(firmwareVer != 'unknown') {
let Ver = Number('0x' + firmwareVer);
if(Ver > gCurrentRfVer) {
tablerow += 'newer';
}
else if (Ver < gCurrentRfVer) {
tablerow += 'older';
}
else if(!Number.isNaN(Ver)){
tablerow += 'current version';
}
}
tablerow += '</td>';
tableRow.innerHTML = tablerow;
table1.appendChild(tableRow);
rowCounter++;
}
};
$('#radio_releasetable').innerHTML = "";
$('#radio_releasetable').appendChild(table1);
}
const table2 = document.createElement('table');
{
const tableHeader2 = document.createElement('tr');
tableHeader2.innerHTML = '<th>Firmware</th><th><center>Update</center></th>';
table2.appendChild(tableHeader2);
const tableRow = document.createElement('tr');
tablerow = '<td title="manual upload, make sure all four files are present">Binaries from <a href="/edit" target="littlefs">file system</a></td>';
tablerow += `<td><button type="button" onclick="otamodule.updateC6H2('')">${gModuleType}</button></td>`;
tableRow.innerHTML = tablerow;
table2.appendChild(tableRow);
}
{
const tableRow = document.createElement('tr');
const Url = "https://raw.githubusercontent.com/" + repo +
"/master/binaries/ESP32-" + gShortName +
"/firmware_" + gShortName + ".json";
tablerow = `<td><a href="https://github.com/${repo}/tree/master/binaries/ESP32-${gShortName}/" target="_new">Latest version from repo</a></td>`;
tablerow += `<td><button type="button" onclick="otamodule.updateC6H2('${Url}')">${gModuleType}</button></td>`;
tableRow.innerHTML = tablerow;
table2.appendChild(tableRow);
}
$('#radio_releasetable1').innerHTML = "";
$('#radio_releasetable1').appendChild(table2);
disableButtons(buttonState);
}
export function updateAll(binUrl, fileUrl, tagname) {
@@ -355,19 +449,18 @@ $('#rollbackBtn').onclick = function () {
disableButtons(false);
}
$('#updateC6Btn').onclick = function () {
export async function updateC6H2(Url) {
if (running) return;
disableButtons(true);
running = true;
errors = 0;
const ReleaseUrl = Url.substring(0,Url.lastIndexOf('/'));
const consoleDiv = document.getElementById('updateconsole');
consoleDiv.scrollTop = consoleDiv.scrollHeight;
print("Flashing ESP32-C6...");
const isChecked = $('#c6download').checked;
const formData = new FormData();
formData.append('download', isChecked ? '1' : '0');
print("Flashing " + gModuleType + " ...");
formData.append('url', ReleaseUrl);
fetch("update_c6", {
method: "POST",
@@ -415,7 +508,7 @@ $('#selectRepo').onclick = function (event) {
if (!responseBody.trim().startsWith("[")) {
throw new Error("Failed to fetch the release info file");
}
const updateData = JSON.parse(responseBody).filter(item => !item.name.endsWith('_full.bin'));
const updateData = JSON.parse(responseBody).filter(item => !item.name.endsWith('_full.bin') && !item.name.includes('_H2.') && !item.name.includes('_C6.'));
const inputParent = $('#environment').parentNode;
const selectElement = document.createElement('select');
@@ -638,3 +731,7 @@ async function fetchAndCheckTagtypes(cleanup) {
print("Error: " + error, "red");
}
}
function normalizeVersion(version) {
return version.replace(/(\.\d*?)0+$/, '$1').replace(/\.$/, '');
}

View File

@@ -13,7 +13,7 @@ platform = espressif32
framework = arduino
lib_deps =
https://github.com/MajenkoLibraries/SoftSPI
bblanchon/ArduinoJson
bblanchon/ArduinoJson@7.1.0
platform_packages =
board_build.filesystem = littlefs
@@ -61,4 +61,4 @@ build_src_filter =
board_build.psram_type=qspi_opi
board_upload.maximum_size = 4194304
board_upload.maximum_ram_size = 327680
board_upload.flash_size = 4MB
board_upload.flash_size = 4MB

View File

@@ -106,9 +106,12 @@ python3 .\88MZ100-OEPL-Flasher.py COM31 write_flash '0130c8144117.bin'
## TI CC1110-based
Use with the -c option for CC1110. Neigher Autoflash is currently not implemented on the Tag_Flasher/S2 version.
> [!IMPORTANT]
> Tag flasher version 50 or later is required for CC1110 support.
>
Use with the -c option for CC1110.
The CC1110 does not have an infopage nor is the Tag's EEPROM accessable.
The CC1110 does not have an infopage nor is the Tag's EEPROM accessable. Autoflash is currently not implemented for the CC1110.
```shell
python3 OEPL-Flasher.py -e -c -p COM31 read blaat.bin --flash

Binary file not shown.

View File

@@ -1,12 +1,15 @@
[{
"filename": "bootloader.bin",
"address": "0x0"
"address": "0x0",
"version": "0001"
},
{
"filename": "partition-table.bin",
"address": "0x8000"
"address": "0x8000",
"version": "0001"
},
{
"filename": "OpenEPaperLink_esp32_C6.bin",
"address": "0x10000"
"address": "0x10000",
"version": "001f"
}]

View File

@@ -0,0 +1,15 @@
[{
"filename": "bootloader_C6.bin",
"address": "0x0",
"version": "0001"
},
{
"filename": "partition-table_C6.bin",
"address": "0x8000",
"version": "0001"
},
{
"filename": "OpenEPaperLink_esp32_C6.bin",
"address": "0x10000",
"version": "001f"
}]

View File

@@ -1,12 +1,15 @@
[{
"filename": "bootloader.bin",
"address": "0x0"
"address": "0x0",
"version": "0001"
},
{
"filename": "partition-table.bin",
"address": "0x8000"
"address": "0x8000",
"version": "0001"
},
{
"filename": "OpenEPaperLink_esp32_H2.bin",
"address": "0x10000"
"address": "0x10000",
"version": "001f"
}]

View File

@@ -0,0 +1,15 @@
[{
"filename": "bootloader_H2.bin",
"address": "0x0",
"version": "0001"
},
{
"filename": "partition-table_H2.bin",
"address": "0x8000",
"version": "0001"
},
{
"filename": "OpenEPaperLink_esp32_H2.bin",
"address": "0x10000",
"version": "001f"
}]

BIN
binaries/TLSR/OEPL_TLSR_AP.bin Executable file

Binary file not shown.

Binary file not shown.

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

View File

@@ -128,7 +128,10 @@
"82": {
"type": "chroma29_ota",
"version": "0010",
"md5": "0d4af3e5285ca20ac56926e6fdd71dbc"
"md5": "0d4af3e5285ca20ac56926e6fdd71dbc",
"type_1": "chroma29_8151_ota",
"version_1": "0013",
"md5_1": "b3dffd044829449998edc1c91d537004"
},
"83": {
"type": "chroma42_ota",

View File

@@ -4,9 +4,12 @@ function doGet(e) {
var country = e?.parameter?.country || 'NL';
var cache = CacheService.getScriptCache();
ExchangeApp.setRatesEndpoint("https://openexchangerates.org/api/latest.json?app_id=*************", 3600)
var cachedData = cache.get("output" + country);
if (false && cachedData != null) {
if (cachedData != null) {
logger('from cache');
logger('All Data: ' + JSON.stringify(cachedData));
return ContentService.createTextOutput(cachedData)
.setMimeType(ContentService.MimeType.JSON);
}
@@ -16,8 +19,8 @@ function doGet(e) {
// https://github.com/bkper/exchange-app
factor = cache.get("currencyNOK");
if (factor == null) {
// factor = ExchangeApp.convert(1, 'EUR', 'NOK');
factor = 11.4;
factor = ExchangeApp.convert(1, 'EUR', 'NOK');
// factor = 11.79;
cache.put("currencyNOK", factor, 3600 * 24);
}
}
@@ -26,8 +29,8 @@ function doGet(e) {
// https://github.com/bkper/exchange-app
factor = cache.get("currencyDKK");
if (factor == null) {
// factor = ExchangeApp.convert(1, 'EUR', 'DKK');
factor = 7.45;
factor = ExchangeApp.convert(1, 'EUR', 'DKK');
// factor = 7.46;
cache.put("currencyDKK", factor, 3600 * 24);
}
}
@@ -36,8 +39,8 @@ function doGet(e) {
// https://github.com/bkper/exchange-app
factor = cache.get("currencySEK");
if (factor == null) {
// factor = ExchangeApp.convert(1, 'EUR', 'SEK');
factor = 11.25;
factor = ExchangeApp.convert(1, 'EUR', 'SEK');
// factor = 11.59;
cache.put("currencySEK", factor, 3600 * 24);
}
}
@@ -45,10 +48,11 @@ function doGet(e) {
var domain = lookupValue(country);
logger(country + ': ' + domain + ' currency: ' + factor);
var xmlUrl = 'https://web-api.tp.entsoe.eu/api?documentType=A44&out_Domain=' + domain + '&in_Domain=' + domain + '&periodStart=' + getFormattedMidnightUTC(-1) + '&periodEnd=' + getFormattedMidnightUTC(2) + '&securityToken=*********';
var xmlUrl = 'https://web-api.tp.entsoe.eu/api?documentType=A44&out_Domain=' + domain + '&in_Domain=' + domain + '&periodStart=' + getFormattedMidnightUTC(-1) + '&periodEnd=' + getFormattedMidnightUTC(2) + '&securityToken=57ba8658-37a5-4e23-9bee-e60fc3f954db';
logger(xmlUrl);
var cachedXml = cache.get(xmlUrl);
if (cachedXml) {
if (true && cachedXml) {
logger('Using cached XML content.');
var xmlContent = cachedXml;
} else {
@@ -65,7 +69,7 @@ function doGet(e) {
var root = document.getRootElement();
var timeSeriesList = getDescendantsByTagName(root, 'TimeSeries');
if (timeSeriesList.length > 0) {
var jsonData = [];
@@ -77,15 +81,18 @@ function doGet(e) {
if (period) {
var startTime = period.getChild('timeInterval').getChildText('start');
var resolution = period.getChildText('resolution');
var stepSeconds = resolution === "PT15M" ? 900 : 3600;
logger("Resolution: " + resolution + " (" + stepSeconds + " seconds per step)");
logger(startTime);
var points = period.getChildren('Point');
for (var i = 0; i < points.length; i++) {
var position = points[i].getChildText('position');
var position = parseInt(points[i].getChildText('position'), 10);
var priceAmount = points[i].getChildText('price.amount') * factor;
// Convert ISO date to epoch time (in seconds)
var epochTime = new Date(startTime).getTime() / 1000 + (position - 1) * 3600;
var epochTime = new Date(startTime).getTime() / 1000 + (position - 1) * stepSeconds;
jsonData.push({
time: epochTime,
@@ -137,7 +144,6 @@ function getFormattedMidnightUTC(offsetDays) {
var localMidnight = new Date(now.getFullYear(), now.getMonth(), now.getDate() + offsetDays, 0, 0, 0, 0);
var utcMidnight = new Date(localMidnight.getTime());
var year = utcMidnight.getUTCFullYear();
var month = ('0' + (utcMidnight.getUTCMonth() + 1)).slice(-2); // Months are zero-based
var day = ('0' + utcMidnight.getUTCDate()).slice(-2);
@@ -149,6 +155,7 @@ function getFormattedMidnightUTC(offsetDays) {
}
// https://transparency.entsoe.eu/content/static_content/Static%20content/web%20api/Guide.html#_areas
// https://transparencyplatform.zendesk.com/hc/en-us/articles/15885757676308-Area-List-with-Energy-Identification-Code-EIC
function lookupValue(country) {
var countryValues = {
@@ -173,6 +180,7 @@ function lookupValue(country) {
'NO4': '10YNO-4--------9',
'NO5': '10Y1001A1001A48H',
'PL': '10YPL-AREA-----S',
'PT': '10YPT-REN------W',
'RO': '10YRO-TEL------P',
'SE1': '10Y1001A1001A44P',
'SE2': '10Y1001A1001A45N',
@@ -184,6 +192,3 @@ function lookupValue(country) {
var value = countryValues[country] || '10YNL----------L';
return value;
}

View File

@@ -56,7 +56,7 @@ void setup() {
String str = "!\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_abcdefghijklmnopqrstuvwxyz{|}~°ÄÅÆÖØÚÜßáäåæéíöøúüýąČěľłńŘřŚŠź";
char[] charsetbasic = str.toCharArray();
str = "ABCDEFGHIJKLMNOPQRSTUVWXYZiortzÁÅÄÖØÚČŚŠ0123456789-";
str = "ABCDEFGHIJKLMNOPQRSTUVWXYZminortzÁÅÄÖØÚČŚŠ0123456789-";
char[] charsetdaynames = str.toCharArray();
str = "0123456789.°-";
char[] charsetnumbers = str.toCharArray();

View File

@@ -7,12 +7,6 @@
#define SOLUM_42_SSD1619 0x02
#define SOLUM_SEG_UK 0xF0
#define SOLUM_SEG_EU 0xF1
#define RESERVED_TESTING 0xFE
#define SOLUM_NODISPLAY 0xFF
#define ESP32_C6 0xC6
// overflow
#define SOLUM_M2_BWR_16 0x20
@@ -24,8 +18,6 @@
#define SOLUM_M2_BW_75 0x26
#define SOLUM_M2_BW_29 0x27
#define SOLUM_M3_BWR_97 0x2E
#define SOLUM_M3_BWR_43 0x2F
@@ -50,7 +42,6 @@
#define SOLUM_M3_BWR_116 0x37
#define SOLUM_M3_BWY_116 0x3F
// M3 Tags overflow
#define SOLUM_M3_BW_29 0x40
#define SOLUM_M3_BWR_58 0x41
@@ -80,7 +71,6 @@
#define GICI_BLE_TFT_21_BW 0xBA
#define GICI_BLE_EPD_BWR_29_SILABS 0xBD
#define GICI_BLE_UNKNOWN 0xBF
#define ATC_MI_THERMOMETER 0xBE
// Solum types - customer data byte 16 in M3 (nRF) UICR
#define STYPE_SIZE_016 0x40
@@ -98,6 +88,16 @@
#define STYPE_SIZE_097 0x64
#define STYPE_SIZE_013 0x4D
// Various types
#define ATC_MI_THERMOMETER 0xBE
#define RESERVED_TESTING 0xFE
#define SOLUM_NODISPLAY 0xFF
#define ESP32_C6 0xC6
#define BWRY_29 0xC0
#define ACEP_40 0xC1
#define SPECTRA_73 0xC2
@@ -119,6 +119,7 @@
#define DATATYPE_IMG_RAW_1BPP 0x20 // 2888 bytes for 1.54" / 4736 2.9" / 15000 4.2"
#define DATATYPE_IMG_RAW_2BPP 0x21 // 5776 bytes for 1.54" / 9472 2.9" / 30000 4.2"
#define DATATYPE_IMG_RAW_3BPP 0x22 // ACEP
#define DATATYPE_IMG_RAW_4BPP 0x23 // Spectra
#define DATATYPE_IMG_ZLIB 0x30 // compressed format.
// [uint32_t uncompressed size][2 byte zlib header][zlib compressed image]
// image format: [uint8_t header length][uint16_t width][uint16_t height][uint8_t bpp (lower 4)][img data]

View File

@@ -1,5 +1,5 @@
{
"version": 3,
"version": 4,
"name": "M2 1.54\"",
"width": 152,
"height": 152,
@@ -13,7 +13,7 @@
"g5_compression": "29",
"shortlut": 2,
"options": [ "button", "customlut" ],
"contentids": [ 22, 23, 1, 2, 3, 4, 5, 7, 10, 14, 15, 17, 18, 19, 20, 21, 27 ],
"contentids": [ 22, 23, 1, 2, 3, 4, 5, 7, 10, 14, 17, 18, 19, 20, 21, 27 ],
"template": {
"1": {
"weekday": [ 76, 9, "fonts/calibrib30" ],

View File

@@ -1,5 +1,5 @@
{
"version": 3,
"version": 4,
"name": "M2 2.9\"",
"width": 296,
"height": 128,
@@ -13,7 +13,7 @@
"g5_compression": "29",
"shortlut": 2,
"options": [ "button", "customlut" ],
"contentids": [ 22, 23, 1, 2, 3, 4, 5, 7, 8, 9, 10, 11, 15, 16, 17, 18, 19, 20, 21, 27 ],
"contentids": [ 22, 23, 1, 2, 3, 4, 5, 7, 8, 9, 10, 11, 16, 17, 18, 19, 20, 21, 27 ],
"template": {
"1": {
"weekday": [ 148, -3, "Signika-SB.ttf", 60 ],

View File

@@ -1,5 +1,5 @@
{
"version": 5,
"version": 6,
"name": "M2 2.2\"",
"width": 212,
"height": 104,
@@ -13,7 +13,7 @@
"g5_compression": "29",
"shortlut": 2,
"options": [ "button", "customlut" ],
"contentids": [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 14, 15, 16, 17, 19, 20, 21, 22, 23, 27 ],
"contentids": [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 14, 16, 17, 19, 20, 21, 22, 23, 27 ],
"template": {
"1": {
"weekday": [ 106, 5, "fonts/calibrib40", 40 ],

View File

@@ -1,5 +1,5 @@
{
"version": 3,
"version": 4,
"name": "M2 2.6\"",
"width": 296,
"height": 152,
@@ -13,7 +13,7 @@
"g5_compression": "29",
"shortlut": 2,
"options": [ "button", "customlut" ],
"contentids": [ 22, 23, 1, 2, 3, 4, 5, 7, 8, 9, 10, 11, 15, 16, 17, 18, 19, 20, 21, 27 ],
"contentids": [ 22, 23, 1, 2, 3, 4, 5, 7, 8, 9, 10, 11, 16, 17, 18, 19, 20, 21, 27 ],
"template": {
"1": {
"weekday": [ 148, 9, "Signika-SB.ttf", 60 ],

View File

@@ -1,5 +1,5 @@
{
"version": 2,
"version": 4,
"name": "Opticon 2.2\"",
"width": 250,
"height": 128,
@@ -11,9 +11,14 @@
"red": [ 255, 0, 0 ],
"yellow": [ 255, 255, 0 ]
},
"perceptual": {
"white": [ 255, 255, 255 ],
"black": [ 0, 0, 0 ],
"yellow": [ 200, 200, 0 ]
},
"g5_compression": "29",
"shortlut": 0,
"options": [ "led" ],
"contentids": [ 22, 23, 1, 2, 3, 4, 5, 7, 8, 9, 10, 11, 14, 15, 16, 17, 18, 19, 20, 21, 27 ],
"contentids": [ 22, 23, 1, 2, 3, 4, 5, 7, 8, 9, 10, 11, 14, 16, 17, 18, 19, 20, 21, 27 ],
"usetemplate": 177
}

View File

@@ -1,5 +1,5 @@
{
"version": 2,
"version": 4,
"name": "Opticon 2.9\"",
"width": 296,
"height": 128,
@@ -11,9 +11,14 @@
"red": [ 255, 0, 0 ],
"yellow": [ 255, 255, 0 ]
},
"perceptual": {
"white": [ 255, 255, 255 ],
"black": [ 0, 0, 0 ],
"yellow": [ 200, 200, 0 ]
},
"g5_compression": "29",
"shortlut": 2,
"options": [ "led" ],
"contentids": [ 22, 23, 1, 2, 3, 4, 5, 7, 8, 9, 10, 11, 14, 15, 16, 17, 18, 19, 20, 21, 27 ],
"contentids": [ 22, 23, 1, 2, 3, 4, 5, 7, 8, 9, 10, 11, 14, 16, 17, 18, 19, 20, 21, 27 ],
"usetemplate": 1
}

View File

@@ -1,5 +1,5 @@
{
"version": 2,
"version": 3,
"name": "M2 2.9\" (UC8151)",
"width": 296,
"height": 128,
@@ -13,6 +13,6 @@
"g5_compression": "29",
"shortlut": 0,
"options": [ "button" ],
"contentids": [ 22, 23, 1, 2, 3, 4, 5, 7, 8, 9, 10, 11, 14, 15, 16, 17, 18, 19, 20, 21, 27 ],
"contentids": [ 22, 23, 1, 2, 3, 4, 5, 7, 8, 9, 10, 11, 14, 16, 17, 18, 19, 20, 21, 27 ],
"usetemplate": 1
}

View File

@@ -1,5 +1,5 @@
{
"version": 1,
"version": 2,
"name": "STGM29XXF 2.9\"",
"width": 296,
"height": 128,
@@ -12,6 +12,6 @@
"highlight_color": 5,
"shortlut": 0,
"options": [ "button", "customlut" ],
"contentids": [ 22, 23, 1, 2, 3, 4, 5, 7, 8, 9, 10, 11, 15, 16, 17, 18, 19, 20, 21 ],
"contentids": [ 22, 23, 1, 2, 3, 4, 5, 7, 8, 9, 10, 11, 16, 17, 18, 19, 20, 21 ],
"usetemplate": 1
}

View File

@@ -1,5 +1,5 @@
{
"version": 2,
"version": 3,
"name": "STGM29MT1 2.9\"",
"width": 296,
"height": 128,
@@ -13,6 +13,6 @@
"highlight_color": 5,
"shortlut": 0,
"options": [ "button", "customlut" ],
"contentids": [ 22, 23, 1, 2, 3, 4, 5, 7, 8, 9, 10, 11, 15, 16, 17, 18, 19, 20, 21 ],
"contentids": [ 22, 23, 1, 2, 3, 4, 5, 7, 8, 9, 10, 11, 16, 17, 18, 19, 20, 21 ],
"usetemplate": 1
}

View File

@@ -1,5 +1,5 @@
{
"version": 2,
"version": 5,
"name": "M3 9.7\"",
"width": 960,
"height": 672,
@@ -21,6 +21,21 @@
"month": [ 480, 480, "Signika-SB.ttf", 160 ],
"day": [ 480, 110, "Signika-SB.ttf", 380 ]
},
"8": {
"location": [ 25, 0, "fonts/calibrib60" ],
"column": [ 7, 137 ],
"day": [ 68, 245, "fonts/calibrib42", 335, 540 ],
"rain": [ 72, 610 ],
"icon": [ 67, 390, 90 ],
"wind": [ 45, 300 ],
"line": [ 240, 672 ],
"currentwind": [ 470, 130, "fonts/calibrib60" ],
"temp": [ 25, 70, "fonts/calibrib120", 1 ],
"currenticon": [ 900, 0, 160, 2 ],
"dir": [ 400, 100, 90 ],
"umbrella": [ 390, 50, 60 ]
},
"9": {
"title": [ 6, 0, "Signika-SB.ttf", 32 ],
"items": 12,
@@ -35,7 +50,7 @@
"rotate": 0,
"mode": 1,
"days": 7,
"gridparam": [ 7, 19, 30, "calibrib16.vlw", "REFSAN12.vlw", 16 ]
"gridparam": [ 3, 25, 40, "fonts/calibrib30", "REFSAN12.vlw", 16 ]
}
}
}
}

View File

@@ -1,47 +1,56 @@
{
"version": 2,
"name": "M3 4.3\"",
"width": 522,
"height": 152,
"rotatebuffer": 3,
"bpp": 2,
"colortable": {
"white": [ 255, 255, 255 ],
"black": [ 0, 0, 0 ],
"red": [ 255, 0, 0 ]
},
"shortlut": 0,
"zlib_compression": "27",
"options": [ "button" ],
"contentids": [ 22, 23, 1, 2, 3, 4, 5, 7, 8, 9, 11, 17, 18, 19, 20, 27 ],
"usetemplate": 1,
"template": {
"1": {
"weekday": [ 160, 5, "Signika-SB.ttf", 60 ],
"month": [ 160, 75, "Signika-SB.ttf", 60 ],
"day": [ 422, -27, "Signika-SB.ttf", 170 ]
},
"2": {
"fonts": [ "Signika-SB.ttf", 170, 170, 170, 170, 170, 170 ],
"xy": [ 261, 63 ]
},
"9": {
"title": [ 6, 0, "Signika-SB.ttf", 25 ],
"items": 1,
"line": [ 9, 40, "calibrib16.vlw" ],
"desc": [ 2, 8, "REFSAN12.vlw", 1.2 ]
},
"11": {
"rotate": 1,
"mode": 1,
"days": 1,
"gridparam": [ 5, 17, 20, "calibrib16.vlw", "tahoma9.vlw", 14 ]
},
"27": {
"bars": [ 27, 468, 100, 18 ],
"time": [ "calibrib16.vlw" ],
"yaxis": [ "tahoma9.vlw", 5, 6 ],
"head": [ "calibrib30.vlw" ]
}
}
"version": 4,
"name": "M3 4.3\"",
"width": 522,
"height": 152,
"rotatebuffer": 3,
"bpp": 2,
"colortable": {
"white": [ 255, 255, 255 ],
"black": [ 0, 0, 0 ],
"red": [ 255, 0, 0 ]
},
"shortlut": 0,
"zlib_compression": "27",
"options": [ "button" ],
"contentids": [ 22, 23, 1, 2, 3, 4, 5, 7, 8, 9, 11, 17, 18, 19, 20, 27 ],
"usetemplate": 4,
"template": {
"1": {
"weekday": [ 160, 5, "Signika-SB.ttf", 60 ],
"month": [ 160, 75, "Signika-SB.ttf", 60 ],
"day": [ 422, -27, "Signika-SB.ttf", 170 ]
},
"2": {
"fonts": [ "Signika-SB.ttf", 170, 170, 170, 170, 170, 170 ],
"xy": [ 261, 63 ]
},
"8": {
"location": [ 5, 12, "t0_14b_tf" ],
"column": [ 7, 74 ],
"day": [ 37, 20, "fonts/twcondensed20", 41, 105 ],
"rain": [ 34, 130 ],
"icon": [ 30, 55, 30 ],
"wind": [ 27, 26 ],
"line": [ 20, 150 ]
},
"9": {
"title": [ 6, 0, "Signika-SB.ttf", 25 ],
"items": 1,
"line": [ 9, 40, "calibrib16.vlw" ],
"desc": [ 2, 8, "REFSAN12.vlw", 1.2 ]
},
"11": {
"rotate": 1,
"mode": 1,
"days": 1,
"gridparam": [ 5, 17, 20, "calibrib16.vlw", "tahoma9.vlw", 14 ]
},
"27": {
"bars": [ 27, 468, 100, 18 ],
"time": [ "calibrib16.vlw" ],
"yaxis": [ "tahoma9.vlw", 5, 6 ],
"head": [ "calibrib30.vlw" ]
}
}
}

Some files were not shown because too many files have changed in this diff Show More