41 Commits

Author SHA1 Message Date
Nic Limper
77cbf92281 bugfix getlocation 2023-08-13 01:48:32 +02:00
Moritz Wirger
c4022e45f9 Fix weather icons exception (#114) 2023-08-13 01:00:47 +02:00
atc1441
a13c220565 Fixed countdown via negative count value 2023-08-12 21:43:37 +02:00
Nic Limper
3989ecc3a3 bugfix date display 2023-08-12 21:17:08 +02:00
Nic Limper
cb0f029900 Update build-test.yml
don't build all small variations. Just the minor platforms, the rest will work.
2023-08-12 21:11:33 +02:00
Nic Limper
443714e9dc build fixes 2023-08-12 20:58:25 +02:00
Nic Limper
f3f163fa00 Revert "Ziped web files"
This reverts commit 8848cfc0f8.
2023-08-12 20:19:01 +02:00
Bot
8848cfc0f8 Ziped web files 2023-08-12 17:16:45 +00:00
Moritz Wirger
6c91c78aa1 Refactor contentmanager (#112)
* Start contentmanager cleanup

- Make destination constant
- Remove unneeded asignments
- Improve loops
- Move printHeap function into util

* Refactor contentmanager::drawNew

* Optimize contentManager::replaceVariables

* Fix missing const in prepareCancelPending

* Refactor drawDate

* Refactor drawWeather, drawForecast & getLocation

- Generalize http get json function

* Add util function for printing largest free block

* Reuse weather icons for both drawWeather & drawForecast

* Make httpGetJson timeout const

* Reafctor more functions

* Add few more const

* Fix spelling mistake

* Add util for debugging streams

- Add util for checking if strings are empty or contain null
- Fix file and string checks

* Remove leftover debug print
2023-08-12 16:57:42 +02:00
Nic Limper
98baa020ff bugfix ap info screen 2023-08-12 16:17:06 +02:00
Nic Limper
56e1e9e6b6 small fixes
- build in led on esp32-s2 active low
- floyd steinberg dithering improved
- add tokyo time zone
2023-08-12 13:18:22 +02:00
jjwbruijn
2817aa7e38 Added 7.5 OTA img 2023-08-12 09:17:55 +02:00
jjwbruijn
2292aeaf7e another beta of the 88mz100 7.5 fw 2023-08-12 02:26:33 +02:00
Nic Limper
3472b5d4b6 drop image files to upload 2023-08-12 01:11:25 +02:00
Moritz Wirger
19f6a093d3 Cleanup ota files (#111)
- Constify everything
- Remove duplicate code
- Fix warnings in js
- Reduces bin size from 1283453 to 1283273 bytes
2023-08-12 00:37:26 +02:00
Nic Limper
d6734a7f79 bugfix database backup button 2023-08-11 22:42:02 +02:00
Nic Limper
da487fbc5f gzipped files in /www are now stored deterministic, without timestamp 2023-08-11 19:46:22 +02:00
Moritz Wirger
3b7fd7d1f6 Fix a few js warnings in main.js (#108)
* Fix indentation of content_cards json
* Fix multiple warnings in main.js
2023-08-11 19:39:20 +02:00
Nic Limper
f4273630ee rework of apinfo screen + variables in jsontemplate
- AP info screen content card. Can run on any tag.
- now, you can use {variables} in the 'text' entries in a json template. You can set variables via a http call. If you update a variable, all tags with a json template containing that variable get updated.
- font name in json template is more flexible. You can just use 'filename.ttf' or 'filename.vlw'. A full path is still possible.
- colors in the json template can now be set using #rrggbb color values, and using 'black', 'white' or 'red'.
- added direct output for the TFT display for the yellow esp32-s3. No file writes needed.
- added POST variable 'ttl' to json template upload and image upload, to set the next checkin time
- added /variables-demo.html to demonstrate the variables.
- json templates received from jsonupload are now saved in /current, and reused.
- known issue: 'backup db' button doesn't work due to some browser policy change. Fixing.
thanks to @steinwedel for the inspiration on the variables and some other fixes.
2023-08-11 18:46:46 +02:00
Jelmer
0cbb9e770e esp32 deep sleep support 2023-08-10 23:22:24 +02:00
jjwbruijn
9dfb37b376 Merge branch 'master' of github.com:jjwbruijn/OpenEPaperLink 2023-08-10 22:38:07 +02:00
jjwbruijn
e457475ea7 zbs243 tag FW 2.0 - added deepsleep 2023-08-10 22:26:50 +02:00
Jonas Niesner
806b2dc9e0 Merge pull request #107 from enwi/symlinks
Add batch for generating symlinks on windows
2023-08-10 22:18:48 +02:00
Jonas Niesner
2b7fc93043 Update release.yml 2023-08-10 22:16:27 +02:00
Moritz Wirger
6a711c99eb Add batch for generating symlinks 2023-08-10 21:49:55 +02:00
Nic Limper
ffd0acff72 tft driver for yellow board 2023-08-09 23:40:39 +02:00
Jelmer
a9c9812525 Merge pull request #104 from AndreKR/3dp-shelf-holder
Add 3D-printed shelf holder
2023-08-09 22:44:12 +02:00
André Hänsel
03ce036083 Accessories: Image of the shelf holder 2023-08-09 22:39:28 +02:00
André Hänsel
a98ad537ea Accessories: Source file for the shelf holder 2023-08-09 22:39:19 +02:00
André Hänsel
2726b4d2fe Accessories: Add a shelf holder
This adds a shelf holder for shelves with a thickness of 2 cm.
2023-08-09 22:38:03 +02:00
Jonas Niesner
fd5adb40f8 Typo fix 2023-08-09 21:32:45 +02:00
Jonas Niesner
89346b5d77 Update and rename esp32-build-test.yml to build-test.yml 2023-08-09 18:03:29 +02:00
Nic Limper
7ac57577df prelimary tagtype def for EL029H3WRA (M3 2.9")
I will finetune the content positions later
2023-08-08 17:31:30 +02:00
Nic Limper
09f7466f6f new flexible tagtype definition
- new: tagtypes are be defined via json in /tagtypes instead of hardcoded
- content template is moved to the tagtype definition
- optimalisation of spr2buffer (also uses psram now, if available)
- bugfixes in spiffseditor
- size fix in painter screen for large screens
2023-08-08 16:31:20 +02:00
atc1441
55e50b1920 Merge branch 'master' of https://github.com/jjwbruijn/OpenEPaperLink 2023-08-08 16:00:25 +02:00
atc1441
7cb5e13a7f Added compiled version of 7.4" 2023-08-08 16:00:05 +02:00
Jonas Niesner
6f08af2c67 Create 32MB_partition table.csv 2023-08-07 17:30:07 +02:00
Jonas Niesner
e185ddd99b Add OutdoorAP 2023-08-07 16:59:47 +02:00
atc1441
3079101c48 Tag DB Size was only 8bit now 32bit, need to be fixed in further UDP usage later as well 2023-08-07 11:53:28 +02:00
atc1441
0b66a23ce4 Added Yellow AP basic parts, for now using Arduino GFX for simplicity 2023-08-06 23:35:30 +02:00
Nic Limper
41c7cb843f cleanup non-.gz files in www folder 2023-08-05 22:51:38 +02:00
108 changed files with 5612 additions and 3681 deletions

81
.github/workflows/build-test.yml vendored Normal file
View File

@@ -0,0 +1,81 @@
name: Firmware build test
on: [push,pull_request]
jobs:
build:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v3
- uses: actions/cache@v3
with:
path: |
~/.cache/pip
~/.platformio/.cache
key: ${{ runner.os }}-pio
- uses: actions/setup-python@v4
with:
python-version: '3.9'
- name: Install PlatformIO Core
run: pip install --upgrade platformio
- name: Build Simple_AP
run: |
cd ESP32_AP-Flasher
pio run --environment Simple_AP
pio run --target buildfs --environment Simple_AP
- name: Build OpenEPaperLink_Mini_AP
run: |
cd ESP32_AP-Flasher
pio run --environment OpenEPaperLink_Mini_AP
pio run --target buildfs --environment OpenEPaperLink_Mini_AP
# - name: Build OpenEPaperLink_Nano_AP
# run: |
# cd ESP32_AP-Flasher
# pio run --environment OpenEPaperLink_Nano_AP
# pio run --target buildfs --environment OpenEPaperLink_Nano_AP
- name: Build OpenEPaperLink_AP_and_Flasher
run: |
cd ESP32_AP-Flasher
pio run --environment OpenEPaperLink_AP_and_Flasher
pio run --target buildfs --environment OpenEPaperLink_AP_and_Flasher
# - name: Build Wemos_d1_mini32_AP
# run: |
# cd ESP32_AP-Flasher
# pio run --environment Wemos_d1_mini32_AP
# pio run --target buildfs --environment Wemos_d1_mini32_AP
# - name: Build M5Stack_Core_ONE_AP
# run: |
# cd ESP32_AP-Flasher
# pio run --environment M5Stack_Core_ONE_AP
# pio run --target buildfs --environment M5Stack_Core_ONE_AP
- name: Build ESP32_S3_16_8_YELLOW_AP]
run: |
cd ESP32_AP-Flasher
pio run --environment ESP32_S3_16_8_YELLOW_AP
pio run --target buildfs --environment ESP32_S3_16_8_YELLOW_AP
# - name: Build Sonoff_zb_bridge_P_AP
# run: |
# cd ESP32_AP-Flasher
# pio run --environment Sonoff_zb_bridge_P_AP
# pio run --target buildfs --environment Sonoff_zb_bridge_P_AP
# - name: Build OpenEPaperLink_CC1352P
# run: |
# cd ESP32_AP-Flasher
# pio run --environment OpenEPaperLink_CC1352P
# pio run --target buildfs --environment OpenEPaperLink_CC1352P
# - name: OutdoorAP
# run: |
# cd ESP32_AP-Flasher
# pio run --environment OutdoorAP
# pio run --target buildfs --environment OutdoorAP

View File

@@ -1,57 +0,0 @@
name: ESP32 firmware builf test
on: [push,pull_request]
jobs:
build:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v3
- uses: actions/cache@v3
with:
path: |
~/.cache/pip
~/.platformio/.cache
key: ${{ runner.os }}-pio
- uses: actions/setup-python@v4
with:
python-version: '3.9'
- name: Install PlatformIO Core
run: pip install --upgrade platformio
- name: Build Simple_AP
run: |
cd ESP32_AP-Flasher
pio run --environment Simple_AP
pio run --target buildfs --environment Simple_AP
- name: Build OpenEPaperLink_Mini_AP
run: |
cd ESP32_AP-Flasher
pio run --environment OpenEPaperLink_Mini_AP
pio run --target buildfs --environment OpenEPaperLink_Mini_AP
- name: Build OpenEPaperLink_Nano_AP
run: |
cd ESP32_AP-Flasher
pio run --environment OpenEPaperLink_Nano_AP
pio run --target buildfs --environment OpenEPaperLink_Nano_AP
- name: Build OpenEPaperLink_AP_and_Flasher
run: |
cd ESP32_AP-Flasher
pio run --environment OpenEPaperLink_AP_and_Flasher
pio run --target buildfs --environment OpenEPaperLink_AP_and_Flasher
- name: Build Wemos_d1_mini32_AP
run: |
cd ESP32_AP-Flasher
pio run --environment Wemos_d1_mini32_AP
pio run --target buildfs --environment Wemos_d1_mini32_AP
- name: Build M5Stack_Core_ONE_AP
run: |
cd ESP32_AP-Flasher
pio run --environment M5Stack_Core_ONE_AP
pio run --target buildfs --environment M5Stack_Core_ONE_AP

View File

@@ -21,6 +21,18 @@ jobs:
with:
python-version: '3.9'
# - name: Zip web files
# run: |
# cd /home/runner/work/OpenEPaperLink/OpenEPaperLink/ESP32_AP-Flasher
# python gzip_wwwfiles.py
# - 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
@@ -138,6 +150,24 @@ 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
export PLATFORMIO_BUILD_FLAGS="-D BUILD_VERSION=${{ github.ref_name }} -D SHA=$GITHUB_SHA"
pio run --environment ESP32_S3_16_8_YELLOW_AP
pio run --target buildfs --environment ESP32_S3_16_8_YELLOW_AP
mkdir /home/runner/work/OpenEPaperLink/OpenEPaperLink/ESP32_S3_16_8_YELLOW_AP
cp ~/.platformio/packages/framework-arduinoespressif32/tools/partitions/boot_app0.bin /home/runner/work/OpenEPaperLink/OpenEPaperLink/ESP32_S3_16_8_YELLOW_AP/boot_app0.bin
cp .pio/build/ESP32_S3_16_8_YELLOW_AP/firmware.bin /home/runner/work/OpenEPaperLink/OpenEPaperLink/ESP32_S3_16_8_YELLOW_AP/firmware.bin
cp .pio/build/ESP32_S3_16_8_YELLOW_AP/bootloader.bin /home/runner/work/OpenEPaperLink/OpenEPaperLink/ESP32_S3_16_8_YELLOW_AP/bootloader.bin
cp .pio/build/ESP32_S3_16_8_YELLOW_AP/partitions.bin /home/runner/work/OpenEPaperLink/OpenEPaperLink/ESP32_S3_16_8_YELLOW_AP/partitions.bin
cp .pio/build/ESP32_S3_16_8_YELLOW_AP/littlefs.bin /home/runner/work/OpenEPaperLink/OpenEPaperLink/ESP32_S3_16_8_YELLOW_AP/littlefs.bin
cd /home/runner/work/OpenEPaperLink/OpenEPaperLink/ESP32_S3_16_8_YELLOW_AP
esptool.py --chip esp32-s3 merge_bin -o merged-firmware.bin --flash_mode dio --flash_freq 80m --flash_size 16MB 0x0000 bootloader.bin 0x8000 partitions.bin 0xe000 boot_app0.bin 0x10000 firmware.bin 0x00910000 littlefs.bin
cd /home/runner/work/OpenEPaperLink/OpenEPaperLink
cp ESP32_S3_16_8_YELLOW_AP/firmware.bin espbinaries/ESP32_S3_16_8_YELLOW_AP.bin
cp ESP32_S3_16_8_YELLOW_AP/merged-firmware.bin espbinaries/ESP32_S3_16_8_YELLOW_AP_full.bin
- name: generate release json file
run: |

View File

@@ -1,2 +1,4 @@
build
*.axf
# Allow
!*.bin

View File

@@ -0,0 +1,98 @@
const char font[256][40]={
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x20
{0x00,0x00,0x00,0x00,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x00,0x00,0x00,0x00,0x06,0x00,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x21
{0x00,0x00,0xC0,0x18,0xC0,0x18,0xC0,0x18,0xC0,0x18,0xC0,0x18,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x22
{0x00,0x00,0x00,0x00,0x00,0x22,0x00,0x22,0x00,0x11,0x00,0x11,0x00,0x11,0xE0,0xFF,0x80,0x08,0x80,0x08,0x80,0x08,0xE0,0x7F,0x40,0x04,0x40,0x04,0x20,0x02,0x20,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x23
{0x00,0x00,0x00,0x04,0x00,0x1F,0x80,0x3F,0xC0,0x24,0xC0,0x04,0xC0,0x04,0x80,0x07,0x00,0x07,0x00,0x1C,0x00,0x1C,0x00,0x34,0x00,0x34,0x40,0x34,0xC0,0x1F,0x80,0x0F,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x24
{0x00,0x00,0x00,0x00,0xE0,0x81,0x30,0x43,0x30,0x23,0x30,0x13,0x30,0x0B,0x30,0x0B,0xE0,0x05,0x00,0x7A,0x00,0xCD,0x00,0xCD,0x80,0xCC,0x40,0xCC,0x20,0xCC,0x10,0x78,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x25
{0x00,0x00,0x00,0x00,0x00,0x0F,0x80,0x1F,0x80,0x19,0x80,0x19,0x80,0x0D,0x00,0x07,0xC0,0x03,0x60,0xC6,0x30,0xCE,0x30,0xCC,0x30,0x78,0x70,0x78,0xE0,0x7F,0xC0,0xEF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x26
{0x00,0x00,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x27
{0x00,0x00,0x00,0x30,0x00,0x3C,0x00,0x0E,0x00,0x06,0x00,0x03,0x00,0x03,0x80,0x01,0x80,0x01,0x80,0x01,0x80,0x01,0x80,0x01,0x80,0x01,0x00,0x03,0x00,0x03,0x00,0x06,0x00,0x0E,0x00,0x3C,0x00,0x30,0x00,0x00}, // 0x28
{0x00,0x00,0xC0,0x00,0xC0,0x03,0x00,0x07,0x00,0x06,0x00,0x0C,0x00,0x0C,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x0C,0x00,0x0C,0x00,0x06,0x00,0x07,0xC0,0x03,0xC0,0x00,0x00,0x00}, // 0x29
{0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x03,0x60,0x33,0xE0,0x3C,0x00,0x00,0x80,0x0D,0xC0,0x19,0x80,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x2A
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0xE0,0x7F,0xE0,0x7F,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x2B
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0x00,0x07,0x00,0x07,0x00,0x06,0x00,0x02,0x00,0x03,0x00,0x00}, // 0x2C
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xC0,0x3F,0xC0,0x3F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x2D
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0x00,0x07,0x00,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x2E
{0x00,0x00,0x00,0x60,0x00,0x30,0x00,0x30,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x0C,0x00,0x0C,0x00,0x06,0x00,0x06,0x00,0x03,0x00,0x03,0x80,0x01,0x80,0x01,0x80,0x01,0xC0,0x00,0xC0,0x00,0x60,0x00,0x00,0x00}, // 0x2F
{0x00,0x00,0x00,0x00,0x00,0x0F,0x80,0x1F,0xC0,0x30,0xC0,0x30,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0xC0,0x30,0xC0,0x30,0x80,0x1F,0x00,0x0F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x30
{0x00,0x00,0x00,0x00,0x00,0x06,0xC0,0x07,0x60,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0xE0,0x7F,0xE0,0x7F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x31
{0x00,0x00,0x00,0x00,0x80,0x0F,0xC0,0x1F,0x40,0x38,0x00,0x30,0x00,0x30,0x00,0x30,0x00,0x18,0x00,0x0C,0x00,0x06,0x00,0x03,0x80,0x01,0xC0,0x00,0xC0,0x3F,0xC0,0x3F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x32
{0x00,0x00,0x00,0x00,0x80,0x0F,0xC0,0x3F,0x40,0x30,0x00,0x30,0x00,0x18,0x80,0x0F,0x80,0x0F,0x00,0x18,0x00,0x30,0x00,0x30,0x00,0x30,0x40,0x38,0xC0,0x1F,0xC0,0x0F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x33
{0x00,0x00,0x00,0x00,0x00,0x18,0x00,0x1C,0x00,0x1E,0x00,0x1A,0x00,0x19,0x80,0x19,0xC0,0x18,0x40,0x18,0xE0,0x7F,0xE0,0x7F,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x34
{0x00,0x00,0x00,0x00,0x80,0x3F,0x80,0x3F,0x80,0x01,0x80,0x01,0x80,0x01,0x80,0x01,0x80,0x0F,0x80,0x1F,0x00,0x38,0x00,0x30,0x00,0x30,0x00,0x38,0x80,0x1F,0x80,0x0F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x35
{0x00,0x00,0x00,0x00,0x00,0x0F,0x80,0x1F,0xC0,0x11,0xC0,0x00,0x60,0x00,0x60,0x0E,0x60,0x1F,0xE0,0x38,0x60,0x30,0x60,0x30,0x60,0x30,0xC0,0x38,0xC0,0x1F,0x00,0x0F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x36
{0x00,0x00,0x00,0x00,0xC0,0x7F,0xC0,0x7F,0x00,0x60,0x00,0x30,0x00,0x10,0x00,0x18,0x00,0x0C,0x00,0x04,0x00,0x06,0x00,0x02,0x00,0x03,0x00,0x03,0x80,0x01,0x80,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x37
{0x00,0x00,0x00,0x00,0x00,0x0F,0xC0,0x1F,0xC0,0x18,0xC0,0x18,0xC0,0x19,0x80,0x0F,0x00,0x07,0xC0,0x1E,0x60,0x38,0x60,0x30,0x60,0x30,0xE0,0x38,0xC0,0x1F,0x80,0x0F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x38
{0x00,0x00,0x00,0x00,0x80,0x07,0xC0,0x1F,0xE0,0x18,0x60,0x30,0x60,0x30,0x60,0x30,0xE0,0x38,0xC0,0x37,0x80,0x33,0x00,0x30,0x00,0x18,0x40,0x1C,0xC0,0x0F,0x80,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x39
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0x00,0x07,0x00,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0x00,0x07,0x00,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x3A
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0x00,0x07,0x00,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0x00,0x07,0x00,0x07,0x00,0x06,0x00,0x02,0x00,0x03,0x00,0x00}, // 0x3B
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x40,0x00,0x70,0x00,0x3C,0x00,0x0E,0x80,0x03,0xE0,0x00,0x80,0x03,0x00,0x0E,0x00,0x3C,0x00,0x70,0x00,0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x3C
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0x7F,0xE0,0x7F,0x00,0x00,0x00,0x00,0xE0,0x7F,0xE0,0x7F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x3D
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x00,0xE0,0x00,0xC0,0x03,0x00,0x07,0x00,0x1C,0x00,0x70,0x00,0x1C,0x00,0x07,0xC0,0x03,0xE0,0x00,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x3E
{0x00,0x00,0x00,0x00,0xE0,0x0F,0xE0,0x3F,0x20,0x38,0x00,0x30,0x00,0x30,0x00,0x18,0x00,0x0C,0x00,0x06,0x00,0x03,0x00,0x03,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x3F
{0x00,0x00,0x00,0x00,0x00,0x1F,0x80,0x31,0xC0,0x60,0x60,0x7C,0x30,0x66,0x30,0x63,0x30,0x63,0x30,0x73,0x30,0x73,0x30,0x6F,0x60,0xE6,0x60,0x00,0xC0,0x00,0x00,0x1F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x40
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x00,0x0F,0x00,0x0F,0x00,0x0D,0x80,0x19,0x80,0x19,0xC0,0x38,0xC0,0x30,0xC0,0x3F,0xE0,0x7F,0x60,0x60,0x60,0x60,0x30,0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x41
{0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0x0F,0xE0,0x1F,0x60,0x18,0x60,0x18,0x60,0x0C,0xE0,0x07,0xE0,0x0F,0x60,0x18,0x60,0x30,0x60,0x30,0x60,0x30,0xE0,0x1F,0xE0,0x0F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x42
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3E,0x80,0x7F,0xC0,0x41,0xC0,0x00,0x60,0x00,0x60,0x00,0x60,0x00,0x60,0x00,0x60,0x00,0xC0,0x00,0xC0,0x43,0x80,0x7F,0x00,0x3E,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x43
{0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0x0F,0xE0,0x1F,0x60,0x38,0x60,0x70,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x30,0x60,0x38,0xE0,0x1F,0xE0,0x0F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x44
{0x00,0x00,0x00,0x00,0x00,0x00,0xC0,0x7F,0xC0,0x7F,0xC0,0x00,0xC0,0x00,0xC0,0x00,0xC0,0x3F,0xC0,0x3F,0xC0,0x00,0xC0,0x00,0xC0,0x00,0xC0,0x00,0xC0,0x7F,0xC0,0x7F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x45
{0x00,0x00,0x00,0x00,0x00,0x00,0xC0,0x7F,0xC0,0x7F,0xC0,0x00,0xC0,0x00,0xC0,0x00,0xC0,0x3F,0xC0,0x3F,0xC0,0x00,0xC0,0x00,0xC0,0x00,0xC0,0x00,0xC0,0x00,0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x46
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3E,0x80,0x7F,0xC0,0x41,0xC0,0x00,0x60,0x00,0x60,0x00,0x60,0x78,0x60,0x78,0x60,0x60,0xC0,0x60,0xC0,0x61,0x80,0x7F,0x00,0x3E,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x47
{0x00,0x00,0x00,0x00,0x00,0x00,0x60,0x30,0x60,0x30,0x60,0x30,0x60,0x30,0x60,0x30,0xE0,0x3F,0xE0,0x3F,0x60,0x30,0x60,0x30,0x60,0x30,0x60,0x30,0x60,0x30,0x60,0x30,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x48
{0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0x1F,0xE0,0x1F,0x00,0x03,0x00,0x03,0x00,0x03,0x00,0x03,0x00,0x03,0x00,0x03,0x00,0x03,0x00,0x03,0x00,0x03,0xE0,0x1F,0xE0,0x1F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x49
{0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x1F,0x80,0x1F,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x1C,0xC0,0x0F,0xC0,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x4A
{0x00,0x00,0x00,0x00,0x00,0x00,0x60,0x70,0x60,0x38,0x60,0x1C,0x60,0x0E,0x60,0x06,0x60,0x03,0xE0,0x03,0x60,0x07,0x60,0x0E,0x60,0x1C,0x60,0x38,0x60,0x70,0x60,0xE0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x4B
{0x00,0x00,0x00,0x00,0x00,0x00,0xC0,0x00,0xC0,0x00,0xC0,0x00,0xC0,0x00,0xC0,0x00,0xC0,0x00,0xC0,0x00,0xC0,0x00,0xC0,0x00,0xC0,0x00,0xC0,0x00,0xC0,0x7F,0xC0,0x7F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x4C
{0x00,0x00,0x00,0x00,0x00,0x00,0x70,0x70,0x70,0x70,0xF0,0x78,0xB0,0x68,0xB0,0x68,0xB0,0x6D,0x30,0x65,0x30,0x65,0x30,0x67,0x30,0x62,0x30,0x60,0x30,0x60,0x30,0x60,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x4D
{0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0x60,0xE0,0x60,0xE0,0x61,0xE0,0x61,0x60,0x63,0x60,0x67,0x60,0x66,0x60,0x6E,0x60,0x6C,0x60,0x78,0x60,0x78,0x60,0x70,0x60,0x70,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x4E
{0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x0F,0xC0,0x1F,0xE0,0x38,0x70,0x70,0x30,0x60,0x30,0x60,0x30,0x60,0x30,0x60,0x30,0x60,0x70,0x70,0xE0,0x38,0xC0,0x1F,0x80,0x0F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x4F
{0x00,0x00,0x00,0x00,0x00,0x00,0xC0,0x1F,0xC0,0x3F,0xC0,0x70,0xC0,0x60,0xC0,0x60,0xC0,0x70,0xC0,0x3F,0xC0,0x0F,0xC0,0x00,0xC0,0x00,0xC0,0x00,0xC0,0x00,0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x50
{0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x0F,0xC0,0x1F,0xE0,0x38,0x70,0x70,0x30,0x60,0x30,0x60,0x30,0x60,0x30,0x60,0x30,0x60,0x60,0x30,0xE0,0x38,0xC0,0x1F,0x80,0x0F,0x00,0x38,0x00,0xF0,0x00,0x40,0x00,0x00}, // 0x51
{0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0x07,0xE0,0x1F,0x60,0x18,0x60,0x18,0x60,0x18,0x60,0x1C,0xE0,0x0F,0xE0,0x07,0x60,0x0E,0x60,0x1C,0x60,0x38,0x60,0x70,0x60,0xE0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x52
{0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x0F,0xC0,0x1F,0x60,0x10,0x60,0x00,0xE0,0x00,0xC0,0x03,0x80,0x0F,0x00,0x3C,0x00,0x30,0x00,0x30,0x60,0x38,0xE0,0x1F,0xC0,0x0F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x53
{0x00,0x00,0x00,0x00,0x00,0x00,0xF0,0xFF,0xF0,0xFF,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x54
{0x00,0x00,0x00,0x00,0x00,0x00,0x60,0x30,0x60,0x30,0x60,0x30,0x60,0x30,0x60,0x30,0x60,0x30,0x60,0x30,0x60,0x30,0x60,0x30,0x60,0x30,0xE0,0x38,0xC0,0x1F,0x80,0x0F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x55
{0x00,0x00,0x00,0x00,0x00,0x00,0x70,0xC0,0x60,0x60,0x60,0x60,0xE0,0x60,0xC0,0x30,0xC0,0x30,0xC0,0x31,0x80,0x19,0x80,0x1B,0x00,0x0B,0x00,0x0F,0x00,0x0F,0x00,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x56
{0x00,0x00,0x00,0x00,0x00,0x00,0x30,0xC0,0x30,0xC0,0x30,0xC0,0x20,0x46,0x20,0x46,0x20,0x6E,0x60,0x6F,0x60,0x69,0x60,0x69,0x60,0x39,0xC0,0x39,0xC0,0x39,0xC0,0x30,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x57
{0x00,0x00,0x00,0x00,0x00,0x00,0x70,0xE0,0xE0,0x60,0xC0,0x30,0x80,0x19,0x80,0x0F,0x00,0x0F,0x00,0x06,0x00,0x0F,0x80,0x1D,0x80,0x19,0xC0,0x30,0x60,0x70,0x30,0xE0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x58
{0x00,0x00,0x00,0x00,0x00,0x00,0x70,0xC0,0x60,0x60,0xC0,0x30,0xC0,0x31,0x80,0x19,0x00,0x0F,0x00,0x0F,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x59
{0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0x7F,0xE0,0x7F,0x00,0x60,0x00,0x30,0x00,0x18,0x00,0x0C,0x00,0x06,0x00,0x03,0x80,0x01,0xC0,0x00,0x60,0x00,0xE0,0x7F,0xE0,0x7F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x5A
{0x00,0x00,0x00,0x3F,0x00,0x3F,0x00,0x03,0x00,0x03,0x00,0x03,0x00,0x03,0x00,0x03,0x00,0x03,0x00,0x03,0x00,0x03,0x00,0x03,0x00,0x03,0x00,0x03,0x00,0x03,0x00,0x03,0x00,0x03,0x00,0x3F,0x00,0x3F,0x00,0x00}, // 0x5B
{0x00,0x00,0x60,0x00,0xC0,0x00,0xC0,0x00,0x80,0x01,0x80,0x01,0x80,0x01,0x00,0x03,0x00,0x03,0x00,0x06,0x00,0x06,0x00,0x0C,0x00,0x0C,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x30,0x00,0x30,0x00,0x60,0x00,0x00}, // 0x5C
{0x00,0x00,0xC0,0x0F,0xC0,0x0F,0x00,0x0C,0x00,0x0C,0x00,0x0C,0x00,0x0C,0x00,0x0C,0x00,0x0C,0x00,0x0C,0x00,0x0C,0x00,0x0C,0x00,0x0C,0x00,0x0C,0x00,0x0C,0x00,0x0C,0x00,0x0C,0xC0,0x0F,0xC0,0x0F,0x00,0x00}, // 0x5D
{0x00,0x00,0x00,0x00,0x00,0x04,0x00,0x04,0x00,0x0E,0x00,0x0A,0x00,0x0B,0x00,0x1B,0x80,0x11,0x80,0x31,0xC0,0x30,0xC0,0x20,0x60,0x60,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x5E
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xF0,0xFF,0xF0,0xFF,0x00,0x00,0x00,0x00}, // 0x5F
{0x00,0x06,0x00,0x0C,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x60
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x1F,0xC0,0x3F,0x40,0x30,0x00,0x30,0x00,0x30,0x80,0x3F,0xC0,0x30,0x60,0x30,0x60,0x38,0xE0,0xFF,0xC0,0xE7,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x61
{0x00,0x00,0x60,0x00,0x60,0x00,0x60,0x00,0x60,0x00,0x60,0x0E,0x60,0x1F,0xE0,0x39,0xE0,0x30,0x60,0x30,0x60,0x30,0x60,0x30,0x60,0x30,0xE0,0x18,0xE0,0x1F,0x60,0x0F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x62
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1F,0xC0,0x3F,0xC0,0x21,0xE0,0x00,0x60,0x00,0x60,0x00,0x60,0x00,0xE0,0x00,0xC0,0x01,0xC0,0x3F,0x00,0x1F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x63
{0x00,0x00,0x00,0x30,0x00,0x30,0x00,0x30,0x00,0x30,0x80,0x37,0xC0,0x3F,0xC0,0x38,0x60,0x30,0x60,0x30,0x60,0x30,0x60,0x30,0x60,0x30,0xE0,0x38,0xC0,0x3F,0x80,0x33,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x64
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xC0,0x1F,0xC0,0x38,0x60,0x30,0xE0,0x3F,0xE0,0x3F,0x60,0x00,0x60,0x00,0xC0,0x20,0xC0,0x3F,0x00,0x1F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x65
{0x00,0x00,0x00,0x7E,0x00,0x7F,0x00,0x03,0x00,0x03,0xE0,0x7F,0xE0,0x7F,0x00,0x03,0x00,0x03,0x00,0x03,0x00,0x03,0x00,0x03,0x00,0x03,0x00,0x03,0x00,0x03,0x00,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x66
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x37,0xC0,0x3F,0xC0,0x38,0x60,0x30,0x60,0x30,0x60,0x30,0x60,0x30,0x60,0x30,0xE0,0x38,0xC0,0x3F,0x80,0x37,0x00,0x30,0x40,0x38,0xC0,0x1F,0x80,0x0F}, // 0x67
{0x00,0x00,0x60,0x00,0x60,0x00,0x60,0x00,0x60,0x00,0x60,0x1E,0x60,0x3F,0xE0,0x31,0x60,0x30,0x60,0x30,0x60,0x30,0x60,0x30,0x60,0x30,0x60,0x30,0x60,0x30,0x60,0x30,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x68
{0x00,0x00,0x00,0x0C,0x00,0x0C,0x00,0x00,0x00,0x00,0xC0,0x0F,0xC0,0x0F,0x00,0x0C,0x00,0x0C,0x00,0x0C,0x00,0x0C,0x00,0x0C,0x00,0x0C,0x00,0x0C,0x00,0x0C,0x00,0x0C,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x69
{0x00,0x00,0x00,0x18,0x00,0x18,0x00,0x00,0x00,0x00,0x80,0x1F,0x80,0x1F,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x1C,0xC0,0x0F,0xC0,0x07}, // 0x6A
{0x00,0x00,0xC0,0x00,0xC0,0x00,0xC0,0x00,0xC0,0x00,0xC0,0x70,0xC0,0x38,0xC0,0x1C,0xC0,0x0E,0xC0,0x06,0xC0,0x07,0xC0,0x0E,0xC0,0x1C,0xC0,0x38,0xC0,0x70,0xC0,0xE0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x6B
{0x00,0x00,0xC0,0x0F,0xC0,0x0F,0x00,0x0C,0x00,0x0C,0x00,0x0C,0x00,0x0C,0x00,0x0C,0x00,0x0C,0x00,0x0C,0x00,0x0C,0x00,0x0C,0x00,0x0C,0x00,0x0C,0x00,0x0C,0x00,0x0C,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x6C
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xB0,0x39,0xF0,0x7F,0x70,0x67,0x30,0x63,0x30,0x63,0x30,0x63,0x30,0x63,0x30,0x63,0x30,0x63,0x30,0x63,0x30,0x63,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x6D
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x60,0x1E,0x60,0x3F,0xE0,0x31,0x60,0x30,0x60,0x30,0x60,0x30,0x60,0x30,0x60,0x30,0x60,0x30,0x60,0x30,0x60,0x30,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x6E
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xC0,0x3F,0xC0,0x30,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0xC0,0x30,0xC0,0x3F,0x00,0x0F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x6F
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x60,0x0F,0xE0,0x1F,0xE0,0x38,0x60,0x30,0x60,0x30,0x60,0x30,0x60,0x30,0x60,0x30,0xE0,0x18,0xE0,0x1F,0x60,0x0F,0x60,0x00,0x60,0x00,0x60,0x00,0x60,0x00}, // 0x70
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x37,0xC0,0x3F,0xC0,0x38,0x60,0x30,0x60,0x30,0x60,0x30,0x60,0x30,0x60,0x30,0xE0,0x38,0xC0,0x37,0x80,0x33,0x00,0x30,0x00,0x30,0x00,0x30,0x00,0x30}, // 0x71
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xC0,0x3C,0xC0,0x3E,0xC0,0x23,0xC0,0x00,0xC0,0x00,0xC0,0x00,0xC0,0x00,0xC0,0x00,0xC0,0x00,0xC0,0x00,0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x72
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x1F,0xC0,0x1F,0xC0,0x00,0xC0,0x00,0xC0,0x03,0x00,0x1F,0x00,0x38,0x00,0x30,0x40,0x30,0xC0,0x1F,0x80,0x0F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x73
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x03,0xE0,0x7F,0xE0,0x7F,0x00,0x03,0x00,0x03,0x00,0x03,0x00,0x03,0x00,0x03,0x00,0x03,0x00,0x03,0x00,0x7F,0x00,0x7E,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x74
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x60,0x30,0x60,0x30,0x60,0x30,0x60,0x30,0x60,0x30,0x60,0x30,0x60,0x30,0x60,0x30,0x60,0x3C,0xE0,0x37,0xC0,0x33,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x75
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x60,0x60,0xC0,0x20,0xC0,0x30,0xC0,0x30,0x80,0x11,0x80,0x19,0x80,0x19,0x00,0x0B,0x00,0x0F,0x00,0x0F,0x00,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x76
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x30,0xC0,0x30,0xC6,0x30,0xC6,0x20,0x4E,0x60,0x4F,0x60,0x49,0x60,0x69,0x60,0x79,0xC0,0x39,0xC0,0x30,0xC0,0x30,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x77
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0x70,0xC0,0x30,0x80,0x19,0x80,0x0B,0x00,0x0F,0x00,0x06,0x00,0x0F,0x80,0x1D,0x80,0x19,0xC0,0x30,0x60,0x70,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x78
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x60,0x60,0xC0,0x20,0xC0,0x30,0xC0,0x31,0x80,0x19,0x80,0x19,0x00,0x0B,0x00,0x0F,0x00,0x0F,0x00,0x06,0x00,0x06,0x00,0x02,0x00,0x03,0xC0,0x03,0xC0,0x01}, // 0x79
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0x3F,0xE0,0x3F,0x00,0x30,0x00,0x18,0x00,0x0C,0x00,0x06,0x00,0x03,0x80,0x01,0xC0,0x00,0xE0,0x3F,0xE0,0x3F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x7A
{0x00,0x00,0x00,0x3C,0x00,0x3E,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0xC0,0x03,0xC0,0x03,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x3E,0x00,0x3C,0x00,0x00}, // 0x7B
{0x00,0x00,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x00}, // 0x7C
{0x00,0x00,0xC0,0x03,0xC0,0x07,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x3C,0x00,0x3C,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0xC0,0x07,0xC0,0x03,0x00,0x00}, // 0x7D
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xC0,0x43,0xE0,0x7F,0x20,0x3C,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x7E
{0x00,0x00,0x00,0x00,0x00,0x00,0xC0,0x3F,0x40,0x20,0x40,0x20,0x40,0x20,0x40,0x20,0x40,0x20,0x40,0x20,0x40,0x20,0x40,0x20,0x40,0x20,0x40,0x20,0x40,0x20,0xC0,0x3F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00} // 0x7F
};

View File

@@ -1,10 +1,11 @@
MZ_FLASHER=python ../88MZ100_Flasher/88MZ100_Uart_flasher.py
ARMGCC=armgcc/bin/
ARMGCC=/usr/bin/
CC=$(ARMGCC)arm-none-eabi-gcc
AS=$(ARMGCC)arm-none-eabi-as
OBJCOPY=$(ARMGCC)arm-none-eabi-objcopy
#-Wall
CC_WARNING_FLAGS=-Wall -Wformat=0 -Wattributes -Wstrict-aliasing=0
CC_FlAGS=-mcpu=cortex-m3 -g -O0 -mthumb -fdata-sections -ffunction-sections -std=c99
@@ -16,10 +17,15 @@ C_EXECUTABLE :=$(C_SOURCES:.c=)
COMPORT = COM12
build: clean compile create_ota_img flash_uart_flash
build: compile
only: clean compile create_ota_img
uart: clean compile flash_uart
#build: clean compile create_ota_img flash_uart_flash
#only: clean compile create_ota_img
#uart: clean compile flash_uart
compile:
@mkdir -p build
@$(AS) -mcpu=cortex-m3 --gdwarf-2 -mthumb-interwork -o build/startup.o startup.S
@@ -40,16 +46,8 @@ compile:
@$(CC) $(CC_FlAGS) -c mz100_gpt.c -o build/mz100_gpt.o
@$(CC) $(CC_FlAGS) -c mz100_sleep.c -o build/mz100_sleep.o
@$(CC) $(CC_FlAGS) -c mz100_uart.c -o build/mz100_uart.o
# UZLIB
@$(CC) $(CC_FlAGS) $(CC_WARNING_FLAGS) -c uzlib/src/adler32.c -o build/adler32.o
@$(CC) $(CC_FlAGS) $(CC_WARNING_FLAGS) -c uzlib/src/crc32.c -o build/crc32.o
@$(CC) $(CC_FlAGS) $(CC_WARNING_FLAGS) -c uzlib/src/defl_static.c -o build/defl_static.o
@$(CC) $(CC_FlAGS) $(CC_WARNING_FLAGS) -c uzlib/src/genlz77.c -o build/genlz77.o
@$(CC) $(CC_FlAGS) $(CC_WARNING_FLAGS) -c uzlib/src/tinfgzip.c -o build/tinfgzip.o
@$(CC) $(CC_FlAGS) $(CC_WARNING_FLAGS) -c uzlib/src/tinflate.c -o build/tinflate.o
@$(CC) $(CC_FlAGS) $(CC_WARNING_FLAGS) -c uzlib/src/tinfzlib.c -o build/tinfzlib.o
# UZLIB END
@$(CC) $(CC_FlAGS) $(CC_WARNING_FLAGS) -c compression.c -o build/compression.o
@$(CC) $(CC_FlAGS) -c mz100_aon_ram.c -o build/mz100_aon_ram.o
@$(CC) $(CC_FlAGS) -c printf.c -o build/printf.o
@$(CC) $(CC_FlAGS) $(CC_WARNING_FLAGS) -c zigbee.c -o build/zigbee.o
@$(CC) $(CC_FlAGS) $(CC_WARNING_FLAGS) -c ccm.c -o build/ccm.o
@$(CC) $(CC_FlAGS) $(CC_WARNING_FLAGS) -c chars.c -o build/chars.o
@@ -61,10 +59,10 @@ compile:
@$(CC) $(CC_FlAGS) $(CC_WARNING_FLAGS) -c timer.c -o build/timer.o
@$(CC) $(CC_FlAGS) $(CC_WARNING_FLAGS) -c util.c -o build/util.o
@$(CC) $(CC_FlAGS) $(CC_WARNING_FLAGS) -c gpio.c -o build/gpio.o
@$(CC) $(CC_FlAGS) $(CC_WARNING_FLAGS) -c nfc.c -o build/nfc.o
@$(CC) $(CC_FlAGS) $(CC_WARNING_FLAGS) -c epd.c -o build/epd.o
@$(CC) $(CC_FlAGS) $(CC_WARNING_FLAGS) -c userinterface.c -o build/userinterface.o
@$(CC) $(CC_FlAGS) $(CC_WARNING_FLAGS) -c main.c -o build/main.o
@$(CC) $(CC_END_FLAGS) $(CC_WARNING_FLAGS) build/main.o build/adler32.o build/crc32.o build/defl_static.o build/genlz77.o build/tinfgzip.o build/tinflate.o build/tinfzlib.o build/compression.o build/zigbee.o build/ccm.o build/chars.o build/drawing.o build/powermgt.o build/syncedproto.o build/comms.o build/settings.o build/timer.o build/util.o build/gpio.o build/nfc.o build/epd.o build/mz100_sleep.o build/core_cm3.o build/mz100_ssp.o build/mz100_wdt.o build/mz100_gpio.o build/mz100_driver.o build/mz100_adc.o build/mz100_flash.o build/mz100_clock.o build/mz100_rtc.o build/mz100_pinmux.o build/mz100_pmu.o build/mz100_qspi.o build/mz100_aes.o build/mz100_gpt.o build/mz100_uart.o build/startup.o -o main.axf
@$(CC) $(CC_END_FLAGS) $(CC_WARNING_FLAGS) build/main.o build/userinterface.o build/printf.o build/mz100_aon_ram.o build/zigbee.o build/chars.o build/drawing.o build/powermgt.o build/syncedproto.o build/comms.o build/settings.o build/timer.o build/util.o build/gpio.o build/epd.o build/mz100_sleep.o build/core_cm3.o build/mz100_ssp.o build/mz100_wdt.o build/mz100_gpio.o build/mz100_driver.o build/mz100_adc.o build/mz100_flash.o build/mz100_clock.o build/mz100_rtc.o build/mz100_pinmux.o build/mz100_pmu.o build/mz100_qspi.o build/mz100_aes.o build/mz100_gpt.o build/mz100_uart.o build/startup.o -o main.axf
@$(OBJCOPY) -v -O binary main.axf main.bin
clean:
@@ -83,4 +81,4 @@ flash_dump:
@$(MZ_FLASHER) $(COMPORT) read dump.bin
create_ota_img:
@$(MZ_FLASHER) img main.bin UPDT0028.BIN
@$(MZ_FLASHER) img main.bin UPDT0028.BIN

View File

@@ -1,96 +1,32 @@
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include "comms.h"
#include "proto.h"
#include <stdint.h>
//#include <stdio.h>
#include <string.h>
#include "ccm.h"
#include "proto.h"
extern uint8_t Zigbee_tx_buffer(uint8_t tx_buffer[], int len);
static uint8_t packet[128];
static uint8_t mSeq = 0;
uint8_t mLastLqi = 0;
int8_t mLastRSSI = 0;
uint8_t commsGetLastPacketLQI(void)
{
return mLastLqi;
uint8_t commsGetLastPacketLQI(void) {
return mLastLqi;
}
int8_t commsGetLastPacketRSSI(void)
{
return mLastRSSI;
int8_t commsGetLastPacketRSSI(void) {
return mLastRSSI;
}
static inline void __attribute__((always_inline)) macCopy(uint8_t *restrict dst, const uint8_t *restrict src)
{
((uint32_t *)dst)[0] = ((const uint32_t *)src)[0];
((uint32_t *)dst)[1] = ((const uint32_t *)src)[1];
static inline void __attribute__((always_inline)) macCopy(uint8_t *restrict dst, const uint8_t *restrict src) {
((uint32_t *)dst)[0] = ((const uint32_t *)src)[0];
((uint32_t *)dst)[1] = ((const uint32_t *)src)[1];
}
static inline bool __attribute__((always_inline)) macIsEq(const uint8_t *restrict dst, const uint8_t *restrict src)
{
return ((uint32_t *)dst)[0] == ((const uint32_t *)src)[0] && ((uint32_t *)dst)[1] == ((const uint32_t *)src)[1];
}
bool commsTx(struct CommsInfo *info, bool bcast, const void *packet_in, uint32_t len)
{
uint8_t nonce[AES_CCM_NONCE_SIZE] = {};
struct MacFrameNormal *mfn;
struct MacFrameBcast *mfb;
uint32_t hdrSz;
char *payload;
static const struct MacFcs normalFcs = {
.frameType = FRAME_TYPE_DATA,
.panIdCompressed = 1,
.destAddrType = ADDR_MODE_LONG,
.srcAddrType = ADDR_MODE_LONG,
};
static const struct MacFcs broadcastFcs = {
.frameType = FRAME_TYPE_DATA,
.destAddrType = ADDR_MODE_SHORT,
.srcAddrType = ADDR_MODE_LONG,
};
if (len > COMMS_MAX_PACKET_SZ)
return false;
if (bcast)
{
mfb = (struct MacFrameBcast *)packet;
hdrSz = sizeof(struct MacFrameBcast);
payload = (char *)(mfb + 1);
mfb->fcs = broadcastFcs;
mfb->seq = mSeq++;
mfb->dstPan = 0xffff;
mfb->dstAddr = 0xffff;
mfb->srcPan = PROTO_PAN_ID;
macCopy(mfb->src, info->myMac);
}
else
{
mfn = (struct MacFrameNormal *)packet;
hdrSz = sizeof(struct MacFrameNormal);
payload = (char *)(mfn + 1);
mfn->fcs = normalFcs;
mfn->seq = mSeq++;
mfn->pan = PROTO_PAN_ID;
macCopy(mfn->dst, info->masterMac);
macCopy(mfn->src, info->myMac);
}
*(uint32_t *)nonce = (*info->nextIV)++;
macCopy(nonce + sizeof(uint32_t), info->myMac);
memcpy(payload, packet_in, len);
aesCcmEnc((void *)packet, (void *)packet, hdrSz, len, info->encrKey, nonce);
*(uint32_t *)(payload + len + AES_CCM_MIC_SIZE) = *(uint32_t *)nonce; // send nonce
len += hdrSz;
len += AES_CCM_MIC_SIZE;
len += sizeof(uint32_t);
return !Zigbee_tx_buffer((uint8_t *)&packet, len);
static inline bool __attribute__((always_inline)) macIsEq(const uint8_t *restrict dst, const uint8_t *restrict src) {
return ((uint32_t *)dst)[0] == ((const uint32_t *)src)[0] && ((uint32_t *)dst)[1] == ((const uint32_t *)src)[1];
}
extern volatile uint8_t rx_buffer[0x400];
@@ -98,94 +34,16 @@ extern volatile uint8_t new_rx;
extern volatile uint8_t new_rssi;
extern volatile int rx_len;
int32_t __attribute__((noinline)) commsRx(struct CommsInfo *info, void *data, uint8_t *fromMacP)
{
uint8_t *buf = packet, nonce[13] = {}, fromMac[8] = {0, 0, 0, 0, 0, 0, 0, 0};
uint32_t len, minNeedLen, hdrLen = 0;
struct MacFrameFromMaster *mfm;
struct MacFrameNormal *mfn;
// sort out how many bytes minimum are a valid packet
minNeedLen = sizeof(struct MacFrameFromMaster); // mac header
minNeedLen += sizeof(uint8_t); // packet type
minNeedLen += AES_CCM_MIC_SIZE; // MIC
minNeedLen += sizeof(uint32_t); // nonce counter
minNeedLen += 2 * sizeof(uint8_t); // RSSI/LQI
if (!new_rx)
return COMMS_RX_ERR_NO_PACKETS;
// some basic checks
mfm = (struct MacFrameFromMaster *)rx_buffer;
if (rx_len >= sizeof(packet) || rx_len < minNeedLen || mfm->fcs.frameType != FRAME_TYPE_DATA ||
mfm->fcs.secure || mfm->fcs.frameVer || mfm->fcs.destAddrType != ADDR_MODE_LONG || !mfm->fcs.panIdCompressed ||
(mfm->fcs.srcAddrType != ADDR_MODE_LONG && mfm->fcs.srcAddrType != ADDR_MODE_SHORT) ||
mfm->pan != PROTO_PAN_ID || !macIsEq(mfm->dst, info->myMac))
{
new_rx = 0;
return COMMS_RX_ERR_INVALID_PACKET;
}
// copy out and release buffer
memcpy(buf, &rx_buffer, len = rx_len - 2 * sizeof(uint8_t));
mLastLqi = rx_buffer[len + 0];
mLastRSSI = rx_buffer[len + 1];
mfm = (struct MacFrameFromMaster *)buf;
mfn = (struct MacFrameNormal *)buf;
new_rx = 0;
// sort out header len, copy mac into nonce
if (mfm->fcs.srcAddrType == ADDR_MODE_LONG)
{
macCopy(fromMac, mfn->src);
hdrLen = sizeof(struct MacFrameNormal);
// re-verify needed length
minNeedLen -= sizeof(struct MacFrameFromMaster);
minNeedLen += sizeof(struct MacFrameNormal);
if (rx_len < minNeedLen)
return COMMS_RX_ERR_INVALID_PACKET;
}
else if (mfm->fcs.srcAddrType == ADDR_MODE_SHORT)
{
macCopy(fromMac, info->masterMac);
hdrLen = sizeof(struct MacFrameFromMaster);
}
// sort out the nonce
macCopy(nonce + sizeof(uint32_t), fromMac);
*(uint32_t *)nonce = *(uint32_t *)(buf + len - sizeof(uint32_t));
// decrypt and auth
len -= hdrLen + AES_CCM_MIC_SIZE + sizeof(uint32_t);
if (!aesCcmDec(buf, buf, hdrLen, len, info->encrKey, nonce))
return COMMS_RX_ERR_MIC_FAIL;
if (fromMacP)
macCopy(fromMacP, fromMac);
memcpy(data, buf + hdrLen, len);
return len;
int32_t __attribute__((noinline)) commsRxUnenc(void *data) {
if (!new_rx)
return COMMS_RX_ERR_NO_PACKETS;
memcpy(data, (uint8_t*)&rx_buffer, rx_len);
mLastLqi = 255 - new_rssi;
mLastRSSI = new_rssi;
new_rx = 0;
return rx_len;
}
int32_t __attribute__((noinline)) commsRxUnenc(void *data)
{
if (!new_rx)
return COMMS_RX_ERR_NO_PACKETS;
memcpy(data, &rx_buffer, rx_len);
mLastLqi = 255 - new_rssi;
mLastRSSI = new_rssi;
new_rx = 0;
return rx_len;
}
void commsTxNoCpy(uint8_t *packetp)
{
Zigbee_tx_buffer((uint8_t *)&packetp[1], (packetp[0] - 2));
void commsTxNoCpy(uint8_t *packetp) {
Zigbee_tx_buffer((uint8_t *)&packetp[1], (packetp[0] - 2));
}

View File

@@ -4,12 +4,6 @@
#include <stdint.h>
#include "ccm.h"
struct CommsInfo {
const uint8_t *myMac;
const uint8_t *masterMac;
const void *encrKey;
uint32_t *nextIV;
};
extern uint8_t mLastLqi;
extern int8_t mLastRSSI;
@@ -23,8 +17,6 @@ extern int8_t mLastRSSI;
#define COMMS_MAX_PACKET_SZ (127 /* max phy len */ - 21 /* max mac frame with panID compression */ - 2 /* FCS len */ - AES_CCM_MIC_SIZE - COMMS_IV_SIZE)
bool commsTx(struct CommsInfo *info, bool bcast, const void *packet, uint32_t len);
int32_t commsRx(struct CommsInfo *info, void *data, uint8_t *fromMac); //returns length or COMMS_RX_ERR_*
uint8_t commsGetLastPacketLQI(void);
int8_t commsGetLastPacketRSSI(void);

View File

@@ -1,7 +1,7 @@
#include "compression.h"
#include "uzlib/src/uzlib.h"
#include <stdio.h>
//#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>
#include <string.h>

View File

@@ -1,7 +1,7 @@
#pragma once
#include "uzlib/src/uzlib.h"
#include <stdio.h>
//#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>
#include <string.h>

View File

@@ -2,6 +2,7 @@
#include <stdbool.h>
#include <stdbool.h>
#include "printf.h"
#include "board.h"
#include "eeprom.h"
@@ -11,366 +12,22 @@
#include "util.h"
#include "epd.h"
#define COMPRESSION_BITPACKED_3x5_to_7 0x62700357 // 3 pixels of 5 possible colors in 7 bits
#define COMPRESSION_BITPACKED_5x3_to_8 0x62700538 // 5 pixels of 3 possible colors in 8 bits
#define COMPRESSION_BITPACKED_3x6_to_8 0x62700368 // 3 pixels of 6 possible colors in 8 bits
struct BitmapFileHeader
{
uint8_t sig[2];
uint32_t fileSz;
uint8_t rfu[4];
uint32_t dataOfst;
uint32_t headerSz; // 40
int32_t width;
int32_t height;
uint16_t colorplanes; // must be one
uint16_t bpp;
uint32_t compression;
uint32_t dataLen; // may be 0
uint32_t pixelsPerMeterX;
uint32_t pixelsPerMeterY;
uint32_t numColors; // if zero, assume 2^bpp
uint32_t numImportantColors;
};
struct BitmapClutEntry
{
uint8_t b, g, r, x;
};
struct BitmapDrawInfo
{
// dimensions
uint16_t w, h, effectiveW, effectiveH, stride /* 0 -> 1, 5 - >7, 255 -> 256 */;
uint8_t numColorsM1;
// data start
uint32_t dataAddr;
// compression state
uint8_t packetPixelDivVal;
uint8_t packetNumPixels;
uint8_t packetBitSz;
uint8_t packetBitMask; // derived from the above
// flags
uint8_t bpp : 4;
uint8_t bottomUp : 1;
};
uint8_t mPassNo = 0;
static const uint8_t mColorMap[][6] = {
// colors are: B, DG, G, LG, W, R
// phase 0 (LUTS: B:W:R:G, purpose: BWR, prepare greys)
{1, 1, 1, 1, 0, 0}, // lo plane (B)
{0, 0, 0, 0, 0, 1} // hi plane (R)
};
static uint8_t mClutMap[256];
static uint8_t mClutMapRed[256];
static struct BitmapDrawInfo mDrawInfo;
static uint32_t drawPrvParseHeader(uint32_t addr) // return clut addr or zero on error
{
/*struct BitmapFileHeader bmph;
uint16_t packetsPerRow;
addr += sizeof(struct EepromImageHeader);
eepromRead(addr, &bmph, sizeof(bmph));
if (bmph.sig[0] != 'B' || bmph.sig[1] != 'M')
goto fail;
if (bmph.colorplanes != 1)
goto fail;
if ((&bmph.headerSz - 40)) // < 40
goto fail;
if (bmph.bpp > 8)
goto fail;
mDrawInfo.bpp = bmph.bpp;
if (!(&bmph.headerSz - 257)) // >= 257
goto fail;
if ((&bmph.numColors))
mDrawInfo.numColorsM1 = (uint8_t)bmph.numColors - (uint8_t)1;
else
mDrawInfo.numColorsM1 = (uint8_t)((uint8_t)1 << (uint8_t)mDrawInfo.bpp) - (uint8_t)1;
if (!(&bmph.height))
goto fail;
if ((&bmph.width - 1) || !(&bmph.width - 0xffff))
goto fail;
mDrawInfo.w = bmph.width;
if ((&bmph.height) < 0)
{
if ((&bmph.height + 0xffff)) // carries if val too negative
goto fail;
mDrawInfo.h = -bmph.height;
mDrawInfo.bottomUp = false;
}
else
{
if (!(&bmph.headerSz - 0xffff)) // no carry if val too big
goto fail;
mDrawInfo.h = bmph.height;
mDrawInfo.bottomUp = true;
}
if (bmph.compression)
{
printf("compression is not supported ;(");
goto fail;
}
mDrawInfo.packetPixelDivVal = 0;
mDrawInfo.packetNumPixels = 1;
if (mDrawInfo.bpp > 1)
{
mDrawInfo.packetBitSz = 2;
}
else
{
mDrawInfo.packetBitSz = 1; // mDrawInfo.bpp;
}
// mDrawInfo.stride = mathPrvDiv32x8(mathPrvMul16x8((mDrawInfo.w + mDrawInfo.packetNumPixels - 1), mDrawInfo.packetBitSz) + 31, 32) * 4UL;
// mDrawInfo.packetBitMask = (uint8_t)(((uint8_t)1) << (uint8_t)mDrawInfo.packetBitSz) - (uint8_t)1;
packetsPerRow = (mDrawInfo.w + mDrawInfo.packetNumPixels - 1) / (mDrawInfo.packetNumPixels);
mDrawInfo.stride = (((packetsPerRow * mDrawInfo.packetBitSz) + 31) / 32) * 4UL;
mDrawInfo.packetBitMask = (uint8_t)(((uint8_t)1) << (uint8_t)mDrawInfo.packetBitSz) - (uint8_t)1;
// calc effective size
mDrawInfo.effectiveH = (mDrawInfo.h > SCREEN_HEIGHT) ? SCREEN_HEIGHT : mDrawInfo.h;
mDrawInfo.effectiveW = (mDrawInfo.w > SCREEN_WIDTH) ? SCREEN_WIDTH : mDrawInfo.w;
// calc addrs
mDrawInfo.dataAddr = addr + bmph.dataOfst;
return addr + bmph.dataOfst - sizeof(struct BitmapClutEntry) * (1 + mDrawInfo.numColorsM1);
fail:
printf("Tried to parse the bmp header, didn't work...");*/
return 0;
}
static void drawPrvLoadAndMapClut(uint32_t clutAddr)
{
/*struct BitmapClutEntry clut;
uint8_t i;
// convert clut to our understanding of color
i = 0;
do
{
uint8_t entry;
eepromRead(clutAddr, &clut, sizeof(clut));
clutAddr += sizeof(struct BitmapClutEntry);
if (SCREEN_EXTRA_COLOR_INDEX >= 0 && clut.r == 0xff && (clut.g == 0xff || clut.g == 0) && clut.b == 0) // yellow/red
entry = SCREEN_EXTRA_COLOR_INDEX;
else
{
uint16_t intensity = 0;
intensity += (0x37 * clut.r);
intensity += (0xB7 * clut.g);
intensity += (0x12 * clut.b);
// adds up to 0xff00 -> fix it
intensity += (uint8_t)(intensity >> 8);
entry = (intensity * SCREEN_NUM_GREYS) >> 16;
entry += SCREEN_FIRST_GREY_IDX;
}
// printf("mapped clut %u (%d %d %d) -> %d\n", i, clut.r, clut.g, clut.b, entry);
mClutMap[i] = entry;
} while (i++ != mDrawInfo.numColorsM1);
// replicate clut down if not a full 256-entry clut
if (mDrawInfo.bpp != 8)
{
uint8_t num = (uint8_t)((uint8_t)1 << (uint8_t)mDrawInfo.bpp);
// we can use the fact that our memcpy always copies forward
memcpy(mClutMap + num, mClutMap, (uint8_t)256 - (uint8_t)num);
}*/
}
static void drawPrvDecodeImageOnce(void)
{
/*uint8_t rowBuf[SCREEN_WIDTH];
uint16_t er, c;
if (mDrawInfo.bottomUp)
er = mDrawInfo.effectiveH - 1;
else
er = 0;
while (1)
{ // we account differently for loop gets compiled worse
uint8_t inIdx = 0, bitpoolInUsed = 0, bitpoolIn = 0;
uint16_t nBytesOut = 0;
#if SCREEN_TX_BPP == 4
uint8_t txPrev = 0;
bool emit = false;
#else
uint8_t bitpoolOutUsedUsed = 0;
uint16_t bitpoolOut = 0;
#endif
// get a row
epdDeselect();
eepromRead((er * mDrawInfo.stride) + mDrawInfo.dataAddr, rowBuf, mDrawInfo.stride);
epdSelect();
// convert to our format
c = mDrawInfo.effectiveW;
do
{
// uartTx('.');
uint8_t packet, packetIdx, packetMembers = mDrawInfo.packetNumPixels;
if (bitpoolInUsed >= mDrawInfo.packetBitSz)
{
bitpoolInUsed -= mDrawInfo.packetBitSz;
packet = bitpoolIn >> bitpoolInUsed;
}
else
{
uint8_t packetBitSz = mDrawInfo.packetBitSz;
uint8_t t = rowBuf[inIdx++];
packet = (bitpoolIn << (packetBitSz - bitpoolInUsed)) | (t >> (8 - (packetBitSz - bitpoolInUsed)));
bitpoolInUsed += 8 - packetBitSz;
bitpoolIn = t;
}
packet &= mDrawInfo.packetBitMask;
// val is now a packet - unpack it
if (packetMembers > c)
packetMembers = c;
for (packetIdx = 0; packetIdx < packetMembers; packetIdx++)
{
uint8_t val;
// extract
if (mDrawInfo.packetPixelDivVal)
{
val = packet % mDrawInfo.packetPixelDivVal;
packet /= mDrawInfo.packetPixelDivVal;
}
else
val = packet;
// map
val = mClutMap[val];
// get bits out
#if SCREEN_TX_BPP == 4
if (emit)
{
emit = false;
ByteDecode(txPrev | val);
nBytesOut++;
txPrev = 0;
}
else
{
emit = true;
txPrev = val << 4;
}
#else
bitpoolOut <<= SCREEN_TX_BPP;
bitpoolOut |= val;
bitpoolOutUsedUsed += SCREEN_TX_BPP;
if (bitpoolOutUsedUsed >= 8)
{
ByteDecode(bitpoolOut >> (bitpoolOutUsedUsed -= 8));
bitpoolOut &= (1 << bitpoolOutUsedUsed) - 1;
nBytesOut++;
}
#endif
}
c -= packetMembers;
} while (c);
#if SCREEN_TX_BPP == 4
if (emit)
{
ByteDecode(txPrev);
nBytesOut++;
}
#else
if (bitpoolOutUsedUsed)
{
ByteDecode(bitpoolOut);
nBytesOut++;
}
#endif
// if we did not produce enough bytes, do so
nBytesOut = ((long)SCREEN_WIDTH * SCREEN_TX_BPP + 7) / 8 - nBytesOut;
while (nBytesOut--)
ByteDecode(SCREEN_BYTE_FILL);
// update row
if (mDrawInfo.bottomUp)
{
if (er)
er--;
else
break;
}
else
{
er++;
if (er == mDrawInfo.effectiveH)
break;
}
}
// fill the rest of the screen
for (er = mDrawInfo.effectiveH - SCREEN_HEIGHT; er; er--)
{
for (c = ((long)SCREEN_WIDTH * SCREEN_TX_BPP + 7) / 8; c; c--)
{
ByteDecode(SCREEN_BYTE_FILL);
}
}*/
}
static uint8_t prev, step = 0;
void ByteDecode(uint8_t byte)
{
/*prev <<= 2;
prev |= (mColorMap[mPassNo][byte >> 4] << 1) | mColorMap[mPassNo][byte & 0x0f];
if (++step == 4)
{
step = 0;
Display_Write_byte(prev);
}*/
}
void drawImageAtAddress(uint32_t addr, uint8_t lut)
{
struct EepromImageHeader *eih = (struct EepromImageHeader *)mClutMap;
eepromRead(addr, mClutMap, sizeof(struct EepromImageHeader));
uint8_t prevVal = 0;
switch (eih->dataType)
{
case DATATYPE_IMG_RAW_1BPP:
@@ -430,34 +87,6 @@ void drawImageAtAddress(uint32_t addr, uint8_t lut)
display_send_stop();
epd_refresh_and_sleep();
break;
case DATATYPE_IMG_BMP:;
uint32_t clutAddr;
printf("sending BMP to EPD - ");
/*clutAddr = drawPrvParseHeader(addr);
if (!clutAddr)
return;
drawPrvLoadAndMapClut(clutAddr);
epdSetup();
if (lut)
selectLUT(lut);
mPassNo = 0;
beginFullscreenImage();
beginWriteFramebuffer(EPD_COLOR_BLACK);
prev = 0;
step = 0;
drawPrvDecodeImageOnce();
endWriteFramebuffer();
mPassNo++;
beginFullscreenImage();
beginWriteFramebuffer(EPD_COLOR_RED);
prev = 0;
step = 0;
drawPrvDecodeImageOnce();
endWriteFramebuffer();*/
printf(" complete.\n");
break;
default: // prevent drawing from an unknown file image type
printf("Image with type 0x%02X was requested, but we don't know what to do with that currently...\n", eih->dataType);
return;

1110
ARM_Tag_FW/88MZ100_OpenEpaperLink_7.4/epd.c Normal file → Executable file

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,8 @@
#pragma once
#include <stdio.h>
//#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdbool.h>
#define DISPLAY_WIDTH (640)
#define DISPLAY_HEIGHT (384)
@@ -16,6 +17,18 @@
#define FORE_COLOR_2 4
#define FORE_COLOR_3 0
#define EPD_LUT_DEFAULT 0
#define EPD_LUT_NO_REPEATS 1
#define EPD_LUT_FAST_NO_REDS 2
#define EPD_LUT_FAST 3
#define EPD_DIRECTION_X false
#define EPD_DIRECTION_Y true
#define EPD_SIZE_SINGLE false
#define EPD_SIZE_DOUBLE true
#define EPD_COLOR_RED true
#define EPD_COLOR_BLACK false
void init_GPIO_EPD();
void display_send_buffer();
@@ -25,5 +38,16 @@ void display_tx_byte(uint8_t data);
void display_send_start(uint8_t inverted);
void display_send_stop();
void setDisplayWindow(uint16_t x, uint16_t y, uint16_t xe, uint16_t ye);
void init_epd();
void refresh_epd();
void refresh_epd();
void lutBeginTX(uint8_t reg);
void lutEndTX();
void epd_pin_enable(int a1);
void fillWindow(uint16_t x, uint16_t y, uint16_t xe, uint16_t ye, uint8_t color);
void epdPrintf(uint16_t x, uint16_t y, bool color, const char* c, ...);

View File

@@ -1,6 +1,6 @@
#include "nfc.h"
#include <stdio.h>
//#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
@@ -12,6 +12,8 @@
#include "mz100_pinmux.h"
#include "mz100_gpio.h"
#include "util.h"
#include "printf.h"
void NVIC_some_IRQ1(unsigned int a1)
{

898
ARM_Tag_FW/88MZ100_OpenEpaperLink_7.4/main.c Normal file → Executable file

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +1,5 @@
#pragma once
#include <stdio.h>
//#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>
#include <string.h>

View File

@@ -4,8 +4,9 @@ GROUP(-lgcc -lc -lnosys)
MEMORY
{
FLASH (rx) : ORIGIN = 0x100000, LENGTH = 80k
RAM (rwx) : ORIGIN = 0x20100000 + 80k, LENGTH = 160k - 80k - 16k
RAM1 (rwx) : ORIGIN = 0x20124000 , LENGTH = 16k
RAM (rwx) : ORIGIN = 0x20100000 + 80k, LENGTH = 160k - 80k - 2k
AONSHADOW (rwx) : ORIGIN = 0x20128000 - 2k, LENGTH = 2k
AON (rwx) : ORIGIN = 0x20130000 , LENGTH = 4k
}
ENTRY(Reset_Handler)
@@ -123,6 +124,27 @@ SECTIONS
/* Check if data + heap + stack exceeds RAM limit */
ASSERT(__StackLimit >= __HeapLimit, "region RAM overflowed with stack")
.aon (NOLOAD):
{
. = ALIGN(4);
_start_of_aon = .;
*(.aon)
*(.aon.*)
. = ALIGN(4);
_end_of_aon = .;
} > AON
.aonshadow (NOLOAD):
{
. = ALIGN(4);
_start_of_aonshadow = .;
*(.aonshadow)
*(.aonshadow.*)
. = ALIGN(4);
_end_of_aonshadow = .;
} > AONSHADOW
}

View File

@@ -0,0 +1,43 @@
#include "mz100_aon_ram.h"
#include <string.h>
#include "stdint.h"
__attribute__((section(".aon"))) volatile uint32_t aonChecksum;
__attribute__((section(".aon"))) volatile uint8_t aonShadow[AONSHADOW_SIZE];
bool aonRamValid = false;
void clearAonRam() {
memset((void *)0x130000, 0, 4096);
}
bool loadValidateAonRam() {
uint32_t testchecksum = aonChecksum;
aonChecksum = 0x00000000;
uint32_t checksum = 0xABBA5FF5;
for (uint32_t c = 0x130000; c < 0x131000; c += 4) {
checksum += *(uint32_t *)c;
}
if (checksum == testchecksum) {
// immediately invalidate the checksum; if we reboot, we want a clean reboot
aonChecksum = 0x5445A00A;
memcpy((void *)(0x128000 - AONSHADOW_SIZE), (uint8_t*)aonShadow, AONSHADOW_SIZE);
return true;
} else {
clearAonRam();
memset((void *)(0x128000 - AONSHADOW_SIZE), 0, AONSHADOW_SIZE);
return false;
}
}
void saveAonRam() {
memcpy((uint8_t*)aonShadow, (void *)(0x128000 - AONSHADOW_SIZE), AONSHADOW_SIZE);
aonChecksum = 0x00000000;
uint32_t checksum = 0xABBA5FF5;
for (uint32_t c = 0x130000; c < 0x131000; c += 4) {
checksum += *(uint32_t *)c;
}
aonChecksum = checksum;
}

View File

@@ -0,0 +1,11 @@
#include "stdint.h"
#include "stdbool.h"
#define AONSHADOW_SIZE 2048
extern bool aonRamValid;
bool loadValidateAonRam();
void saveAonRam();
void clearAonRam();

View File

@@ -1,4 +1,4 @@
#include <stdio.h>
//#include <stdio.h>
#include <stdint.h>
#include "core_cm3.h"
#include "mz100_gpio.h"
@@ -14,6 +14,9 @@
#include "gpio.h"
#include "main.h"
#include "proto.h"
#include "printf.h"
extern void saveAonRam();
void AON_level_VDD(int state)
{
@@ -90,9 +93,11 @@ extern struct AvailDataInfo curDataInfo; // last 'AvailDataInfo' we received fro
extern bool requestPartialBlock; // if we should ask the AP to get this block from the host or not
void sleep_with_with_wakeup(uint32_t sleep_time_ms)
{
memcpy((uint8_t *)&(*(volatile unsigned int *)0x130500), (uint8_t *)&curBlock, sizeof(struct blockRequest));
memcpy((uint8_t *)&(*(volatile unsigned int *)0x130600), (uint8_t *)&curDataInfo, sizeof(struct AvailDataInfo));
printf("sleep: %u\r\n", sleep_time_ms);
saveAonRam();
//memcpy((uint8_t *)&(*(volatile unsigned int *)0x130500), (uint8_t *)&curBlock, sizeof(struct blockRequest));
//memcpy((uint8_t *)&(*(volatile unsigned int *)0x130600), (uint8_t *)&curDataInfo, sizeof(struct AvailDataInfo));
//sleep_time_ms = 10000;
printf("sleep! %u\n", sleep_time_ms);
uint32_t sleep_time_ms_1;
AON_level_VDD(7);
AON_level_VAA(0);

View File

@@ -1,6 +1,6 @@
#pragma once
#include <stdio.h>
//#include <stdio.h>
#include <stdint.h>
#include "core_cm3.h"
#include "mz100_gpio.h"

View File

@@ -1,5 +1,5 @@
#include "nfc.h"
#include <stdio.h>
//#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
@@ -17,6 +17,8 @@
#include "timer.h"
#include "epd.h"
#include "proto.h"
#include "printf.h"
#define WHO_AM_I 0x04
uint8_t i2c_address = 0xAA;

View File

@@ -1,5 +1,5 @@
#pragma once
#include <stdio.h>
//#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>
#include <string.h>

143
ARM_Tag_FW/88MZ100_OpenEpaperLink_7.4/powermgt.c Normal file → Executable file
View File

@@ -3,64 +3,69 @@
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
// #include <stdio.h>
#include <string.h>
#include "mz100_sleep.h"
#include "zigbee.h"
#include "eeprom.h"
#include "board.h"
#include "screen.h"
#include "eeprom.h"
#include "main.h"
#include "util.h"
#include "mz100_sleep.h"
#include "printf.h"
#include "screen.h"
#include "syncedproto.h"
#include <stdbool.h>
#include "util.h"
#include "zigbee.h"
uint16_t dataReqAttemptArr[POWER_SAVING_SMOOTHING] = {0}; // Holds the amount of attempts required per data_req/check-in
uint8_t dataReqAttemptArrayIndex = 0;
uint8_t dataReqLastAttempt = 0;
uint16_t nextCheckInFromAP = 0;
uint8_t wakeUpReason = 0;
uint8_t scanAttempts = 0;
__attribute__((section(".aonshadow"))) uint16_t dataReqAttemptArr[POWER_SAVING_SMOOTHING] = {0}; // Holds the amount of attempts required per data_req/check-in
__attribute__((section(".aonshadow"))) uint8_t dataReqAttemptArrayIndex = 0;
__attribute__((section(".aonshadow"))) uint8_t dataReqLastAttempt = 0;
__attribute__((section(".aonshadow"))) uint16_t nextCheckInFromAP = 0;
__attribute__((section(".aonshadow"))) uint8_t wakeUpReason = 0;
__attribute__((section(".aonshadow"))) uint8_t scanAttempts = 0;
int8_t temperature = 0;
uint16_t batteryVoltage = 0;
bool lowBattery = false;
uint16_t longDataReqCounter = 0;
uint16_t voltageCheckCounter = 0;
__attribute__((section(".aonshadow"))) int8_t temperature = 0;
__attribute__((section(".aonshadow"))) uint16_t batteryVoltage = 0;
__attribute__((section(".aonshadow"))) bool lowBattery = false;
__attribute__((section(".aonshadow"))) uint16_t longDataReqCounter = 0;
__attribute__((section(".aonshadow"))) uint16_t voltageCheckCounter = 0;
uint8_t capabilities = 0;
__attribute__((section(".aonshadow"))) uint8_t capabilities = 0;
bool spiActive = false;
bool uartActive = false;
bool eepromActive = false;
bool i2cActive = false;
extern int8_t adcSampleTemperature(void); // in degrees C
uint8_t checkButtonOrJig()
{
extern int8_t adcSampleTemperature(void); // in degrees C
uint8_t checkButtonOrJig() {
return DETECT_P1_0_NOTHING;
}
void setupPortsInitial()
{
void setupPortsInitial() {
}
void initPowerSaving(const uint16_t initialValue)
{
for (uint8_t c = 0; c < POWER_SAVING_SMOOTHING; c++)
{
uint16_t doVoltageReading() {
batteryVoltage = (uint16_t)measureBattery();
if (batteryVoltage < BATTERY_VOLTAGE_MINIMUM) {
lowBattery = true;
} else {
lowBattery = false;
}
return batteryVoltage;
}
void initPowerSaving(const uint16_t initialValue) {
for (uint8_t c = 0; c < POWER_SAVING_SMOOTHING; c++) {
dataReqAttemptArr[c] = initialValue;
}
}
static void configSPI(const bool setup)
{
static void configSPI(const bool setup) {
spiActive = setup;
}
static void configUART(const bool setup)
{
static void configUART(const bool setup) {
/* if (setup == uartActive)
return;
uartActive = setup;
@@ -70,69 +75,64 @@ static void configUART(const bool setup)
Serial.end();*/
}
static void configEEPROM(const bool setup)
{
static void configEEPROM(const bool setup) {
}
static void configI2C(const bool setup)
{
static void configI2C(const bool setup) {
}
void powerUp(const uint8_t parts)
{
printf("Power up: %d\r\n", parts);
if (parts & INIT_RADIO)
{
void powerUp(const uint8_t parts) {
// printf("Power up: %d\r\n", parts);
if (parts & INIT_RADIO) {
radioInit();
// radioRxFilterCfg(mSelfMac, 0x10000, PROTO_PAN_ID);
// radioSetTxPower(10);
if (currentChannel >= 11 && currentChannel <= 27)
{
if (currentChannel >= 11 && currentChannel <= 27) {
radioSetChannel(currentChannel);
}
else
{
} else {
radioSetChannel(RADIO_FIRST_CHANNEL);
}
}
if (parts & INIT_UART) {
configUART(true);
}
if (parts & INIT_EPD) {
configSPI(true);
}
if (parts & INIT_EEPROM) {
configEEPROM(true);
}
if (parts & INIT_I2C) {
configI2C(true);
}
}
void powerDown(const uint8_t parts)
{
printf("Power down: %d\r\n", parts);
void powerDown(const uint8_t parts) {
// printf("Power down: %d\r\n", parts);
}
void doSleep(const uint32_t t)
{
void doSleep(const uint32_t t) {
printf("Sleeping for: %d ms\r\n", t);
// sleepForMs(t);
delay(t);
}
uint32_t getNextScanSleep(const bool increment)
{
if (increment)
{
uint32_t getNextScanSleep(const bool increment) {
if (increment) {
if (scanAttempts < 255)
scanAttempts++;
}
if (scanAttempts < INTERVAL_1_ATTEMPTS)
{
if (scanAttempts < INTERVAL_1_ATTEMPTS) {
return INTERVAL_1_TIME;
}
else if (scanAttempts < (INTERVAL_1_ATTEMPTS + INTERVAL_2_ATTEMPTS))
{
} else if (scanAttempts < (INTERVAL_1_ATTEMPTS + INTERVAL_2_ATTEMPTS)) {
return INTERVAL_2_TIME;
}
else
{
} else {
return INTERVAL_3_TIME;
}
}
void addAverageValue()
{
void addAverageValue() {
uint16_t curval = INTERVAL_AT_MAX_ATTEMPTS - INTERVAL_BASE;
curval *= dataReqLastAttempt;
curval /= DATA_REQ_MAX_ATTEMPTS;
@@ -141,14 +141,11 @@ void addAverageValue()
dataReqAttemptArrayIndex++;
}
uint16_t getNextSleep()
{
/*uint16_t avg = 0;
for (uint8_t c = 0; c < POWER_SAVING_SMOOTHING; c++)
{
uint16_t getNextSleep() {
uint16_t avg = 0;
for (uint8_t c = 0; c < POWER_SAVING_SMOOTHING; c++) {
avg += dataReqAttemptArr[c];
}
avg /= POWER_SAVING_SMOOTHING;
return avg;*/
return 30;
return avg;
}

View File

@@ -60,20 +60,22 @@ extern void doSleep(const uint32_t t);
extern void addAverageValue();
extern uint16_t getNextSleep();
extern uint16_t doVoltageReading();
extern uint32_t getNextScanSleep(const bool increment);
extern void initPowerSaving(const uint16_t initialValue);
extern uint8_t wakeUpReason;
extern uint8_t capabilities;
extern __attribute__((section(".aonshadow"))) uint8_t capabilities;
extern uint16_t nextCheckInFromAP;
extern uint8_t dataReqLastAttempt;
extern int8_t temperature;
extern uint16_t batteryVoltage;
extern bool lowBattery;
extern uint8_t scanAttempts;
extern uint16_t longDataReqCounter;
extern uint16_t voltageCheckCounter;
extern __attribute__((section(".aonshadow"))) uint8_t dataReqLastAttempt;
extern __attribute__((section(".aonshadow"))) int8_t temperature;
extern __attribute__((section(".aonshadow"))) uint16_t batteryVoltage;
extern __attribute__((section(".aonshadow"))) bool lowBattery;
extern __attribute__((section(".aonshadow"))) uint8_t scanAttempts;
extern __attribute__((section(".aonshadow"))) uint16_t longDataReqCounter;
extern __attribute__((section(".aonshadow"))) uint16_t voltageCheckCounter;
#endif

View File

@@ -0,0 +1,914 @@
///////////////////////////////////////////////////////////////////////////////
// \author (c) Marco Paland (info@paland.com)
// 2014-2019, PALANDesign Hannover, Germany
//
// \license The MIT License (MIT)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// \brief Tiny printf, sprintf and (v)snprintf implementation, optimized for speed on
// embedded systems with a very limited resources. These routines are thread
// safe and reentrant!
// Use this instead of the bloated standard/newlib printf cause these use
// malloc for printf (and may not be thread safe).
//
///////////////////////////////////////////////////////////////////////////////
#include <stdbool.h>
#include <stdint.h>
#include "printf.h"
// define this globally (e.g. gcc -DPRINTF_INCLUDE_CONFIG_H ...) to include the
// printf_config.h header file
// default: undefined
#ifdef PRINTF_INCLUDE_CONFIG_H
#include "printf_config.h"
#endif
// 'ntoa' conversion buffer size, this must be big enough to hold one converted
// numeric number including padded zeros (dynamically created on stack)
// default: 32 byte
#ifndef PRINTF_NTOA_BUFFER_SIZE
#define PRINTF_NTOA_BUFFER_SIZE 32U
#endif
// 'ftoa' conversion buffer size, this must be big enough to hold one converted
// float number including padded zeros (dynamically created on stack)
// default: 32 byte
#ifndef PRINTF_FTOA_BUFFER_SIZE
#define PRINTF_FTOA_BUFFER_SIZE 32U
#endif
// support for the floating point type (%f)
// default: activated
#ifndef PRINTF_DISABLE_SUPPORT_FLOAT
#define PRINTF_SUPPORT_FLOAT
#endif
// support for exponential floating point notation (%e/%g)
// default: activated
#ifndef PRINTF_DISABLE_SUPPORT_EXPONENTIAL
#define PRINTF_SUPPORT_EXPONENTIAL
#endif
// define the default floating point precision
// default: 6 digits
#ifndef PRINTF_DEFAULT_FLOAT_PRECISION
#define PRINTF_DEFAULT_FLOAT_PRECISION 6U
#endif
// define the largest float suitable to print with %f
// default: 1e9
#ifndef PRINTF_MAX_FLOAT
#define PRINTF_MAX_FLOAT 1e9
#endif
// support for the long long types (%llu or %p)
// default: activated
#ifndef PRINTF_DISABLE_SUPPORT_LONG_LONG
#define PRINTF_SUPPORT_LONG_LONG
#endif
// support for the ptrdiff_t type (%t)
// ptrdiff_t is normally defined in <stddef.h> as long or long long type
// default: activated
#ifndef PRINTF_DISABLE_SUPPORT_PTRDIFF_T
#define PRINTF_SUPPORT_PTRDIFF_T
#endif
///////////////////////////////////////////////////////////////////////////////
// internal flag definitions
#define FLAGS_ZEROPAD (1U << 0U)
#define FLAGS_LEFT (1U << 1U)
#define FLAGS_PLUS (1U << 2U)
#define FLAGS_SPACE (1U << 3U)
#define FLAGS_HASH (1U << 4U)
#define FLAGS_UPPERCASE (1U << 5U)
#define FLAGS_CHAR (1U << 6U)
#define FLAGS_SHORT (1U << 7U)
#define FLAGS_LONG (1U << 8U)
#define FLAGS_LONG_LONG (1U << 9U)
#define FLAGS_PRECISION (1U << 10U)
#define FLAGS_ADAPT_EXP (1U << 11U)
// import float.h for DBL_MAX
#if defined(PRINTF_SUPPORT_FLOAT)
#include <float.h>
#endif
// output function type
typedef void (*out_fct_type)(char character, void* buffer, size_t idx, size_t maxlen);
// wrapper (used as buffer) for output function type
typedef struct {
void (*fct)(char character, void* arg);
void* arg;
} out_fct_wrap_type;
// internal buffer output
static inline void _out_buffer(char character, void* buffer, size_t idx, size_t maxlen)
{
if (idx < maxlen) {
((char*)buffer)[idx] = character;
}
}
// internal null output
static inline void _out_null(char character, void* buffer, size_t idx, size_t maxlen)
{
(void)character; (void)buffer; (void)idx; (void)maxlen;
}
// internal _putchar wrapper
static inline void _out_char(char character, void* buffer, size_t idx, size_t maxlen)
{
(void)buffer; (void)idx; (void)maxlen;
if (character) {
_putchar(character);
}
}
// internal output function wrapper
static inline void _out_fct(char character, void* buffer, size_t idx, size_t maxlen)
{
(void)idx; (void)maxlen;
if (character) {
// buffer is the output fct pointer
((out_fct_wrap_type*)buffer)->fct(character, ((out_fct_wrap_type*)buffer)->arg);
}
}
// internal secure strlen
// \return The length of the string (excluding the terminating 0) limited by 'maxsize'
static inline unsigned int _strnlen_s(const char* str, size_t maxsize)
{
const char* s;
for (s = str; *s && maxsize--; ++s);
return (unsigned int)(s - str);
}
// internal test if char is a digit (0-9)
// \return true if char is a digit
static inline bool _is_digit(char ch)
{
return (ch >= '0') && (ch <= '9');
}
// internal ASCII string to unsigned int conversion
static unsigned int _atoi(const char** str)
{
unsigned int i = 0U;
while (_is_digit(**str)) {
i = i * 10U + (unsigned int)(*((*str)++) - '0');
}
return i;
}
// output the specified string in reverse, taking care of any zero-padding
static size_t _out_rev(out_fct_type out, char* buffer, size_t idx, size_t maxlen, const char* buf, size_t len, unsigned int width, unsigned int flags)
{
const size_t start_idx = idx;
// pad spaces up to given width
if (!(flags & FLAGS_LEFT) && !(flags & FLAGS_ZEROPAD)) {
for (size_t i = len; i < width; i++) {
out(' ', buffer, idx++, maxlen);
}
}
// reverse string
while (len) {
out(buf[--len], buffer, idx++, maxlen);
}
// append pad spaces up to given width
if (flags & FLAGS_LEFT) {
while (idx - start_idx < width) {
out(' ', buffer, idx++, maxlen);
}
}
return idx;
}
// internal itoa format
static size_t _ntoa_format(out_fct_type out, char* buffer, size_t idx, size_t maxlen, char* buf, size_t len, bool negative, unsigned int base, unsigned int prec, unsigned int width, unsigned int flags)
{
// pad leading zeros
if (!(flags & FLAGS_LEFT)) {
if (width && (flags & FLAGS_ZEROPAD) && (negative || (flags & (FLAGS_PLUS | FLAGS_SPACE)))) {
width--;
}
while ((len < prec) && (len < PRINTF_NTOA_BUFFER_SIZE)) {
buf[len++] = '0';
}
while ((flags & FLAGS_ZEROPAD) && (len < width) && (len < PRINTF_NTOA_BUFFER_SIZE)) {
buf[len++] = '0';
}
}
// handle hash
if (flags & FLAGS_HASH) {
if (!(flags & FLAGS_PRECISION) && len && ((len == prec) || (len == width))) {
len--;
if (len && (base == 16U)) {
len--;
}
}
if ((base == 16U) && !(flags & FLAGS_UPPERCASE) && (len < PRINTF_NTOA_BUFFER_SIZE)) {
buf[len++] = 'x';
}
else if ((base == 16U) && (flags & FLAGS_UPPERCASE) && (len < PRINTF_NTOA_BUFFER_SIZE)) {
buf[len++] = 'X';
}
else if ((base == 2U) && (len < PRINTF_NTOA_BUFFER_SIZE)) {
buf[len++] = 'b';
}
if (len < PRINTF_NTOA_BUFFER_SIZE) {
buf[len++] = '0';
}
}
if (len < PRINTF_NTOA_BUFFER_SIZE) {
if (negative) {
buf[len++] = '-';
}
else if (flags & FLAGS_PLUS) {
buf[len++] = '+'; // ignore the space if the '+' exists
}
else if (flags & FLAGS_SPACE) {
buf[len++] = ' ';
}
}
return _out_rev(out, buffer, idx, maxlen, buf, len, width, flags);
}
// internal itoa for 'long' type
static size_t _ntoa_long(out_fct_type out, char* buffer, size_t idx, size_t maxlen, unsigned long value, bool negative, unsigned long base, unsigned int prec, unsigned int width, unsigned int flags)
{
char buf[PRINTF_NTOA_BUFFER_SIZE];
size_t len = 0U;
// no hash for 0 values
if (!value) {
flags &= ~FLAGS_HASH;
}
// write if precision != 0 and value is != 0
if (!(flags & FLAGS_PRECISION) || value) {
do {
const char digit = (char)(value % base);
buf[len++] = digit < 10 ? '0' + digit : (flags & FLAGS_UPPERCASE ? 'A' : 'a') + digit - 10;
value /= base;
} while (value && (len < PRINTF_NTOA_BUFFER_SIZE));
}
return _ntoa_format(out, buffer, idx, maxlen, buf, len, negative, (unsigned int)base, prec, width, flags);
}
// internal itoa for 'long long' type
#if defined(PRINTF_SUPPORT_LONG_LONG)
static size_t _ntoa_long_long(out_fct_type out, char* buffer, size_t idx, size_t maxlen, unsigned long long value, bool negative, unsigned long long base, unsigned int prec, unsigned int width, unsigned int flags)
{
char buf[PRINTF_NTOA_BUFFER_SIZE];
size_t len = 0U;
// no hash for 0 values
if (!value) {
flags &= ~FLAGS_HASH;
}
// write if precision != 0 and value is != 0
if (!(flags & FLAGS_PRECISION) || value) {
do {
const char digit = (char)(value % base);
buf[len++] = digit < 10 ? '0' + digit : (flags & FLAGS_UPPERCASE ? 'A' : 'a') + digit - 10;
value /= base;
} while (value && (len < PRINTF_NTOA_BUFFER_SIZE));
}
return _ntoa_format(out, buffer, idx, maxlen, buf, len, negative, (unsigned int)base, prec, width, flags);
}
#endif // PRINTF_SUPPORT_LONG_LONG
#if defined(PRINTF_SUPPORT_FLOAT)
#if defined(PRINTF_SUPPORT_EXPONENTIAL)
// forward declaration so that _ftoa can switch to exp notation for values > PRINTF_MAX_FLOAT
static size_t _etoa(out_fct_type out, char* buffer, size_t idx, size_t maxlen, double value, unsigned int prec, unsigned int width, unsigned int flags);
#endif
// internal ftoa for fixed decimal floating point
static size_t _ftoa(out_fct_type out, char* buffer, size_t idx, size_t maxlen, double value, unsigned int prec, unsigned int width, unsigned int flags)
{
char buf[PRINTF_FTOA_BUFFER_SIZE];
size_t len = 0U;
double diff = 0.0;
// powers of 10
static const double pow10[] = { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000 };
// test for special values
if (value != value)
return _out_rev(out, buffer, idx, maxlen, "nan", 3, width, flags);
if (value < -DBL_MAX)
return _out_rev(out, buffer, idx, maxlen, "fni-", 4, width, flags);
if (value > DBL_MAX)
return _out_rev(out, buffer, idx, maxlen, (flags & FLAGS_PLUS) ? "fni+" : "fni", (flags & FLAGS_PLUS) ? 4U : 3U, width, flags);
// test for very large values
// standard printf behavior is to print EVERY whole number digit -- which could be 100s of characters overflowing your buffers == bad
if ((value > PRINTF_MAX_FLOAT) || (value < -PRINTF_MAX_FLOAT)) {
#if defined(PRINTF_SUPPORT_EXPONENTIAL)
return _etoa(out, buffer, idx, maxlen, value, prec, width, flags);
#else
return 0U;
#endif
}
// test for negative
bool negative = false;
if (value < 0) {
negative = true;
value = 0 - value;
}
// set default precision, if not set explicitly
if (!(flags & FLAGS_PRECISION)) {
prec = PRINTF_DEFAULT_FLOAT_PRECISION;
}
// limit precision to 9, cause a prec >= 10 can lead to overflow errors
while ((len < PRINTF_FTOA_BUFFER_SIZE) && (prec > 9U)) {
buf[len++] = '0';
prec--;
}
int whole = (int)value;
double tmp = (value - whole) * pow10[prec];
unsigned long frac = (unsigned long)tmp;
diff = tmp - frac;
if (diff > 0.5) {
++frac;
// handle rollover, e.g. case 0.99 with prec 1 is 1.0
if (frac >= pow10[prec]) {
frac = 0;
++whole;
}
}
else if (diff < 0.5) {
}
else if ((frac == 0U) || (frac & 1U)) {
// if halfway, round up if odd OR if last digit is 0
++frac;
}
if (prec == 0U) {
diff = value - (double)whole;
if ((!(diff < 0.5) || (diff > 0.5)) && (whole & 1)) {
// exactly 0.5 and ODD, then round up
// 1.5 -> 2, but 2.5 -> 2
++whole;
}
}
else {
unsigned int count = prec;
// now do fractional part, as an unsigned number
while (len < PRINTF_FTOA_BUFFER_SIZE) {
--count;
buf[len++] = (char)(48U + (frac % 10U));
if (!(frac /= 10U)) {
break;
}
}
// add extra 0s
while ((len < PRINTF_FTOA_BUFFER_SIZE) && (count-- > 0U)) {
buf[len++] = '0';
}
if (len < PRINTF_FTOA_BUFFER_SIZE) {
// add decimal
buf[len++] = '.';
}
}
// do whole part, number is reversed
while (len < PRINTF_FTOA_BUFFER_SIZE) {
buf[len++] = (char)(48 + (whole % 10));
if (!(whole /= 10)) {
break;
}
}
// pad leading zeros
if (!(flags & FLAGS_LEFT) && (flags & FLAGS_ZEROPAD)) {
if (width && (negative || (flags & (FLAGS_PLUS | FLAGS_SPACE)))) {
width--;
}
while ((len < width) && (len < PRINTF_FTOA_BUFFER_SIZE)) {
buf[len++] = '0';
}
}
if (len < PRINTF_FTOA_BUFFER_SIZE) {
if (negative) {
buf[len++] = '-';
}
else if (flags & FLAGS_PLUS) {
buf[len++] = '+'; // ignore the space if the '+' exists
}
else if (flags & FLAGS_SPACE) {
buf[len++] = ' ';
}
}
return _out_rev(out, buffer, idx, maxlen, buf, len, width, flags);
}
#if defined(PRINTF_SUPPORT_EXPONENTIAL)
// internal ftoa variant for exponential floating-point type, contributed by Martijn Jasperse <m.jasperse@gmail.com>
static size_t _etoa(out_fct_type out, char* buffer, size_t idx, size_t maxlen, double value, unsigned int prec, unsigned int width, unsigned int flags)
{
// check for NaN and special values
if ((value != value) || (value > DBL_MAX) || (value < -DBL_MAX)) {
return _ftoa(out, buffer, idx, maxlen, value, prec, width, flags);
}
// determine the sign
const bool negative = value < 0;
if (negative) {
value = -value;
}
// default precision
if (!(flags & FLAGS_PRECISION)) {
prec = PRINTF_DEFAULT_FLOAT_PRECISION;
}
// determine the decimal exponent
// based on the algorithm by David Gay (https://www.ampl.com/netlib/fp/dtoa.c)
union {
uint64_t U;
double F;
} conv;
conv.F = value;
int exp2 = (int)((conv.U >> 52U) & 0x07FFU) - 1023; // effectively log2
conv.U = (conv.U & ((1ULL << 52U) - 1U)) | (1023ULL << 52U); // drop the exponent so conv.F is now in [1,2)
// now approximate log10 from the log2 integer part and an expansion of ln around 1.5
int expval = (int)(0.1760912590558 + exp2 * 0.301029995663981 + (conv.F - 1.5) * 0.289529654602168);
// now we want to compute 10^expval but we want to be sure it won't overflow
exp2 = (int)(expval * 3.321928094887362 + 0.5);
const double z = expval * 2.302585092994046 - exp2 * 0.6931471805599453;
const double z2 = z * z;
conv.U = (uint64_t)(exp2 + 1023) << 52U;
// compute exp(z) using continued fractions, see https://en.wikipedia.org/wiki/Exponential_function#Continued_fractions_for_ex
conv.F *= 1 + 2 * z / (2 - z + (z2 / (6 + (z2 / (10 + z2 / 14)))));
// correct for rounding errors
if (value < conv.F) {
expval--;
conv.F /= 10;
}
// the exponent format is "%+03d" and largest value is "307", so set aside 4-5 characters
unsigned int minwidth = ((expval < 100) && (expval > -100)) ? 4U : 5U;
// in "%g" mode, "prec" is the number of *significant figures* not decimals
if (flags & FLAGS_ADAPT_EXP) {
// do we want to fall-back to "%f" mode?
if ((value >= 1e-4) && (value < 1e6)) {
if ((int)prec > expval) {
prec = (unsigned)((int)prec - expval - 1);
}
else {
prec = 0;
}
flags |= FLAGS_PRECISION; // make sure _ftoa respects precision
// no characters in exponent
minwidth = 0U;
expval = 0;
}
else {
// we use one sigfig for the whole part
if ((prec > 0) && (flags & FLAGS_PRECISION)) {
--prec;
}
}
}
// will everything fit?
unsigned int fwidth = width;
if (width > minwidth) {
// we didn't fall-back so subtract the characters required for the exponent
fwidth -= minwidth;
} else {
// not enough characters, so go back to default sizing
fwidth = 0U;
}
if ((flags & FLAGS_LEFT) && minwidth) {
// if we're padding on the right, DON'T pad the floating part
fwidth = 0U;
}
// rescale the float value
if (expval) {
value /= conv.F;
}
// output the floating part
const size_t start_idx = idx;
idx = _ftoa(out, buffer, idx, maxlen, negative ? -value : value, prec, fwidth, flags & ~FLAGS_ADAPT_EXP);
// output the exponent part
if (minwidth) {
// output the exponential symbol
out((flags & FLAGS_UPPERCASE) ? 'E' : 'e', buffer, idx++, maxlen);
// output the exponent value
idx = _ntoa_long(out, buffer, idx, maxlen, (expval < 0) ? -expval : expval, expval < 0, 10, 0, minwidth-1, FLAGS_ZEROPAD | FLAGS_PLUS);
// might need to right-pad spaces
if (flags & FLAGS_LEFT) {
while (idx - start_idx < width) out(' ', buffer, idx++, maxlen);
}
}
return idx;
}
#endif // PRINTF_SUPPORT_EXPONENTIAL
#endif // PRINTF_SUPPORT_FLOAT
// internal vsnprintf
static int _vsnprintf(out_fct_type out, char* buffer, const size_t maxlen, const char* format, va_list va)
{
unsigned int flags, width, precision, n;
size_t idx = 0U;
if (!buffer) {
// use null output function
out = _out_null;
}
while (*format)
{
// format specifier? %[flags][width][.precision][length]
if (*format != '%') {
// no
out(*format, buffer, idx++, maxlen);
format++;
continue;
}
else {
// yes, evaluate it
format++;
}
// evaluate flags
flags = 0U;
do {
switch (*format) {
case '0': flags |= FLAGS_ZEROPAD; format++; n = 1U; break;
case '-': flags |= FLAGS_LEFT; format++; n = 1U; break;
case '+': flags |= FLAGS_PLUS; format++; n = 1U; break;
case ' ': flags |= FLAGS_SPACE; format++; n = 1U; break;
case '#': flags |= FLAGS_HASH; format++; n = 1U; break;
default : n = 0U; break;
}
} while (n);
// evaluate width field
width = 0U;
if (_is_digit(*format)) {
width = _atoi(&format);
}
else if (*format == '*') {
const int w = va_arg(va, int);
if (w < 0) {
flags |= FLAGS_LEFT; // reverse padding
width = (unsigned int)-w;
}
else {
width = (unsigned int)w;
}
format++;
}
// evaluate precision field
precision = 0U;
if (*format == '.') {
flags |= FLAGS_PRECISION;
format++;
if (_is_digit(*format)) {
precision = _atoi(&format);
}
else if (*format == '*') {
const int prec = (int)va_arg(va, int);
precision = prec > 0 ? (unsigned int)prec : 0U;
format++;
}
}
// evaluate length field
switch (*format) {
case 'l' :
flags |= FLAGS_LONG;
format++;
if (*format == 'l') {
flags |= FLAGS_LONG_LONG;
format++;
}
break;
case 'h' :
flags |= FLAGS_SHORT;
format++;
if (*format == 'h') {
flags |= FLAGS_CHAR;
format++;
}
break;
#if defined(PRINTF_SUPPORT_PTRDIFF_T)
case 't' :
flags |= (sizeof(ptrdiff_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG);
format++;
break;
#endif
case 'j' :
flags |= (sizeof(intmax_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG);
format++;
break;
case 'z' :
flags |= (sizeof(size_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG);
format++;
break;
default :
break;
}
// evaluate specifier
switch (*format) {
case 'd' :
case 'i' :
case 'u' :
case 'x' :
case 'X' :
case 'o' :
case 'b' : {
// set the base
unsigned int base;
if (*format == 'x' || *format == 'X') {
base = 16U;
}
else if (*format == 'o') {
base = 8U;
}
else if (*format == 'b') {
base = 2U;
}
else {
base = 10U;
flags &= ~FLAGS_HASH; // no hash for dec format
}
// uppercase
if (*format == 'X') {
flags |= FLAGS_UPPERCASE;
}
// no plus or space flag for u, x, X, o, b
if ((*format != 'i') && (*format != 'd')) {
flags &= ~(FLAGS_PLUS | FLAGS_SPACE);
}
// ignore '0' flag when precision is given
if (flags & FLAGS_PRECISION) {
flags &= ~FLAGS_ZEROPAD;
}
// convert the integer
if ((*format == 'i') || (*format == 'd')) {
// signed
if (flags & FLAGS_LONG_LONG) {
#if defined(PRINTF_SUPPORT_LONG_LONG)
const long long value = va_arg(va, long long);
idx = _ntoa_long_long(out, buffer, idx, maxlen, (unsigned long long)(value > 0 ? value : 0 - value), value < 0, base, precision, width, flags);
#endif
}
else if (flags & FLAGS_LONG) {
const long value = va_arg(va, long);
idx = _ntoa_long(out, buffer, idx, maxlen, (unsigned long)(value > 0 ? value : 0 - value), value < 0, base, precision, width, flags);
}
else {
const int value = (flags & FLAGS_CHAR) ? (char)va_arg(va, int) : (flags & FLAGS_SHORT) ? (short int)va_arg(va, int) : va_arg(va, int);
idx = _ntoa_long(out, buffer, idx, maxlen, (unsigned int)(value > 0 ? value : 0 - value), value < 0, base, precision, width, flags);
}
}
else {
// unsigned
if (flags & FLAGS_LONG_LONG) {
#if defined(PRINTF_SUPPORT_LONG_LONG)
idx = _ntoa_long_long(out, buffer, idx, maxlen, va_arg(va, unsigned long long), false, base, precision, width, flags);
#endif
}
else if (flags & FLAGS_LONG) {
idx = _ntoa_long(out, buffer, idx, maxlen, va_arg(va, unsigned long), false, base, precision, width, flags);
}
else {
const unsigned int value = (flags & FLAGS_CHAR) ? (unsigned char)va_arg(va, unsigned int) : (flags & FLAGS_SHORT) ? (unsigned short int)va_arg(va, unsigned int) : va_arg(va, unsigned int);
idx = _ntoa_long(out, buffer, idx, maxlen, value, false, base, precision, width, flags);
}
}
format++;
break;
}
#if defined(PRINTF_SUPPORT_FLOAT)
case 'f' :
case 'F' :
if (*format == 'F') flags |= FLAGS_UPPERCASE;
idx = _ftoa(out, buffer, idx, maxlen, va_arg(va, double), precision, width, flags);
format++;
break;
#if defined(PRINTF_SUPPORT_EXPONENTIAL)
case 'e':
case 'E':
case 'g':
case 'G':
if ((*format == 'g')||(*format == 'G')) flags |= FLAGS_ADAPT_EXP;
if ((*format == 'E')||(*format == 'G')) flags |= FLAGS_UPPERCASE;
idx = _etoa(out, buffer, idx, maxlen, va_arg(va, double), precision, width, flags);
format++;
break;
#endif // PRINTF_SUPPORT_EXPONENTIAL
#endif // PRINTF_SUPPORT_FLOAT
case 'c' : {
unsigned int l = 1U;
// pre padding
if (!(flags & FLAGS_LEFT)) {
while (l++ < width) {
out(' ', buffer, idx++, maxlen);
}
}
// char output
out((char)va_arg(va, int), buffer, idx++, maxlen);
// post padding
if (flags & FLAGS_LEFT) {
while (l++ < width) {
out(' ', buffer, idx++, maxlen);
}
}
format++;
break;
}
case 's' : {
const char* p = va_arg(va, char*);
unsigned int l = _strnlen_s(p, precision ? precision : (size_t)-1);
// pre padding
if (flags & FLAGS_PRECISION) {
l = (l < precision ? l : precision);
}
if (!(flags & FLAGS_LEFT)) {
while (l++ < width) {
out(' ', buffer, idx++, maxlen);
}
}
// string output
while ((*p != 0) && (!(flags & FLAGS_PRECISION) || precision--)) {
out(*(p++), buffer, idx++, maxlen);
}
// post padding
if (flags & FLAGS_LEFT) {
while (l++ < width) {
out(' ', buffer, idx++, maxlen);
}
}
format++;
break;
}
case 'p' : {
width = sizeof(void*) * 2U;
flags |= FLAGS_ZEROPAD | FLAGS_UPPERCASE;
#if defined(PRINTF_SUPPORT_LONG_LONG)
const bool is_ll = sizeof(uintptr_t) == sizeof(long long);
if (is_ll) {
idx = _ntoa_long_long(out, buffer, idx, maxlen, (uintptr_t)va_arg(va, void*), false, 16U, precision, width, flags);
}
else {
#endif
idx = _ntoa_long(out, buffer, idx, maxlen, (unsigned long)((uintptr_t)va_arg(va, void*)), false, 16U, precision, width, flags);
#if defined(PRINTF_SUPPORT_LONG_LONG)
}
#endif
format++;
break;
}
case '%' :
out('%', buffer, idx++, maxlen);
format++;
break;
default :
out(*format, buffer, idx++, maxlen);
format++;
break;
}
}
// termination
out((char)0, buffer, idx < maxlen ? idx : maxlen - 1U, maxlen);
// return written chars without terminating \0
return (int)idx;
}
///////////////////////////////////////////////////////////////////////////////
int printf_(const char* format, ...)
{
va_list va;
va_start(va, format);
char buffer[1];
const int ret = _vsnprintf(_out_char, buffer, (size_t)-1, format, va);
va_end(va);
return ret;
}
int sprintf_(char* buffer, const char* format, ...)
{
va_list va;
va_start(va, format);
const int ret = _vsnprintf(_out_buffer, buffer, (size_t)-1, format, va);
va_end(va);
return ret;
}
int snprintf_(char* buffer, size_t count, const char* format, ...)
{
va_list va;
va_start(va, format);
const int ret = _vsnprintf(_out_buffer, buffer, count, format, va);
va_end(va);
return ret;
}
int vprintf_(const char* format, va_list va)
{
char buffer[1];
return _vsnprintf(_out_char, buffer, (size_t)-1, format, va);
}
int vsnprintf_(char* buffer, size_t count, const char* format, va_list va)
{
return _vsnprintf(_out_buffer, buffer, count, format, va);
}
int fctprintf(void (*out)(char character, void* arg), void* arg, const char* format, ...)
{
va_list va;
va_start(va, format);
const out_fct_wrap_type out_fct_wrap = { out, arg };
const int ret = _vsnprintf(_out_fct, (char*)(uintptr_t)&out_fct_wrap, (size_t)-1, format, va);
va_end(va);
return ret;
}

View File

@@ -0,0 +1,116 @@
///////////////////////////////////////////////////////////////////////////////
// \author (c) Marco Paland (info@paland.com)
// 2014-2019, PALANDesign Hannover, Germany
//
// \license The MIT License (MIT)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// \brief Tiny printf, sprintf and snprintf implementation, optimized for speed on
// embedded systems with a very limited resources.
// Use this instead of bloated standard/newlib printf.
// These routines are thread safe and reentrant.
//
///////////////////////////////////////////////////////////////////////////////
#ifndef _PRINTF_H_
#define _PRINTF_H_
#include <stdarg.h>
#include <stddef.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* Output a character to a custom device like UART, used by the printf() function
* This function is declared here only. You have to write your custom implementation somewhere
* \param character Character to output
*/
void _putchar(char character);
/**
* Tiny printf implementation
* You have to implement _putchar if you use printf()
* To avoid conflicts with the regular printf() API it is overridden by macro defines
* and internal underscore-appended functions like printf_() are used
* \param format A string that specifies the format of the output
* \return The number of characters that are written into the array, not counting the terminating null character
*/
#define printf printf_
int printf_(const char* format, ...);
/**
* Tiny sprintf implementation
* Due to security reasons (buffer overflow) YOU SHOULD CONSIDER USING (V)SNPRINTF INSTEAD!
* \param buffer A pointer to the buffer where to store the formatted string. MUST be big enough to store the output!
* \param format A string that specifies the format of the output
* \return The number of characters that are WRITTEN into the buffer, not counting the terminating null character
*/
#define sprintf sprintf_
int sprintf_(char* buffer, const char* format, ...);
/**
* Tiny snprintf/vsnprintf implementation
* \param buffer A pointer to the buffer where to store the formatted string
* \param count The maximum number of characters to store in the buffer, including a terminating null character
* \param format A string that specifies the format of the output
* \param va A value identifying a variable arguments list
* \return The number of characters that COULD have been written into the buffer, not counting the terminating
* null character. A value equal or larger than count indicates truncation. Only when the returned value
* is non-negative and less than count, the string has been completely written.
*/
#define snprintf snprintf_
#define vsnprintf vsnprintf_
int snprintf_(char* buffer, size_t count, const char* format, ...);
int vsnprintf_(char* buffer, size_t count, const char* format, va_list va);
/**
* Tiny vprintf implementation
* \param format A string that specifies the format of the output
* \param va A value identifying a variable arguments list
* \return The number of characters that are WRITTEN into the buffer, not counting the terminating null character
*/
#define vprintf vprintf_
int vprintf_(const char* format, va_list va);
/**
* printf with output function
* You may use this as dynamic alternative to printf() with its fixed _putchar() output
* \param out An output function which takes one character and an argument pointer
* \param arg An argument pointer for user data passed to output function
* \param format A string that specifies the format of the output
* \return The number of characters that are sent to the output function, not counting the terminating null character
*/
int fctprintf(void (*out)(char character, void* arg), void* arg, const char* format, ...);
#ifdef __cplusplus
}
#endif
#endif // _PRINTF_H_

View File

@@ -4,49 +4,49 @@
#include <stdint.h>
/*
All communications are direct from tag to station, EXCEPT association (tag will broadcast).
All comms shall be encrypted and authenticated with AES-CCM. Shared key shall be burned into the firmware.
Master shall provision new key at association. All non-bcast packets shall have pan id compression.
Master may skip "from" field. Tag checking in confirms it got the master's provisioning reply.
Sadly filtering on MZ100 fails for long addr with no src addr. so short addr for src is used
T = tag, S = station
PACKET TYPE USE PAYLOAD STRUCT NOTES
ASSOC_REQ T2bcast TagInfo tag's info and assoc request (encrypted with shared key)
ASSOC_RESP S2T AssocInfo tag's association info (encrypted with shared key)
CHECKIN T2S CheckinInfo tag checking in occasionally
CHECKOUT S2T PendingInfo station's checkin reply telling tag what we have for it
CHUNK_REQ T2S ChunkReqInfo tag requesting a piece of data
CHUNK_RESP S2T ChunkInfo station provides chunk
All communications are direct from tag to station, EXCEPT association (tag will broadcast).
All comms shall be encrypted and authenticated with AES-CCM. Shared key shall be burned into the firmware.
Master shall provision new key at association. All non-bcast packets shall have pan id compression.
Master may skip "from" field. Tag checking in confirms it got the master's provisioning reply.
Sadly filtering on MZ100 fails for long addr with no src addr. so short addr for src is used
T = tag, S = station
PACKET TYPE USE PAYLOAD STRUCT NOTES
ASSOC_REQ T2bcast TagInfo tag's info and assoc request (encrypted with shared key)
ASSOC_RESP S2T AssocInfo tag's association info (encrypted with shared key)
CHECKIN T2S CheckinInfo tag checking in occasionally
CHECKOUT S2T PendingInfo station's checkin reply telling tag what we have for it
CHUNK_REQ T2S ChunkReqInfo tag requesting a piece of data
CHUNK_RESP S2T ChunkInfo station provides chunk
*/
#define PROTO_PRESHARED_KEY {0x34D906D3, 0xE3E5298E, 0x3429BF58, 0xC1022081}
#define PROTO_PRESHARED_KEY \
{ 0x34D906D3, 0xE3E5298E, 0x3429BF58, 0xC1022081 }
#define PROTO_PAN_ID (0x4447) //PAN ID compression shall be used
#define PROTO_PAN_ID (0x4447) // PAN ID compression shall be used
#define PKT_ASSOC_REQ (0xF0)
#define PKT_ASSOC_RESP (0xF1)
#define PKT_CHECKIN (0xF2)
#define PKT_CHECKOUT (0xF3)
#define PKT_CHUNK_REQ (0xF4)
#define PKT_CHUNK_RESP (0xF5)
#define PKT_ASSOC_REQ (0xF0)
#define PKT_ASSOC_RESP (0xF1)
#define PKT_CHECKIN (0xF2)
#define PKT_CHECKOUT (0xF3)
#define PKT_CHUNK_REQ (0xF4)
#define PKT_CHUNK_RESP (0xF5)
#define PROTO_VER_0 (0)
#define PROTO_VER_CURRENT (PROTO_VER_0)
#define PROTO_VER_0 (0)
#define PROTO_VER_CURRENT (PROTO_VER_0)
#define PROTO_COMPR_TYPE_LZ (0x0001)
#define PROTO_COMPR_TYPE_BITPACK (0x0002)
#define PROTO_MAX_DL_LEN (88)
#define PROTO_COMPR_TYPE_LZ (0x0001)
#define PROTO_COMPR_TYPE_BITPACK (0x0002)
#define PROTO_MAX_DL_LEN (88)
//////////////// NEW
#include "tag_types.h"
/*
// power saving algorithm
#define INTERVAL_BASE 40 // interval (in seconds) (when 1 packet is sent/received) for target current (7.2µA)
#define INTERVAL_AT_MAX_ATTEMPTS 600 // interval (in seconds) (at max attempts) for target average current
@@ -68,7 +68,7 @@
#define INTERVAL_2_TIME 7200UL // Try every 2 hours
#define INTERVAL_2_ATTEMPTS 12 // for 12 attempts (an additional day)
#define INTERVAL_3_TIME 86400UL // Finally, try every day
*/
#pragma pack(1)
enum TagScreenType {
TagScreenEink_BW_1bpp,
@@ -96,8 +96,6 @@ enum TagScreenType {
TagScreenTypeOther = 0x7f,
};
#define RADIO_MAX_PACKET_LEN (125) // useful payload, not including the crc
#define ADDR_MODE_NONE (0)
@@ -122,7 +120,7 @@ struct MacFcs {
uint8_t destAddrType : 2;
uint8_t frameVer : 2;
uint8_t srcAddrType : 2;
} ;
};
struct MacFrameFromMaster {
struct MacFcs fcs;
@@ -130,7 +128,7 @@ struct MacFrameFromMaster {
uint16_t pan;
uint8_t dst[8];
uint16_t from;
} ;
};
struct MacFrameNormal {
struct MacFcs fcs;
@@ -138,7 +136,7 @@ struct MacFrameNormal {
uint16_t pan;
uint8_t dst[8];
uint8_t src[8];
} ;
};
struct MacFrameBcast {
struct MacFcs fcs;
@@ -147,7 +145,7 @@ struct MacFrameBcast {
uint16_t dstAddr;
uint16_t srcPan;
uint8_t src[8];
} ;
};
#define PKT_AVAIL_DATA_SHORTREQ 0xE3
#define PKT_AVAIL_DATA_REQ 0xE5
@@ -175,7 +173,7 @@ struct AvailDataReq {
uint8_t currentChannel;
uint8_t customMode;
uint8_t reserved[8];
} ;
};
struct oldAvailDataReq {
uint8_t checksum;
@@ -186,7 +184,7 @@ struct oldAvailDataReq {
uint8_t hwType;
uint8_t wakeupReason;
uint8_t capabilities;
} ;
};
struct AvailDataInfo {
uint8_t checksum;
@@ -195,31 +193,31 @@ struct AvailDataInfo {
uint8_t dataType;
uint8_t dataTypeArgument; // extra specification or instruction for the tag (LUT to be used for drawing image)
uint16_t nextCheckIn; // when should the tag check-in again? Measured in minutes
} ;
} __attribute__((packed)) ;
struct pendingData {
struct AvailDataInfo availdatainfo;
uint16_t attemptsLeft;
uint8_t targetMac[8];
} ;
};
struct blockPart {
uint8_t checksum;
uint8_t blockId;
uint8_t blockPart;
uint8_t data[];
} ;
};
struct blockData {
uint16_t size;
uint16_t checksum;
uint8_t data[];
} ;
};
struct burstMacData {
uint16_t offset;
uint8_t targetMac[8];
} ;
};
#define BLOCK_PART_DATA_SIZE 99
#define BLOCK_MAX_PARTS 42
@@ -238,140 +236,131 @@ struct blockRequest {
struct blockRequestAck {
uint8_t checksum;
uint16_t pleaseWaitMs;
} ;
};
struct espBlockRequest {
uint8_t checksum;
uint64_t ver;
uint8_t blockId;
uint8_t src[8];
} ;
};
struct espXferComplete {
uint8_t checksum;
uint8_t src[8];
} ;
};
struct espAvailDataReq {
uint8_t checksum;
uint8_t src[8];
struct AvailDataReq adr;
} ;
};
struct espSetChannelPower {
uint8_t checksum;
uint8_t channel;
uint8_t power;
} ;
};
#pragma pack(0)
///////////////// NEW END
#ifndef __packed
#define __packed __attribute__((packed))
#define __packed __attribute__((packed))
#endif
struct TagState {
uint64_t swVer;
uint16_t hwType;
uint16_t batteryMv;
uint64_t swVer;
uint16_t hwType;
uint16_t batteryMv;
} __packed;
struct TagInfo {
uint8_t protoVer; //PROTO_VER_*
struct TagState state;
uint8_t rfu1[1]; //shall be ignored for now
uint16_t screenPixWidth;
uint16_t screenPixHeight;
uint16_t screenMmWidth;
uint16_t screenMmHeight;
uint16_t compressionsSupported; //COMPR_TYPE_* bitfield
uint16_t maxWaitMsec; //how long tag will wait for packets before going to sleep
uint8_t screenType; //enum TagScreenType
uint8_t rfu[11]; //shall be zero for now
uint8_t protoVer; // PROTO_VER_*
struct TagState state;
uint8_t rfu1[1]; // shall be ignored for now
uint16_t screenPixWidth;
uint16_t screenPixHeight;
uint16_t screenMmWidth;
uint16_t screenMmHeight;
uint16_t compressionsSupported; // COMPR_TYPE_* bitfield
uint16_t maxWaitMsec; // how long tag will wait for packets before going to sleep
uint8_t screenType; // enum TagScreenType
uint8_t rfu[11]; // shall be zero for now
} __packed;
struct AssocInfo {
uint32_t checkinDelay; //space between checkins, in msec
uint32_t retryDelay; //if download fails mid-way wait thi smany msec to retry (IFF progress was made)
uint16_t failedCheckinsTillBlank; //how many fails till we go blank
uint16_t failedCheckinsTillDissoc; //how many fails till we dissociate
uint32_t newKey[4];
uint8_t rfu[8]; //shall be zero for now
uint32_t checkinDelay; // space between checkins, in msec
uint32_t retryDelay; // if download fails mid-way wait thi smany msec to retry (IFF progress was made)
uint16_t failedCheckinsTillBlank; // how many fails till we go blank
uint16_t failedCheckinsTillDissoc; // how many fails till we dissociate
uint32_t newKey[4];
uint8_t rfu[8]; // shall be zero for now
} __packed;
#define CHECKIN_TEMP_OFFSET 0x7f
#define CHECKIN_TEMP_OFFSET 0x7f
struct CheckinInfo {
struct TagState state;
uint8_t lastPacketLQI; //zero if not reported/not supported to be reported
int8_t lastPacketRSSI; //zero if not reported/not supported to be reported
uint8_t temperature; //zero if not reported/not supported to be reported. else, this minus CHECKIN_TEMP_OFFSET is temp in degrees C
uint8_t rfu[6]; //shall be zero for now
struct TagState state;
uint8_t lastPacketLQI; // zero if not reported/not supported to be reported
int8_t lastPacketRSSI; // zero if not reported/not supported to be reported
uint8_t temperature; // zero if not reported/not supported to be reported. else, this minus CHECKIN_TEMP_OFFSET is temp in degrees C
uint8_t rfu[6]; // shall be zero for now
} __packed;
struct PendingInfo {
uint64_t imgUpdateVer;
uint32_t imgUpdateSize;
uint64_t osUpdateVer; //version of OS update avail
uint32_t osUpdateSize;
uint8_t rfu[8]; //shall be zero for now
uint64_t imgUpdateVer;
uint32_t imgUpdateSize;
uint64_t osUpdateVer; // version of OS update avail
uint32_t osUpdateSize;
uint8_t rfu[8]; // shall be zero for now
} __packed;
struct ChunkReqInfo {
uint64_t versionRequested;
uint32_t offset;
uint8_t len;
uint8_t osUpdatePlz : 1;
uint8_t rfu[6]; //shall be zero for now
uint64_t versionRequested;
uint32_t offset;
uint8_t len;
uint8_t osUpdatePlz : 1;
uint8_t rfu[6]; // shall be zero for now
} __packed;
struct ChunkInfo {
uint32_t offset;
uint8_t osUpdatePlz : 1;
uint8_t rfu; //shall be zero for now
uint8_t data[]; //no data means request is out of bounds of this version no longer exists
uint32_t offset;
uint8_t osUpdatePlz : 1;
uint8_t rfu; // shall be zero for now
uint8_t data[]; // no data means request is out of bounds of this version no longer exists
} __packed;
#define MACFMT "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x"
#define MACCVT(x) ((const uint8_t*)(x))[7], ((const uint8_t*)(x))[6], ((const uint8_t*)(x))[5], ((const uint8_t*)(x))[4], ((const uint8_t*)(x))[3], ((const uint8_t*)(x))[2], ((const uint8_t*)(x))[1], ((const uint8_t*)(x))[0]
#define VERSION_SIGNIFICANT_MASK (0x0000ffffffffffffull)
#define MACFMT "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x"
#define MACCVT(x) ((const uint8_t*)(x))[7], ((const uint8_t*)(x))[6], ((const uint8_t*)(x))[5], ((const uint8_t*)(x))[4], ((const uint8_t*)(x))[3], ((const uint8_t*)(x))[2], ((const uint8_t*)(x))[1], ((const uint8_t*)(x))[0]
#define HW_TYPE_42_INCH_SAMSUNG (1)
#define HW_TYPE_42_INCH_SAMSUNG_ROM_VER_OFST (0xEFF8)
#define HW_TYPE_74_INCH_DISPDATA (2)
#define HW_TYPE_74_INCH_DISPDATA_FRAME_MODE (3)
#define HW_TYPE_74_INCH_DISPDATA_ROM_VER_OFST (0x008b)
#define VERSION_SIGNIFICANT_MASK (0x0000ffffffffffffull)
#define HW_TYPE_ZBD_EPOP50 (4)
#define HW_TYPE_ZBD_EPOP50_ROM_VER_OFST (0x008b)
#define HW_TYPE_42_INCH_SAMSUNG (1)
#define HW_TYPE_42_INCH_SAMSUNG_ROM_VER_OFST (0xEFF8)
#define HW_TYPE_ZBD_EPOP900 (5)
#define HW_TYPE_ZBD_EPOP900_ROM_VER_OFST (0x008b)
#define HW_TYPE_74_INCH_DISPDATA (2)
#define HW_TYPE_74_INCH_DISPDATA_FRAME_MODE (3)
#define HW_TYPE_74_INCH_DISPDATA_ROM_VER_OFST (0x008b)
#define HW_TYPE_ZBD_EPOP50 (4)
#define HW_TYPE_ZBD_EPOP50_ROM_VER_OFST (0x008b)
#define HW_TYPE_ZBD_EPOP900 (5)
#define HW_TYPE_ZBD_EPOP900_ROM_VER_OFST (0x008b)
#define HW_TYPE_29_INCH_DISPDATA (6)
#define HW_TYPE_29_INCH_DISPDATA_FRAME_MODE (7)
#define HW_TYPE_29_INCH_DISPDATA_ROM_VER_OFST (0x008b)
#define HW_TYPE_29_INCH_ZBS_026 (8)
#define HW_TYPE_29_INCH_ZBS_026_FRAME_MODE (9)
#define HW_TYPE_29_INCH_ZBS_025 (10)
#define HW_TYPE_29_INCH_ZBS_025_FRAME_MODE (11)
#define HW_TYPE_29_INCH_ZBS_ROM_VER_OFST (0x008b)
#define HW_TYPE_74_INCH_BWR (40)
#define HW_TYPE_74_INCH_BWR_ROM_VER_OFST (0x0160)
#define HW_TYPE_58_INCH_BWR (41)
#define HW_TYPE_58_INCH_BWR_ROM_VER_OFST (0x0160)
#define HW_TYPE_42_INCH_BWR (42)
#define HW_TYPE_42_INCH_BWR_ROM_VER_OFST (0x0160)
#define HW_TYPE_29_INCH_DISPDATA (6)
#define HW_TYPE_29_INCH_DISPDATA_FRAME_MODE (7)
#define HW_TYPE_29_INCH_DISPDATA_ROM_VER_OFST (0x008b)
#define HW_TYPE_29_INCH_ZBS_026 (8)
#define HW_TYPE_29_INCH_ZBS_026_FRAME_MODE (9)
#define HW_TYPE_29_INCH_ZBS_025 (10)
#define HW_TYPE_29_INCH_ZBS_025_FRAME_MODE (11)
#define HW_TYPE_29_INCH_ZBS_ROM_VER_OFST (0x008b)
#define HW_TYPE_74_INCH_BWR (40)
#define HW_TYPE_74_INCH_BWR_ROM_VER_OFST (0x0160)
#define HW_TYPE_58_INCH_BWR (41)
#define HW_TYPE_58_INCH_BWR_ROM_VER_OFST (0x0160)
#define HW_TYPE_42_INCH_BWR (42)
#define HW_TYPE_42_INCH_BWR_ROM_VER_OFST (0x0160)
#endif

View File

@@ -3,143 +3,24 @@
#include "eeprom.h"
#include "util.h"
#include "mz100_flash.h"
#include "powermgt.h"
__attribute__((section(".aonshadow"))) struct tagsettings tagSettings = {0};
extern uint8_t blockXferBuffer[];
uint8_t* infopageTempBuffer = 1024 + blockXferBuffer;
#define EEPROM_NUM_SETTINGS_PAGES (EEPROM_SETTINGS_AREA_LEN / EEPROM_PAGE_SIZE)
#define SETTINGS_MAGIC (0x31415926)
static uint32_t mCurSettingsAddr;
static void settingsPrvDoWriteAtLocation(uint32_t addr, struct Settings* settings)
{
settings->hdr.revision++;
mCurSettingsAddr = addr;
FLASH_Write(false, addr, (uint8_t*)settings, sizeof(struct Settings));
}
//this is impossible to call before calling read. thus mCurSettingsAddr will be set
void settingsPrvDoWrite(struct Settings* settings)
{
struct SettingsHeader sh;
uint32_t i, addr;
uint8_t byte;
//first we try to fit in the current page, after current (latest) settings
if (mCurSettingsAddr) {
FLASH_Read(0, mCurSettingsAddr, (uint8_t*)&sh, sizeof(struct SettingsHeader));
addr = mCurSettingsAddr + sh.structSize;
//is there space?
if (addr % EEPROM_PAGE_SIZE != 0 && addr % EEPROM_PAGE_SIZE + sizeof(struct Settings) <= EEPROM_PAGE_SIZE) {
//is it erased
for (i = 0; i < sizeof(struct Settings); i++) {
FLASH_Read(0, addr, &byte, 1);
if (byte != 0xff)
break;
}
if (i == sizeof(struct Settings)) {
settingsPrvDoWriteAtLocation(addr, settings);
return;
}
}
}
//we need to erase - use next page (or 0th page if no current page at all)
if (mCurSettingsAddr) {
addr = (mCurSettingsAddr + EEPROM_PAGE_SIZE - 1) / EEPROM_PAGE_SIZE * EEPROM_PAGE_SIZE;
if (addr == EEPROM_SETTINGS_AREA_START + EEPROM_SETTINGS_AREA_LEN)
addr = EEPROM_SETTINGS_AREA_START;
}
else
addr = EEPROM_SETTINGS_AREA_START;
qspiEraseRange(addr, EEPROM_PAGE_SIZE);
settingsPrvDoWriteAtLocation(addr, settings);
}
void settingsRead(struct Settings* settings)
{
uint32_t bestAddr = 0, page, ofst;
uint64_t bestRevision = 0;
struct SettingsHeader sh;
bool doWrite = true;
for (page = 0; page < EEPROM_NUM_SETTINGS_PAGES; page++) {
for (ofst = 0; ofst < EEPROM_PAGE_SIZE - sizeof(struct SettingsHeader); ofst += sh.structSize) {
uint32_t addr = EEPROM_SETTINGS_AREA_START + page * EEPROM_PAGE_SIZE + ofst;
FLASH_Read(0, addr, (uint8_t*)&sh, sizeof(struct SettingsHeader));
//sanity checks. struct is only allowed to grow in size...
if (sh.magic != SETTINGS_MAGIC || ofst + sh.structSize > EEPROM_PAGE_SIZE || sh.structSize > sizeof(struct Settings))
break;
if (sh.revision > bestRevision) {
bestRevision = sh.revision;
bestAddr = addr;
}
}
}
if (bestAddr) {
FLASH_Read(0, bestAddr, (uint8_t*)&sh, sizeof(struct SettingsHeader)); //to get size
FLASH_Read(0, bestAddr, (uint8_t*)settings, sh.structSize);
mCurSettingsAddr = bestAddr;
}
else {
settings->hdr.structVersion = SETTINGS_VER_NONE;
settings->hdr.revision = 1;
mCurSettingsAddr = 0;
}
//migrate
switch (settings->hdr.structVersion) {
//current version here - mark as such
case SETTINGS_CURRENT:
doWrite = false;
break;
case SETTINGS_VER_NONE: //migrate to v1
memset(settings, 0, sizeof(*settings));
settings->hdr.magic = SETTINGS_MAGIC;
//fallthrough
case SETTINGS_VER_1: //migrate to v2
settings->prevDlProgress = 0xffff;
//fallthrough
case SETTINGS_VER_2: //migrate to v3
settings->lastRxedRSSI = 0;
//fallthrough
//new migrations here in order from lower vers to higher vers
settings->hdr.structVersion = SETTINGS_CURRENT;
settings->hdr.structSize = sizeof(struct Settings);
break;
}
if (doWrite)
settingsPrvDoWrite(settings);
}
void settingsWrite(struct Settings* settings)
{
struct Settings s;
settingsRead(&s);
if (memcmp(&s, settings, sizeof(struct Settings)))
settingsPrvDoWrite(settings);
void loadDefaultSettings() {
tagSettings.settingsVer = SETTINGS_STRUCT_VERSION;
tagSettings.enableFastBoot = DEFAULT_SETTING_FASTBOOT;
tagSettings.enableRFWake = DEFAULT_SETTING_RFWAKE;
tagSettings.enableTagRoaming = DEFAULT_SETTING_TAGROAMING;
tagSettings.enableScanForAPAfterTimeout = DEFAULT_SETTING_SCANFORAP;
tagSettings.enableLowBatSymbol = DEFAULT_SETTING_LOWBATSYMBOL;
tagSettings.enableNoRFSymbol = DEFAULT_SETTING_NORFSYMBOL;
tagSettings.customMode = 0;
tagSettings.fastBootCapabilities = 0;
tagSettings.minimumCheckInTime = INTERVAL_BASE;
tagSettings.fixedChannel = 0;
tagSettings.batLowVoltage = BATTERY_VOLTAGE_MINIMUM;
}

View File

@@ -1,65 +1,41 @@
#ifndef _SETTINGS_H_
#define _SETTINGS_H_
#ifndef SETTINGS_H
#define SETTINGS_H
#include <stdint.h>
#define SETTINGS_VER_NONE (0x00000000)
#define SETTINGS_VER_1 (0x00000001)
#define SETTINGS_VER_2 (0x00000002)
#define SETTINGS_VER_3 (0x00000003)
#define FW_VERSION 20 // version number (max 2.5.5 :) )
#define FW_VERSION_SUFFIX "-75" // suffix, like -RC1 or whatever.
// #define DEBUGBLOCKS // uncomment to enable extra debug information on the block transfers
// #define PRINT_LUT // uncomment if you want the tag to print the LUT for the current temperature bracket
struct SettingsHeader {
uint32_t magic;
uint64_t revision;
uint8_t structVersion;
uint8_t structSize; //incl this header
#define SETTINGS_STRUCT_VERSION 0x01
#define DEFAULT_SETTING_FASTBOOT 0
#define DEFAULT_SETTING_RFWAKE 0
#define DEFAULT_SETTING_TAGROAMING 0
#define DEFAULT_SETTING_SCANFORAP 1
#define DEFAULT_SETTING_LOWBATSYMBOL 1
#define DEFAULT_SETTING_NORFSYMBOL 1
struct tagsettings {
uint8_t settingsVer; // the version of the struct as written to the infopage
uint8_t enableFastBoot; // default 0; if set, it will skip splashscreen
uint8_t enableRFWake; // default 0; if set, it will enable RF wake. This will add about ~0.9µA idle power consumption
uint8_t enableTagRoaming; // default 0; if set, the tag will scan for an accesspoint every few check-ins. This will increase power consumption quite a bit
uint8_t enableScanForAPAfterTimeout; // default 1; if a the tag failed to check in, after a few attempts it will try to find a an AP on other channels
uint8_t enableLowBatSymbol; // default 1; tag will show 'low battery' icon on screen if the battery is depleted
uint8_t enableNoRFSymbol; // default 1; tag will show 'no signal' icon on screen if it failed to check in for a longer period of time
uint8_t fastBootCapabilities; // holds the byte with 'capabilities' as detected during a normal tag boot; allows the tag to skip detecting buttons and NFC chip
uint8_t customMode; // default 0; if anything else, tag will bootup in a different 'mode'
uint16_t batLowVoltage; // Low battery threshold voltage (2450 for 2.45v). defaults to BATTERY_VOLTAGE_MINIMUM from powermgt.h
uint16_t minimumCheckInTime; // defaults to BASE_INTERVAL from powermgt.h
uint8_t fixedChannel; // default 0; if set to a valid channel number, the tag will stick to that channel
} __attribute__((packed));
enum SettingsThingType {
SettingsThingTypeNone,
SettingsThingTypeImage,
SettingsThingTypeUpdate,
};
extern __attribute__((section(".aonshadow")))struct tagsettings tagSettings;
#define SETTING_CHANNEL_OFFSET 11
struct Settings { //V1
struct SettingsHeader hdr;
//master address
uint8_t masterMac[8];
//encryption things
uint32_t encrKey[4];
uint32_t nextIV;
//checkin tracking
uint32_t checkinDelay; //space between checkins, in msec
uint32_t retryDelay;
uint16_t failedCheckinsTillBlank; //how many fails till we go blank
uint16_t failedCheckinsTillDissoc; //how many fails till we dissociate
uint16_t numFailedCheckins;
//state
uint8_t lastRxedLQI;
uint8_t isPaired : 1;
uint8_t channel : 4; //minus SETTING_CHANNEL_OFFSET
uint8_t reserved : 3;
uint16_t prevDlProgress;
int8_t lastRxedRSSI;
uint32_t helperInit;
} __attribute__((packed));
#define SETTINGS_CURRENT SETTINGS_VER_3
void settingsRead(struct Settings* settings);
void settingsWrite(struct Settings* settings);
#endif
void loadDefaultSettings();
void writeSettings();
void loadSettings();
void loadSettingsFromBuffer(uint8_t* p);
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -1,14 +1,19 @@
#pragma once
#include <stdint.h>
#include <stdbool.h>
#include <stdint.h>
#include "settings.h"
extern uint8_t mSelfMac[];
extern uint8_t currentChannel;
extern __attribute__((section(".aonshadow"))) uint8_t mSelfMac[];
extern __attribute__((section(".aonshadow"))) volatile uint8_t currentChannel;
extern __attribute__((section(".aonshadow"))) struct blockRequest curBlock; // used by the block-requester, contains the next request that we'll send
extern __attribute__((section(".aonshadow"))) struct AvailDataInfo curDataInfo; // last 'AvailDataInfo' we received from the AP // __attribute__((section(".aon")))
extern uint8_t APmac[];
extern uint8_t curImgSlot;
extern __attribute__((section(".aonshadow"))) uint8_t curImgSlot;
extern void setupRadio(void);
extern void killRadio(void);

View File

@@ -0,0 +1,181 @@
#include "userinterface.h"
#include <stdbool.h>
#include <string.h>
#include "bitmaps.h"
#include "board.h"
#include "comms.h"
#include "epd.h"
#include "font.h"
#include "powermgt.h"
#include "printf.h"
#include "proto.h"
#include "screen.h"
#include "settings.h"
#include "syncedproto.h" // for APmac / Channel
#include "timer.h"
const uint16_t fwVersion = FW_VERSION;
const char fwVersionSuffix[] = FW_VERSION_SUFFIX;
extern uint8_t capabilities;
bool __attribute__((section(".aonshadow"))) lowBatteryShown = false;
bool __attribute__((section(".aonshadow"))) noAPShown = false;
void addCapabilities() {
// if (capabilities) epdpr("Options: ");
if (capabilities & CAPABILITY_HAS_NFC) {
// epdpr("-NFC");
if (capabilities & CAPABILITY_NFC_WAKE) {
// epdpr("+WAKE");
} else {
// epdpr(" ");
}
}
if (capabilities & CAPABILITY_HAS_WAKE_BUTTON) {
// epdpr("-WAKE BUTTON");
}
}
void addOverlay() {
if ((currentChannel == 0) && (tagSettings.enableNoRFSymbol)) {
// loadRawBitmap(ant, SCREEN_WIDTH - 24, 6, EPD_COLOR_BLACK);
// loadRawBitmap(cross, SCREEN_WIDTH - 16, 13, EPD_COLOR_RED);
noAPShown = true;
} else {
noAPShown = false;
}
if ((batteryVoltage < tagSettings.batLowVoltage) && (tagSettings.enableLowBatSymbol)) {
// loadRawBitmap(battery, SCREEN_WIDTH - 16, SCREEN_HEIGHT - 10, EPD_COLOR_BLACK);
lowBatteryShown = true;
} else {
lowBatteryShown = false;
}
}
void afterFlashScreenSaver() {
// selectLUT(EPD_LUT_DEFAULT);
// clearScreen();
#if (SCREEN_WIDTH == 400) // 4.2"
epdPrintBegin(3, 3, EPD_DIRECTION_X, EPD_SIZE_DOUBLE, EPD_COLOR_BLACK);
epdpr("OpenEPaperLink");
epdPrintEnd();
#endif
// drawWithSleep();
}
void showSplashScreen() {
// selectLUT(EPD_LUT_NO_REPEATS);
// clearScreen();
#if (SCREEN_WIDTH == 400) // 4.2"
epdPrintBegin(3, 3, EPD_DIRECTION_X, EPD_SIZE_DOUBLE, EPD_COLOR_BLACK);
epdpr("Starting");
epdPrintEnd();
epdPrintBegin(2, 252, EPD_DIRECTION_X, EPD_SIZE_SINGLE, EPD_COLOR_BLACK);
addCapabilities();
epdPrintEnd();
epdPrintBegin(3, 268, EPD_DIRECTION_X, EPD_SIZE_SINGLE, EPD_COLOR_BLACK);
epdpr("zbs42v033 %d.%d.%d%s", fwVersion / 100, (fwVersion % 100) / 10, (fwVersion % 10), fwVersionSuffix);
epdPrintEnd();
epdPrintBegin(3, 284, EPD_DIRECTION_X, EPD_SIZE_SINGLE, EPD_COLOR_RED);
epdpr("MAC: %02X:%02X", mSelfMac[7], mSelfMac[6]);
epdpr(":%02X:%02X", mSelfMac[5], mSelfMac[4]);
epdpr(":%02X:%02X", mSelfMac[3], mSelfMac[2]);
epdpr(":%02X:%02X", mSelfMac[1], mSelfMac[0]);
epdPrintEnd();
loadRawBitmap(oepli, 136, 22, EPD_COLOR_BLACK);
loadRawBitmap(cloud, 136, 10, EPD_COLOR_RED);
uint8_t __xdata buffer[17];
spr(buffer, "%02X%02X", mSelfMac[7], mSelfMac[6]);
spr(buffer + 4, "%02X%02X", mSelfMac[5], mSelfMac[4]);
spr(buffer + 8, "%02X%02X", mSelfMac[3], mSelfMac[2]);
spr(buffer + 12, "%02X%02X", mSelfMac[1], mSelfMac[0]);
printBarcode(buffer, 392, 264);
printBarcode(buffer, 384, 264);
#endif
// drawWithSleep();
}
void showApplyUpdate() {
// selectLUT(1);
// clearScreen();
#if (SCREEN_WIDTH == 400)
epdPrintBegin(136, 134, EPD_DIRECTION_X, EPD_SIZE_DOUBLE, EPD_COLOR_BLACK);
#endif
// epdpr("Updating!");
// epdPrintEnd();
// drawNoWait();
}
void showAPFound() {
init_epd();
fillWindow(0, 0, 640, 384, 1);
epdPrintf(10, 10, 1, "OpenEPaperLink");
epdPrintf(10, 40, 1, "AP Found at channel %d", currentChannel);
epdPrintf(10, 60, 1, "AP MAC: %02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X", APmac[7], APmac[6], APmac[5], APmac[4], APmac[3], APmac[2], APmac[1], APmac[0]);
epdPrintf(10, 330, 1, "Battery: %d.%dV", batteryVoltage / 1000, batteryVoltage % 1000);
epdPrintf(10, 350, 1, "Tag MAC: %02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X", mSelfMac[7], mSelfMac[6], mSelfMac[5], mSelfMac[4], mSelfMac[3], mSelfMac[2], mSelfMac[1], mSelfMac[0]);
epd_refresh_and_sleep();
}
void showNoAP() {
init_epd();
fillWindow(0, 0, 640, 384, 1);
epdPrintf(10, 10, 1, "OpenEPaperLink ");
epdPrintf(10, 40, 1, "No AP found... We'll try again in a little while though!");
epdPrintf(10, 350, 1, "Tag MAC: %02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X", mSelfMac[7], mSelfMac[6], mSelfMac[5], mSelfMac[4], mSelfMac[3], mSelfMac[2], mSelfMac[1], mSelfMac[0]);
epd_refresh_and_sleep();
}
void showLongTermSleep() {
// selectLUT(EPD_LUT_NO_REPEATS);
// clearScreen();
// epdPrintBegin(2, SCREEN_HEIGHT - 16, EPD_DIRECTION_X, EPD_SIZE_SINGLE, EPD_COLOR_BLACK);
// epdpr("zZ");
// epdPrintEnd();
addOverlay();
// drawWithSleep();
}
void showNoEEPROM() {
// selectLUT(EPD_LUT_NO_REPEATS);
// clearScreen();
#if (SCREEN_WIDTH == 400) // 4.2"
epdPrintBegin(50, 3, EPD_DIRECTION_X, EPD_SIZE_DOUBLE, EPD_COLOR_BLACK);
epdpr("EEPROM FAILED :(");
epdPrintEnd();
loadRawBitmap(failed, 176, 126, EPD_COLOR_RED);
epdPrintBegin(100, 284, EPD_DIRECTION_X, EPD_SIZE_SINGLE, EPD_COLOR_BLACK);
epdpr("Sleeping forever :'(");
epdPrintEnd();
#endif
// drawWithSleep();
}
void showNoMAC() {
// selectLUT(EPD_LUT_NO_REPEATS);
// clearScreen();
#if (SCREEN_WIDTH == 400) // 4.2"
epdPrintBegin(100, 3, EPD_DIRECTION_X, EPD_SIZE_DOUBLE, EPD_COLOR_BLACK);
epdpr("NO MAC SET :(");
epdPrintEnd();
loadRawBitmap(failed, 176, 126, EPD_COLOR_RED);
epdPrintBegin(100, 284, EPD_DIRECTION_X, EPD_SIZE_SINGLE, EPD_COLOR_BLACK);
epdpr("Sleeping forever :'(");
epdPrintEnd();
#endif
// drawWithSleep();
}

View File

@@ -0,0 +1,22 @@
#ifndef _UI_H_
#define _UI_H_
#include <stdint.h>
#include <stdbool.h>
void addOverlay();
void afterFlashScreenSaver();
void showSplashScreen();
void showApplyUpdate();
void showAPFound();
void showNoAP();
void showLongTermSleep();
void showNoEEPROM();
void showNoMAC();
extern const uint16_t fwVersion;
extern const char fwVersionSuffix[];
extern __attribute__((section(".aon"))) bool lowBatteryShown;
extern __attribute__((section(".aon"))) bool noAPShown;
#endif

View File

@@ -1,213 +1,185 @@
#include <stdarg.h>
#include <stdio.h>
#include "eeprom.h"
#include "timer.h"
#include "mz100.h"
#include "util.h"
#include "mz100_flash.h"
#include <stdarg.h>
//#include <stdio.h>
#include "printf.h"
#include "eeprom.h"
#include "mz100.h"
#include "mz100_adc.h"
#include "mz100_flash.h"
#include "mz100_wdt.h"
#include "timer.h"
void wdt10s()
{
void wdt10s() {
WDT_RestartCounter();
}
void wdt30s()
{
void wdt30s() {
WDT_RestartCounter();
}
void wdt60s()
{
void wdt60s() {
WDT_RestartCounter();
}
void delay(int cnt)
{
volatile unsigned int i;
for (i = 107 * cnt; i; --i)
;
void delay(int cnt) {
volatile unsigned int i;
for (i = 107 * cnt; i; --i)
;
}
void delay_us(unsigned int result)
{
volatile unsigned int i;
void delay_us(unsigned int result) {
volatile unsigned int i;
for (i = 0; i < result; ++i)
;
for (i = 0; i < result; ++i)
;
}
uint16_t crc16(uint16_t cur_crc, uint8_t data)
{
cur_crc ^= data;
for (uint8_t i = 8; i > 0; i--)
{
if ((cur_crc & 0x001) != 0)
{
cur_crc >>= 1;
cur_crc ^= 0x8005; // poly
}
else
{
cur_crc >>= 1;
}
}
return cur_crc;
uint16_t crc16(uint16_t cur_crc, uint8_t data) {
cur_crc ^= data;
for (uint8_t i = 8; i > 0; i--) {
if ((cur_crc & 0x001) != 0) {
cur_crc >>= 1;
cur_crc ^= 0x8005; // poly
} else {
cur_crc >>= 1;
}
}
return cur_crc;
}
uint32_t measureTemp(void)
{
uint32_t result = 0;
ADC_CFG_Type adc_config;
adc_config.adcResolution = ADC_RESOLUTION_14BIT;
adc_config.adcVrefSource = ADC_VREF_INTERNAL; // 1.2V
adc_config.adcGainSel = ADC_GAIN_1;
adc_config.adcClockDivider = ADC_CLOCK_DIVIDER_4;
adc_config.adcBiasMode = ADC_BIAS_FULL;
uint32_t measureTemp(void) {
uint32_t result = 0;
ADC_CFG_Type adc_config;
adc_config.adcResolution = ADC_RESOLUTION_14BIT;
adc_config.adcVrefSource = ADC_VREF_INTERNAL; // 1.2V
adc_config.adcGainSel = ADC_GAIN_1;
adc_config.adcClockDivider = ADC_CLOCK_DIVIDER_4;
adc_config.adcBiasMode = ADC_BIAS_FULL;
ADC_Reset();
ADC_ModeSelect(ADC_MODE_TSENSOR);
ADC_TSensorConfig(ADC_TEMPP, ADC_SENSOR_INTERNAL);
ADC_Init(&adc_config);
ADC_Enable();
for (int i = 0; i < 32; i++)
{
ADC_ConversionStart();
ADC_IntClr(ADC_RDY);
while (!ADC_GetStatus(ADC_STATUS_RDY))
;
ADC_ConversionStop();
}
for (int i = 0; i < 128; i++)
{
ADC_ConversionStart();
ADC_IntClr(ADC_RDY);
while (!ADC_GetStatus(ADC_STATUS_RDY))
;
ADC_ConversionStop();
result += (ADC_GetConversionResult() - 458) / 1.7;
}
result /= 128;
printf("Temp: %iC\r\n", result);
return result;
ADC_Reset();
ADC_ModeSelect(ADC_MODE_TSENSOR);
ADC_TSensorConfig(ADC_TEMPP, ADC_SENSOR_INTERNAL);
ADC_Init(&adc_config);
ADC_Enable();
for (int i = 0; i < 32; i++) {
ADC_ConversionStart();
ADC_IntClr(ADC_RDY);
while (!ADC_GetStatus(ADC_STATUS_RDY))
;
ADC_ConversionStop();
}
for (int i = 0; i < 128; i++) {
ADC_ConversionStart();
ADC_IntClr(ADC_RDY);
while (!ADC_GetStatus(ADC_STATUS_RDY))
;
ADC_ConversionStop();
result += (ADC_GetConversionResult() - 458) / 1.7;
}
result /= 128;
printf("Temp: %iC\r\n", result);
return result;
}
uint32_t measureBattery(void)
{
uint32_t result = 0;
ADC_CFG_Type adc_config;
adc_config.adcResolution = ADC_RESOLUTION_16BIT;
adc_config.adcVrefSource = ADC_VREF_VCAU; // 1.8V
adc_config.adcGainSel = ADC_GAIN_1; // range 0 - 1.8V
adc_config.adcClockDivider = ADC_CLOCK_DIVIDER_4;
adc_config.adcBiasMode = ADC_BIAS_FULL;
uint32_t measureBattery(void) {
uint32_t result = 0;
ADC_CFG_Type adc_config;
adc_config.adcResolution = ADC_RESOLUTION_16BIT;
adc_config.adcVrefSource = ADC_VREF_VCAU; // 1.8V
adc_config.adcGainSel = ADC_GAIN_1; // range 0 - 1.8V
adc_config.adcClockDivider = ADC_CLOCK_DIVIDER_4;
adc_config.adcBiasMode = ADC_BIAS_FULL;
ADC_Reset();
ADC_ModeSelect(ADC_MODE_ADC);
ADC_ChannelConfig(ADC_VBATS); // 0.33 of Actual Voltage
ADC_Init(&adc_config);
ADC_Enable();
ADC_ConversionStart();
ADC_IntClr(ADC_RDY);
for (int i = 0; i < 32; i++)
{
ADC_ConversionStart();
ADC_IntClr(ADC_RDY);
while (!ADC_GetStatus(ADC_STATUS_RDY))
;
ADC_ConversionStop();
}
for (int i = 0; i < 128; i++)
{
ADC_ConversionStart();
ADC_IntClr(ADC_RDY);
while (!ADC_GetStatus(ADC_STATUS_RDY))
;
ADC_ConversionStop();
result += ADC_GetConversionResult() * 5940 / 32768;
}
result /= 128;
printf("Voltage: %imV\r\n", result);
return result;
ADC_Reset();
ADC_ModeSelect(ADC_MODE_ADC);
ADC_ChannelConfig(ADC_VBATS); // 0.33 of Actual Voltage
ADC_Init(&adc_config);
ADC_Enable();
ADC_ConversionStart();
ADC_IntClr(ADC_RDY);
for (int i = 0; i < 32; i++) {
ADC_ConversionStart();
ADC_IntClr(ADC_RDY);
while (!ADC_GetStatus(ADC_STATUS_RDY))
;
ADC_ConversionStop();
}
for (int i = 0; i < 128; i++) {
ADC_ConversionStart();
ADC_IntClr(ADC_RDY);
while (!ADC_GetStatus(ADC_STATUS_RDY))
;
ADC_ConversionStop();
result += ADC_GetConversionResult() * 5940 / 32768;
}
result /= 128;
printf("Voltage: %imV\r\n", result);
return result;
}
void qspiEraseRange(uint32_t addr, uint32_t len)
{
uint64_t time;
// round starting address down
if (addr % EEPROM_PAGE_SIZE)
{
len += addr % EEPROM_PAGE_SIZE;
addr = addr / EEPROM_PAGE_SIZE * EEPROM_PAGE_SIZE;
}
void qspiEraseRange(uint32_t addr, uint32_t len) {
uint64_t time;
// round starting address down
if (addr % EEPROM_PAGE_SIZE) {
len += addr % EEPROM_PAGE_SIZE;
addr = addr / EEPROM_PAGE_SIZE * EEPROM_PAGE_SIZE;
}
// round length up
len = (len + EEPROM_PAGE_SIZE - 1) / EEPROM_PAGE_SIZE * EEPROM_PAGE_SIZE;
// round length up
len = (len + EEPROM_PAGE_SIZE - 1) / EEPROM_PAGE_SIZE * EEPROM_PAGE_SIZE;
while (len)
{
while (len) {
uint32_t now;
bool ok;
uint32_t now;
bool ok;
WDT_RestartCounter();
if (!(addr % 0x10000) && len >= 0x10000) {
ok = FLASH_Block64KErase(addr / 0x10000);
now = 0x10000;
} else if (!(addr % 0x8000) && len >= 0x8000) {
ok = FLASH_Block32KErase(addr / 0x8000);
now = 0x8000;
} else {
ok = FLASH_SectorErase(addr / 0x1000);
now = 0x1000;
}
WDT_RestartCounter();
if (!(addr % 0x10000) && len >= 0x10000)
{
ok = FLASH_Block64KErase(addr / 0x10000);
now = 0x10000;
}
else if (!(addr % 0x8000) && len >= 0x8000)
{
ok = FLASH_Block32KErase(addr / 0x8000);
now = 0x8000;
}
else
{
ok = FLASH_SectorErase(addr / 0x1000);
now = 0x1000;
}
if (!ok)
printf("ERZ fail at 0x%08x + %u\r\n", addr, now);
if (!ok)
printf("ERZ fail at 0x%08x + %u\r\n", addr, now);
addr += now;
len -= now;
if (len)
{
// let the caps recharge
time = timerGet();
while (timerGet() - time < TIMER_TICKS_PER_SEC / 10)
;
}
}
WDT_RestartCounter();
addr += now;
len -= now;
if (len) {
// let the caps recharge
time = timerGet();
while (timerGet() - time < TIMER_TICKS_PER_SEC / 10)
;
}
}
WDT_RestartCounter();
}
bool eepromWrite(uint32_t addr, const void *srcP, uint16_t len)
{
FLASH_Write(false, addr, srcP, len);
return true;
bool eepromWrite(uint32_t addr, const void *srcP, uint16_t len) {
FLASH_Write(false, addr, (void*)srcP, len);
return true;
}
bool eepromErase(uint32_t addr, uint16_t nSec)
{
qspiEraseRange(addr, nSec);
return true;
bool eepromErase(uint32_t addr, uint16_t nSec) {
qspiEraseRange(addr, nSec);
return true;
}
void eepromRead(uint32_t addr, void *dstP, uint16_t len)
{
uint8_t *dst = (uint8_t *)dstP;
FLASH_Read(0, addr, dst, len);
void eepromRead(uint32_t addr, void *dstP, uint16_t len) {
uint8_t *dst = (uint8_t *)dstP;
FLASH_Read(0, addr, dst, len);
}
uint32_t eepromGetSize(void)
{
return EEPROM_IMG_LEN;
uint32_t eepromGetSize(void) {
return EEPROM_IMG_LEN;
}
void radioShutdown(void)
{
// i have no idea what these do, determined by writing random registers and watching the current drawn
*(volatile uint32_t *)0x4C000000 = 0;
*(volatile uint32_t *)0x4C010000 = 0;
*(volatile uint32_t *)0x4C010004 = 0x10000000;
void radioShutdown(void) {
// i have no idea what these do, determined by writing random registers and watching the current drawn
*(volatile uint32_t *)0x4C000000 = 0;
*(volatile uint32_t *)0x4C010000 = 0;
*(volatile uint32_t *)0x4C010004 = 0x10000000;
}

View File

@@ -1,7 +1,8 @@
#include <stdio.h>
//#include <stdio.h>
#include <stdint.h>
#include "util.h"
#include "zigbee.h"
#include "printf.h"
volatile uint8_t calibration_irq_ocoured = 0;
volatile uint8_t zigbee_tx_done = 0;
@@ -145,24 +146,29 @@ void fill_rx_regs()
;
}
void sub_1021E6()
void load_calib()
{
int v0;
unsigned int v1;
unsigned int i;
v0 = (*(volatile unsigned int *)0x4C01000C);
v1 = get_register(0x130004);
v1 = zigbeeCalibData.len;
(*(volatile unsigned int *)0x4C010000) |= 4u;
while (((*(volatile unsigned int *)0x4C010008) & 0x1000000) == 0)
;
for (i = 0; i < v1; ++i)
set_register(v0 + 4 * i + 0x4C014000, *(uint32_t *)(4 * i + 0x130008));
set_register(v0 + 4 * i + 0x4C014000, zigbeeCalibData.data[i]);
(*(volatile unsigned int *)0x4C010000) &= ~4u;
while (((*(volatile unsigned int *)0x4C010008) & 0x1000000) != 0)
;
}
// It is from 0x130000 up to 0x1301000 But you can only use 0x130404 up to the 0x1301000
void save_calib_in_ram()
{
int v0;
@@ -171,14 +177,14 @@ void save_calib_in_ram()
v0 = (*(volatile unsigned int *)0x4C01000C) + 0x4C014000;
v1 = ((unsigned int)(uint8_t)((*(volatile unsigned int *)0x4C010008) >> 2) + 3) >> 2;
set_register(0x130000u, 0x464C4147);
set_register(0x130004u, v1);
zigbeeCalibData.isValid = true;
(*(volatile unsigned int *)0x4C01001C) = -5;
(*(volatile unsigned int *)0x4C010000) |= 2u;
while (((*(volatile unsigned int *)0x4C010008) & 0x1000000) == 0)
;
for (i = 0; i < v1; ++i)
set_register(4 * i + 0x130008, *(uint32_t *)(v0 + 4 * i));
for (i = 0; i < v1; ++i){
zigbeeCalibData.data[i] = *(uint32_t *)(v0 + 4 * i);
}
(*(volatile unsigned int *)0x4C010000) &= ~2u;
while (((*(volatile unsigned int *)0x4C010008) & 0x1000000) != 0)
;
@@ -189,12 +195,12 @@ int inner_calibration()
int is_in_ram;
(*(volatile unsigned int *)0x4C010000) |= 0x20u;
if (get_register(0x130000) == 0x464C4147)
if(zigbeeCalibData.isValid)
{
is_in_ram = 1;
(*(volatile unsigned int *)0x4C010000) |= 8u;
(*(volatile unsigned int *)0x4C010000) &= ~0x10u;
sub_1021E6();
load_calib();
}
else
{

View File

@@ -1,4 +1,5 @@
#pragma once
#include <stdlib.h>
extern uint8_t channelList[6];
@@ -10,4 +11,11 @@ uint8_t Zigbee_tx_buffer(uint8_t *tx_buffer, int len);
void radioInit();
void radioSetChannel(uint8_t channel);
void radioRxEnable(uint8_t channel);
void radioRxFlush();
void radioRxFlush();
struct zigbeeCalibDataStruct {
uint16_t len;
bool isValid;
uint32_t data[30];
};
extern __attribute__((section(".aon"))) volatile struct zigbeeCalibDataStruct zigbeeCalibData;

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, 0x480000,
app1, app, ota_1, 0x490000,0x480000,
spiffs, data, spiffs, 0x910000,0x16E0000,
coredump, data, coredump,0x1FF0000,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 0x480000
5 app1 app ota_1 0x490000 0x480000
6 spiffs data spiffs 0x910000 0x16E0000
7 coredump data coredump 0x1FF0000 0x10000

View File

@@ -1,129 +0,0 @@
{
"1": {
"0": {
"weekday": [ 76, 10, "fonts/calibrib30" ],
"month": [ 76, 120, "fonts/calibrib30" ],
"day": [ 76, 42, "fonts/calibrib100" ]
},
"1": {
"weekday": [ 148, 10, "fonts/calibrib60" ],
"date": [ 148, 73, "fonts/calibrib50" ]
},
"2": {
"weekday": [ 200, 25, "fonts/calibrib60" ],
"month": [ 200, 225, "fonts/calibrib60" ],
"day": [ 200, 95, "fonts/calibrib150" ]
}
},
"2": {
"0": {
"fonts": [ "fonts/calibrib120", "fonts/calibrib80", "fonts/calibrib50", "fonts/calibrib50" ],
"xy": [ 76, 83 ]
},
"1": {
"fonts": [ "fonts/calibrib150", "fonts/calibrib150", "fonts/calibrib120", "fonts/calibrib100" ],
"xy": [ 148, 74 ]
},
"2": {
"fonts": [ "fonts/calibrib150", "fonts/calibrib150", "fonts/calibrib150", "fonts/calibrib120" ],
"xy": [ 200, 148 ]
}
},
"4": {
"0": {
"location": [ 10, 145, "t0_14b_tf" ],
"wind": [ 140, 10, "fonts/bahnschrift30" ],
"temp": [ 10, 10, "fonts/bahnschrift30" ],
"icon": [ 75, 26, 70, 1 ],
"dir": [ 110, -12, 40 ],
"umbrella": [ 125, 110, 30 ]
},
"1": {
"location": [ 5, 5, "fonts/bahnschrift30" ],
"wind": [ 280, 5, "fonts/bahnschrift30" ],
"temp": [ 5, 65, "fonts/bahnschrift70" ],
"icon": [ 285, 20, 70, 2 ],
"dir": [ 245, -12, 40 ],
"umbrella": [ 190, -50, 25 ]
},
"2": {
"location": [ 20, 20, "fonts/calibrib30" ],
"wind": [ 90, 83, "fonts/calibrib60" ],
"temp": [ 20, 170, "fonts/calibrib150" ],
"icon": [ 385, 0, 100, 2 ],
"dir": [ 40, 50, 80 ],
"umbrella": [ 325, 155, 78 ]
}
},
"8": {
"1": {
"location": [ 5, 12, "t0_14b_tf" ],
"column": [ 5, 59 ],
"day": [ 30, 18, "fonts/twcondensed20", 41, 108 ],
"icon": [ 30, 55, 30 ],
"wind": [ 18, 26 ],
"line": [ 20, 128 ]
},
"2": {
"location": [ 10, 10, "fonts/calibrib30" ],
"column": [ 6, 66 ],
"day": [ 33, 60, "fonts/bahnschrift20", 104, 230 ],
"rain": [ 34, 260 ],
"icon": [ 32, 145, 30 ],
"wind": [ 17, 90 ],
"line": [ 50, 300 ]
}
},
"9": {
"1": {
"title": [ 5, 3, "fonts/bahnschrift20" ],
"items": 8,
"line": [ 5, 34, 13 ],
"font": "glasstown_nbp_tf"
},
"2": {
"title": [ 10, 10, "fonts/calibrib30" ],
"items": 12,
"line": [ 10, 60, 20 ],
"font": "7x14_tf"
}
},
"10": {
"0": {
"title": [ 10, 15, "t0_14b_tf" ],
"pos": [ 76, 20 ]
},
"1": {
"title": [ 10, 5, "fonts/bahnschrift20" ],
"pos": [ 149, 25 ]
},
"2": {
"title": [ 10, 10, "fonts/bahnschrift20" ],
"pos": [ 200, 30 ]
}
},
"11": {
"1": {
"title": [ 5, 2, "fonts/bahnschrift20" ],
"date": [ 290, 2 ],
"items": 7,
"red": [ 0, 21, 296, 14 ],
"line": [ 5, 32, 15, "t0_14b_tf", 50 ]
},
"2": {
"title": [ 10, 10, "fonts/bahnschrift30" ],
"date": [ 390, 10 ],
"items": 12,
"red": [ 0, 48, 400, 17 ],
"line": [ 10, 61, 18, "7x14_tf", 60 ]
}
},
"16": {
"1": {
"location": [ 5, 5, "fonts/bahnschrift30" ],
"title": [ 247, 11, "glasstown_nbp_tf" ],
"cols": [ 1, 125, 12, "glasstown_nbp_tf" ],
"bars": [ 5, 111, 10 ]
}
}
}

View File

@@ -0,0 +1,48 @@
{
"name": "STGR16000 1.54\"",
"width": 152,
"height": 152,
"rotatebuffer": 0,
"bpp": 2,
"colors": 3,
"colortable": {
"white": [255, 255, 255],
"black": [0, 0, 0],
"red": [255, 0, 0],
"gray": [150, 150, 150]
},
"capabilities": ["button", "customlut"],
"template": {
"1": {
"weekday": [ 76, 10, "fonts/calibrib30" ],
"month": [ 76, 120, "fonts/calibrib30" ],
"day": [ 76, 42, "fonts/calibrib100" ]
},
"2": {
"fonts": [ "fonts/calibrib120", "fonts/calibrib80", "fonts/calibrib50", "fonts/calibrib50" ],
"xy": [ 76, 83 ]
},
"4": {
"location": [ 10, 145, "t0_14b_tf" ],
"wind": [ 140, 10, "fonts/bahnschrift30" ],
"temp": [ 10, 10, "fonts/bahnschrift30" ],
"icon": [ 75, 26, 70, 1 ],
"dir": [ 110, -12, 40 ],
"umbrella": [ 125, 110, 30 ]
},
"10": {
"title": [ 10, 15, "t0_14b_tf" ],
"pos": [ 76, 20 ]
},
"21": [
{ "text": [ 2, 5, "OpenEpaperLink", "bahnschrift20", 1, 0, 0 ] },
{ "text": [ 2, 25, "Access Point", "bahnschrift20", 1, 0, 0 ] },
{ "text": [ 3, 65, "IP address:", "glasstown_nbp_tf", 1, 0, 0 ] },
{ "text": [ 10, 80, "{ap_ip}", "t0_14b_tf", 1, 0, 0 ] },
{ "text": [ 3, 95, "Channel:", "glasstown_nbp_tf", 1, 0, 0 ] },
{ "text": [ 10, 110, "{ap_ch}", "t0_14b_tf", 1, 0, 0 ] },
{ "text": [ 3, 125, "Tag count:", "glasstown_nbp_tf", 1, 0, 0 ] },
{ "text": [ 10, 140, "{ap_tagcount}", "t0_14b_tf", 1, 0, 0 ] }
]
}
}

View File

@@ -0,0 +1,73 @@
{
"name": "STGR29000 2.9\"",
"width": 296,
"height": 128,
"rotatebuffer": 1,
"bpp": 2,
"colors": 3,
"colortable": {
"white": [255, 255, 255],
"black": [0, 0, 0],
"red": [255, 0, 0],
"gray": [150, 150, 150]
},
"capabilities": ["button", "customlut"],
"template": {
"1": {
"weekday": [148, 10, "fonts/calibrib60"],
"date": [148, 73, "fonts/calibrib50"]
},
"16": {
"location": [ 5, 5, "fonts/bahnschrift30" ],
"title": [ 247, 11, "glasstown_nbp_tf" ],
"cols": [ 1, 125, 12, "glasstown_nbp_tf" ],
"bars": [ 5, 111, 10 ]
},
"2": {
"fonts": ["fonts/calibrib150", "fonts/calibrib150", "fonts/calibrib120", "fonts/calibrib100"],
"xy": [148, 74]
},
"4": {
"location": [5, 5, "fonts/bahnschrift30"],
"wind": [280, 5, "fonts/bahnschrift30"],
"temp": [5, 65, "fonts/bahnschrift70"],
"icon": [285, 20, 70, 2],
"dir": [245, -12, 40],
"umbrella": [190, -50, 25]
},
"8": {
"location": [5, 12, "t0_14b_tf"],
"column": [5, 59],
"day": [30, 18, "fonts/twcondensed20", 41, 108],
"icon": [30, 55, 30],
"wind": [18, 26],
"line": [20, 128]
},
"9": {
"title": [5, 3, "fonts/bahnschrift20"],
"items": 8,
"line": [5, 34, 13],
"font": "glasstown_nbp_tf"
},
"10": {
"title": [10, 5, "fonts/bahnschrift20"],
"pos": [149, 25]
},
"11": {
"title": [5, 2, "fonts/bahnschrift20"],
"date": [290, 2],
"items": 7,
"red": [0, 21, 296, 14],
"line": [5, 32, 15, "t0_14b_tf", 50]
},
"21": [
{ "text": [ 5, 5, "OpenEpaperLink AP", "bahnschrift20", 1, 0, 0 ] },
{ "text": [ 5, 50, "IP address:", "t0_14b_tf", 1, 0, 0 ] },
{ "text": [ 120, 50, "{ap_ip}", "t0_14b_tf", 1, 0, 0 ] },
{ "text": [ 5, 70, "Channel:", "t0_14b_tf", 1, 0, 0 ] },
{ "text": [ 120, 70, "{ap_ch}", "t0_14b_tf", 1, 0, 0 ] },
{ "text": [ 5, 90, "Tag count:", "t0_14b_tf", 1, 0, 0 ] },
{ "text": [ 120, 90, "{ap_tagcount}", "t0_14b_tf", 1, 0, 0 ] }
]
}
}

View File

@@ -0,0 +1,60 @@
{
"name": "STGR420B3N2 4.2\"",
"width": 400,
"height": 300,
"rotatebuffer": 0,
"bpp": 2,
"colors": 3,
"colortable": {
"white": [255, 255, 255],
"black": [0, 0, 0],
"red": [255, 0, 0],
"gray": [150, 150, 150]
},
"capabilities": ["button", "customlut"],
"template": {
"1": {
"weekday": [ 200, 25, "fonts/calibrib60" ],
"month": [ 200, 225, "fonts/calibrib60" ],
"day": [ 200, 95, "fonts/calibrib150" ]
},
"2": {
"fonts": [ "fonts/calibrib150", "fonts/calibrib150", "fonts/calibrib150", "fonts/calibrib120" ],
"xy": [ 200, 148 ]
},
"4": {
"location": [ 20, 20, "fonts/calibrib30" ],
"wind": [ 90, 83, "fonts/calibrib60" ],
"temp": [ 20, 170, "fonts/calibrib150" ],
"icon": [ 385, 0, 100, 2 ],
"dir": [ 40, 50, 80 ],
"umbrella": [ 325, 155, 78 ]
},
"8": {
"location": [ 10, 10, "fonts/calibrib30" ],
"column": [ 6, 66 ],
"day": [ 33, 60, "fonts/bahnschrift20", 104, 230 ],
"rain": [ 34, 260 ],
"icon": [ 32, 145, 30 ],
"wind": [ 17, 90 ],
"line": [ 50, 300 ]
},
"9": {
"title": [ 10, 10, "fonts/calibrib30" ],
"items": 12,
"line": [ 10, 60, 20 ],
"font": "7x14_tf"
},
"10": {
"title": [ 10, 10, "fonts/bahnschrift20" ],
"pos": [ 200, 30 ]
},
"11": {
"title": [ 10, 10, "fonts/bahnschrift30" ],
"date": [ 390, 10 ],
"items": 12,
"red": [ 0, 48, 400, 17 ],
"line": [ 10, 61, 18, "7x14_tf", 60 ]
}
}
}

View File

@@ -0,0 +1,60 @@
{
"name": "STGR750BN 7.4\"",
"width": 640,
"height": 384,
"rotatebuffer": 0,
"bpp": 2,
"colors": 3,
"colortable": {
"white": [255, 255, 255],
"black": [0, 0, 0],
"red": [255, 0, 0],
"gray": [150, 150, 150]
},
"capabilities": ["button", "customlut"],
"template": {
"1": {
"weekday": [ 200, 25, "fonts/calibrib60" ],
"month": [ 200, 225, "fonts/calibrib60" ],
"day": [ 200, 95, "fonts/calibrib150" ]
},
"2": {
"fonts": [ "fonts/calibrib150", "fonts/calibrib150", "fonts/calibrib150", "fonts/calibrib120" ],
"xy": [ 200, 148 ]
},
"4": {
"location": [ 20, 20, "fonts/calibrib30" ],
"wind": [ 90, 83, "fonts/calibrib60" ],
"temp": [ 20, 170, "fonts/calibrib150" ],
"icon": [ 385, 0, 100, 2 ],
"dir": [ 40, 50, 80 ],
"umbrella": [ 325, 155, 78 ]
},
"8": {
"location": [ 10, 10, "fonts/calibrib30" ],
"column": [ 6, 66 ],
"day": [ 33, 60, "fonts/bahnschrift20", 104, 230 ],
"rain": [ 34, 260 ],
"icon": [ 32, 145, 30 ],
"wind": [ 17, 90 ],
"line": [ 50, 300 ]
},
"9": {
"title": [ 10, 10, "fonts/calibrib30" ],
"items": 12,
"line": [ 10, 60, 20 ],
"font": "7x14_tf"
},
"10": {
"title": [ 10, 10, "fonts/bahnschrift20" ],
"pos": [ 200, 30 ]
},
"11": {
"title": [ 10, 10, "fonts/bahnschrift30" ],
"date": [ 390, 10 ],
"items": 12,
"red": [ 0, 48, 400, 17 ],
"line": [ 10, 61, 18, "7x14_tf", 60 ]
}
}
}

View File

@@ -0,0 +1,16 @@
{
"name": "ST-GR2900L 2.9\" (UC8151)",
"width": 296,
"height": 128,
"rotatebuffer": 1,
"bpp": 2,
"colors": 3,
"colortable": {
"white": [255, 255, 255],
"black": [0, 0, 0],
"red": [255, 0, 0],
"gray": [150, 150, 150]
},
"capabilities": ["button", "customlut"],
"usetemplate": 1
}

View File

@@ -0,0 +1,64 @@
{
"name": "EL029GSWRN 2.9\"",
"width": 384,
"height": 168,
"rotatebuffer": 1,
"bpp": 2,
"colors": 3,
"colortable": {
"white": [255, 255, 255],
"black": [0, 0, 0],
"red": [255, 0, 0],
"gray": [150, 150, 150]
},
"capabilities": ["button", "customlut"],
"template": {
"1": {
"weekday": [148, 10, "fonts/calibrib60"],
"date": [148, 73, "fonts/calibrib50"]
},
"16": {
"location": [ 5, 5, "fonts/bahnschrift30" ],
"title": [ 247, 11, "glasstown_nbp_tf" ],
"cols": [ 1, 125, 12, "glasstown_nbp_tf" ],
"bars": [ 5, 111, 10 ]
},
"2": {
"fonts": ["fonts/calibrib150", "fonts/calibrib150", "fonts/calibrib120", "fonts/calibrib100"],
"xy": [148, 74]
},
"4": {
"location": [5, 5, "fonts/bahnschrift30"],
"wind": [280, 5, "fonts/bahnschrift30"],
"temp": [5, 65, "fonts/bahnschrift70"],
"icon": [285, 20, 70, 2],
"dir": [245, -12, 40],
"umbrella": [190, -50, 25]
},
"8": {
"location": [5, 12, "t0_14b_tf"],
"column": [5, 59],
"day": [30, 18, "fonts/twcondensed20", 41, 108],
"icon": [30, 55, 30],
"wind": [18, 26],
"line": [20, 128]
},
"9": {
"title": [5, 3, "fonts/bahnschrift20"],
"items": 8,
"line": [5, 34, 13],
"font": "glasstown_nbp_tf"
},
"10": {
"title": [10, 5, "fonts/bahnschrift20"],
"pos": [149, 25]
},
"11": {
"title": [5, 2, "fonts/bahnschrift20"],
"date": [290, 2],
"items": 7,
"red": [0, 21, 296, 14],
"line": [5, 32, 15, "t0_14b_tf", 50]
}
}
}

View File

@@ -0,0 +1,29 @@
{
"name": "TFT 320x170",
"width": 320,
"height": 170,
"rotatebuffer": 0,
"bpp": 16,
"colors": 4,
"colortable": {
"white": [ 255, 255, 255 ],
"black": [ 0, 0, 0 ],
"red": [ 255, 0, 0 ],
"gray": [ 150, 150, 150 ]
},
"capabilities": [ ],
"contentids": [ 0, 1, 2, 3, 4, 8, 16, 9, 7, 19, 10, 11, 21 ],
"usetemplate": 1,
"template": {
"21": [
{ "box": [ 0, 0, 320, 170, 1 ] },
{ "text": [ 5, 5, "OpenEpaperLink AP", "calibrib30", 2, 0, 0, 1 ] },
{ "text": [ 5, 60, "IP address:", "bahnschrift20", "#888888", 0, 0, 1 ] },
{ "text": [ 120, 60, "{ap_ip}", "bahnschrift20", 0, 0, 0, 1 ] },
{ "text": [ 5, 85, "Channel:", "bahnschrift20", "#888888", 0, 0, 1 ] },
{ "text": [ 120, 85, "{ap_ch}", "bahnschrift20", 0, 0, 0, "1" ] },
{ "text": [ 5, 110, "Tag count:", "bahnschrift20", "#888888", 0, 0, 1 ] },
{ "text": [ 120, 110, "{ap_tagcount}", "bahnschrift20", 0, 0, 0, "1" ] }
]
}
}

View File

@@ -0,0 +1,12 @@
{
"name": "SLTEM007 Segmented",
"width": 0,
"height": 0,
"rotatebuffer": 0,
"bpp": 1,
"colors": 0,
"colortable": {},
"capabilities": [],
"template": {
}
}

View File

@@ -14,8 +14,13 @@
"/alignment.jpg",
"/gradient.jpg",
"/demo_image_generator.py",
"/www/content_cards.json",
"/www/favicon.ico",
"/www/index.html",
"/www/jsontemplate-demo.html",
"/www/main.css",
"/www/main.js",
"/www/ota.js",
"/www/painter.js",
"/www/setup.html",
"/www/setup.js",

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -16,7 +16,7 @@ def gzip_files(source_folder, destination_folder):
print(f"Gzipping: {file}")
with open(source_file_path, 'rb') as f_in, gzip.open(destination_file_path, 'wb') as f_out:
with open(source_file_path, 'rb') as f_in, gzip.GzipFile(destination_file_path, 'wb', mtime=0) as f_out:
shutil.copyfileobj(f_in, f_out)
if __name__ == "__main__":
@@ -24,4 +24,3 @@ if __name__ == "__main__":
destination_folder = "data/www" # Replace with the path of the destination folder
gzip_files(source_folder, destination_folder)

View File

@@ -9,40 +9,40 @@
struct contentTypes {
uint16_t id;
String name;
uint16_t tagTypes;
uint16_t tagTypes;
void (*functionname)();
String description;
String optionList;
String description;
String optionList;
};
void contentRunner();
void drawNew(uint8_t mac[8], bool buttonPressed, tagRecord *&taginfo);
bool updateTagImage(String &filename, 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 = 0);
void checkVars();
void drawNew(const uint8_t mac[8], const bool buttonPressed, 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 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);
void drawWeather(String &filename, JsonObject &cfgobj, tagRecord *&taginfo, imgParam &imageParams);
void drawForecast(String &filename, JsonObject &cfgobj, tagRecord *&taginfo, imgParam &imageParams);
void drawIdentify(String &filename, tagRecord *&taginfo, imgParam &imageParams);
void drawWeather(String &filename, JsonObject &cfgobj, const tagRecord *taginfo, imgParam &imageParams);
void drawForecast(String &filename, JsonObject &cfgobj, const tagRecord *taginfo, imgParam &imageParams);
int getImgURL(String &filename, String URL, time_t fetched, imgParam &imageParams, String MAC);
bool getRssFeed(String &filename, String URL, String title, tagRecord *&taginfo, imgParam &imageParams);
bool getCalFeed(String &filename, String URL, String title, tagRecord *&taginfo, imgParam &imageParams);
void drawQR(String &filename, String qrcontent, String title, tagRecord *&taginfo, imgParam &imageParams);
uint8_t drawBuienradar(String &filename, JsonObject &cfgobj, tagRecord *&taginfo, imgParam &imageParams);
void drawAPinfo(String &filename, JsonObject &cfgobj, tagRecord *&taginfo, imgParam &imageParams);
int getJsonTemplateFile(String &filename, String jsonfile, tagRecord *&taginfo, imgParam &imageParams);
int getJsonTemplateUrl(String &filename, String URL, time_t fetched, String MAC, tagRecord *&taginfo, imgParam &imageParams);
void drawJsonStream(Stream &stream, String &filename, tagRecord *&taginfo, imgParam &imageParams);
void drawElement(const JsonObject &element, TFT_eSprite &spr);
uint16_t getColor(uint8_t color);
uint16_t getColor(const String &color);
char *formatHttpDate(time_t t);
String urlEncode(const char *msg);
int windSpeedToBeaufort(float windSpeed);
String windDirectionIcon(int degrees);
int windSpeedToBeaufort(const float windSpeed);
String windDirectionIcon(const int degrees);
void getLocation(JsonObject &cfgobj);
void prepareNFCReq(uint8_t* dst, const char* url);
void prepareLUTreq(uint8_t *dst, String input);
void prepareConfigFile(uint8_t *dst, JsonObject config);
void getTemplate(JsonDocument &json, const char *filePath, uint8_t id, uint8_t hwtype);
void prepareNFCReq(const uint8_t *dst, const char *url);
void prepareLUTreq(const uint8_t *dst, const String &input);
void prepareConfigFile(const uint8_t *dst, const JsonObject &config);
void getTemplate(JsonDocument &json, const uint8_t id, const uint8_t hwtype);
void setU8G2Font(const String &title, U8g2_for_TFT_eSPI &u8f);
void showIpAddress(String dst);

View File

@@ -5,6 +5,24 @@
#include <FastLED.h>
#endif
const uint8_t PROGMEM gamma8[] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2,
2, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 5, 5, 5,
5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 9, 9, 9, 10,
10, 10, 11, 11, 11, 12, 12, 13, 13, 13, 14, 14, 15, 15, 16, 16,
17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22, 23, 24, 24, 25,
25, 26, 27, 27, 28, 29, 29, 30, 31, 32, 32, 33, 34, 35, 35, 36,
37, 38, 39, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 50,
51, 52, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 66, 67, 68,
69, 70, 72, 73, 74, 75, 77, 78, 79, 81, 82, 83, 85, 86, 87, 89,
90, 92, 93, 95, 96, 98, 99, 101, 102, 104, 105, 107, 109, 110, 112, 114,
115, 117, 119, 120, 122, 124, 126, 127, 129, 131, 133, 135, 137, 138, 140, 142,
144, 146, 148, 150, 152, 154, 156, 158, 160, 162, 164, 167, 169, 171, 173, 175,
177, 180, 182, 184, 186, 189, 191, 193, 196, 198, 200, 203, 205, 208, 210, 213,
215, 218, 220, 223, 225, 228, 231, 233, 236, 239, 241, 244, 247, 249, 252, 255};
void ledTask(void* parameter);
void setBrightness(int brightness);
void updateBrightnessFromConfig();

View File

@@ -3,14 +3,21 @@
#pragma once
extern TFT_eSPI tft;
struct imgParam {
bool hasRed;
uint8_t dataType;
bool dither;
bool grayLut = false;
uint8_t bpp = 8;
uint8_t bufferbpp = 8;
uint8_t rotate = 0;
uint16_t width;
uint16_t height;
uint8_t rotatebuffer;
uint8_t bpp;
char segments[12];
uint16_t symbols;
bool invert;

View File

@@ -4,20 +4,21 @@ extern void addCRC(void* p, uint8_t len);
extern bool checkCRC(void* p, uint8_t len);
extern void processBlockRequest(struct espBlockRequest* br);
extern void prepareIdleReq(uint8_t* dst, uint16_t nextCheckin);
extern void prepareDataAvail(uint8_t* data, uint16_t len, uint8_t dataType, uint8_t* dst);
extern bool prepareDataAvail(String* filename, uint8_t dataType, uint8_t* dst, uint16_t nextCheckin);
extern void prepareCancelPending(const uint8_t dst[8]);
extern void prepareIdleReq(const uint8_t* dst, uint16_t nextCheckin);
extern void prepareDataAvail(uint8_t* data, uint16_t len, uint8_t dataType, const uint8_t* dst);
extern bool prepareDataAvail(String* filename, uint8_t dataType, const uint8_t* dst, uint16_t nextCheckin);
extern void prepareExternalDataAvail(struct pendingData* pending, IPAddress remoteIP);
extern void processXferComplete(struct espXferComplete* xfc, bool local);
extern void processXferTimeout(struct espXferComplete* xfc, bool local);
extern void processDataReq(struct espAvailDataReq* adr, bool local);
extern bool sendTagCommand(uint8_t* dst, uint8_t cmd, bool local);
extern bool sendAPSegmentedData(uint8_t* dst, String data, uint16_t icons, bool inverted, bool local);
extern bool showAPSegmentedInfo(uint8_t* dst, bool local);
extern bool sendTagCommand(const uint8_t* dst, uint8_t cmd, 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);
bool checkMirror(struct tagRecord* taginfo, struct pendingData* pending);
void refreshAllPending();
void updateContent(uint8_t* dst);
void updateContent(const uint8_t* dst);
void setAPchannel();

View File

@@ -8,6 +8,6 @@ void handleGetExtUrl(AsyncWebServerRequest* request);
void handleLittleFSUpload(AsyncWebServerRequest* request, String filename, size_t index, uint8_t* data, size_t len, bool final);
void handleUpdateOTA(AsyncWebServerRequest* request);
void firmwareUpdateTask(void* parameter);
void updateFirmware(const char* url, const char* expectedMd5, size_t size);
void updateFirmware(const char* url, const char* expectedMd5, const size_t size);
void handleRollback(AsyncWebServerRequest* request);
void handleUpdateActions(AsyncWebServerRequest* request);

View File

@@ -1,6 +1,7 @@
#include <Arduino.h>
#include <ArduinoJson.h>
#include <unordered_map>
#include <vector>
#pragma pack(push, 1)
@@ -18,8 +19,7 @@
class tagRecord {
public:
tagRecord() : mac{0}, alias(""), lastseen(0), nextupdate(0), contentMode(0), pending(false), md5{0}, md5pending{0}, expectedNextCheckin(0), modeConfigJson(""), LQI(0), RSSI(0), temperature(0), batteryMv(0), hwType(0), wakeupReason(0), capabilities(0), lastfullupdate(0), isExternal(false), pendingIdle(0), hasCustomLUT(false), rotate(0), lut(0), tagSoftwareVersion(0), currentChannel(0),
dataType(0), filename(""), data(nullptr), len(0) {}
tagRecord() : mac{0}, alias(""), lastseen(0), nextupdate(0), contentMode(0), pending(false), md5{0}, md5pending{0}, expectedNextCheckin(0), modeConfigJson(""), LQI(0), RSSI(0), temperature(0), batteryMv(0), hwType(0), wakeupReason(0), capabilities(0), lastfullupdate(0), isExternal(false), pendingIdle(0), hasCustomLUT(false), rotate(0), lut(0), tagSoftwareVersion(0), currentChannel(0), dataType(0), filename(""), data(nullptr), len(0) {}
uint8_t mac[8];
String alias;
@@ -52,7 +52,7 @@ class tagRecord {
uint8_t* data;
uint32_t len;
static tagRecord* findByMAC(uint8_t mac[8]);
static tagRecord* findByMAC(const uint8_t mac[8]);
};
struct Config {
@@ -68,21 +68,38 @@ struct Config {
char timeZone[52];
};
struct HwType {
uint16_t width;
uint16_t height;
uint8_t rotatebuffer;
uint8_t bpp;
};
struct varStruct {
String value;
bool changed;
};
// extern SemaphoreHandle_t tagDBOwner;
extern Config config;
extern std::vector<tagRecord*> tagDB;
extern std::unordered_map<int, HwType> hwtype;
extern std::unordered_map<std::string, varStruct> varDB;
extern DynamicJsonDocument APconfig;
String tagDBtoJson(uint8_t mac[8] = nullptr, uint8_t startPos = 0);
bool deleteRecord(uint8_t mac[8]);
void fillNode(JsonObject &tag, tagRecord* &taginfo);
String tagDBtoJson(const uint8_t mac[8] = nullptr, uint8_t startPos = 0);
bool deleteRecord(const uint8_t mac[8]);
void fillNode(JsonObject& tag, tagRecord*& taginfo);
void saveDB(String filename);
void loadDB(String filename);
void destroyDB();
uint8_t getTagCount();
void mac2hex(uint8_t* mac, char* hexBuffer);
uint32_t getTagCount();
uint32_t getTagCount(uint32_t& timeoutcount);
void mac2hex(const uint8_t* mac, char* hexBuffer);
bool hex2mac(const String& hexString, uint8_t* mac);
void clearPending(tagRecord* taginfo);
void initAPconfig();
void saveAPconfig();
HwType getHwType(uint8_t id);
bool setVarDB(const std::string& key, const String& value);
#pragma pack(pop)
#pragma pack(pop)

View File

@@ -0,0 +1,107 @@
#pragma once
#include <Arduino.h>
#include <ArduinoJson.h>
#include <HTTPClient.h>
#include "web.h"
namespace util {
/// @brief Can be used to wrap a stream and see what's going on
class DebugStream : public Stream {
public:
DebugStream(Stream &stream) : _stream(stream) {}
int available() override {
return _stream.available();
}
int read() override {
int data = _stream.read();
Serial.write(data);
return data;
}
int peek() override {
int data = _stream.peek();
Serial.print("Peek: ");
Serial.println(data);
return data;
}
void flush() override {
_stream.flush();
Serial.println("Flush");
}
size_t write(uint8_t data) override {
Serial.write(data);
return _stream.write(data);
}
size_t write(const uint8_t *buffer, size_t size) override {
for (size_t i = 0; i < size; i++) {
Serial.print("Write: ");
Serial.println(buffer[i]);
}
return _stream.write(buffer, size);
}
private:
Stream &_stream;
};
/// @brief Prints free heap, allocatbale heap and free stack
static void printHeap() {
const uint32_t freeStack = uxTaskGetStackHighWaterMark(NULL);
Serial.printf("Free heap: %d allocatable: %d stack: %d\n", ESP.getFreeHeap(), ESP.getMaxAllocHeap(), freeStack);
}
/// @brief Prints the maximum continuous heap space
static void printLargestFreeBlock() {
Serial.println("Maximum Continuous Heap Space: " + String(heap_caps_get_largest_free_block(MALLOC_CAP_DEFAULT)));
}
/// @brief Do a GET request to the given url and fill the given json with the response
/// @param url Request URL
/// @param json Json document to fill
/// @param timeout Request timeout
/// @param redirects Redirects handling
/// @return True on success, false on error (httpCode != 200 || deserialization error)
static bool httpGetJson(String &url, JsonDocument &json, const uint16_t timeout, JsonDocument *filter = nullptr) //, const followRedirects_t redirects = followRedirects_t::HTTPC_DISABLE_FOLLOW_REDIRECTS)
{
HTTPClient http;
http.begin(url);
http.setTimeout(timeout);
// http.setFollowRedirects(redirects);
const int httpCode = http.GET();
if (httpCode != 200) {
http.end();
wsErr("http " + httpCode);
return false;
}
DeserializationError error;
if (filter) {
error = deserializeJson(json, http.getString(), DeserializationOption::Filter(*filter));
} else {
error = deserializeJson(json, http.getString());
}
http.end();
if (error) {
Serial.println(error.c_str());
return false;
}
return true;
}
/// @brief Check if the given string is empty or contains "null"
///
/// @param str String to check
/// @return True if empty or null, false if not
static inline bool isEmptyOrNull(const String &str) {
return str.isEmpty() || str == "null";
}
} // namespace util

View File

@@ -9,9 +9,9 @@ void doJsonUpload(AsyncWebServerRequest *request);
extern void networkProcess(void *parameter);
void wsLog(String text);
void wsErr(String text);
void wsSendTaginfo(uint8_t *mac, uint8_t syncMode);
void wsSendTaginfo(const uint8_t *mac, uint8_t syncMode);
void wsSendSysteminfo();
void wsSendAPitem(struct APlist* apitem);
void wsSendAPitem(struct APlist *apitem);
void wsSerial(String text);
uint8_t wsClientCount();

View File

@@ -34,73 +34,6 @@ build_flags =
-D ILI9341_DRIVER
-D SMOOTH_FONT
; ----------------------------------------------------------------------------------------
; !!! this configuration expects an SONOFF ZB Bridge-P
;
; ----------------------------------------------------------------------------------------
[env:Sonoff_zb_bridge_P_AP]
board = esp32dev
board_build.partitions = default.csv
build_flags =
${env.build_flags}
-D CORE_DEBUG_LEVEL=0
-D POWER_NO_SOFT_POWER
; -DBOARD_HAS_PSRAM
; -mfix-esp32-psram-cache-issue
-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
-D FLASHER_AP_TXD=19
-D FLASHER_AP_RXD=23
-D FLASHER_LED=2
build_src_filter =
+<*>-<usbflasher.cpp>-<swd.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 UniRfInterface PCB with EBYTE E79 CC1352P Module
;
; ----------------------------------------------------------------------------------------
[env:OpenEPaperLink_CC1352P]
platform = https://github.com/platformio/platform-espressif32.git
board=lolin_s2_mini
board_build.partitions = default.csv
build_unflags =
-D CONFIG_MBEDTLS_INTERNAL_MEM_ALLOC=y
build_flags =
${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 BOARD_HAS_PSRAM
-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=21
-D FLASHER_AP_POWER={-1} ;this board has no soft power control
-D FLASHER_AP_TXD=17
-D FLASHER_AP_RXD=16
-D FLASHER_AP_TEST=-1
-D FLASHER_LED=2
-D FLASHER_RGB_LED=-1
build_src_filter =
+<*>-<usbflasher.cpp>-<swd.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 Mini_AP
;
; ----------------------------------------------------------------------------------------
@@ -317,3 +250,162 @@ build_flags =
-D SMOOTH_FONT
build_src_filter =
+<*>-<usbflasher.cpp>-<swd.cpp>
; ----------------------------------------------------------------------------------------
; !!! this configuration expects an ESP32-S3 16MB Flash 8MB RAM
;
; ----------------------------------------------------------------------------------------
[env:ESP32_S3_16_8_YELLOW_AP]
board = esp32-s3-devkitc-1
board_build.partitions = large_spiffs_16MB.csv
build_unflags =
-D ARDUINO_USB_MODE=1
-D CONFIG_MBEDTLS_INTERNAL_MEM_ALLOC=y
-D ILI9341_DRIVER
build_flags =
${env.build_flags}
-D YELLOW_IPS_AP
-D CORE_DEBUG_LEVEL=0
-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 FLASHER_AP_SS=-1
-D FLASHER_AP_CLK=-1
-D FLASHER_AP_MOSI=-1
-D FLASHER_AP_MISO=-1
-D FLASHER_AP_RESET=47
-D FLASHER_AP_POWER={-1}
-D FLASHER_AP_TEST=-1
-D FLASHER_AP_TXD=17
-D FLASHER_AP_RXD=18
-D FLASHER_LED=14
-D ST7789_DRIVER
-D TFT_WIDTH=170
-D TFT_HEIGHT=320
-D TFT_MISO=-1
-D TFT_MOSI=13
-D TFT_SCLK=12
-D TFT_CS=10
-D TFT_DC=11
-D TFT_RST=1
-D TFT_RGB_ORDER=TFT_BGR
-D USE_HSPI_PORT
-D LOAD_FONT2
build_src_filter =
+<*>-<usbflasher.cpp>-<swd.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 SONOFF ZB Bridge-P
;
; ----------------------------------------------------------------------------------------
[env:Sonoff_zb_bridge_P_AP]
board = esp32dev
board_build.partitions = default.csv
build_flags =
${env.build_flags}
-D CORE_DEBUG_LEVEL=0
-D POWER_NO_SOFT_POWER
; -DBOARD_HAS_PSRAM
; -mfix-esp32-psram-cache-issue
-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
-D FLASHER_AP_TXD=19
-D FLASHER_AP_RXD=23
-D FLASHER_LED=2
build_src_filter =
+<*>-<usbflasher.cpp>-<swd.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 UniRfInterface PCB with EBYTE E79 CC1352P Module
;
; ----------------------------------------------------------------------------------------
[env:OpenEPaperLink_CC1352P]
platform = https://github.com/platformio/platform-espressif32.git
board=lolin_s2_mini
board_build.partitions = default.csv
build_unflags =
-D CONFIG_MBEDTLS_INTERNAL_MEM_ALLOC=y
build_flags =
${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 BOARD_HAS_PSRAM
-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=21
-D FLASHER_AP_POWER={-1} ;this board has no soft power control
-D FLASHER_AP_TXD=17
-D FLASHER_AP_RXD=16
-D FLASHER_AP_TEST=-1
-D FLASHER_LED=2
-D FLASHER_RGB_LED=-1
build_src_filter =
+<*>-<usbflasher.cpp>-<swd.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 is work in progress, do not use for now
;
; ----------------------------------------------------------------------------------------
[env:OutdoorAP]
board = esp32-s3-devkitc-1
board_build.partitions = 32MB_partition table.csv
build_unflags =
-D ARDUINO_USB_MODE=1
-D CONFIG_MBEDTLS_INTERNAL_MEM_ALLOC=y
build_flags =
${env.build_flags}
-D OutdoorAP
-D HAS_RGB_LED
-D CORE_DEBUG_LEVEL=0
-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 FLASHER_AP_SS=-1
-D FLASHER_AP_CLK=-1
-D FLASHER_AP_MOSI=-1
-D FLASHER_AP_MISO=-1
-D FLASHER_AP_RESET=47
-D FLASHER_AP_POWER={-1}
-D FLASHER_AP_TEST=-1
-D FLASHER_AP_TXD=17
-D FLASHER_AP_RXD=18
-D FLASHER_LED=21
-D FLASHER_RGB_LED=38
build_src_filter =
+<*>-<usbflasher.cpp>-<swd.cpp>
board_build.flash_mode=opi
board_build.arduino.memory_type = opi_opi
board_build.psram_type=qspi_opi
board_upload.maximum_size = 16777216
board_upload.maximum_ram_size = 327680
board_upload.flash_size = 32MB
#upload_flags = --no-stub

View File

@@ -129,6 +129,13 @@ void SPIFFSEditor::handleUpload(AsyncWebServerRequest *request, const String &fi
if (filename.c_str()[0] != '/') {
request->_tempFile = _fs.open("/" + filename, "w");
} else {
int lastSlash = filename.lastIndexOf('/');
if (lastSlash != -1) {
String folderPath = filename.substring(0, lastSlash);
if (!_fs.exists(folderPath)) {
_fs.mkdir(folderPath);
}
}
request->_tempFile = _fs.open(filename, "w");
}
_startTime = millis();

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,96 @@
#ifdef YELLOW_IPS_AP
#include <Arduino.h>
#include <FS.h>
#include <TFT_eSPI.h>
#include <WiFi.h>
#include "commstructs.h"
#include "newproto.h"
#include "storage.h"
#include "tag_db.h"
TFT_eSPI tft2 = TFT_eSPI();
bool first_run = 0;
time_t last_update = 0;
time_t last_checkin = 0;
int32_t tftid = -1;
int32_t findId(uint8_t mac[8]) {
for (uint32_t c = 0; c < tagDB.size(); c++) {
tagRecord* tag = nullptr;
tag = tagDB.at(c);
if (memcmp(tag->mac, mac, 8) == 0) {
return c;
}
}
return -1;
}
void sendAvail(uint8_t wakeupReason) {
struct espAvailDataReq eadr = {0};
uint8_t mac[6];
WiFi.macAddress(mac);
memcpy(&eadr.src, mac, 6);
eadr.adr.lastPacketRSSI = WiFi.RSSI();
eadr.adr.currentChannel = WiFi.channel();
eadr.adr.hwType = 0xE0;
eadr.adr.wakeupReason = wakeupReason;
eadr.adr.capabilities = 0;
eadr.adr.tagSoftwareVersion = 0;
eadr.adr.customMode = 0;
processDataReq(&eadr, true);
if (wakeupReason) tftid = findId(eadr.src);
}
void yellow_ap_display_init(void) {
tft2.init();
tft2.setRotation(3);
tft2.fillScreen(TFT_BLACK);
tft2.setCursor(0, 0, 2);
tft2.setTextColor(TFT_WHITE);
tft2.println(" Init\n");
}
void yellow_ap_display_loop(void) {
if (millis() - last_checkin >= 60000) {
sendAvail(0);
last_checkin = millis();
}
if (millis() - last_update >= 1000) {
if (first_run == 0) {
sendAvail(0xFC);
first_run = 1;
}
// if ((uint32_t)WiFi.localIP() == (uint32_t)0) {}
tagRecord* tag = nullptr;
tag = tagDB.at(tftid);
if (tag->pending) {
String filename = tag->filename;
fs::File file = contentFS->open(filename);
if (!file) {
Serial.print("No current file. Canceling request\n");
prepareCancelPending(tag->mac);
return;
}
TFT_eSprite spr = TFT_eSprite(&tft2);
if (tag->len == tft2.width() * tft2.height() * 2) spr.setColorDepth(16);
if (tag->len == tft2.width() * tft2.height()) spr.setColorDepth(8);
spr.createSprite(tft2.width(), tft2.height());
void* spriteData = spr.getPointer();
size_t bytesRead = file.readBytes((char*)spriteData, spr.width() * spr.height() * 2);
file.close();
spr.pushSprite(0,0);
struct espXferComplete xfc = {0};
memcpy(xfc.src, tag->mac, 8);
processXferComplete(&xfc, true);
}
last_update = millis();
}
}
#endif

View File

@@ -5,6 +5,7 @@
#include <FastLED.h>
#endif
#include "leds.h"
#include "settings.h"
#include "tag_db.h"
@@ -32,24 +33,6 @@ struct ledInstruction {
bool reQueue = false;
};
const uint8_t PROGMEM gamma8[] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2,
2, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 5, 5, 5,
5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 9, 9, 9, 10,
10, 10, 11, 11, 11, 12, 12, 13, 13, 13, 14, 14, 15, 15, 16, 16,
17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22, 23, 24, 24, 25,
25, 26, 27, 27, 28, 29, 29, 30, 31, 32, 32, 33, 34, 35, 35, 36,
37, 38, 39, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 50,
51, 52, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 66, 67, 68,
69, 70, 72, 73, 74, 75, 77, 78, 79, 81, 82, 83, 85, 86, 87, 89,
90, 92, 93, 95, 96, 98, 99, 101, 102, 104, 105, 107, 109, 110, 112, 114,
115, 117, 119, 120, 122, 124, 126, 127, 129, 131, 133, 135, 137, 138, 140, 142,
144, 146, 148, 150, 152, 154, 156, 158, 160, 162, 164, 167, 169, 171, 173, 175,
177, 180, 182, 184, 186, 189, 191, 193, 196, 198, 200, 203, 205, 208, 210, 213,
215, 218, 220, 223, 225, 228, 231, 233, 236, 239, 241, 244, 247, 249, 252, 255};
#ifdef HAS_RGB_LED
void addToRGBQueue(struct ledInstructionRGB* rgb, bool requeue) {
@@ -197,7 +180,11 @@ void addFadeMono(uint8_t value) {
}
void showMono(uint8_t brightness) {
#ifdef CONFIG_IDF_TARGET_ESP32S2
ledcWrite(7, gamma8[brightness]);
#else
ledcWrite(7, 255 - gamma8[brightness]);
#endif
}
void quickBlink(uint8_t repeat) {
@@ -335,7 +322,7 @@ void ledTask(void* parameter) {
showMono(monoled->value);
}
} else {
//monoIdleStep();
// monoIdleStep();
}
} else {
if (monoled->fadeTime) {

View File

@@ -3,12 +3,11 @@
#include <WiFi.h>
#include <time.h>
#include "storage.h"
#include "contentmanager.h"
#include "flasher.h"
#include "makeimage.h"
#include "serialap.h"
#include "settings.h"
#include "storage.h"
#include "system.h"
#include "tag_db.h"
@@ -19,6 +18,7 @@
#include "language.h"
#include "leds.h"
#include "udp.h"
#include "util.h"
#include "web.h"
void pinTest();
@@ -35,12 +35,15 @@ void delayedStart(void* parameter) {
void timeTask(void* parameter) {
wsSendSysteminfo();
Serial.printf("Free heap: %.2f kB\n", ESP.getFreeHeap() / 1024.0f);
util::printHeap();
while (1) {
time_t now;
time(&now);
if (now % 5 == 0 || apInfo.state != AP_STATE_ONLINE || config.runStatus != RUNSTATUS_RUN) wsSendSysteminfo();
if (now % 5 == 0 || apInfo.state != AP_STATE_ONLINE || config.runStatus != RUNSTATUS_RUN) {
wsSendSysteminfo();
checkVars();
}
if (now % 300 == 6 && config.runStatus != RUNSTATUS_STOP) saveDB("/current/tagDB.json");
if (apInfo.state == AP_STATE_ONLINE) contentRunner();
@@ -51,6 +54,10 @@ void timeTask(void* parameter) {
void setup() {
Serial.begin(115200);
Serial.print(">\n");
#ifdef YELLOW_IPS_AP
extern void yellow_ap_display_init(void);
yellow_ap_display_init();
#endif
xTaskCreate(ledTask, "ledhandler", 2000, NULL, 2, NULL);
vTaskDelay(10 / portTICK_PERIOD_MS);
@@ -154,8 +161,13 @@ void loop() {
while (1) {
// pinTest();
while (1) {
#ifdef YELLOW_IPS_AP
extern void yellow_ap_display_loop(void);
yellow_ap_display_loop();
#else
vTaskDelay(10000 / portTICK_PERIOD_MS);
// pinTest();
#endif
}
#ifdef OPENEPAPERLINK_PCB
if (extTagConnected()) {

View File

@@ -1,11 +1,14 @@
#include <Arduino.h>
#include <FS.h>
#include "storage.h"
#include <TFT_eSPI.h>
#include <TJpg_Decoder.h>
#include <makeimage.h>
#include <web.h>
#include "leds.h"
#include "storage.h"
#include "util.h"
TFT_eSPI tft = TFT_eSPI();
TFT_eSprite spr = TFT_eSprite(&tft);
@@ -24,7 +27,7 @@ void jpg2buffer(String filein, String fileout, imgParam &imageParams) {
filein = "/" + filein;
}
TJpgDec.getFsJpgSize(&w, &h, filein, *contentFS);
if (w==0 && h==0) {
if (w == 0 && h == 0) {
wsErr("invalid jpg");
return;
}
@@ -38,10 +41,10 @@ void jpg2buffer(String filein, String fileout, imgParam &imageParams) {
spr.createSprite(w, h);
if (spr.getPointer() == nullptr) {
wsErr("low on memory. Fallback to 1bpp");
Serial.println("Maximum Continuous Heap Space: " + String(heap_caps_get_largest_free_block(MALLOC_CAP_DEFAULT)));
util::printLargestFreeBlock();
spr.setColorDepth(1);
spr.setBitmapColor(TFT_WHITE, TFT_BLACK);
imageParams.bpp = 1;
imageParams.bufferbpp = 1;
spr.createSprite(w, h);
}
if (spr.getPointer() == nullptr) {
@@ -63,38 +66,31 @@ struct Color {
};
struct Error {
float r;
float g;
float b;
int32_t r;
int32_t g;
int32_t b;
};
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 + 1 * b_diff * b_diff;
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);
int32_t r_diff = gamma8[c1.r] + e1.r - gamma8[c2.r];
int32_t g_diff = gamma8[c1.g] + e1.g - gamma8[c2.g];
int32_t b_diff = gamma8[c1.b] + e1.b - gamma8[c2.b];
return 22 * r_diff * r_diff + 50 * g_diff * g_diff + 20 * b_diff * b_diff;
}
uint8_t *spr2color(TFT_eSprite &spr, imgParam &imageParams, size_t *buffer_size, bool is_red) {
bool dither = true;
void spr2color(TFT_eSprite &spr, imgParam &imageParams, uint8_t *buffer, size_t buffer_size, bool is_red) {
uint8_t rotate = imageParams.rotate;
long bufw = spr.width(), bufh = spr.height();
if (bufw > bufh && bufw!=400 && bufh!=300 && bufw!=800 && bufh!=480 && bufw!=640 && bufh!=384) {
if (imageParams.rotatebuffer) {
rotate = (rotate + 3) % 4;
bufw = spr.height();
bufh = spr.width();
}
*buffer_size = (bufw * bufh) / 8;
uint8_t *buffer = (uint8_t*) malloc(*buffer_size);
if (!buffer) {
Serial.println("Failed to allocate buffer");
Serial.println("Maximum Continuous Heap Space: " + String(heap_caps_get_largest_free_block(MALLOC_CAP_DEFAULT)));
return nullptr;
}
memset(buffer, 0, *buffer_size);
memset(buffer, 0, buffer_size);
std::vector<Color> palette = {
{255, 255, 255}, // White
@@ -107,7 +103,7 @@ uint8_t *spr2color(TFT_eSprite &spr, imgParam &imageParams, size_t *buffer_size,
Serial.println("rendering with gray");
}
int num_colors = palette.size();
if (imageParams.bpp == 1) num_colors = 2;
if (imageParams.bufferbpp == 1) num_colors = 2;
Color color;
Error *error_bufferold = new Error[bufw + 4];
Error *error_buffernew = new Error[bufw + 4];
@@ -134,6 +130,7 @@ uint8_t *spr2color(TFT_eSprite &spr, imgParam &imageParams, size_t *buffer_size,
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;
@@ -147,12 +144,12 @@ uint8_t *spr2color(TFT_eSprite &spr, imgParam &imageParams, size_t *buffer_size,
// this looks a bit ugly, but it's performing better than shorter notations
switch (best_color_index) {
case 1:
if(!is_red)
if (!is_red)
buffer[byteIndex] |= (1 << bitIndex);
break;
case 2:
imageParams.hasRed = true;
if(is_red)
if (is_red)
buffer[byteIndex] |= (1 << bitIndex);
break;
case 3:
@@ -163,39 +160,39 @@ uint8_t *spr2color(TFT_eSprite &spr, imgParam &imageParams, size_t *buffer_size,
if (imageParams.dither) {
Error error = {
static_cast<float>(color.r) + error_bufferold[x].r - static_cast<float>(palette[best_color_index].r),
static_cast<float>(color.g) + error_bufferold[x].g - static_cast<float>(palette[best_color_index].g),
static_cast<float>(color.b) + error_bufferold[x].b - static_cast<float>(palette[best_color_index].b) };
color.r + error_bufferold[x].r - palette[best_color_index].r,
color.g + error_bufferold[x].g - palette[best_color_index].g,
color.b + error_bufferold[x].b - palette[best_color_index].b};
// Burkes Dithering
error_buffernew[x].r += error.r / 4.0f;
error_buffernew[x].g += error.g / 4.0f;
error_buffernew[x].b += error.b / 4.0f;
error_buffernew[x].r += error.r >> 2;
error_buffernew[x].g += error.g >> 2;
error_buffernew[x].b += error.b >> 2;
if (x > 0) {
error_buffernew[x - 1].r += error.r / 8.0f;
error_buffernew[x - 1].g += error.g / 8.0f;
error_buffernew[x - 1].b += error.b / 8.0f;
error_buffernew[x - 1].r += error.r >> 3;
error_buffernew[x - 1].g += error.g >> 3;
error_buffernew[x - 1].b += error.b >> 3;
}
if (x > 1) {
error_buffernew[x - 2].r += error.r / 16.0f;
error_buffernew[x - 2].g += error.g / 16.0f;
error_buffernew[x - 2].b += error.b / 16.0f;
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 + 1].r += error.r / 8.0f;
error_buffernew[x + 1].g += error.g / 8.0f;
error_buffernew[x + 1].b += error.b / 8.0f;
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 / 4.0f;
error_bufferold[x + 1].g += error.g / 4.0f;
error_bufferold[x + 1].b += error.b / 4.0f;
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 + 2].r += error.r / 16.0f;
error_buffernew[x + 2].g += error.g / 16.0f;
error_buffernew[x + 2].b += error.b / 16.0f;
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 + 2].r += error.r / 8.0f;
error_bufferold[x + 2].g += error.g / 8.0f;
error_bufferold[x + 2].b += error.b / 8.0f;
error_bufferold[x + 2].r += error.r >> 3;
error_bufferold[x + 2].g += error.g >> 3;
error_bufferold[x + 2].b += error.b >> 3;
}
}
memcpy(error_bufferold, error_buffernew, bufw * sizeof(Error));
@@ -204,30 +201,53 @@ uint8_t *spr2color(TFT_eSprite &spr, imgParam &imageParams, size_t *buffer_size,
delete[] error_buffernew;
delete[] error_bufferold;
return buffer;
return;
}
void spr2buffer(TFT_eSprite &spr, String &fileout, imgParam &imageParams) {
long t = millis();
Storage.begin();
fs::File f_out = contentFS->open(fileout, "w");
size_t bufferSize;
uint8_t *blackBuffer = (uint8_t*) spr2color(spr, imageParams, &bufferSize, false);
if(!blackBuffer)
#ifdef YELLOW_IPS_AP
if (fileout == "direct") {
tft.setRotation(3);
spr.pushSprite(0, 0);
return;
f_out.write(blackBuffer, bufferSize);
free(blackBuffer);
if (imageParams.hasRed) {
uint8_t *redBuffer = (uint8_t*) spr2color(spr, imageParams, &bufferSize, true);
if(!redBuffer) {
imageParams.hasRed = false;
return;
}
f_out.write(redBuffer, bufferSize);
free(redBuffer);
}
}
#endif
fs::File f_out = contentFS->open(fileout, "w");
switch (imageParams.bpp) {
case 1:
case 2: {
long bufw = spr.width(), bufh = spr.height();
size_t buffer_size = (bufw * bufh) / 8;
#ifdef BOARD_HAS_PSRAM
uint8_t *buffer = (uint8_t *)ps_malloc(buffer_size);
#else
uint8_t *buffer = (uint8_t *)malloc(buffer_size);
#endif
if (!buffer) {
Serial.println("Failed to allocate buffer");
util::printLargestFreeBlock();
return;
}
spr2color(spr, imageParams, buffer, buffer_size, false);
f_out.write(buffer, buffer_size);
if (imageParams.hasRed && imageParams.bpp > 1) {
spr2color(spr, imageParams, buffer, buffer_size, true);
f_out.write(buffer, buffer_size);
}
free(buffer);
} break;
case 16: {
size_t spriteDataSize = (spr.getColorDepth() == 1) ? (spr.width() * spr.height() / 8) : ((spr.getColorDepth() == 8) ? (spr.width() * spr.height()) : ((spr.getColorDepth() == 16) ? (spr.width() * spr.height() * 2) : 0));
f_out.write((const uint8_t *)spr.getPointer(), spriteDataSize);
} break;
}
f_out.close();
Serial.println("finished writing buffer " + String(millis() - t) + "ms");

View File

@@ -3,15 +3,13 @@
#include <Arduino.h>
#include <FS.h>
#include <HTTPClient.h>
#include "storage.h"
#include <MD5Builder.h>
#include <makeimage.h>
#include <time.h>
#include "storage.h"
#include "commstructs.h"
#include "serialap.h"
#include "settings.h"
#include "storage.h"
#include "system.h"
#include "tag_db.h"
#include "udp.h"
@@ -48,7 +46,7 @@ uint8_t* getDataForFile(fs::File* file) {
return ret;
}
void prepareCancelPending(uint8_t dst[8]) {
void prepareCancelPending(const uint8_t dst[8]) {
struct pendingData pending = {0};
memcpy(pending.targetMac, dst, 8);
sendCancelPending(&pending);
@@ -64,7 +62,7 @@ void prepareCancelPending(uint8_t dst[8]) {
wsSendTaginfo(dst, SYNC_TAGSTATUS);
}
void prepareIdleReq(uint8_t* dst, uint16_t nextCheckin) {
void prepareIdleReq(const uint8_t* dst, uint16_t nextCheckin) {
if (nextCheckin > config.maxsleep) nextCheckin = config.maxsleep;
if (nextCheckin > 0) {
struct pendingData pending = {0};
@@ -80,9 +78,8 @@ void prepareIdleReq(uint8_t* dst, uint16_t nextCheckin) {
}
}
void prepareDataAvail(uint8_t* data, uint16_t len, uint8_t dataType, uint8_t* dst) {
tagRecord* taginfo = nullptr;
taginfo = tagRecord::findByMAC(dst);
void prepareDataAvail(uint8_t* data, uint16_t len, uint8_t dataType, const uint8_t* dst) {
tagRecord* taginfo = tagRecord::findByMAC(dst);
if (taginfo == nullptr) {
wsErr("Tag not found, this shouldn't happen.");
return;
@@ -118,10 +115,18 @@ void prepareDataAvail(uint8_t* data, uint16_t len, uint8_t dataType, uint8_t* ds
wsSendTaginfo(dst, SYNC_TAGSTATUS);
}
bool prepareDataAvail(String* filename, uint8_t dataType, uint8_t* dst, uint16_t nextCheckin) {
bool prepareDataAvail(String* filename, uint8_t dataType, const uint8_t* dst, uint16_t nextCheckin) {
if (nextCheckin > config.maxsleep) nextCheckin = config.maxsleep;
if (wsClientCount() && config.stopsleep == 1) nextCheckin=0;
if (wsClientCount() && config.stopsleep == 1) nextCheckin = 0;
#ifdef YELLOW_IPS_AP
if (*filename == "direct") {
char dst_path[64];
sprintf(dst_path, "/current/%02X%02X%02X%02X%02X%02X%02X%02X.raw\0", dst[7], dst[6], dst[5], dst[4], dst[3], dst[2], dst[1], dst[0]);
contentFS->remove(dst_path);
return true;
}
#endif
tagRecord* taginfo = nullptr;
taginfo = tagRecord::findByMAC(dst);
if (taginfo == nullptr) {
@@ -305,7 +310,7 @@ void prepareExternalDataAvail(struct pendingData* pending, IPAddress remoteIP) {
taginfo->dataType = pending->availdatainfo.dataType;
taginfo->pending = true;
taginfo->len = len;
}
}
}
http.end();
break;
@@ -482,9 +487,12 @@ void processDataReq(struct espAvailDataReq* eadr, bool local) {
if (local) {
const char* reason = "";
if (eadr->adr.wakeupReason == WAKEUP_REASON_FIRSTBOOT) reason = "Booting";
else if (eadr->adr.wakeupReason == WAKEUP_REASON_NETWORK_SCAN) reason = "Network scan";
else if (eadr->adr.wakeupReason == WAKEUP_REASON_WDT_RESET) reason = "Watchdog reset";
if (eadr->adr.wakeupReason == WAKEUP_REASON_FIRSTBOOT)
reason = "Booting";
else if (eadr->adr.wakeupReason == WAKEUP_REASON_NETWORK_SCAN)
reason = "Network scan";
else if (eadr->adr.wakeupReason == WAKEUP_REASON_WDT_RESET)
reason = "Watchdog reset";
sprintf(buffer, "%02X%02X%02X%02X%02X%02X%02X%02X %s", eadr->src[7], eadr->src[6], eadr->src[5], eadr->src[4], eadr->src[3], eadr->src[2], eadr->src[1], eadr->src[0], reason);
logLine(buffer);
}
@@ -507,11 +515,11 @@ void processDataReq(struct espAvailDataReq* eadr, bool local) {
}
if (local) {
sprintf(buffer, "<ADR %02X%02X%02X%02X%02X%02X%02X%02X\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);
} else {
sprintf(buffer, "<REMOTE ADR %02X%02X%02X%02X%02X%02X%02X%02X\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]);
// sprintf(buffer, "<REMOTE ADR %02X%02X%02X%02X%02X%02X%02X%02X\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);
wsSendTaginfo(eadr->src, SYNC_TAGSTATUS);
if (local) {
udpsync.netProcessDataReq(eadr);
@@ -532,7 +540,7 @@ void refreshAllPending() {
}
};
void updateContent(uint8_t* dst) {
void updateContent(const uint8_t* dst) {
tagRecord* taginfo = nullptr;
taginfo = tagRecord::findByMAC(dst);
if (taginfo != nullptr) {
@@ -557,7 +565,7 @@ void setAPchannel() {
}
}
bool sendAPSegmentedData(uint8_t* dst, String data, uint16_t icons, bool inverted, bool local) {
bool sendAPSegmentedData(const uint8_t* dst, String data, uint16_t icons, bool inverted, bool local) {
struct pendingData pending = {0};
memcpy(pending.targetMac, dst, 8);
pending.availdatainfo.dataType = DATATYPE_UK_SEGMENTED;
@@ -577,7 +585,7 @@ bool sendAPSegmentedData(uint8_t* dst, String data, uint16_t icons, bool inverte
}
}
bool showAPSegmentedInfo(uint8_t* dst, bool local) {
bool showAPSegmentedInfo(const uint8_t* dst, bool local) {
struct pendingData pending = {0};
memcpy(pending.targetMac, dst, 8);
pending.availdatainfo.dataType = DATATYPE_UK_SEGMENTED;
@@ -597,7 +605,7 @@ bool showAPSegmentedInfo(uint8_t* dst, bool local) {
}
}
bool sendTagCommand(uint8_t* dst, uint8_t cmd, bool local) {
bool sendTagCommand(const uint8_t* dst, uint8_t cmd, bool local) {
struct pendingData pending = {0};
memcpy(pending.targetMac, dst, 8);
pending.availdatainfo.dataType = DATATYPE_COMMAND_DATA;
@@ -673,7 +681,6 @@ bool checkMirror(struct tagRecord* taginfo, struct pendingData* pending) {
JsonObject cfgobj = doc.as<JsonObject>();
uint8_t mac[8] = {0};
if (hex2mac(cfgobj["mac"], mac) && memcmp(mac, taginfo->mac, sizeof(mac)) == 0) {
if (taginfo->data == nullptr) {
fs::File file = contentFS->open(taginfo->filename);
if (!file) {
@@ -687,7 +694,7 @@ bool checkMirror(struct tagRecord* taginfo, struct pendingData* pending) {
taginfo2->expectedNextCheckin = taginfo->expectedNextCheckin;
taginfo2->filename = taginfo->filename;
taginfo2->len = taginfo->len;
taginfo2->data = taginfo->data; // copy buffer pointer
taginfo2->data = taginfo->data; // copy buffer pointer
taginfo2->dataType = taginfo->dataType;
taginfo2->pending = true;
taginfo2->nextupdate = 3216153600;

View File

@@ -4,11 +4,12 @@
#include <ArduinoJson.h>
#include <FS.h>
#include <HTTPClient.h>
#include "storage.h"
#include <MD5Builder.h>
#include <Update.h>
#include "storage.h"
#include "tag_db.h"
#include "util.h"
#include "web.h"
#ifndef BUILD_ENV_NAME
@@ -38,7 +39,7 @@ void handleSysinfoRequest(AsyncWebServerRequest* request) {
doc["flashsize"] = ESP.getFlashChipSize();
doc["rollback"] = Update.canRollBack();
size_t bufferSize = measureJson(doc) + 1;
const size_t bufferSize = measureJson(doc) + 1;
AsyncResponseStream* response = request->beginResponseStream("application/json", bufferSize);
serializeJson(doc, *response);
request->send(response);
@@ -50,7 +51,7 @@ void handleCheckFile(AsyncWebServerRequest* request) {
return;
}
String filePath = request->getParam("path")->value();
const String filePath = request->getParam("path")->value();
File file = contentFS->open(filePath, "r");
if (!file) {
StaticJsonDocument<64> doc;
@@ -62,13 +63,13 @@ void handleCheckFile(AsyncWebServerRequest* request) {
return;
}
size_t fileSize = file.size();
const size_t fileSize = file.size();
MD5Builder md5;
md5.begin();
md5.addStream(file, fileSize);
md5.calculate();
String md5Hash = md5.toString();
const String md5Hash = md5.toString();
file.close();
@@ -77,22 +78,21 @@ void handleCheckFile(AsyncWebServerRequest* request) {
doc["md5"] = md5Hash;
String jsonResponse;
serializeJson(doc, jsonResponse);
request->send(200, "application/json", jsonResponse);
}
void handleGetExtUrl(AsyncWebServerRequest* request) {
if (request->hasParam("url")) {
String url = request->getParam("url")->value();
const String url = request->getParam("url")->value();
HTTPClient http;
http.begin(url);
http.setConnectTimeout(5000);
http.setFollowRedirects(HTTPC_FORCE_FOLLOW_REDIRECTS);
int httpResponseCode = http.GET();
const int httpResponseCode = http.GET();
if (httpResponseCode > 0) {
Serial.println(httpResponseCode);
String contentType = http.header("Content-Type");
size_t contentLength = http.getSize();
const String contentType = http.header("Content-Type");
const size_t contentLength = http.getSize();
if (contentLength > 0) {
String content = http.getString();
AsyncWebServerResponse* response = request->beginResponse(200, contentType, content);
@@ -169,7 +169,7 @@ void firmwareUpdateTask(void* parameter) {
} else {
const char* url = params->url.c_str();
const char* md5 = params->md5.c_str();
size_t size = params->size;
const size_t size = params->size;
updateFirmware(url, md5, size);
}
@@ -177,9 +177,8 @@ void firmwareUpdateTask(void* parameter) {
vTaskDelete(NULL);
}
void updateFirmware(const char* url, const char* expectedMd5, size_t size) {
uint32_t freeStack = uxTaskGetStackHighWaterMark(NULL);
Serial.printf("Free heap: %d allocatable: %d stack: %d\n", ESP.getFreeHeap(), ESP.getMaxAllocHeap(), freeStack);
void updateFirmware(const char* url, const char* expectedMd5, const size_t size) {
util::printHeap();
config.runStatus = RUNSTATUS_STOP;
vTaskDelay(3000 / portTICK_PERIOD_MS);
@@ -194,11 +193,9 @@ void updateFirmware(const char* url, const char* expectedMd5, size_t size) {
httpClient.begin(url);
httpClient.setFollowRedirects(HTTPC_FORCE_FOLLOW_REDIRECTS);
freeStack = uxTaskGetStackHighWaterMark(NULL);
Serial.printf("Free heap: %d allocatable: %d stack: %d\n", ESP.getFreeHeap(), ESP.getMaxAllocHeap(), freeStack);
int httpCode = httpClient.GET();
freeStack = uxTaskGetStackHighWaterMark(NULL);
Serial.printf("Free heap: %d allocatable: %d stack: %d\n", ESP.getFreeHeap(), ESP.getMaxAllocHeap(), freeStack);
util::printHeap();
const int httpCode = httpClient.GET();
util::printHeap();
if (httpCode == HTTP_CODE_OK) {
if (Update.begin(size)) {
@@ -215,7 +212,7 @@ void updateFirmware(const char* url, const char* expectedMd5, size_t size) {
}
});
size_t written = Update.writeStream(httpClient.getStream());
const size_t written = Update.writeStream(httpClient.getStream());
if (written == httpClient.getSize()) {
if (Update.end(true)) {
wsSerial("Firmware update successful");
@@ -248,7 +245,7 @@ void updateFirmware(const char* url, const char* expectedMd5, size_t size) {
void handleRollback(AsyncWebServerRequest* request) {
if (Update.canRollBack()) {
bool rollbackSuccess = Update.rollBack();
const bool rollbackSuccess = Update.rollBack();
if (rollbackSuccess) {
request->send(200, "Rollback successful");
wsSerial("Rollback successful");
@@ -276,7 +273,7 @@ void handleUpdateActions(AsyncWebServerRequest* request) {
}
StaticJsonDocument<1000> doc;
DeserializationError error = deserializeJson(doc, file);
JsonArray deleteFiles = doc["deletefile"].as<JsonArray>();
const JsonArray deleteFiles = doc["deletefile"].as<JsonArray>();
for (const auto& filePath : deleteFiles) {
if (contentFS->remove(filePath.as<const char*>())) {
wsSerial("deleted file: " + filePath.as<String>());

View File

@@ -533,17 +533,11 @@ void rxSerialTask(void* parameter) {
}
void ShowAPInfo() {
Serial.printf("| AP Information - type %02X |\n", apInfo.type);
Serial.printf("| Channel | 0x%02X |\n", apInfo.channel);
Serial.printf("| Power | %02X |\n", apInfo.power);
Serial.printf("| MAC | %02X%02X%02X%02X%02X%02X%02X%02X |\n", apInfo.mac[7], apInfo.mac[6], apInfo.mac[5], apInfo.mac[4], apInfo.mac[3], apInfo.mac[2], apInfo.mac[1], apInfo.mac[0]);
Serial.printf("| Version | 0x%04X |\n", apInfo.version);
if (apInfo.type == SOLUM_154_SSD1619 || apInfo.type == SOLUM_29_SSD1619 || apInfo.type == SOLUM_29_UC8151 || apInfo.type == SOLUM_42_SSD1619) {
char macString[50];
sprintf(macString, "%02X%02X%02X%02X%02X%02X%02X%02X", apInfo.mac[7], apInfo.mac[6], apInfo.mac[5], apInfo.mac[4], apInfo.mac[3], apInfo.mac[2], apInfo.mac[1], apInfo.mac[0]);
showIpAddress(macString);
}
Serial.printf("| AP Info - type %02X |\n", apInfo.type);
Serial.printf("| Ch | 0x%02X |\n", apInfo.channel);
Serial.printf("| Power| %02X |\n", apInfo.power);
Serial.printf("| MAC | %02X%02X%02X%02X%02X%02X%02X%02X |\n", apInfo.mac[7], apInfo.mac[6], apInfo.mac[5], apInfo.mac[4], apInfo.mac[3], apInfo.mac[2], apInfo.mac[1], apInfo.mac[0]);
Serial.printf("| Ver | 0x%04X |\n", apInfo.version);
}
void notifySegmentedFlash() {

View File

@@ -3,18 +3,26 @@
#include <Arduino.h>
#include <ArduinoJson.h>
#include <FS.h>
#include <unordered_map>
#include <vector>
#include "storage.h"
#include "language.h"
#include "storage.h"
#include "util.h"
std::vector<tagRecord*> tagDB;
std::unordered_map<std::string, varStruct> varDB;
std::unordered_map<int, HwType> hwdata = {
{0, {152, 152, 0, 2}},
{1, {296, 128, 1, 2}},
{2, {400, 300, 0, 2}}};
Config config;
// SemaphoreHandle_t tagDBOwner;
tagRecord* tagRecord::findByMAC(uint8_t mac[8]) {
for (int16_t c = 0; c < tagDB.size(); c++) {
tagRecord* tagRecord::findByMAC(const uint8_t mac[8]) {
for (int32_t c = 0; c < tagDB.size(); c++) {
tagRecord* tag = nullptr;
tag = tagDB.at(c);
if (memcmp(tag->mac, mac, 8) == 0) {
@@ -24,10 +32,9 @@ tagRecord* tagRecord::findByMAC(uint8_t mac[8]) {
return nullptr;
}
bool deleteRecord(uint8_t mac[8]) {
for (int16_t c = 0; c < tagDB.size(); c++) {
tagRecord* tag = nullptr;
tag = tagDB.at(c);
bool deleteRecord(const uint8_t mac[8]) {
for (int32_t c = 0; c < tagDB.size(); c++) {
tagRecord* tag = tagDB.at(c);
if (memcmp(tag->mac, mac, 8) == 0) {
if (tag->data != nullptr) {
free(tag->data);
@@ -41,7 +48,7 @@ bool deleteRecord(uint8_t mac[8]) {
return false;
}
void mac2hex(uint8_t* mac, char* hexBuffer) {
void mac2hex(const uint8_t* mac, char* hexBuffer) {
sprintf(hexBuffer, "%02X%02X%02X%02X%02X%02X%02X%02X",
mac[7], mac[6], mac[5], mac[4], mac[3], mac[2], mac[1], mac[0]);
}
@@ -55,14 +62,14 @@ bool hex2mac(const String& hexString, uint8_t* mac) {
mac[6] = 0;
mac[7] = 0;
return (sscanf(hexString.c_str(), "%02hhX%02hhX%02hhX%02hhX%02hhX%02hhX",
&mac[5], &mac[4], &mac[3], &mac[2], &mac[1], &mac[0]) == 6);
&mac[5], &mac[4], &mac[3], &mac[2], &mac[1], &mac[0]) == 6);
} else {
return (sscanf(hexString.c_str(), "%02hhX%02hhX%02hhX%02hhX%02hhX%02hhX%02hhX%02hhX",
&mac[7], &mac[6], &mac[5], &mac[4], &mac[3], &mac[2], &mac[1], &mac[0]) == 8);
&mac[7], &mac[6], &mac[5], &mac[4], &mac[3], &mac[2], &mac[1], &mac[0]) == 8);
}
}
String tagDBtoJson(uint8_t mac[8], uint8_t startPos) {
String tagDBtoJson(const uint8_t mac[8], uint8_t startPos) {
DynamicJsonDocument doc(5000);
JsonArray tags = doc.createNestedArray("tags");
@@ -85,15 +92,15 @@ String tagDBtoJson(uint8_t mac[8], uint8_t startPos) {
break;
}
}
if (doc.capacity() - doc.memoryUsage() < doc.memoryUsage()/(c+1) + 150) {
doc["continu"] = c+1;
if (doc.capacity() - doc.memoryUsage() < doc.memoryUsage() / (c + 1) + 500) {
doc["continu"] = c + 1;
break;
}
}
return doc.as<String>();
}
void fillNode(JsonObject &tag, tagRecord* &taginfo) {
void fillNode(JsonObject& tag, tagRecord*& taginfo) {
char hexmac[17];
mac2hex(taginfo->mac, hexmac);
tag["mac"] = String(hexmac);
@@ -137,7 +144,7 @@ void saveDB(String filename) {
file.write('[');
for (int16_t c = 0; c < tagDB.size(); c++) {
for (int32_t c = 0; c < tagDB.size(); c++) {
doc.clear();
tagRecord* taginfo = nullptr;
taginfo = tagDB.at(c);
@@ -199,16 +206,16 @@ void loadDB(String filename) {
taginfo->lastseen = (uint32_t)tag["lastseen"];
taginfo->nextupdate = (uint32_t)tag["nextupdate"];
taginfo->expectedNextCheckin = (uint16_t)tag["nextcheckin"];
if (taginfo->expectedNextCheckin < now - 1800) {
taginfo->expectedNextCheckin = now + 1800;
if (taginfo->expectedNextCheckin < now) {
taginfo->expectedNextCheckin = now + 1800;
}
taginfo->pending = false;
taginfo->alias = tag["alias"].as<String>();
taginfo->contentMode = tag["contentMode"];
taginfo->LQI = tag["LQI"];
taginfo->RSSI = tag["RSSI"];
taginfo->temperature = tag["temperature"];
taginfo->batteryMv = tag["batteryMv"];
taginfo->LQI = tag["LQI"];
taginfo->RSSI = tag["RSSI"];
taginfo->temperature = tag["temperature"];
taginfo->batteryMv = tag["batteryMv"];
taginfo->hwType = (uint8_t)tag["hwType"];
taginfo->wakeupReason = tag["wakeupReason"];
taginfo->capabilities = tag["capabilities"];
@@ -234,8 +241,8 @@ void loadDB(String filename) {
void destroyDB() {
Serial.println("destoying DB");
Serial.printf("before, free heap: %d\n", ESP.getFreeHeap());
for (int16_t c = 0; c < tagDB.size(); c++) {
util::printHeap();
for (uint32_t c = 0; c < tagDB.size(); c++) {
tagRecord* tag = nullptr;
tag = tagDB.at(c);
if (tag->data != nullptr) {
@@ -245,15 +252,32 @@ void destroyDB() {
delete tagDB[c];
tagDB.erase(tagDB.begin() + c);
}
Serial.printf("after, free heap: %d\n", ESP.getFreeHeap());
util::printHeap();
}
uint8_t getTagCount() {
uint8_t tagcount = 0;
for (int16_t c = 0; c < tagDB.size(); c++) {
uint32_t getTagCount() {
uint32_t temp = 0;
return getTagCount(temp);
}
uint32_t getTagCount(uint32_t& timeoutcount) {
uint32_t tagcount = 0;
time_t now;
time(&now);
for (uint32_t c = 0; c < tagDB.size(); c++) {
tagRecord* taginfo = nullptr;
taginfo = tagDB.at(c);
if (taginfo->isExternal == false) tagcount++;
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++;
}
}
}
return tagcount;
}
@@ -261,15 +285,13 @@ uint8_t getTagCount() {
void clearPending(tagRecord* taginfo) {
taginfo->filename = String();
if (taginfo->data != nullptr) {
//check if this is the last copy of the buffer
// check if this is the last copy of the buffer
int datacount = 0;
for (int16_t c = 0; c < tagDB.size(); c++) {
for (uint32_t c = 0; c < tagDB.size(); c++) {
if (tagDB.at(c)->data == taginfo->data) datacount++;
}
if (datacount == 1) free(taginfo->data);
taginfo->data = nullptr;
}
taginfo->pending = false;
}
@@ -318,4 +340,56 @@ void saveAPconfig() {
APconfig["timezone"] = config.timeZone;
serializeJsonPretty(APconfig, configFile);
configFile.close();
}
}
HwType getHwType(uint8_t id) {
try {
return hwdata.at(id);
} catch (const std::out_of_range&) {
char filename[20];
snprintf(filename, sizeof(filename), "/tagtypes/%02X.json", id);
Serial.printf("read %s\n", filename);
File jsonFile = contentFS->open(filename, "r");
if (jsonFile) {
StaticJsonDocument<100> filter;
filter["width"] = true;
filter["height"] = true;
filter["rotatebuffer"] = true;
filter["bpp"] = true;
StaticJsonDocument<250> doc;
DeserializationError error = deserializeJson(doc, jsonFile, DeserializationOption::Filter(filter));
jsonFile.close();
if (error) {
Serial.println("json error in " + String(filename));
Serial.println(error.c_str());
} else {
hwdata[id].width = doc["width"];
hwdata[id].height = doc["height"];
hwdata[id].rotatebuffer = doc["rotatebuffer"];
hwdata[id].bpp = doc["bpp"];
return hwdata.at(id);
}
}
return {0, 0, 0, 0};
}
}
bool setVarDB(const std::string& key, const String& value) {
auto it = varDB.find(key);
if (it == varDB.end()) {
varStruct newVar;
newVar.value = value;
newVar.changed = true;
varDB[key] = newVar;
return true;
} else {
if (it->second.value != value) {
it->second.value = value;
it->second.changed = true;
return true;
} else {
return false;
}
}
}

View File

@@ -60,11 +60,12 @@ void wsErr(String text) {
if (wsMutex) xSemaphoreGive(wsMutex);
}
size_t dbSize(){
size_t dbSize() {
size_t size = tagDB.size() * sizeof(tagRecord);
for(auto &tag : tagDB) {
if (tag->data)
for (auto &tag : tagDB) {
if (tag->data) {
size += tag->len;
}
size += tag->modeConfigJson.length();
}
return size;
@@ -89,12 +90,24 @@ void wsSendSysteminfo() {
sys["wifistatus"] = WiFi.status();
sys["wifissid"] = WiFi.SSID();
uint32_t timeoutcount = 0;
uint32_t tagcount = getTagCount(timeoutcount);
char result[40];
if (timeoutcount > 0) {
snprintf(result, sizeof(result), "%lu / %lu, %lu timed out", tagcount, tagDB.size(), timeoutcount);
} else {
snprintf(result, sizeof(result), "%lu / %lu", tagcount, tagDB.size());
}
setVarDB("ap_tagcount", result);
setVarDB("ap_ip", WiFi.localIP().toString());
setVarDB("ap_ch", String(apInfo.channel));
xSemaphoreTake(wsMutex, portMAX_DELAY);
ws.textAll(doc.as<String>());
xSemaphoreGive(wsMutex);
}
void wsSendTaginfo(uint8_t *mac, uint8_t syncMode) {
void wsSendTaginfo(const uint8_t *mac, uint8_t syncMode) {
if (syncMode != SYNC_DELETE) {
String json = "";
json = tagDBtoJson(mac);
@@ -189,6 +202,7 @@ void init_web() {
});
server.serveStatic("/current", *contentFS, "/current/").setCacheControl("max-age=604800");
server.serveStatic("/tagtypes", *contentFS, "/tagtypes/").setCacheControl("max-age=604800");
server.serveStatic("/", *contentFS, "/www/").setDefaultFile("index.html");
server.on(
@@ -296,6 +310,9 @@ void init_web() {
if (strcmp(cmdValue, "reset") == 0) {
sendTagCommand(mac, CMD_DO_RESET_SETTINGS, !taginfo->isExternal);
}
if (strcmp(cmdValue, "deepsleep") == 0) {
sendTagCommand(mac, CMD_DO_DEEPSLEEP, !taginfo->isExternal);
}
request->send(200, "text/plain", "Ok, done");
} else {
request->send(200, "text/plain", "Error: mac not found");
@@ -360,6 +377,37 @@ void init_web() {
request->send(200, "text/plain", "Ok, saved");
});
server.on("/set_var", HTTP_POST, [](AsyncWebServerRequest *request) {
if (request->hasParam("key", true) && request->hasParam("val", true)) {
std::string key = request->getParam("key", true)->value().c_str();
String val = request->getParam("val", true)->value();
Serial.printf("set key %s value %s\n", key.c_str(), val);
setVarDB(key, val);
request->send(200, "text/plain", "Ok, saved");
} else {
request->send(500, "text/plain", "param error");
}
});
server.on("/set_vars", HTTP_POST, [](AsyncWebServerRequest *request) {
if (request->hasParam("json", true)) {
DynamicJsonDocument jsonDocument(2048);
DeserializationError error = deserializeJson(jsonDocument, request->getParam("json", true)->value());
if (error) {
request->send(400, "text/plain", "Failed to parse JSON");
return;
}
for (JsonPair kv : jsonDocument.as<JsonObject>()) {
std::string key = kv.key().c_str();
String val = kv.value().as<String>();
Serial.printf("set key %s value %s\n", key.c_str(), val);
setVarDB(key, val);
}
request->send(200, "text/plain", "JSON uploaded and processed");
} else {
request->send(400, "text/plain", "No 'json' parameter found in request");
}
});
// setup
server.on("/setup", HTTP_GET, [](AsyncWebServerRequest *request) {
@@ -435,10 +483,7 @@ void init_web() {
server.on("/backup_db", HTTP_GET, [](AsyncWebServerRequest *request) {
saveDB("/current/tagDB.json");
File file = contentFS->open("/current/tagDB.json", "r");
AsyncWebServerResponse *response = request->beginResponse(file, "tagDB.json", String(), true);
request->send(response);
file.close();
request->send(*contentFS, "/current/tagDB.json", String(), true);
});
server.on("/sysinfo", HTTP_GET, handleSysinfoRequest);
@@ -486,16 +531,20 @@ void doImageUpload(AsyncWebServerRequest *request, String filename, size_t index
request->_tempFile.close();
if (request->hasParam("mac", true)) {
String dst = request->getParam("mac", true)->value();
bool dither = true;
if (request->hasParam("dither", true)) {
if (request->getParam("dither", true)->value() == "0") dither = false;
}
uint8_t mac[8];
if (hex2mac(dst, mac)) {
tagRecord *taginfo = nullptr;
taginfo = tagRecord::findByMAC(mac);
if (taginfo != nullptr) {
taginfo->modeConfigJson = "{\"filename\":\"" + dst + ".jpg\",\"timetolive\":\"0\",\"dither\":\"" + String(dither) + "\",\"delete\":\"1\"}";
bool dither = true;
if (request->hasParam("dither", true)) {
if (request->getParam("dither", true)->value() == "0") dither = false;
}
uint32_t ttl = 0;
if (request->hasParam("ttl", true)) {
ttl = request->getParam("ttl", true)->value().toInt();
}
taginfo->modeConfigJson = "{\"filename\":\"" + dst + ".jpg\",\"timetolive\":\"" + String(ttl) + "\",\"dither\":\"" + String(dither) + "\",\"delete\":\"1\"}";
taginfo->contentMode = 0;
taginfo->nextupdate = 0;
wsSendTaginfo(mac, SYNC_USERCFG);
@@ -517,19 +566,23 @@ void doJsonUpload(AsyncWebServerRequest *request) {
}
if (request->hasParam("mac", true) && request->hasParam("json", true)) {
String dst = request->getParam("mac", true)->value();
File file = LittleFS.open("/" + dst + ".json", "w");
if (!file) {
request->send(400, "text/plain", "Failed to create file");
return;
}
file.print(request->getParam("json", true)->value());
file.close();
uint8_t mac[8];
if (hex2mac(dst, mac)) {
File file = LittleFS.open("/current/" + dst + ".json", "w");
if (!file) {
request->send(400, "text/plain", "Failed to create file");
return;
}
file.print(request->getParam("json", true)->value());
file.close();
tagRecord *taginfo = nullptr;
taginfo = tagRecord::findByMAC(mac);
if (taginfo != nullptr) {
taginfo->modeConfigJson = "{\"filename\":\"/" + dst + ".json\"}";
uint32_t ttl = 0;
if (request->hasParam("ttl", true)) {
ttl = request->getParam("ttl", true)->value().toInt();
}
taginfo->modeConfigJson = "{\"filename\":\"/current/" + dst + ".json\",\"interval\":\"" + String(ttl) + "\"}";
taginfo->contentMode = 19;
taginfo->nextupdate = 0;
wsSendTaginfo(mac, SYNC_USERCFG);

View File

@@ -7,6 +7,8 @@
0,
1,
2,
5,
51,
17
],
"param": [
@@ -42,6 +44,7 @@
0,
1,
2,
5,
17,
51,
240
@@ -56,7 +59,9 @@
0,
1,
2,
5,
17,
51,
240
],
"param": [
@@ -75,6 +80,7 @@
0,
1,
2,
51,
17
]
}
@@ -88,7 +94,9 @@
0,
1,
2,
5,
17,
51,
240
],
"param": [
@@ -107,6 +115,8 @@
0,
1,
2,
5,
51,
17
]
}
@@ -120,7 +130,9 @@
0,
1,
2,
5,
17,
51,
240
],
"param": [
@@ -151,6 +163,8 @@
"hwtype": [
1,
2,
5,
51,
17
],
"param": [
@@ -180,6 +194,7 @@
"desc": "Dutch rain predictions for the next two hours. Only works for locations in the Netherlands and Belgium.",
"hwtype": [
1,
51,
17
],
"param": [
@@ -210,6 +225,8 @@
"hwtype": [
1,
2,
5,
51,
17
],
"param": [
@@ -241,6 +258,8 @@
0,
1,
2,
5,
51,
17
],
"param": [
@@ -266,19 +285,27 @@
0,
1,
2,
5,
51,
17
],
"param": [
{
"key": "url",
"name": "URL",
"desc": "Full URL of the json template. See OpenEpaperLink wiki for the right json format",
"desc": "Full URL of the json template. See OpenEpaperLink wiki for the right json format. Specify a url OR a filename",
"type": "text"
},
{
"key": "filename",
"name": "Filename",
"desc": "Filename of the json template. See OpenEpaperLink wiki for the right json format. Specify a url OR a filename",
"type": "text"
},
{
"key": "interval",
"name": "Interval",
"desc": "How often (in minutes) the template is being fetched. Minimum is 3 minutes.",
"desc": "In case of an url, wow often (in minutes) the template is being fetched. Minimum is 3 minutes.",
"type": "int"
}
]
@@ -291,6 +318,7 @@
0,
1,
2,
5,
17,
51
],
@@ -316,6 +344,8 @@
"hwtype": [
1,
2,
5,
51,
17
],
"param": [
@@ -347,7 +377,9 @@
0,
1,
2,
5,
17,
51,
240
],
"param": [
@@ -436,7 +468,9 @@
0,
1,
2,
5,
17,
51,
240
],
"param": [
@@ -448,7 +482,9 @@
"options": {
"0": "Reboot",
"1": "Scan Channels",
"2": "Clear settings"
"2": "Clear settings",
"3": "Enter Deep Sleep"
}
}
]
@@ -461,7 +497,9 @@
0,
1,
2,
5,
17,
51,
240
],
"param": [
@@ -563,6 +601,8 @@
0,
1,
2,
5,
51,
17
],
"param": [
@@ -573,5 +613,11 @@
"type": "text"
}
]
},
{
"id": 21,
"name": "Display access point info",
"desc": "Displays information about the currently connected access point",
"hwtype": [0, 1]
}
]

View File

@@ -612,8 +612,8 @@
if (status != 200) {
alert("ERROR[" + status + "]: " + responseText);
} else {
treeRoot.removeChild(treeRoot.childNodes[0]);
httpGet(treeRoot, "/");
var el = ge(path);
el.parentNode.removeChild(el);
}
}
}

View File

@@ -48,6 +48,7 @@
<button id="cfgclrpending">clear pending</button>
<button id="cfgtagreboot">reboot</button>
<button id="cfgscan">scan</button>
<button id="cfgdeepsleep">deep sleep</button>
<button id="cfgreset">reset settings</button>
<button id="cfgdelete" title="remove"><img src="data:image/gif;base64,R0lGODlhEAAQAPMAANXV1e3t7d/f39HR0dvb2/Hx8dTU1OLi4urq6mZmZpmZmf///wAAAAAAAAAAAAAAACH5BAEAAAwALAAAAAAQABAAAARBkMlJq71Yrp3ZXkr4WWCYnOZSgQVyEMYwJCq1nHhe20qgCAoA7QLyAYU7njE4JPV+zOSkCEUSFbmTVPPpbjvgTAQAOw== "></button>
</p>
@@ -68,7 +69,7 @@
<p>
<label for="apcfgchid">Channel</label>
<select id="apcfgchid">
<option value="0">auto</option>
<option value="0" selected>auto</option>
<option value="11">11</option>
<option value="15">15</option>
<option value="20">20</option>
@@ -82,7 +83,7 @@
<select id="apcfgledbrightness">
<option value="-1">off</option>
<option value="64">25%</option>
<option value="128">50%</option>
<option value="128" selected>50%</option>
<option value="192">75%</option>
<option value="255">100%</option>
</select>
@@ -90,7 +91,7 @@
<p>
<label for="apcfglanguage">Content language</label>
<select id="apcfglanguage">
<option value="0">EN English</option>
<option value="0" selected>EN English</option>
<option value="1">NL Nederlands</option>
<option value="2">DE Deutsch</option>
</select>
@@ -101,7 +102,7 @@ longer periods when no updates are expected
the maximum sleep time.">
<label for="apclatency">Maximum sleep</label>
<select id="apclatency">
<option value="0">shortest (40 sec)</option>
<option value="0" selected>shortest (40 sec)</option>
<option value="5">5 minutes</option>
<option value="10">10 minute</option>
<option value="30">30 minutes</option>
@@ -113,14 +114,14 @@ Latency will be around 40 seconds.">
<label for="apcpreventsleep">Shorten latency during config</label>
<select id="apcpreventsleep">
<option value="0">no</option>
<option value="1">yes</option>
<option value="1" selected>yes</option>
</select>
</p>
<p title="Turn off preview images on the webpage if you want to manage many tags,
to save file system space">
<label for="apcpreview">Preview images</label>
<select id="apcpreview">
<option value="1">yes</option>
<option value="1" selected>yes</option>
<option value="0">no</option>
</select>
</p>
@@ -134,7 +135,7 @@ to save file system space">
<option value="60">15.0 dBm</option>
<option value="52">13.0 dBm</option>
<option value="44">11.0 dBm</option>
<option value="34">8.5 dBm</option>
<option value="34" selected>8.5 dBm</option>
<option value="28">7.0 dBm</option>
<option value="20">5.0 dBm</option>
<option value="8">2.0 dBm</option>
@@ -144,7 +145,7 @@ to save file system space">
<label for="apctimezone">Local time zone</label>
<select id="apctimezone">
<optgroup label="Europe">
<option value="CET-1CEST-2,M3.5.0/02:00:00,M10.5.0/03:00:00">Central European Time</option>
<option value="CET-1CEST-2,M3.5.0/02:00:00,M10.5.0/03:00:00" selected>Central European Time</option>
<option value="EET-2EEST-3,M3.5.0/03:00:00,M10.5.0/04:00:00">Athens, Greece</option>
<option value="GMT+0IST-1,M3.5.0/01:00:00,M10.5.0/02:00:00">Dublin, Ireland</option>
<option value="EET-2EEST-3,M3.5.0/03:00:00,M10.5.0/04:00:00">Helsinki, Finland</option>
@@ -171,6 +172,7 @@ to save file system space">
<option value="NZST-12NZDT-13,M9.4.0/02:00:00,M4.1.0/03:00:00">New Zealand</option>
</optgroup>
<optgroup label="Asia">
<option value="JST-9">Tokyo</option>
<option value="WIB-7">Jakarta</option>
<option value="GMT+2">Jerusalem</option>
<option value="SGT-8">Singapore</option>

View File

@@ -122,7 +122,7 @@ select {
position: fixed;
top: 65px;
left: 15px;
width: 380px;
min-width: 380px;
padding: 15px;
background-color: #f0e6d3;
z-index: 999;
@@ -425,6 +425,11 @@ ul.messages li.new {
background-color: #aaaaaa;
}
.drophighlight {
border: 1px solid red;
box-shadow: 7px 10px 52px -19px rgba(255, 0, 0, 0.63);
}
/* painter */
#canvasdiv {

View File

@@ -8,48 +8,7 @@ const WAKEUP_REASON_FIRSTBOOT = 0xFC;
const WAKEUP_REASON_NETWORK_SCAN = 0xFD;
const WAKEUP_REASON_WDT_RESET = 0xFE;
const models = ["1.54\" 152x152px", "2.9\" 296x128px", "4.2\" 400x300px"];
models[240] = "Segmented tag"
models[17] = "2.9\" 296x128px (UC8151)"
models[0x30] = "1.6\" BWR 200x200px"
models[0x38] = "1.6\" BWY 200x200px"
models[0x31] = "2.2\" BWR 296x160px"
models[0x39] = "2.2\" BWY 296x160px"
models[0x32] = "2.6\" BWR 360x184px"
models[0x3A] = "2.6\" BWY 360x184px"
models[0x33] = "2.9\" BWR 384x168px"
models[0x3B] = "2.9\" BWY 384x168px"
models[0x34] = "4.2\" BWR 400x300px"
models[0x3C] = "4.2\" BWY 400x300px"
models[0x35] = "6.0\" BWR 600x448px"
models[0x3D] = "6.0\" BWY 600x448px"
models[0x36] = "7.5\" BWR 880x528px"
models[0x3E] = "7.5\" BWY 880x528px"
models[0x37] = "11.6\" BWR 640x960px"
models[0x3F] = "11.6\" BWY 640x960px"
const displaySizeLookup = { 0: [152, 152, 4], 1: [128, 296, 2], 2: [400, 300, 2] }; // w, h, rotate
displaySizeLookup[17] = [128, 296, 2];
displaySizeLookup[240] = [0, 0, 0];
displaySizeLookup[0x30] = [200, 200, 4];//"1.6\" BWR 200x200px"
displaySizeLookup[0x38] = [200, 200, 4];//"1.6\" BWY 200x200px"
displaySizeLookup[0x31] = [296, 160, 2];//"2.2\" BWR 296x160px"
displaySizeLookup[0x39] = [296, 160, 2];//"2.2\" BWY 296x160px"
displaySizeLookup[0x32] = [360, 184, 2];//"2.6\" BWR 360x184px"
displaySizeLookup[0x3A] = [360, 184, 2];//"2.6\" BWY 360x184px"
displaySizeLookup[0x33] = [384, 168, 2];//"2.9\" BWR 384x168px"
displaySizeLookup[0x3B] = [384, 168, 2];//"2.9\" BWY 384x168px"
displaySizeLookup[0x34] = [400, 300, 2];//"4.2\" BWR 400x300px"
displaySizeLookup[0x3C] = [400, 300, 2];//"4.2\" BWY 400x300px"
displaySizeLookup[0x35] = [600, 448, 2];//"6.0\" BWR 600x448px"
displaySizeLookup[0x3D] = [600, 448, 2];//"6.0\" BWY 600x448px"
displaySizeLookup[0x36] = [880, 528, 2];//"7.5\" BWR 880x528px"
displaySizeLookup[0x3E] = [880, 528, 2];//"7.5\" BWY 880x528px"
displaySizeLookup[0x37] = [640, 960, 4];//"11.6\" BWR 640x960px"
displaySizeLookup[0x3F] = [640, 960, 4];//"11.6\" BWY 640x960px"
const colorTable = { 0: [255, 255, 255], 1: [0, 0, 0], 2: [255, 0, 0], 3: [150, 150, 150] };
let tagTypes = {};
const apstate = [
{ state: "offline", color: "red" },
@@ -71,18 +30,10 @@ const imageQueue = [];
let isProcessing = false;
let servertimediff = 0;
let paintLoaded = false, paintShow = false;
var cardconfig;
let cardconfig;
let otamodule;
window.addEventListener("load", function () {
fetch("/get_ap_config")
.then(response => response.json())
.then(data => {
if (data.alias) {
$(".logo").innerHTML = data.alias;
this.document.title = data.alias;
}
});
fetch('/content_cards.json')
.then(response => response.json())
.then(data => {
@@ -93,8 +44,17 @@ window.addEventListener("load", function () {
})
.catch(error => {
console.error('Error:', error);
alert("I can\'t load /www/content_cards.json.\r\nHave you upload it to the data partition?");
alert("I can't load /www/content_cards.json.\r\nHave you upload it to the data partition?");
});
fetch("/get_ap_config")
.then(response => response.json())
.then(data => {
if (data.alias) {
$(".logo").innerHTML = data.alias;
this.document.title = data.alias;
}
});
dropUpload();
});
let socket;
@@ -143,7 +103,7 @@ function connect() {
servertimediff = (Date.now() / 1000) - msg.sys.currtime;
}
if (msg.apitem) {
var row = $("#aptable").insertRow();
let row = $("#aptable").insertRow();
row.insertCell(0).innerHTML = "<a href=\"http://" + msg.apitem.ip + "\" target=\"_new\">" + msg.apitem.ip + "</a>";
row.insertCell(1).innerHTML = msg.apitem.alias;
row.insertCell(2).innerHTML = msg.apitem.count;
@@ -151,7 +111,7 @@ function connect() {
row.insertCell(4).innerHTML = msg.apitem.version;
}
if (msg.console) {
if (otamodule && typeof(otamodule.print) === "function") {
if (otamodule && typeof (otamodule.print) === "function") {
let color = "#c0c0c0";
if (msg.console.startsWith("Fail") || msg.console.startsWith("Err")) {
color = "red";
@@ -168,20 +128,20 @@ function connect() {
}
function convertSize(bytes) {
if (bytes >= 1073741824) { bytes = (bytes / 1073741824).toFixed(2) + " GB"; }
else if (bytes >= 1048576) { bytes = (bytes / 1048576).toFixed(2) + " MB"; }
else if (bytes >= 1024) { bytes = (bytes / 1024).toFixed(2) + " kB"; }
else if (bytes > 1) { bytes = bytes + " bytes"; }
else if (bytes == 1) { bytes = bytes + " byte"; }
else { bytes = "0 bytes"; }
if (bytes >= 1073741824) { bytes = (bytes / 1073741824).toFixed(2) + " GB"; }
else if (bytes >= 1048576) { bytes = (bytes / 1048576).toFixed(2) + " MB"; }
else if (bytes >= 1024) { bytes = (bytes / 1024).toFixed(2) + " kB"; }
else if (bytes > 1) { bytes = bytes + " bytes"; }
else if (bytes == 1) { bytes = bytes + " byte"; }
else { bytes = "0 bytes"; }
return bytes;
}
function processTags(tagArray) {
for (const element of tagArray) {
tagmac = element.mac;
const tagmac = element.mac;
var div = $('#tag' + tagmac);
let div = $('#tag' + tagmac);
if (div == null) {
div = $('#tagtemplate').cloneNode(true);
div.setAttribute('id', 'tag' + tagmac);
@@ -214,7 +174,11 @@ function processTags(tagArray) {
if (contentDefObj) $('#tag' + tagmac + ' .contentmode').innerHTML = contentDefObj.name;
if (element.RSSI) {
div.dataset.hwtype = element.hwType;
$('#tag' + tagmac + ' .model').innerHTML = models[element.hwType];
(async () => {
const localTagmac = tagmac;
const data = await getTagtype(element.hwType);
$('#tag' + localTagmac + ' .model').innerHTML = data.name;
})();
let statusline = "";
if (element.RSSI != 100) {
if (element.ch > 0) statusline += `CH ${element.ch}, `;
@@ -239,15 +203,15 @@ function processTags(tagArray) {
if (element.contentMode == 20) {
$('#tag' + tagmac + ' .tagimg').style.display = 'none';
} else if (div.dataset.hash != element.hash && div.dataset.hwtype > -1 && (element.isexternal == false || element.contentMode != 12)) {
} else if (div.dataset.hash != element.hash && div.dataset.hwtype > -1 && (!element.isexternal || element.contentMode != 12)) {
loadImage(tagmac, '/current/' + tagmac + '.raw?' + element.hash);
div.dataset.hash = element.hash;
}
if (element.isexternal == true && element.contentMode == 12) $('#tag' + tagmac + ' .tagimg').style.display = 'none';
if (element.isexternal && element.contentMode == 12) $('#tag' + tagmac + ' .tagimg').style.display = 'none';
if (element.nextupdate > 1672531200 && element.nextupdate != 3216153600) {
var date = new Date(element.nextupdate * 1000);
var options = { hour: '2-digit', minute: '2-digit', second: '2-digit', hour12: false };
const date = new Date(element.nextupdate * 1000);
const options = { hour: '2-digit', minute: '2-digit', second: '2-digit', hour12: false };
$('#tag' + tagmac + ' .nextupdate').innerHTML = "<span>next update</span>" + date.toLocaleString('nl-NL', options);
} else {
$('#tag' + tagmac + ' .nextupdate').innerHTML = "";
@@ -274,6 +238,7 @@ function processTags(tagArray) {
case WAKEUP_REASON_TIMED:
break;
case WAKEUP_REASON_BOOT:
case WAKEUP_REASON_FIRSTBOOT:
$('#tag' + tagmac + ' .nextcheckin').innerHTML = "<font color=yellow>First boot</font>"
$('#tag' + tagmac).style.background = "#b0d0b0";
break;
@@ -284,10 +249,6 @@ function processTags(tagArray) {
case WAKEUP_REASON_NFC:
$('#tag' + tagmac + ' .nextcheckin').innerHTML = "NFC wakeup"
break;
case WAKEUP_REASON_FIRSTBOOT:
$('#tag' + tagmac + ' .nextcheckin').innerHTML = "<font color=yellow>First boot</font>"
$('#tag' + tagmac).style.background = "#b0d0b0";
break;
case WAKEUP_REASON_NETWORK_SCAN:
$('#tag' + tagmac + ' .nextcheckin').innerHTML = "<font color=yellow>Network scan</font>"
$('#tag' + tagmac).style.background = "#c0c0d0";
@@ -308,10 +269,11 @@ function processTags(tagArray) {
}
function updatecards() {
if (servertimediff > 1000000000) servertimediff = 0;
$('#taglist').querySelectorAll('[data-mac]').forEach(item => {
let tagmac = item.dataset.mac;
if (item.dataset.lastseen && item.dataset.lastseen > 1672531200) {
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) {
@@ -334,6 +296,8 @@ function updatecards() {
if (item.dataset.nextcheckin > 1672531200 && parseInt(item.dataset.wakeupreason) == 0) {
let nextcheckin = item.dataset.nextcheckin - ((Date.now() / 1000) - servertimediff);
$('#tag' + tagmac + ' .nextcheckin').innerHTML = "<span>expected checkin</span>" + displayTime(Math.floor(nextcheckin));
} else {
$('#tag' + tagmac + ' .nextcheckin').innerHTML = "";
}
})
}
@@ -371,7 +335,7 @@ function loadContentCard(mac) {
fetch("/get_db?mac=" + mac)
.then(response => response.json())
.then(data => {
var tagdata = data.tags[0];
const tagdata = data.tags[0];
$('#cfgalias').value = tagdata.alias;
$('#cfgmore').style.display = "none";
if (populateSelectTag(tagdata.hwType, tagdata.capabilities)) {
@@ -424,7 +388,7 @@ $('#cfgsave').onclick = function () {
formData.append("alias", $('#cfgalias').value);
if (contentMode) {
extraoptions.forEach(element => {
extraoptions?.forEach(element => {
if ($('#opt' + element.key)) {
obj[element.key] = $('#opt' + element.key).value;
}
@@ -462,7 +426,7 @@ function sendCmd(mac, cmd) {
})
.then(response => response.text())
.then(data => {
var div = $('#tag' + $('#cfgmac').dataset.mac);
let div = $('#tag' + $('#cfgmac').dataset.mac);
if (cmd == "del") div.remove();
showMessage(data);
})
@@ -491,6 +455,10 @@ $('#cfgscan').onclick = function () {
sendCmd($('#cfgmac').dataset.mac, "scan");
}
$('#cfgdeepsleep').onclick = function () {
sendCmd($('#cfgmac').dataset.mac, "deepsleep");
}
$('#cfgreset').onclick = function () {
sendCmd($('#cfgmac').dataset.mac, "reset");
}
@@ -504,9 +472,9 @@ $('#rebootbutton').onclick = function () {
}
$('#apconfigbutton').onclick = function () {
var table = document.getElementById("aptable");
var rowCount = table.rows.length;
for (var i = rowCount - 1; i > 0; i--) {
let table = document.getElementById("aptable");
const rowCount = table.rows.length;
for (let i = rowCount - 1; i > 0; i--) {
table.deleteRow(i);
}
fetch("/get_ap_config")
@@ -569,8 +537,7 @@ $('#paintbutton').onclick = function () {
$('#customoptions').innerHTML = "<div id=\"buttonbar\"></div><div id=\"canvasdiv\"></div><div id=\"layersdiv\"></div><p id=\"savebar\"></p>";
const mac = $('#cfgmac').dataset.mac
const hwtype = $('#tag' + mac).dataset.hwtype;
var [width, height] = displaySizeLookup[hwtype] || [0, 0];
if (height > width) [width, height] = [height, width];
const [width, height] = [tagTypes[hwtype].width, tagTypes[hwtype].height] || [0, 0];
if (paintLoaded) {
startPainter(mac, width, height);
} else {
@@ -582,7 +549,7 @@ $('#paintbutton').onclick = function () {
}
function loadScript(url, callback) {
var script = document.createElement('script');
let script = document.createElement('script');
script.src = url;
script.onload = function () {
if (callback) {
@@ -595,7 +562,7 @@ function loadScript(url, callback) {
function contentselected() {
let contentMode = $('#cfgcontent').value;
$('#customoptions').innerHTML = "";
var obj = {};
let obj = {};
if ($('#cfgcontent').dataset.json && ($('#cfgcontent').dataset.json != "null")) {
obj = JSON.parse($('#cfgcontent').dataset.json);
}
@@ -607,15 +574,15 @@ function contentselected() {
}
$('#paintbutton').style.display = (contentMode == 0 ? 'inline-block' : 'none');
let extraoptions = contentDef?.param ?? null;
extraoptions.forEach(element => {
var label = document.createElement("label");
extraoptions?.forEach(element => {
let label = document.createElement("label");
label.innerHTML = element.name;
label.setAttribute("for", 'opt' + element.key);
if (element.desc) {
label.style.cursor = 'help';
label.title = element.desc;
}
var input = document.createElement("input");
let input = document.createElement("input");
switch (element.type) {
case 'text':
input.type = "text";
@@ -633,7 +600,7 @@ function contentselected() {
const optionElement = document.createElement("option");
optionElement.value = key;
optionElement.text = element.options[key];
if (element.options[key].substring(0,1)=="-") {
if (element.options[key].substring(0, 1) == "-") {
optionElement.text = element.options[key].substring(1);
optionElement.selected = true;
} else {
@@ -646,7 +613,7 @@ function contentselected() {
input.id = 'opt' + element.key;
input.title = element.desc;
if (obj[element.key]) input.value = obj[element.key];
var p = document.createElement("p");
let p = document.createElement("p");
p.appendChild(label);
p.appendChild(input);
$('#customoptions').appendChild(p);
@@ -657,14 +624,14 @@ function contentselected() {
}
function populateSelectTag(hwtype, capabilities) {
var selectTag = $("#cfgcontent");
let selectTag = $("#cfgcontent");
selectTag.innerHTML = "";
var optionsAdded = false;
var option;
let optionsAdded = false;
let option;
cardconfig.forEach(item => {
var capcheck = item.capabilities ?? 0;
var hwtypeArray = item.hwtype;
if (hwtypeArray.includes(hwtype) && (capabilities & capcheck || capcheck == 0)) {
const capcheck = item.capabilities ?? 0;
const hwtypeArray = item.hwtype ?? [];
if ((hwtypeArray.includes(hwtype) || tagTypes[hwtype].contentids.includes(item.id)) && (capabilities & capcheck || capcheck == 0)) {
option = document.createElement("option");
option.value = item.id;
option.text = item.name;
@@ -673,11 +640,11 @@ function populateSelectTag(hwtype, capabilities) {
}
});
var rotateTag = $("#cfgrotate");
let rotateTag = $("#cfgrotate");
rotateTag.innerHTML = "";
for (let i = 0; i < 4; i++) {
if (i == 0 || displaySizeLookup[hwtype][2] == 4 || (i == 2 && displaySizeLookup[hwtype][2] == 2)) {
if (i == 0 || tagTypes[hwtype].width == tagTypes[hwtype].height || (i == 2)) {
option = document.createElement("option");
option.value = i;
option.text = (i * 90) + " degrees";
@@ -685,7 +652,7 @@ function populateSelectTag(hwtype, capabilities) {
}
}
var lutTag = $("#cfglut");
let lutTag = $("#cfglut");
lutTag.innerHTML = "";
option = document.createElement("option");
@@ -705,14 +672,14 @@ function populateSelectTag(hwtype, capabilities) {
function getContentDefById(id) {
if (id == null) return null;
var obj = cardconfig.find(item => item.id == id);
return obj ? obj : null;
const obj = cardconfig.find(item => item.id == id);
return obj || null;
}
function showMessage(message, iserr) {
const messages = $('#messages');
var date = new Date(),
time = date.toLocaleTimeString('nl-NL', { hour12: false, hour: '2-digit', minute: '2-digit', second: '2-digit' });
const date = new Date();
const time = date.toLocaleTimeString('nl-NL', { hour12: false, hour: '2-digit', minute: '2-digit', second: '2-digit' });
if (iserr) {
messages.insertAdjacentHTML("afterbegin", '<li class="new error">' + htmlEncode(time + ' ' + message) + '</li>');
} else {
@@ -740,31 +707,56 @@ function processQueue() {
}
isProcessing = true;
const { id, imageSrc } = imageQueue.shift();
const hwtype = $('#tag' + id).dataset.hwtype;
if (tagTypes[hwtype]?.busy) {
imageQueue.push({ id, imageSrc });
setTimeout(processQueue, 50);
return;
};
const canvas = $('#tag' + id + ' .tagimg');
canvas.style.display = 'block';
const hwtype = $('#tag' + id).dataset.hwtype;
fetch(imageSrc, { cache: "force-cache" })
.then(response => response.arrayBuffer())
.then(buffer => {
[canvas.width, canvas.height] = displaySizeLookup[hwtype] || [0, 0];
[canvas.width, canvas.height] = [tagTypes[hwtype].width, tagTypes[hwtype].height] || [0, 0];
if (tagTypes[hwtype].rotatebuffer) [canvas.width, canvas.height] = [canvas.height, canvas.width];
const ctx = canvas.getContext('2d');
const imageData = ctx.createImageData(canvas.width, canvas.height);
const data = new Uint8ClampedArray(buffer);
const offsetRed = (data.length >= (canvas.width * canvas.height / 8) * 2) ? canvas.width * canvas.height / 8 : 0;
var pixelValue = 0;
for (let i = 0; i < data.length; i++) {
for (let j = 0; j < 8; j++) {
const pixelIndex = i * 8 + j;
if (offsetRed) {
pixelValue = ((data[i] & (1 << (7 - j))) ? 1 : 0) | (((data[i + offsetRed] & (1 << (7 - j))) ? 1 : 0) << 1);
} else {
pixelValue = ((data[i] & (1 << (7 - j))) ? 1 : 0);
if (data.length == 0) canvas.style.display = 'none';
if (tagTypes[hwtype].bpp == 16) {
const is16Bit = data.length == tagTypes[hwtype].width * tagTypes[hwtype].height * 2;
for (let i = 0; i < min(tagTypes[hwtype].width * tagTypes[hwtype].height, data.length); i++) {
const dataIndex = is16Bit ? i * 2 : i;
const rgb = is16Bit ? (data[dataIndex] << 8) | data[dataIndex + 1] : data[dataIndex];
imageData.data[i * 4] = is16Bit ? ((rgb >> 11) & 0x1F) << 3 : (((rgb >> 5) & 0x07) << 5) * 1.13;
imageData.data[i * 4 + 1] = is16Bit ? ((rgb >> 5) & 0x3F) << 2 : (((rgb >> 2) & 0x07) << 5) * 1.13;
imageData.data[i * 4 + 2] = is16Bit ? (rgb & 0x1F) << 3 : ((rgb & 0x03) << 6) * 1.3;
imageData.data[i * 4 + 3] = 255;
}
} else {
const offsetRed = (data.length >= (canvas.width * canvas.height / 8) * 2) ? canvas.width * canvas.height / 8 : 0;
let pixelValue = 0;
const colorTable = tagTypes[hwtype].colortable;
for (let i = 0; i < data.length; i++) {
for (let j = 0; j < 8; j++) {
const pixelIndex = i * 8 + j;
if (offsetRed) {
pixelValue = ((data[i] & (1 << (7 - j))) ? 1 : 0) | (((data[i + offsetRed] & (1 << (7 - j))) ? 1 : 0) << 1);
} else {
pixelValue = ((data[i] & (1 << (7 - j))) ? 1 : 0);
}
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;
}
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;
}
}
@@ -824,7 +816,7 @@ function GroupSortFilter() {
gridItems.forEach(item => {
if (grouping) {
const group = String(grouping).startsWith('data-') ? item.dataset[grouping.slice(5)] || '': item.querySelector('.' + grouping).textContent || '';
const group = String(grouping).startsWith('data-') ? item.dataset[grouping.slice(5)] || '' : item.querySelector('.' + grouping).textContent || '';
if (group !== currentGroup && group != '') {
let header = document.getElementById('header' + group);
if (!header) {
@@ -855,7 +847,7 @@ function GroupSortFilter() {
if ($('input[name="filter"][value="local"]').checked && item.dataset.isexternal == "true") show = false;
if ($('input[name="filter"][value="inactive"]').checked && item.querySelector('.warningicon').style.display != 'inline-block') show = false;
if ($('input[name="filter"][value="pending"]').checked && !item.classList.contains("tagpending")) show = false;
if (show == false) item.style.display = 'none'; else item.style.display = 'block';
if (!show) item.style.display = 'none'; else item.style.display = 'block';
item.style.order = order++;
});
@@ -875,3 +867,138 @@ $('#toggleFilters').addEventListener('click', () => {
filterOptions.style.maxHeight = 0;
}
});
async function getTagtype(hwtype) {
if (tagTypes[hwtype]?.busy) {
await new Promise(resolve => {
const checkBusy = setInterval(() => {
if (!tagTypes[hwtype].busy) {
clearInterval(checkBusy);
resolve();
}
}, 10);
});
}
if (tagTypes[hwtype]) {
return tagTypes[hwtype];
}
try {
tagTypes[hwtype] = { busy: true };
const response = await fetch('/tagtypes/' + hwtype.toString(16).padStart(2, '0').toUpperCase() + '.json');
if (!response.ok) {
let data = { name: 'unknown id ' + hwtype, width: 0, height: 0, bpp: 0, rotatebuffer: 0, colortable: [], busy: false };
tagTypes[hwtype] = data;
return data;
}
const jsonData = await response.json();
let data = {
name: jsonData.name,
width: parseInt(jsonData.width),
height: parseInt(jsonData.height),
bpp: parseInt(jsonData.bpp),
rotatebuffer: jsonData.rotatebuffer,
colortable: Object.values(jsonData.colortable),
contentids: Object.values(jsonData.contentids ?? []),
busy: false
};
tagTypes[hwtype] = data;
return data;
} catch (error) {
console.error('Error fetching data:', error);
return null;
}
}
function dropUpload() {
const dropZone = $('#taglist');
let timeoutId;
dropZone.addEventListener('dragenter', (event) => {
const tagCard = event.target.closest('.tagcard');
tagCard?.classList.add('drophighlight');
});
dropZone.addEventListener('dragover', (event) => {
event.preventDefault();
const tagCard = event.target.closest('.tagcard');
tagCard?.classList.add('drophighlight');
});
dropZone.addEventListener('dragleave', (event) => {
const tagCard = event.target.closest('.tagcard');
tagCard?.classList.remove('drophighlight');
});
dropZone.addEventListener('drop', (event) => {
event.preventDefault();
const file = event.dataTransfer.files[0];
const tagCard = event.target.closest('.tagcard');
if (tagCard && file && file.type.startsWith('image/')) {
tagCard.classList.remove('drophighlight');
const itemId = tagCard.id;
const reader = new FileReader();
reader.onload = function (e) {
const image = new Image();
image.src = e.target.result;
image.onload = function () {
const hwtype = tagCard.dataset.hwtype;
const mac = tagCard.dataset.mac;
const [width, height] = [tagTypes[hwtype].width, tagTypes[hwtype].height] || [0, 0];
const canvas = createCanvas(width, height);
const ctx = canvas.getContext('2d');
const scaleFactor = Math.max(
canvas.width / image.width,
canvas.height / image.height
);
const newWidth = image.width * scaleFactor;
const newHeight = image.height * scaleFactor;
const x = (canvas.width - newWidth) / 2;
const y = (canvas.height - newHeight) / 2;
ctx.drawImage(image, x, y, newWidth, newHeight);
canvas.toBlob(async (blob) => {
const formData = new FormData();
formData.append('mac', mac);
formData.append('file', blob, 'image.jpg');
try {
const response = await fetch('/imgupload', {
method: 'POST',
body: formData,
});
if (response.ok) {
console.log('Resized image uploaded successfully');
} else {
console.error('Image upload failed');
}
} catch (error) {
console.error('Image upload failed', error);
}
}, 'image/jpeg'); };
image.onerror = function () {
console.error('Failed to load image.');
};
};
reader.readAsDataURL(file);
}
});
function createCanvas(width, height) {
const canvas = document.createElement('canvas');
canvas.width = width;
canvas.height = height;
return canvas;
}
}

View File

@@ -29,14 +29,14 @@ export async function initUpdate() {
print("Update it manually one last time.");
disableButtons(true);
}
return "{}";
return {};
} else {
return response.json();
}
})
.then(data => {
if (data.env) {
let matchtest='';
let matchtest = '';
if (data.buildversion != filesystemversion && filesystemversion != "custom" && data.buildversion != "custom") matchtest = " <- not matching!"
print(`env: ${data.env}`);
print(`build date: ${formatEpoch(data.buildtime)}`);
@@ -45,7 +45,7 @@ export async function initUpdate() {
print(`sha: ${data.sha}`);
print(`psram size: ${data.psramsize}`);
print(`flash size: ${data.flashsize}`);
print("--------------------------","gray");
print("--------------------------", "gray");
env = data.env;
currentVer = data.buildversion;
currentBuildtime = data.buildtime;
@@ -61,7 +61,6 @@ export async function initUpdate() {
.then(data => {
const releaseDetails = data.map(release => {
const assets = release.assets;
let fileUrl = null;
const filesJsonAsset = assets.find(asset => asset.name === 'filesystem.json');
const binariesJsonAsset = assets.find(asset => asset.name === 'binaries.json');
if (filesJsonAsset && binariesJsonAsset) {
@@ -82,7 +81,7 @@ export async function initUpdate() {
easyupdate.innerHTML = ("No releases found.");
} else {
const release = releaseDetails[0];
if (release && release.tag_name) {
if (release?.tag_name) {
if (release.tag_name == currentVer) {
easyupdate.innerHTML = `Version ${currentVer}. You are up to date`;
} else if (release.date < formatEpoch(currentBuildtime)) {
@@ -100,7 +99,7 @@ export async function initUpdate() {
table.appendChild(tableHeader);
releaseDetails.forEach(release => {
if (release && release.html_url) {
if (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 onclick="otamodule.updateESP('${release.bin_url}', true)">ESP32</button></td><td><button onclick="otamodule.updateWebpage('${release.file_url}','${release.tag_name}', true)">Filesystem</button></td>`;
if (release.tag_name == currentVer) {
@@ -163,7 +162,6 @@ export async function updateWebpage(fileUrl, tagname, showReload) {
});
const checkfiles = async (files) => {
const updateactions = files.find(files => files.name === "update_actions.json");
if (updateactions) {
await fetchAndPost(updateactions.url, updateactions.name, updateactions.path);
@@ -173,7 +171,7 @@ export async function updateWebpage(fileUrl, tagname, showReload) {
body: ''
});
if (response.ok) {
const data = await response.text();
await response.text();
} else {
print(`error performing update actions: ${response.status}`, "red");
errors++;
@@ -227,12 +225,12 @@ export async function updateWebpage(fileUrl, tagname, showReload) {
consoleDiv.appendChild(newLine);
consoleDiv.scrollTop = consoleDiv.scrollHeight;
}
};
};
} catch (error) {
print('Error: ' + error, "red");
errors++;
reject(error);
}
reject(error);
}
})();
});
}
@@ -259,17 +257,16 @@ export async function updateESP(fileUrl, showConfirm) {
while (retryCount < maxRetries) {
try {
const response = await fetch("/getexturl?url=" + fileUrl);
const responseBody = await response.text();
if (!response.ok) {
throw new Error("Network response was not OK");
throw new Error("Network response was not OK: " + responseBody);
}
const responseBody = await response.text();
if (responseBody.trim()[0] !== "[") {
if (!responseBody.trim().startsWith("[")) {
throw new Error("Failed to fetch the release info file");
}
const data = JSON.parse(responseBody);
const file = data.find((entry) => entry.name == env + '.bin');
if (file) {
binurl = file.url;
@@ -291,7 +288,7 @@ export async function updateESP(fileUrl, showConfirm) {
});
if (response.ok) {
const result = await response.text();
await response.text();
print('OTA update initiated.');
} else {
print('Failed to initiate OTA update: ' + response.status, "red");
@@ -313,7 +310,7 @@ export async function updateESP(fileUrl, showConfirm) {
if (retryCount === maxRetries) {
print("Reached maximum retry count. Failed to execute the update.", "red");
}
}
running = false;
disableButtons(false);
@@ -327,9 +324,9 @@ $('#rollbackBtn').onclick = function () {
errors = 0;
const consoleDiv = document.getElementById('updateconsole');
consoleDiv.scrollTop = consoleDiv.scrollHeight;
print("Rolling back...");
fetch("/rollback", {
method: "POST",
body: ''
@@ -337,7 +334,6 @@ $('#rollbackBtn').onclick = function () {
running = false;
disableButtons(false);
}
export function print(line, color = "white") {
@@ -360,7 +356,7 @@ export function print(line, color = "white") {
export function reboot() {
print("Rebooting now... Reloading webpage in 5 seconds...", "yellow");
fetch("/reboot",{method: "POST"});
fetch("/reboot", { method: "POST" });
setTimeout(() => {
location.reload();
}, 5000);

View File

@@ -0,0 +1,41 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Variables upload Form</title>
</head>
<body>
<h3>demo Json variables</h3>
<p>You can use this as an example how to change variables to be uses in a json template. Look at the html source for the workings. You can use the variables in any json template, using <pre>{variablename}</pre>. As soon as a variable is changed, the tag is being refreshed with the new info.</p>
<p>
Change one variable:<br>
<form method="POST" action="/set_var">
key: <input type="text" name="key" value="testkey"><br>
value: <input type="text" name="val" value="MyCoolValue"><br>
<input type="submit" value="submit"><br>
</form>
</p>
<p>
Change multiple variables, using json:<br>
<form method="POST" action="/set_vars">
<p>
<label for="vars">Place the json string here</label><br>
<textarea id="vars" name="json" style="width:500px;height:80px;">
{ "temperature": 28.5,
"door": "open",
"testkey": "MyCoolValue"
}
</textarea>
</p>
<p>
<input type="submit" value="submit json">
</p>
</form>
</p>
</body>
</html>

Binary file not shown.

Binary file not shown.

View File

@@ -5,3 +5,5 @@
There are holder and stands for every tag. 1.54", 2.9" and 4.2".
![image](https://github.com/slimline33/OpenEPaperLink/assets/3323812/836875fd-7a5f-4a14-8cbe-e83381784879)
![image](../../docs/assets/2-9_Solum_shelf_holder_2cm.jpg)

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

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