mirror of
https://github.com/OpenEPaperLink/OpenEPaperLink.git
synced 2026-03-21 14:06:31 +01:00
Compare commits
259 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a251ed10e7 | ||
|
|
65410e3e8d | ||
|
|
b2174c2980 | ||
|
|
7db482a6ba | ||
|
|
bac7cf7ab5 | ||
|
|
a3e739b8da | ||
|
|
cc1dcd9d29 | ||
|
|
10468313e1 | ||
|
|
2e55c49a92 | ||
|
|
e45845e00e | ||
|
|
940795d05d | ||
|
|
a3fc1da6d2 | ||
|
|
d778aab553 | ||
|
|
f460046c51 | ||
|
|
e5ddba02ff | ||
|
|
7ba80f57c9 | ||
|
|
d6feb5a8ca | ||
|
|
1e034c83fa | ||
|
|
1e93a24b53 | ||
|
|
dbf76e2176 | ||
|
|
93639ff8ac | ||
|
|
48b2925b9f | ||
|
|
9c83d8b5a5 | ||
|
|
f487092f7f | ||
|
|
eae7c3a159 | ||
|
|
4d78df09cc | ||
|
|
e8740b2022 | ||
|
|
6c4e254db0 | ||
|
|
5ccbcb4719 | ||
|
|
617b49eb86 | ||
|
|
5868abf189 | ||
|
|
94be1ea3aa | ||
|
|
5190327d5a | ||
|
|
6ecf6410ee | ||
|
|
e72e89d85e | ||
|
|
054146677f | ||
|
|
58d9fe2217 | ||
|
|
205dfa0ce2 | ||
|
|
5c7b53b740 | ||
|
|
5196c1b212 | ||
|
|
e7fbaffbab | ||
|
|
c68b582be7 | ||
|
|
9d07df27f0 | ||
|
|
064bd4258c | ||
|
|
8e31a1b8eb | ||
|
|
9598bea315 | ||
|
|
6d2ed6915b | ||
|
|
6ffe967577 | ||
|
|
e169dbab6e | ||
|
|
b9d3289852 | ||
|
|
b0971bd111 | ||
|
|
11193a1fbf | ||
|
|
db88de1f75 | ||
|
|
e9a554a2bf | ||
|
|
dfde0c0efe | ||
|
|
2065072747 | ||
|
|
a2abe466f5 | ||
|
|
396536522a | ||
|
|
c228104c29 | ||
|
|
8b0c516598 | ||
|
|
d02f51882b | ||
|
|
1414814b4d | ||
|
|
aaf4f94df7 | ||
|
|
c6012a3cb5 | ||
|
|
da095a2382 | ||
|
|
726a8055b3 | ||
|
|
b010787d7d | ||
|
|
e91891d283 | ||
|
|
3e4175a790 | ||
|
|
6380cd15cb | ||
|
|
20d46df302 | ||
|
|
3788608e63 | ||
|
|
cd288e79f4 | ||
|
|
2bdb82d146 | ||
|
|
26731ffe7b | ||
|
|
b0a0e4b85b | ||
|
|
49d86620bd | ||
|
|
f73ce60468 | ||
|
|
92c8d6178d | ||
|
|
5f3918e882 | ||
|
|
cab72aa29e | ||
|
|
8b024bb347 | ||
|
|
b0eb1a3997 | ||
|
|
7b081431de | ||
|
|
a888991ecc | ||
|
|
0641547d73 | ||
|
|
f1df44fbb1 | ||
|
|
6afc2a020e | ||
|
|
4545a7a504 | ||
|
|
ada68b1a33 | ||
|
|
533f0cec78 | ||
|
|
92368f4499 | ||
|
|
691febcf6a | ||
|
|
d7d7261167 | ||
|
|
feb8e7e69c | ||
|
|
0cc7ba4fc6 | ||
|
|
0380ea2cb0 | ||
|
|
962e4995e2 | ||
|
|
7dc14c9229 | ||
|
|
03968dda72 | ||
|
|
c66565df71 | ||
|
|
ad91a24c98 | ||
|
|
0c672433cb | ||
|
|
f0035f5e87 | ||
|
|
c8458fcf51 | ||
|
|
a2aea16de8 | ||
|
|
999c57deb1 | ||
|
|
ecbbff75f8 | ||
|
|
ed0189ceb1 | ||
|
|
1330c18b86 | ||
|
|
321d0111b5 | ||
|
|
c52cb89be4 | ||
|
|
5aa7174600 | ||
|
|
755d676f07 | ||
|
|
484bb00341 | ||
|
|
1a9bf422ea | ||
|
|
0646e85a70 | ||
|
|
59f44e4a82 | ||
|
|
bf6021d6cd | ||
|
|
03647ada44 | ||
|
|
c7e7b49d49 | ||
|
|
f1ed22d131 | ||
|
|
437c4fdcc0 | ||
|
|
15bcba19f6 | ||
|
|
2b3f066568 | ||
|
|
61ee713539 | ||
|
|
ae4311e346 | ||
|
|
a5165eb946 | ||
|
|
0ec2713305 | ||
|
|
2746e9d114 | ||
|
|
19a08de207 | ||
|
|
8f2ab2823a | ||
|
|
28f8667baa | ||
|
|
1123e5cb9e | ||
|
|
0bae760eaa | ||
|
|
4b6efb7626 | ||
|
|
355f1bc108 | ||
|
|
480b4624d3 | ||
|
|
be9dba5a30 | ||
|
|
beb0e639e2 | ||
|
|
16ebd9194e | ||
|
|
23bddd296f | ||
|
|
6b9f790c63 | ||
|
|
b2bd6a7fe4 | ||
|
|
7b662dd65e | ||
|
|
504d150e7b | ||
|
|
4080273df1 | ||
|
|
f7de700634 | ||
|
|
40074c6bf9 | ||
|
|
fd77e0a77c | ||
|
|
1c0a45fcd7 | ||
|
|
1b363860d1 | ||
|
|
e3bfaaa62f | ||
|
|
cfee590f01 | ||
|
|
9ccf3ab029 | ||
|
|
22e9c23924 | ||
|
|
2060edb54c | ||
|
|
bccd617881 | ||
|
|
117120c0df | ||
|
|
25cfe44784 | ||
|
|
5a1488c986 | ||
|
|
3b55fb01a5 | ||
|
|
1cd7b67d95 | ||
|
|
2ab6a7a798 | ||
|
|
d9eb97af26 | ||
|
|
36d30ee0cb | ||
|
|
9158549cb3 | ||
|
|
a46678386e | ||
|
|
37a189c998 | ||
|
|
e3ea9121b4 | ||
|
|
aec5cc07e7 | ||
|
|
b7a339c034 | ||
|
|
0edb93be1a | ||
|
|
cb9b9f164a | ||
|
|
981fe9f312 | ||
|
|
013e6263a9 | ||
|
|
b7583e0028 | ||
|
|
ad7b75a582 | ||
|
|
9d29ffff68 | ||
|
|
9a824b87ef | ||
|
|
6629b77d3d | ||
|
|
45e3d7427e | ||
|
|
db1fc60b07 | ||
|
|
20364563be | ||
|
|
df2f35db8e | ||
|
|
7d15f41b56 | ||
|
|
562ee3e095 | ||
|
|
f2e7b5b332 | ||
|
|
0f166b3a4f | ||
|
|
b5117fdcb0 | ||
|
|
eed25ab60b | ||
|
|
00ebbda1c7 | ||
|
|
ffe90a5b9b | ||
|
|
c202a1ecec | ||
|
|
0f97c290a1 | ||
|
|
26446ca087 | ||
|
|
c474c364e6 | ||
|
|
09d6dd2fbb | ||
|
|
e080499e6a | ||
|
|
6f509ebf0f | ||
|
|
75bb534de8 | ||
|
|
70fb112961 | ||
|
|
27ff235593 | ||
|
|
316e32cd9b | ||
|
|
8335bf4270 | ||
|
|
372b96ef6b | ||
|
|
df805965c2 | ||
|
|
3ff40e526d | ||
|
|
431fdc1abe | ||
|
|
a78002543b | ||
|
|
55b254fdec | ||
|
|
984f46c4f1 | ||
|
|
bf66a49038 | ||
|
|
faf2d4cc19 | ||
|
|
7bd0c8c815 | ||
|
|
92870a8886 | ||
|
|
21bb64d20d | ||
|
|
8c7cacddfc | ||
|
|
1a6d3d6408 | ||
|
|
13451e3fde | ||
|
|
2cdb2b8a6f | ||
|
|
d92681ef02 | ||
|
|
5057bb7627 | ||
|
|
ea36618295 | ||
|
|
d5399ba309 | ||
|
|
3ad8d0e9fe | ||
|
|
6b33a41e85 | ||
|
|
38ccabe825 | ||
|
|
283fba0ebe | ||
|
|
c8c6a8c6fb | ||
|
|
7ba0fd1e82 | ||
|
|
f3c7a949a6 | ||
|
|
e19c9dc612 | ||
|
|
97897dd234 | ||
|
|
2b35c0f249 | ||
|
|
be9d5fc082 | ||
|
|
059ab15722 | ||
|
|
9212aad8d8 | ||
|
|
e7e8af39e2 | ||
|
|
215ebf7c44 | ||
|
|
27ecf54fb6 | ||
|
|
70690da3a8 | ||
|
|
e4853e105a | ||
|
|
2bb7a8c7b5 | ||
|
|
e944607228 | ||
|
|
1b2a6c448d | ||
|
|
8352e5811d | ||
|
|
5205338635 | ||
|
|
5bc988c90e | ||
|
|
add3d568a4 | ||
|
|
9b4fba818f | ||
|
|
c8b0812759 | ||
|
|
ef09cf4999 | ||
|
|
3a80c3f5e9 | ||
|
|
29c0deb541 | ||
|
|
09bc3056bf | ||
|
|
85a3e28f2e | ||
|
|
3abf3de984 | ||
|
|
ea7a76be89 |
27
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
27
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
title: ''
|
||||
labels: 'bug'
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Describe the bug**
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
**To Reproduce**
|
||||
Steps to reproduce the behavior:
|
||||
1. Go to '...'
|
||||
2. Click on '....'
|
||||
3. Scroll down to '....'
|
||||
4. See error
|
||||
|
||||
**Expected behavior**
|
||||
A clear and concise description of what you expected to happen.
|
||||
|
||||
**Screenshots**
|
||||
If applicable, add screenshots to help explain your problem.
|
||||
|
||||
**Additional context**
|
||||
Add any other context about the problem here.
|
||||
20
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
20
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
title: 'Feature request: '
|
||||
labels: 'enhancement'
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
A clear and concise description of what the problem is.
|
||||
|
||||
**Describe the solution you'd like**
|
||||
A clear and concise description of what you want to happen.
|
||||
|
||||
**Describe alternatives you've considered**
|
||||
A clear and concise description of any alternative solutions or features you've considered.
|
||||
|
||||
**Additional context**
|
||||
Add any other context or screenshots about the feature request here.
|
||||
57
.github/workflows/esp32-build-test.yml
vendored
Normal file
57
.github/workflows/esp32-build-test.yml
vendored
Normal file
@@ -0,0 +1,57 @@
|
||||
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 Weemos_AP
|
||||
run: |
|
||||
cd ESP32_AP-Flasher
|
||||
pio run --environment Weemos_AP
|
||||
pio run --target buildfs --environment Weemos_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
|
||||
196
.github/workflows/release.yml
vendored
Normal file
196
.github/workflows/release.yml
vendored
Normal file
@@ -0,0 +1,196 @@
|
||||
name: Release binaries
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- '*'
|
||||
|
||||
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: Install esptool
|
||||
run: pip install esptool
|
||||
|
||||
- name: create folders
|
||||
run: |
|
||||
mkdir espbinaries
|
||||
|
||||
- name: Build firmware for Simple_AP
|
||||
run: |
|
||||
cd ESP32_AP-Flasher
|
||||
export PLATFORMIO_BUILD_FLAGS="-D BUILD_VERSION=${{ github.ref_name }} -D SHA=$GITHUB_SHA"
|
||||
pio run --environment Simple_AP
|
||||
pio run --target buildfs --environment Simple_AP
|
||||
mkdir /home/runner/work/OpenEPaperLink/OpenEPaperLink/Simple_AP
|
||||
cp ~/.platformio/packages/framework-arduinoespressif32/tools/partitions/boot_app0.bin /home/runner/work/OpenEPaperLink/OpenEPaperLink/Simple_AP/boot_app0.bin
|
||||
cp .pio/build/Simple_AP/firmware.bin /home/runner/work/OpenEPaperLink/OpenEPaperLink/Simple_AP/firmware.bin
|
||||
cp .pio/build/Simple_AP/bootloader.bin /home/runner/work/OpenEPaperLink/OpenEPaperLink/Simple_AP/bootloader.bin
|
||||
cp .pio/build/Simple_AP/partitions.bin /home/runner/work/OpenEPaperLink/OpenEPaperLink/Simple_AP/partitions.bin
|
||||
cp .pio/build/Simple_AP/littlefs.bin /home/runner/work/OpenEPaperLink/OpenEPaperLink/Simple_AP/littlefs.bin
|
||||
cd /home/runner/work/OpenEPaperLink/OpenEPaperLink/Simple_AP
|
||||
esptool.py --chip esp32 merge_bin -o merged-firmware.bin --flash_mode dio --flash_freq 40m --flash_size 4MB 0x1000 bootloader.bin 0x8000 partitions.bin 0xe000 boot_app0.bin 0x10000 firmware.bin 0x290000 littlefs.bin
|
||||
cd /home/runner/work/OpenEPaperLink/OpenEPaperLink
|
||||
cp Simple_AP/firmware.bin espbinaries/Simple_AP.bin
|
||||
cp Simple_AP/merged-firmware.bin espbinaries/Simple_AP_full.bin
|
||||
|
||||
- name: Build firmware for Weemos_AP
|
||||
run: |
|
||||
cd ESP32_AP-Flasher
|
||||
export PLATFORMIO_BUILD_FLAGS="-D BUILD_VERSION=${{ github.ref_name }} -D SHA=$GITHUB_SHA"
|
||||
pio run --environment Weemos_AP
|
||||
pio run --target buildfs --environment Weemos_AP
|
||||
mkdir /home/runner/work/OpenEPaperLink/OpenEPaperLink/Weemos_AP
|
||||
cp ~/.platformio/packages/framework-arduinoespressif32/tools/partitions/boot_app0.bin /home/runner/work/OpenEPaperLink/OpenEPaperLink/Weemos_AP/boot_app0.bin
|
||||
cp .pio/build/Weemos_AP/firmware.bin /home/runner/work/OpenEPaperLink/OpenEPaperLink/Weemos_AP/firmware.bin
|
||||
cp .pio/build/Weemos_AP/bootloader.bin /home/runner/work/OpenEPaperLink/OpenEPaperLink/Weemos_AP/bootloader.bin
|
||||
cp .pio/build/Weemos_AP/partitions.bin /home/runner/work/OpenEPaperLink/OpenEPaperLink/Weemos_AP/partitions.bin
|
||||
cp .pio/build/Weemos_AP/littlefs.bin /home/runner/work/OpenEPaperLink/OpenEPaperLink/Weemos_AP/littlefs.bin
|
||||
cd /home/runner/work/OpenEPaperLink/OpenEPaperLink/Weemos_AP
|
||||
esptool.py --chip esp32 merge_bin -o merged-firmware.bin --flash_mode dio --flash_freq 40m --flash_size 4MB 0x1000 bootloader.bin 0x8000 partitions.bin 0xe000 boot_app0.bin 0x10000 firmware.bin 0x290000 littlefs.bin
|
||||
cd /home/runner/work/OpenEPaperLink/OpenEPaperLink
|
||||
cp Weemos_AP/firmware.bin espbinaries/Weemos_AP.bin
|
||||
cp Weemos_AP/merged-firmware.bin espbinaries/Weemos_AP_full.bin
|
||||
|
||||
- name: Build firmware for M5Stack_Core_ONE_AP
|
||||
run: |
|
||||
cd ESP32_AP-Flasher
|
||||
export PLATFORMIO_BUILD_FLAGS="-D BUILD_VERSION=${{ github.ref_name }} -D SHA=$GITHUB_SHA"
|
||||
pio run --environment M5Stack_Core_ONE_AP
|
||||
pio run --target buildfs --environment M5Stack_Core_ONE_AP
|
||||
mkdir /home/runner/work/OpenEPaperLink/OpenEPaperLink/M5Stack_Core_ONE_AP
|
||||
cp ~/.platformio/packages/framework-arduinoespressif32/tools/partitions/boot_app0.bin /home/runner/work/OpenEPaperLink/OpenEPaperLink/M5Stack_Core_ONE_AP/boot_app0.bin
|
||||
cp .pio/build/M5Stack_Core_ONE_AP/firmware.bin /home/runner/work/OpenEPaperLink/OpenEPaperLink/M5Stack_Core_ONE_AP/firmware.bin
|
||||
cp .pio/build/M5Stack_Core_ONE_AP/bootloader.bin /home/runner/work/OpenEPaperLink/OpenEPaperLink/M5Stack_Core_ONE_AP/bootloader.bin
|
||||
cp .pio/build/M5Stack_Core_ONE_AP/partitions.bin /home/runner/work/OpenEPaperLink/OpenEPaperLink/M5Stack_Core_ONE_AP/partitions.bin
|
||||
cp .pio/build/M5Stack_Core_ONE_AP/littlefs.bin /home/runner/work/OpenEPaperLink/OpenEPaperLink/M5Stack_Core_ONE_AP/littlefs.bin
|
||||
cd /home/runner/work/OpenEPaperLink/OpenEPaperLink/M5Stack_Core_ONE_AP
|
||||
esptool.py --chip esp32 merge_bin -o merged-firmware.bin --flash_mode dio --flash_freq 40m --flash_size 4MB 0x1000 bootloader.bin 0x8000 partitions.bin 0xe000 boot_app0.bin 0x10000 firmware.bin 0x2B0000 littlefs.bin
|
||||
cd /home/runner/work/OpenEPaperLink/OpenEPaperLink
|
||||
cp M5Stack_Core_ONE_AP/firmware.bin espbinaries/M5Stack_Core_ONE_AP.bin
|
||||
cp M5Stack_Core_ONE_AP/merged-firmware.bin espbinaries/M5Stack_Core_ONE_AP_full.bin
|
||||
|
||||
- name: Build firmware for OpenEPaperLink_Mini_AP
|
||||
run: |
|
||||
cd ESP32_AP-Flasher
|
||||
export PLATFORMIO_BUILD_FLAGS="-D BUILD_VERSION=${{ github.ref_name }} -D SHA=$GITHUB_SHA"
|
||||
pio run --environment OpenEPaperLink_Mini_AP
|
||||
pio run --target buildfs --environment OpenEPaperLink_Mini_AP
|
||||
mkdir /home/runner/work/OpenEPaperLink/OpenEPaperLink/OpenEPaperLink_Mini_AP
|
||||
cp ~/.platformio/packages/framework-arduinoespressif32/tools/partitions/boot_app0.bin /home/runner/work/OpenEPaperLink/OpenEPaperLink/OpenEPaperLink_Mini_AP/boot_app0.bin
|
||||
cp .pio/build/OpenEPaperLink_Mini_AP/firmware.bin /home/runner/work/OpenEPaperLink/OpenEPaperLink/OpenEPaperLink_Mini_AP/firmware.bin
|
||||
cp .pio/build/OpenEPaperLink_Mini_AP/bootloader.bin /home/runner/work/OpenEPaperLink/OpenEPaperLink/OpenEPaperLink_Mini_AP/bootloader.bin
|
||||
cp .pio/build/OpenEPaperLink_Mini_AP/partitions.bin /home/runner/work/OpenEPaperLink/OpenEPaperLink/OpenEPaperLink_Mini_AP/partitions.bin
|
||||
cp .pio/build/OpenEPaperLink_Mini_AP/littlefs.bin /home/runner/work/OpenEPaperLink/OpenEPaperLink/OpenEPaperLink_Mini_AP/littlefs.bin
|
||||
cd /home/runner/work/OpenEPaperLink/OpenEPaperLink/OpenEPaperLink_Mini_AP
|
||||
esptool.py --chip esp32-s2 merge_bin -o merged-firmware.bin --flash_mode dio --flash_freq 80m --flash_size 4MB 0x1000 bootloader.bin 0x8000 partitions.bin 0xe000 boot_app0.bin 0x10000 firmware.bin 0x290000 littlefs.bin
|
||||
cd /home/runner/work/OpenEPaperLink/OpenEPaperLink
|
||||
cp OpenEPaperLink_Mini_AP/firmware.bin espbinaries/OpenEPaperLink_Mini_AP.bin
|
||||
cp OpenEPaperLink_Mini_AP/merged-firmware.bin espbinaries/OpenEPaperLink_Mini_AP_full.bin
|
||||
|
||||
- name: Build firmware for OpenEPaperLink_Nano_AP
|
||||
run: |
|
||||
cd ESP32_AP-Flasher
|
||||
export PLATFORMIO_BUILD_FLAGS="-D BUILD_VERSION=${{ github.ref_name }} -D SHA=$GITHUB_SHA"
|
||||
pio run --environment OpenEPaperLink_Nano_AP
|
||||
pio run --target buildfs --environment OpenEPaperLink_Nano_AP
|
||||
mkdir /home/runner/work/OpenEPaperLink/OpenEPaperLink/OpenEPaperLink_Nano_AP
|
||||
cp ~/.platformio/packages/framework-arduinoespressif32/tools/partitions/boot_app0.bin /home/runner/work/OpenEPaperLink/OpenEPaperLink/OpenEPaperLink_Nano_AP/boot_app0.bin
|
||||
cp .pio/build/OpenEPaperLink_Nano_AP/firmware.bin /home/runner/work/OpenEPaperLink/OpenEPaperLink/OpenEPaperLink_Nano_AP/firmware.bin
|
||||
cp .pio/build/OpenEPaperLink_Nano_AP/bootloader.bin /home/runner/work/OpenEPaperLink/OpenEPaperLink/OpenEPaperLink_Nano_AP/bootloader.bin
|
||||
cp .pio/build/OpenEPaperLink_Nano_AP/partitions.bin /home/runner/work/OpenEPaperLink/OpenEPaperLink/OpenEPaperLink_Nano_AP/partitions.bin
|
||||
cp .pio/build/OpenEPaperLink_Nano_AP/littlefs.bin /home/runner/work/OpenEPaperLink/OpenEPaperLink/OpenEPaperLink_Nano_AP/littlefs.bin
|
||||
cd /home/runner/work/OpenEPaperLink/OpenEPaperLink/OpenEPaperLink_Nano_AP
|
||||
esptool.py --chip esp32-s2 merge_bin -o merged-firmware.bin --flash_mode dio --flash_freq 80m --flash_size 4MB 0x1000 bootloader.bin 0x8000 partitions.bin 0xe000 boot_app0.bin 0x10000 firmware.bin 0x290000 littlefs.bin
|
||||
cd /home/runner/work/OpenEPaperLink/OpenEPaperLink
|
||||
cp OpenEPaperLink_Nano_AP/firmware.bin espbinaries/OpenEPaperLink_Nano_AP.bin
|
||||
cp OpenEPaperLink_Nano_AP/merged-firmware.bin espbinaries/OpenEPaperLink_Nano_AP_full.bin
|
||||
|
||||
- name: Build firmware for OpenEPaperLink_AP_and_Flasher
|
||||
run: |
|
||||
cd ESP32_AP-Flasher
|
||||
export PLATFORMIO_BUILD_FLAGS="-D BUILD_VERSION=${{ github.ref_name }} -D SHA=$GITHUB_SHA"
|
||||
pio run --environment OpenEPaperLink_AP_and_Flasher
|
||||
pio run --target buildfs --environment OpenEPaperLink_AP_and_Flasher
|
||||
mkdir /home/runner/work/OpenEPaperLink/OpenEPaperLink/OpenEPaperLink_AP_and_Flasher
|
||||
cp ~/.platformio/packages/framework-arduinoespressif32/tools/partitions/boot_app0.bin /home/runner/work/OpenEPaperLink/OpenEPaperLink/OpenEPaperLink_AP_and_Flasher/boot_app0.bin
|
||||
cp .pio/build/OpenEPaperLink_AP_and_Flasher/firmware.bin /home/runner/work/OpenEPaperLink/OpenEPaperLink/OpenEPaperLink_AP_and_Flasher/firmware.bin
|
||||
cp .pio/build/OpenEPaperLink_AP_and_Flasher/bootloader.bin /home/runner/work/OpenEPaperLink/OpenEPaperLink/OpenEPaperLink_AP_and_Flasher/bootloader.bin
|
||||
cp .pio/build/OpenEPaperLink_AP_and_Flasher/partitions.bin /home/runner/work/OpenEPaperLink/OpenEPaperLink/OpenEPaperLink_AP_and_Flasher/partitions.bin
|
||||
cp .pio/build/OpenEPaperLink_AP_and_Flasher/littlefs.bin /home/runner/work/OpenEPaperLink/OpenEPaperLink/OpenEPaperLink_AP_and_Flasher/littlefs.bin
|
||||
cd /home/runner/work/OpenEPaperLink/OpenEPaperLink/OpenEPaperLink_AP_and_Flasher
|
||||
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 0x00c90000 littlefs.bin
|
||||
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: generate release json file
|
||||
run: |
|
||||
mkdir jsonfiles
|
||||
python genfilelist.py ${{ github.ref_name }} $GITHUB_REPOSITORY $GITHUB_SHA
|
||||
|
||||
- name: Add file lists to release
|
||||
uses: svenstaro/upload-release-action@v2
|
||||
with:
|
||||
repo_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
file: jsonfiles/*
|
||||
tag: ${{ github.ref }}
|
||||
file_glob: true
|
||||
overwrite: true
|
||||
|
||||
- name: Add esp bins to release
|
||||
uses: svenstaro/upload-release-action@v2
|
||||
with:
|
||||
repo_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
file: espbinaries/*
|
||||
tag: ${{ github.ref }}
|
||||
file_glob: true
|
||||
overwrite: true
|
||||
|
||||
- name: Add tag bins to release
|
||||
uses: svenstaro/upload-release-action@v2
|
||||
with:
|
||||
repo_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
file: binaries/*
|
||||
tag: ${{ github.ref }}
|
||||
file_glob: true
|
||||
overwrite: true
|
||||
|
||||
# - name: Add www folder to release
|
||||
# uses: svenstaro/upload-release-action@v2
|
||||
# with:
|
||||
# repo_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
# file: ESP32_AP-Flasher/data/www/*
|
||||
# tag: ${{ github.ref }}
|
||||
# file_glob: true
|
||||
|
||||
# - name: Add fonts folder to release
|
||||
# uses: svenstaro/upload-release-action@v2
|
||||
# with:
|
||||
# repo_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
# file: ESP32_AP-Flasher/data/fonts/*
|
||||
# tag: ${{ github.ref }}
|
||||
# file_glob: true
|
||||
|
||||
# - name: Add data folder to release
|
||||
# uses: svenstaro/upload-release-action@v2
|
||||
# with:
|
||||
# repo_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
# file: ESP32_AP-Flasher/data/*
|
||||
# tag: ${{ github.ref }}
|
||||
# file_glob: true
|
||||
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
@@ -1,3 +0,0 @@
|
||||
{
|
||||
"cmake.sourceDirectory": "${workspaceFolder}/esp32_fw/.pio/libdeps/lolin32_lite/ESP Async WebServer"
|
||||
}
|
||||
3
ESP32_AP-Flasher/CMakeLists.txt
Normal file
3
ESP32_AP-Flasher/CMakeLists.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
cmake_minimum_required(VERSION 3.16.0)
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(esp32_fw)
|
||||
1
ESP32_AP-Flasher/data/AP_FW_Pack.bin
Symbolic link
1
ESP32_AP-Flasher/data/AP_FW_Pack.bin
Symbolic link
@@ -0,0 +1 @@
|
||||
../../binaries/AP_FW_Pack.bin
|
||||
BIN
ESP32_AP-Flasher/data/alignment.jpg
Normal file
BIN
ESP32_AP-Flasher/data/alignment.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 23 KiB |
129
ESP32_AP-Flasher/data/content_template.json
Normal file
129
ESP32_AP-Flasher/data/content_template.json
Normal file
@@ -0,0 +1,129 @@
|
||||
{
|
||||
"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, 130, 2 ],
|
||||
"wind": [ 140, 10, "fonts/bahnschrift30" ],
|
||||
"temp": [ 10, 10, "fonts/bahnschrift30" ],
|
||||
"icon": [ 33, 33, "fonts/weathericons78" ],
|
||||
"dir": [ 100, -2, "fonts/weathericons30" ],
|
||||
"umbrella": [ 115, 110 ]
|
||||
},
|
||||
"1": {
|
||||
"location": [ 5, 5, "fonts/bahnschrift30" ],
|
||||
"wind": [ 280, 5, "fonts/bahnschrift30" ],
|
||||
"temp": [ 5, 65, "fonts/bahnschrift70" ],
|
||||
"icon": [ 185, 32, "fonts/weathericons70" ],
|
||||
"dir": [ 240, -3, "fonts/weathericons30" ],
|
||||
"umbrella": [ 190, 0 ]
|
||||
},
|
||||
"2": {
|
||||
"location": [ 20, 20, "fonts/calibrib30" ],
|
||||
"wind": [ 290, 83, "fonts/calibrib60" ],
|
||||
"temp": [ 20, 170, "fonts/calibrib150" ],
|
||||
"icon": [ 100, 50, "fonts/weathericons78" ],
|
||||
"dir": [ 220, 50, "fonts/weathericons78" ],
|
||||
"umbrella": [ 330, 10 ]
|
||||
}
|
||||
},
|
||||
"8": {
|
||||
"1": {
|
||||
"location": [ 5, 0, 2 ],
|
||||
"column": [ 5, 59 ],
|
||||
"day": [ 30, 18, "fonts/twcondensed20", 41, 108 ],
|
||||
"icon": [ 12, 58, "fonts/weathericons30" ],
|
||||
"wind": [ 17, 25 ],
|
||||
"line": [ 20, 128 ]
|
||||
},
|
||||
"2": {
|
||||
"location": [ 10, 10, "fonts/calibrib30" ],
|
||||
"column": [ 6, 66 ],
|
||||
"day": [ 33, 60, "fonts/bahnschrift20", 104, 230 ],
|
||||
"rain": [ 34, 260 ],
|
||||
"icon": [ 15, 145, "fonts/weathericons30" ],
|
||||
"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, 3, 2 ],
|
||||
"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 ],
|
||||
"bars": [ 5, 111, 10 ]
|
||||
}
|
||||
}
|
||||
}
|
||||
64
ESP32_AP-Flasher/data/demo_image_generator.py
Normal file
64
ESP32_AP-Flasher/data/demo_image_generator.py
Normal file
@@ -0,0 +1,64 @@
|
||||
import requests
|
||||
from PIL import Image, ImageDraw, ImageFont
|
||||
|
||||
mac = "00000197E5CB3B38" # destination mac address
|
||||
dither = 0 # set dither to 1 is you're sending photos etc
|
||||
apip = "192.168.178.192" # ip address of your access point
|
||||
|
||||
# Create a new paletted image with indexed colors
|
||||
image = Image.new('P', (296, 128))
|
||||
|
||||
# Define the color palette (white, black, red)
|
||||
palette = [
|
||||
255, 255, 255, # white
|
||||
0, 0, 0, # black
|
||||
255, 0, 0 # red
|
||||
]
|
||||
|
||||
# Assign the color palette to the image
|
||||
image.putpalette(palette)
|
||||
|
||||
# Initialize the drawing context
|
||||
draw = ImageDraw.Draw(image)
|
||||
|
||||
# Define the text lines
|
||||
line1 = 'OpenEPaperLink'
|
||||
line2 = 'Demo image'
|
||||
|
||||
# Define the fonts and sizes
|
||||
font_line1 = ImageFont.truetype('arial.ttf', size=36) # Change the font file and size as per your preference
|
||||
font_line2 = ImageFont.truetype('arial.ttf', size=16) # Change the font file and size as per your preference
|
||||
|
||||
# Calculate the text bounding boxes to get the text widths and heights
|
||||
text_bbox_line1 = draw.textbbox((0, 0), line1, font=font_line1)
|
||||
text_bbox_line2 = draw.textbbox((0, 0), line2, font=font_line2)
|
||||
|
||||
# Calculate the text positions to center the lines horizontally
|
||||
text_position_line1 = ((image.width - (text_bbox_line1[2] - text_bbox_line1[0])) // 2, 20)
|
||||
text_position_line2 = ((image.width - (text_bbox_line2[2] - text_bbox_line2[0])) // 2, 80)
|
||||
|
||||
# Write the text on the image
|
||||
draw.text(text_position_line1, line1, fill=2, font=font_line1) # Use palette index 1 for black color
|
||||
draw.text(text_position_line2, line2, fill=1, font=font_line2) # Use palette index 2 for red color
|
||||
|
||||
# Convert the image to 24-bit RGB
|
||||
rgb_image = image.convert('RGB')
|
||||
|
||||
# Save the image as JPEG with maximum quality
|
||||
image_path = 'output.jpg'
|
||||
rgb_image.save(image_path, 'JPEG', quality="maximum")
|
||||
|
||||
# Prepare the HTTP POST request
|
||||
url = "http://" + apip + "/imgupload"
|
||||
payload = {"dither": dither, "mac": mac} # Additional POST parameter
|
||||
files = {"file": open(image_path, "rb")} # File to be uploaded
|
||||
|
||||
# Send the HTTP POST request
|
||||
response = requests.post(url, data=payload, files=files)
|
||||
|
||||
# Check the response status
|
||||
if response.status_code == 200:
|
||||
print("Image uploaded successfully!")
|
||||
else:
|
||||
print("Failed to upload the image.")
|
||||
|
||||
BIN
ESP32_AP-Flasher/data/fonts/bahnschrift20.vlw
Normal file
BIN
ESP32_AP-Flasher/data/fonts/bahnschrift20.vlw
Normal file
Binary file not shown.
BIN
ESP32_AP-Flasher/data/fonts/bahnschrift30.vlw
Normal file
BIN
ESP32_AP-Flasher/data/fonts/bahnschrift30.vlw
Normal file
Binary file not shown.
BIN
ESP32_AP-Flasher/data/fonts/bahnschrift70.vlw
Normal file
BIN
ESP32_AP-Flasher/data/fonts/bahnschrift70.vlw
Normal file
Binary file not shown.
BIN
ESP32_AP-Flasher/data/fonts/calibrib100.vlw
Normal file
BIN
ESP32_AP-Flasher/data/fonts/calibrib100.vlw
Normal file
Binary file not shown.
BIN
ESP32_AP-Flasher/data/fonts/calibrib120.vlw
Normal file
BIN
ESP32_AP-Flasher/data/fonts/calibrib120.vlw
Normal file
Binary file not shown.
BIN
ESP32_AP-Flasher/data/fonts/calibrib150.vlw
Normal file
BIN
ESP32_AP-Flasher/data/fonts/calibrib150.vlw
Normal file
Binary file not shown.
BIN
ESP32_AP-Flasher/data/fonts/calibrib30.vlw
Normal file
BIN
ESP32_AP-Flasher/data/fonts/calibrib30.vlw
Normal file
Binary file not shown.
BIN
ESP32_AP-Flasher/data/fonts/calibrib50.vlw
Normal file
BIN
ESP32_AP-Flasher/data/fonts/calibrib50.vlw
Normal file
Binary file not shown.
BIN
ESP32_AP-Flasher/data/fonts/calibrib60.vlw
Normal file
BIN
ESP32_AP-Flasher/data/fonts/calibrib60.vlw
Normal file
Binary file not shown.
BIN
ESP32_AP-Flasher/data/fonts/calibrib80.vlw
Normal file
BIN
ESP32_AP-Flasher/data/fonts/calibrib80.vlw
Normal file
Binary file not shown.
BIN
ESP32_AP-Flasher/data/fonts/twcondensed20.vlw
Normal file
BIN
ESP32_AP-Flasher/data/fonts/twcondensed20.vlw
Normal file
Binary file not shown.
BIN
ESP32_AP-Flasher/data/fonts/weathericons30.vlw
Normal file
BIN
ESP32_AP-Flasher/data/fonts/weathericons30.vlw
Normal file
Binary file not shown.
BIN
ESP32_AP-Flasher/data/fonts/weathericons70.vlw
Normal file
BIN
ESP32_AP-Flasher/data/fonts/weathericons70.vlw
Normal file
Binary file not shown.
BIN
ESP32_AP-Flasher/data/fonts/weathericons78.vlw
Normal file
BIN
ESP32_AP-Flasher/data/fonts/weathericons78.vlw
Normal file
Binary file not shown.
BIN
ESP32_AP-Flasher/data/gradient.jpg
Normal file
BIN
ESP32_AP-Flasher/data/gradient.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 17 KiB |
92
ESP32_AP-Flasher/data/tag_md5_db.json
Normal file
92
ESP32_AP-Flasher/data/tag_md5_db.json
Normal file
@@ -0,0 +1,92 @@
|
||||
[
|
||||
{
|
||||
"name":"1.54 White",
|
||||
"mac_offset":64518,
|
||||
"mac_format":1,
|
||||
"mac_suffix":"3410",
|
||||
"MD5":"EC4CF26432B8E250BD3C56EFA0B6E852",
|
||||
"type":0,
|
||||
"note":"White 1.54 033"
|
||||
},
|
||||
{
|
||||
"name":"1.54 Black NFC",
|
||||
"mac_offset":64518,
|
||||
"mac_format":1,
|
||||
"mac_suffix":"3430",
|
||||
"MD5":"F77F2BDA76EC584CFF143DFD74654354",
|
||||
"type":0,
|
||||
"note":"Black 1.54 032 w/NFC"
|
||||
},
|
||||
{
|
||||
"name":"Segmented UK",
|
||||
"mac_offset":30722,
|
||||
"mac_format":2,
|
||||
"mac_suffix":"0131",
|
||||
"MD5":"A27D978C48888FA045E8D3F90DC27029",
|
||||
"type":240,
|
||||
"note":"Segmented, UK"
|
||||
},
|
||||
{
|
||||
"name":"NoDisplay ZBS243 AP",
|
||||
"mac_offset":0,
|
||||
"mac_format":0,
|
||||
"mac_suffix":"0000",
|
||||
"MD5":"ABBAABBAD3ADD0D0CAFED3F90DC27029",
|
||||
"type":255,
|
||||
"note":"NoDisplay ZBS243 AP"
|
||||
},
|
||||
{
|
||||
"name":"4.2 Black + NFC",
|
||||
"mac_offset":64518,
|
||||
"mac_format":1,
|
||||
"mac_suffix":"4830",
|
||||
"MD5":"A7E30D170AC8F2569F4D7E815594FB9F",
|
||||
"type":2,
|
||||
"note":"4.2 v033 FW"
|
||||
},
|
||||
{
|
||||
"name":"2.9 White",
|
||||
"mac_offset":64518,
|
||||
"mac_format":1,
|
||||
"mac_suffix":"3B10",
|
||||
"MD5":"A41315C16017BA64CF5BE12DFCA0F781",
|
||||
"type":1,
|
||||
"note":"2.9 v033 FW"
|
||||
},
|
||||
{
|
||||
"name":"2.9 White - 2nd",
|
||||
"mac_offset":64518,
|
||||
"mac_format":1,
|
||||
"mac_suffix":"3B10",
|
||||
"MD5":"4FFCE4402E1540824BDDA62BC93FC3D9",
|
||||
"type":1,
|
||||
"note":"2.9 v033 FW - 2nd version"
|
||||
},
|
||||
{
|
||||
"name":"2.9 White + NFC UC8151",
|
||||
"mac_offset":64518,
|
||||
"mac_format":1,
|
||||
"mac_suffix":"3B30",
|
||||
"MD5":"F707C29A240D7DD237C3C1265FAF4B68",
|
||||
"type":17,
|
||||
"note":"2.9 White + NFC UC8151 V032 FW"
|
||||
},
|
||||
{
|
||||
"name":"2.9 White + NFC UC8151",
|
||||
"mac_offset":64518,
|
||||
"mac_format":1,
|
||||
"mac_suffix":"3B30",
|
||||
"MD5":"D7E45FEECA144BB4ED750BEADE2CAD0A",
|
||||
"type":17,
|
||||
"note":"2.9 White + NFC UC8151 V033 FW"
|
||||
},
|
||||
{
|
||||
"name":"2.9 White + NFC UC8151",
|
||||
"mac_offset":64518,
|
||||
"mac_format":1,
|
||||
"mac_suffix":"3B30",
|
||||
"MD5":"4643F4F74321FC9B4F9F0ECBD93B74AB",
|
||||
"type":17,
|
||||
"note":"2.9 White + NFC UC8151 V025 FW"
|
||||
}
|
||||
]
|
||||
15
ESP32_AP-Flasher/data/update_actions.json
Normal file
15
ESP32_AP-Flasher/data/update_actions.json
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"deletefile": [
|
||||
"/fonts/calibrib62.vlw",
|
||||
"/fonts/GillSC16.vlw",
|
||||
"/fonts/GillSC20.vlw",
|
||||
"/fonts/numbers1-1.vlw",
|
||||
"/fonts/numbers1-2.vlw",
|
||||
"/fonts/numbers2-1.vlw",
|
||||
"/fonts/numbers2-2.vlw",
|
||||
"/fonts/numbers3-1.vlw",
|
||||
"/fonts/numbers3-2.vlw",
|
||||
"/fonts/tw20.vlw",
|
||||
"/fonts/twbold20.vlw"
|
||||
]
|
||||
}
|
||||
537
ESP32_AP-Flasher/data/www/content_cards.json
Normal file
537
ESP32_AP-Flasher/data/www/content_cards.json
Normal file
@@ -0,0 +1,537 @@
|
||||
[
|
||||
{
|
||||
"id": 0,
|
||||
"name": "Static image",
|
||||
"desc": "Shows a static image, from file system, painter or external source",
|
||||
"hwtype": [
|
||||
0,
|
||||
1,
|
||||
2,
|
||||
17
|
||||
],
|
||||
"param": [
|
||||
{
|
||||
"key": "filename",
|
||||
"name": "Filename",
|
||||
"desc": "Local filename on the littlefs drive",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"key": "timetolive",
|
||||
"name": "TimeToLive",
|
||||
"desc": "Amount (minutes) that this image will stay valid. The tag might not respond meanwhile",
|
||||
"type": "int"
|
||||
},
|
||||
{
|
||||
"key": "dither",
|
||||
"name": "Dithering",
|
||||
"desc": "Turn halftone dithering on or off. Turn it on when displaying photos. For texts, you better leave if off",
|
||||
"type": "select",
|
||||
"options": {
|
||||
"0": "off",
|
||||
"1": "on"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 1,
|
||||
"name": "Current date",
|
||||
"desc": "Shows the current date",
|
||||
"hwtype": [
|
||||
0,
|
||||
1,
|
||||
2,
|
||||
17,
|
||||
240
|
||||
],
|
||||
"param": []
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"name": "Count days",
|
||||
"desc": "Counts days, starting with the value below. If the count value gets higher than the threshold, the number is displayed in red, otherwise it's black",
|
||||
"hwtype": [
|
||||
0,
|
||||
1,
|
||||
2,
|
||||
17,
|
||||
240
|
||||
],
|
||||
"param": [
|
||||
{
|
||||
"key": "counter",
|
||||
"name": "Counter value",
|
||||
"desc": "Current value",
|
||||
"type": "int"
|
||||
},
|
||||
{
|
||||
"key": "thresholdred",
|
||||
"name": "Threshold",
|
||||
"desc": "Value is displayed in red if higher than the threshold",
|
||||
"type": "int",
|
||||
"hwtype": [
|
||||
0,
|
||||
1,
|
||||
2,
|
||||
17
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"name": "Count hours",
|
||||
"desc": "Counts hours, starting with the value below. If the count value gets higher than the threshold, the number is displayed in red, otherwise it's black",
|
||||
"hwtype": [
|
||||
0,
|
||||
1,
|
||||
2,
|
||||
17,
|
||||
240
|
||||
],
|
||||
"param": [
|
||||
{
|
||||
"key": "counter",
|
||||
"name": "Counter",
|
||||
"desc": "Current value",
|
||||
"type": "int"
|
||||
},
|
||||
{
|
||||
"key": "thresholdred",
|
||||
"name": "Threshold",
|
||||
"desc": "Value is displayed in red if higher than the threshold",
|
||||
"type": "int",
|
||||
"hwtype": [
|
||||
0,
|
||||
1,
|
||||
2,
|
||||
17
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 4,
|
||||
"name": "Current weather",
|
||||
"desc": "Current weather. Weather data by Open-Meteo.com",
|
||||
"hwtype": [
|
||||
0,
|
||||
1,
|
||||
2,
|
||||
17,
|
||||
240
|
||||
],
|
||||
"param": [
|
||||
{
|
||||
"key": "location",
|
||||
"name": "Location",
|
||||
"desc": "Name of the city. This is used to lookup the lat/long data, and to display as the title",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"key": "#lat",
|
||||
"name": "Lat",
|
||||
"desc": "Latitude (set automatic when generating image)",
|
||||
"type": "ro"
|
||||
},
|
||||
{
|
||||
"key": "#lon",
|
||||
"name": "Lon",
|
||||
"desc": "Longitude (set automatic when generating image)",
|
||||
"type": "ro"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 8,
|
||||
"name": "Weather forecast",
|
||||
"desc": "Weather forecast for the next five days. Weather data by Open-Meteo.com",
|
||||
"hwtype": [
|
||||
1,
|
||||
2,
|
||||
17
|
||||
],
|
||||
"param": [
|
||||
{
|
||||
"key": "location",
|
||||
"name": "Location",
|
||||
"desc": "Name of the city. This is used to lookup the lat/long data, and to display as the title",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"key": "#lat",
|
||||
"name": "Lat",
|
||||
"desc": "Latitude (set automatic when generating image)",
|
||||
"type": "ro"
|
||||
},
|
||||
{
|
||||
"key": "#lon",
|
||||
"name": "Lon",
|
||||
"desc": "Longitude (set automatic when generating image)",
|
||||
"type": "ro"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 16,
|
||||
"name": "Buienradar",
|
||||
"desc": "Dutch rain predictions for the next two hours. Only works for locations in the Netherlands and Belgium.",
|
||||
"hwtype": [
|
||||
1,
|
||||
17
|
||||
],
|
||||
"param": [
|
||||
{
|
||||
"key": "location",
|
||||
"name": "Location",
|
||||
"desc": "Name of the city. This is used to lookup the lat/long data, and to display as the title",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"key": "ttl",
|
||||
"name": "Time To Live",
|
||||
"desc": "How often (in minutes) should this be refreshed. Minimum is 5 minutes, but will shorten battery lifetime",
|
||||
"type": "int"
|
||||
},
|
||||
{
|
||||
"key": "#lat",
|
||||
"name": "Lat",
|
||||
"desc": "Latitude (set automatic when generating image)",
|
||||
"type": "ro"
|
||||
},
|
||||
{
|
||||
"key": "#lon",
|
||||
"name": "Lon",
|
||||
"desc": "Longitude (set automatic when generating image)",
|
||||
"type": "ro"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 9,
|
||||
"name": "RSS feed",
|
||||
"desc": "Gets an RSS feed, and display the first few lines of it",
|
||||
"hwtype": [
|
||||
1,
|
||||
2,
|
||||
17
|
||||
],
|
||||
"param": [
|
||||
{
|
||||
"key": "title",
|
||||
"name": "Title",
|
||||
"desc": "Displayed title",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"key": "url",
|
||||
"name": "URL",
|
||||
"desc": "Full URL of the RSS feed",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"key": "interval",
|
||||
"name": "Interval",
|
||||
"desc": "How often (in minutes) the feed is being refreshed",
|
||||
"type": "int"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 7,
|
||||
"name": "Image URL",
|
||||
"desc": "Gets an external image and displays it",
|
||||
"hwtype": [
|
||||
0,
|
||||
1,
|
||||
2,
|
||||
17
|
||||
],
|
||||
"param": [
|
||||
{
|
||||
"key": "url",
|
||||
"name": "URL",
|
||||
"desc": "Full URL of the image. Image should be in jpeg format (non-progressive), and with exactly the right resolution for the screen (eg 128x296 or 152x152). Will be auto-rotated. Colors will be dithered",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"key": "interval",
|
||||
"name": "Interval",
|
||||
"desc": "How often (in minutes) the image is being fetched",
|
||||
"type": "int"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 10,
|
||||
"name": "QR code",
|
||||
"desc": "Displayes a full screen QR code",
|
||||
"hwtype": [
|
||||
0,
|
||||
1,
|
||||
2,
|
||||
17
|
||||
],
|
||||
"param": [
|
||||
{
|
||||
"key": "title",
|
||||
"name": "Title",
|
||||
"desc": "Displayed title",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"key": "qr-content",
|
||||
"name": "QR content",
|
||||
"desc": "Any content that can be coded into a QR code",
|
||||
"type": "text"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 11,
|
||||
"name": "Google calendar",
|
||||
"desc": "Displays the current and upcoming appointments (next 24 hours) from a Google calendar. To let this work, you need a small Google Apps Script to interface with your calendar. See documentation on github how to do that",
|
||||
"hwtype": [
|
||||
1,
|
||||
2,
|
||||
17
|
||||
],
|
||||
"param": [
|
||||
{
|
||||
"key": "title",
|
||||
"name": "Title",
|
||||
"desc": "Displayed title",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"key": "apps_script_url",
|
||||
"name": "Apps Script URL",
|
||||
"desc": "URL given by Google Apps Script",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"key": "interval",
|
||||
"name": "Interval",
|
||||
"desc": "How often (in minutes) the calendar is being refreshed",
|
||||
"type": "int"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 5,
|
||||
"name": "Firmware update",
|
||||
"desc": "To update tag firmware",
|
||||
"hwtype": [
|
||||
0,
|
||||
1,
|
||||
2,
|
||||
17,
|
||||
240
|
||||
],
|
||||
"param": [
|
||||
{
|
||||
"key": "filename",
|
||||
"name": "Filename",
|
||||
"desc": "Local file on littlefs partition",
|
||||
"type": "text"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 12,
|
||||
"name": "Remote content",
|
||||
"desc": "Content is generated by a different Access Point",
|
||||
"hwtype": []
|
||||
},
|
||||
{
|
||||
"id": 13,
|
||||
"name": "Set segments",
|
||||
"desc": "Used for debugging. Work in progress",
|
||||
"hwtype": [
|
||||
240
|
||||
],
|
||||
"param": [
|
||||
{
|
||||
"key": "line1",
|
||||
"name": "line 1",
|
||||
"desc": "8888",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"key": "line2",
|
||||
"name": "line 2",
|
||||
"desc": "88",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"key": "line3",
|
||||
"name": "line 3",
|
||||
"desc": "8888",
|
||||
"type": "text"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 14,
|
||||
"name": "Set NFC URL",
|
||||
"desc": "Send the URL to the NFC chip. The URL is transmitted to a NFC reader (like your phone) if you hold it next to the tag",
|
||||
"hwtype": [
|
||||
0,
|
||||
17
|
||||
],
|
||||
"capabilities": 64,
|
||||
"param": [
|
||||
{
|
||||
"key": "url",
|
||||
"name": "URL",
|
||||
"desc": "Full URL",
|
||||
"type": "text"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 15,
|
||||
"name": "Send custom LUT",
|
||||
"desc": "EXPERIMENTAL. Don't use. YOU RISK DAMAGING YOUR SCREEN.",
|
||||
"hwtype": [
|
||||
1
|
||||
],
|
||||
"capabilities": 4,
|
||||
"param": [
|
||||
{
|
||||
"key": "bytes",
|
||||
"name": "bytes",
|
||||
"desc": "76 bytes, formatted as 0x00,0x00,...",
|
||||
"type": "text"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 17,
|
||||
"name": "Send Command",
|
||||
"desc": "Send a command to a tag to execute",
|
||||
"hwtype": [
|
||||
0,
|
||||
1,
|
||||
2,
|
||||
17,
|
||||
240
|
||||
],
|
||||
"param": [
|
||||
{
|
||||
"key": "cmd",
|
||||
"name": "CMD",
|
||||
"desc": "Action",
|
||||
"type": "select",
|
||||
"options": {
|
||||
"0": "Reboot",
|
||||
"1": "Scan Channels",
|
||||
"2": "Clear settings"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 18,
|
||||
"name": "Set Tag Config",
|
||||
"desc": "Sets tag options. The options you see below are the default options. This may or may not match current tag settings",
|
||||
"hwtype": [
|
||||
0,
|
||||
1,
|
||||
2,
|
||||
17,
|
||||
240
|
||||
],
|
||||
"param": [
|
||||
{
|
||||
"key": "fastboot",
|
||||
"name": "Boot method",
|
||||
"desc": "How the tag should boot, fast or normal",
|
||||
"type": "select",
|
||||
"options": {
|
||||
"0": "-Normal boot",
|
||||
"1": "Fast boot"
|
||||
}
|
||||
},
|
||||
{
|
||||
"key": "rfwake",
|
||||
"name": "RF Wake",
|
||||
"desc": "If the tag should support RF wake or not. This adds a 0.9µA current draw",
|
||||
"type": "select",
|
||||
"options": {
|
||||
"0": "-Disabled",
|
||||
"1": "Enabled"
|
||||
}
|
||||
},
|
||||
{
|
||||
"key": "tagroaming",
|
||||
"name": "Tag Roaming",
|
||||
"desc": "If enabled, the tag will periodically scan for AP's and will switch to a different channel if a stronger signal is found",
|
||||
"type": "select",
|
||||
"options": {
|
||||
"0": "-Disabled",
|
||||
"1": "Enabled"
|
||||
}
|
||||
},
|
||||
{
|
||||
"key": "tagscanontimeout",
|
||||
"name": "Scan for AP on timeout",
|
||||
"desc": "If a tag hasn't found an AP for an hour, should it rescan the channels for another AP?",
|
||||
"type": "select",
|
||||
"options": {
|
||||
"1": "-Enabled",
|
||||
"0": "Disabled"
|
||||
}
|
||||
},
|
||||
{
|
||||
"key": "showlowbat",
|
||||
"name": "Low Battery symbol",
|
||||
"desc": "Should the tag display the 'low battery' symbol if the battery a voltage threshold has been reached?",
|
||||
"type": "select",
|
||||
"options": {
|
||||
"1": "-Enabled",
|
||||
"0": "Disabled"
|
||||
}
|
||||
},
|
||||
{
|
||||
"key": "shownorf",
|
||||
"name": "No AP symbol",
|
||||
"desc": "Should the tag display the 'No-signal/AP' symbol if it hasn't been able to contact an AP?",
|
||||
"type": "select",
|
||||
"options": {
|
||||
"1": "-Enabled",
|
||||
"0": "Disabled"
|
||||
}
|
||||
},
|
||||
{
|
||||
"key": "lowvoltage",
|
||||
"name": "Low voltage threshold",
|
||||
"desc": "Below what voltage should the tag display the 'low bat' symbol?",
|
||||
"type": "select",
|
||||
"options": {
|
||||
"2600": "-2.6v",
|
||||
"2500": "2.5v",
|
||||
"2400": "2.4v",
|
||||
"2300": "2.3v",
|
||||
"2200": "2.2v"
|
||||
}
|
||||
},
|
||||
{
|
||||
"key": "fixedchannel",
|
||||
"name": "Fixed Channel",
|
||||
"desc": "What channel should the tag initially join?",
|
||||
"type": "select",
|
||||
"options": {
|
||||
"0": "-Auto",
|
||||
"11": "11",
|
||||
"15": "15",
|
||||
"20": "20",
|
||||
"25": "25",
|
||||
"26": "26",
|
||||
"27": "27"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
BIN
ESP32_AP-Flasher/data/www/favicon.ico
Normal file
BIN
ESP32_AP-Flasher/data/www/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 7.0 KiB |
209
ESP32_AP-Flasher/data/www/index.html
Normal file
209
ESP32_AP-Flasher/data/www/index.html
Normal file
@@ -0,0 +1,209 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta http-equiv="content-type" content="text/html; charset=utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=0" />
|
||||
|
||||
<title>Open EPaper Link Access Point</title>
|
||||
<link rel="stylesheet" href="main.css" type="text/css" />
|
||||
<link rel="icon" type="image/vnd.icon" href="favicon.ico">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<header>
|
||||
<div class="logo">Open EPaper Link Access Point</div>
|
||||
</header>
|
||||
|
||||
<div id="configbox">
|
||||
<div class="closebtn">✖</div>
|
||||
<h3 id="cfgmac">00000000</h3>
|
||||
<p>
|
||||
<label for="cfgalias">Alias</label>
|
||||
<input id="cfgalias" type="text">
|
||||
</p>
|
||||
<p>
|
||||
<label for="cfgcontent">Content</label>
|
||||
<select id="cfgcontent" onchange="contentselected()">
|
||||
</select>
|
||||
<button id="paintbutton"><i>A</i>🖌</button>
|
||||
</p>
|
||||
<div id="customoptions"></div>
|
||||
<div id="advancedoptions" style="height: 0px;">
|
||||
<p>Advanced options</p>
|
||||
<p>
|
||||
<label for="cfgrotate">Rotate image</label>
|
||||
<select id="cfgrotate">
|
||||
<option value="0">0 degrees</option>
|
||||
</select>
|
||||
</p>
|
||||
<p>
|
||||
<label for="cfglut">LUT</label>
|
||||
<select id="cfglut">
|
||||
<option value="0">auto</option>
|
||||
</select>
|
||||
</p>
|
||||
<p>
|
||||
<button id="cfgrefresh">force refresh</button>
|
||||
<button id="cfgclrpending">clear pending</button>
|
||||
<button id="cfgdelete"><img src="data:image/gif;base64,R0lGODlhEAAQAPMAANXV1e3t7d/f39HR0dvb2/Hx8dTU1OLi4urq6mZmZpmZmf///wAAAAAAAAAAAAAAACH5BAEAAAwALAAAAAAQABAAAARBkMlJq71Yrp3ZXkr4WWCYnOZSgQVyEMYwJCq1nHhe20qgCAoA7QLyAYU7njE4JPV+zOSkCEUSFbmTVPPpbjvgTAQAOw==
|
||||
"></button>
|
||||
</p>
|
||||
</div>
|
||||
<p id="savebar">
|
||||
<span><input type="button" value="Save" id="cfgsave"></span>
|
||||
<span id="cfgmore" title="advanced options">🞃</span>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div id="apconfigbox">
|
||||
<div class="closebtn">✖</div>
|
||||
<h3>Access Point config</h3>
|
||||
<p>
|
||||
<label for="apcfgalias">Alias</label>
|
||||
<input id="apcfgalias" type="text">
|
||||
</p>
|
||||
<p>
|
||||
<label for="apcfgchid">Channel</label>
|
||||
<select id="apcfgchid">
|
||||
<option value="0">auto</option>
|
||||
<option value="11">11</option>
|
||||
<option value="15">15</option>
|
||||
<option value="20">20</option>
|
||||
<option value="25">25</option>
|
||||
<option value="26">26</option>
|
||||
<option value="27">27</option>
|
||||
</select>
|
||||
</p>
|
||||
<p>
|
||||
<label for="apcfgledbrightness">LED brightness</label>
|
||||
<select id="apcfgledbrightness">
|
||||
<option value="-1">off</option>
|
||||
<option value="64">25%</option>
|
||||
<option value="128">50%</option>
|
||||
<option value="192">75%</option>
|
||||
<option value="255">100%</option>
|
||||
</select>
|
||||
</p>
|
||||
<p>
|
||||
<label for="apcfglanguage">Content language</label>
|
||||
<select id="apcfglanguage">
|
||||
<option value="0">EN English</option>
|
||||
<option value="1">NL Nederlands</option>
|
||||
<option value="2">DE Deutsch</option>
|
||||
</select>
|
||||
</p>
|
||||
<p title="Depending on the content, a tag can sleep for
|
||||
longer periods when no updates are expected
|
||||
(like a date display). This setting specifies
|
||||
the maximum sleep time.">
|
||||
<label for="apclatency">Maximum sleep</label>
|
||||
<select id="apclatency">
|
||||
<option value="0">shortest (40 sec)</option>
|
||||
<option value="5">5 minutes</option>
|
||||
<option value="10">10 minute</option>
|
||||
<option value="30">30 minutes</option>
|
||||
<option value="60">1 hour</option>
|
||||
</select>
|
||||
</p>
|
||||
<p title="If connected to the website, don't sleep extra.
|
||||
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>
|
||||
</select>
|
||||
</p>
|
||||
<p>
|
||||
<input type="button" value="Save" id="apcfgsave">
|
||||
</p>
|
||||
<p>
|
||||
Active access points:<br>
|
||||
<table id="aptable">
|
||||
<tr>
|
||||
<th>ip</th>
|
||||
<th>alias</th>
|
||||
<th>tags</th>
|
||||
<th>ch</th>
|
||||
<th>AP ver</th>
|
||||
</tr>
|
||||
</table>
|
||||
</p>
|
||||
<p>
|
||||
<span id="rebootbutton">reboot AP</span>
|
||||
<a href="/backup_db" id="downloadDBbutton">download tagDB</a>
|
||||
<span id="updatebutton">update</span>
|
||||
</p>
|
||||
<p>
|
||||
<a href="https://github.com/jjwbruijn/OpenEPaperLink" target="_new">Github OpenEPaperLink</a>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div id="apupdatebox">
|
||||
<div class="closebtn">✖</div>
|
||||
<h3>Update dashboard</h3>
|
||||
<div id="easyupdate"></div>
|
||||
<div id="advanceddiv">
|
||||
<div id="releasetable"></div>
|
||||
<div id="rollbackOption" style="display:none"><button id="rollbackBtn">Rollback to previous firmware</button></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<form>
|
||||
<div class="container">
|
||||
|
||||
<div class="window">
|
||||
|
||||
<div class="actionbox">
|
||||
<div>
|
||||
<div>Currently active tags:</div>
|
||||
<div><span id="runstate"></div>
|
||||
<div><span id="apstatecolor">⬤</span> <span id="apstate">loading</span></div>
|
||||
<div><span id="apconfigbutton">AP config</span></div>
|
||||
<div><a href="/edit" target="littlefs" class="filebutton">edit contentFS</a></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="taglist" class="taglist">
|
||||
<div class="tagcard" id="tagtemplate">
|
||||
<div class="currimg"><canvas class="tagimg"></div>
|
||||
<div class="mac"></div>
|
||||
<div class="alias"></div>
|
||||
<div class="model"></div>
|
||||
|
||||
<div class="received">
|
||||
RSSI <div class="rssi"></div>, LQI <div class="lqi"></div><div class="temperature"></div><div class="batt"></div>
|
||||
</div>
|
||||
|
||||
<div class="contentmode"></div>
|
||||
<div class="lastseen"></div>
|
||||
<div class="nextcheckin"></div>
|
||||
<div class="nextupdate"></div>
|
||||
<div class="corner">
|
||||
<div class="pendingicon" title="A new message is waiting for the tag to pick up">↻</div>
|
||||
<div class="warningicon" title="This tag has not been seen for a long time">⚠</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="logbox">
|
||||
<p>
|
||||
<span>logging</span>
|
||||
<span><img id="clearlog" src="data:image/gif;base64,R0lGODlhEAAQAPMAANXV1e3t7d/f39HR0dvb2/Hx8dTU1OLi4urq6mZmZpmZmf///wAAAAAAAAAAAAAAACH5BAEAAAwALAAAAAAQABAAAARBkMlJq71Yrp3ZXkr4WWCYnOZSgQVyEMYwJCq1nHhe20qgCAoA7QLyAYU7njE4JPV+zOSkCEUSFbmTVPPpbjvgTAQAOw==
|
||||
"></span>
|
||||
<span id="sysinfo"></span>
|
||||
</p>
|
||||
<ul id="messages" class="messages">
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<script src="main.js"></script>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
||||
596
ESP32_AP-Flasher/data/www/main.css
Normal file
596
ESP32_AP-Flasher/data/www/main.css
Normal file
@@ -0,0 +1,596 @@
|
||||
* {
|
||||
margin:0;
|
||||
padding:0;
|
||||
border:0;
|
||||
list-style-type: none;
|
||||
outline: none;
|
||||
font-weight: 400;
|
||||
-webkit-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
font-smooth: auto;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
|
||||
html, body {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
body {
|
||||
font-size: 12px;
|
||||
font-family: Helvetica, Arial, Verdana, sans-serif;
|
||||
line-height: 1.5;
|
||||
background-color: #e4e4e0;
|
||||
}
|
||||
|
||||
header {
|
||||
height: 50px;
|
||||
background-color: #646260;
|
||||
}
|
||||
|
||||
label {
|
||||
width: 120px;
|
||||
display: inline-block;
|
||||
vertical-align: top;
|
||||
padding: 3px 0px;
|
||||
}
|
||||
|
||||
.logo {
|
||||
margin: 0 auto;
|
||||
height: 50px;
|
||||
text-indent: 50px;
|
||||
overflow:hidden;
|
||||
font-size: 2.5em;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.window {
|
||||
margin: 0 auto;
|
||||
max-width: 94%;
|
||||
}
|
||||
|
||||
.actionbox>div:first-child {
|
||||
padding: 10px;
|
||||
background-color: white;
|
||||
margin: 5px;
|
||||
}
|
||||
|
||||
.actionbox>div:first-child>div:first-child {
|
||||
flex-grow: 2;
|
||||
}
|
||||
|
||||
.actionbox>div {
|
||||
display:flex;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
#rebootbutton, #updatebutton, #downloadDBbutton, #apconfigbutton, .filebutton {
|
||||
padding: 2px 5px;
|
||||
background-color: #cccccc;
|
||||
text-decoration: none;
|
||||
color: black;
|
||||
cursor: pointer;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.columns div {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
textarea,
|
||||
input.text,
|
||||
input[type="text"],
|
||||
input[type="button"],
|
||||
input[type="submit"],
|
||||
button {
|
||||
appearance: none;
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
input {
|
||||
border: solid 1px #cccccc;
|
||||
padding: 4px;
|
||||
border-radius: 0px;
|
||||
}
|
||||
|
||||
input[type=button], button {
|
||||
border: 0px;
|
||||
padding: 4px 10px;
|
||||
cursor:pointer;
|
||||
}
|
||||
|
||||
input[type=button]:hover,
|
||||
button:hover {
|
||||
background-color:#aaaaaa;
|
||||
}
|
||||
|
||||
select {
|
||||
padding: 3px 4px;
|
||||
border-radius: 0px;
|
||||
border: solid 1px #cccccc;
|
||||
}
|
||||
|
||||
#configbox, #apconfigbox, #apupdatebox {
|
||||
display: none;
|
||||
position: fixed;
|
||||
top: 65px;
|
||||
left: 15px;
|
||||
width: 380px;
|
||||
padding: 15px;
|
||||
background-color: #f0e6d3;
|
||||
z-index: 999;
|
||||
box-shadow: 7px 10px 52px -19px rgba(0, 0, 0, 0.63);
|
||||
}
|
||||
|
||||
#configbox p, #apconfigbox p, #apupdatebox p {
|
||||
padding: 5px;
|
||||
display: flex;
|
||||
gap: 5px;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
#configbox h3, #apconfigbox h3, #apupdatebox h3 {
|
||||
font-size: 1.5em;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
#configbox input, #apconfigbox input {
|
||||
border: solid 1px #cccccc;
|
||||
}
|
||||
|
||||
#configbox input[type=number] {
|
||||
width: 80px;
|
||||
}
|
||||
|
||||
#advancedoptions {
|
||||
overflow: hidden;
|
||||
transition: height 0.3s ease;
|
||||
}
|
||||
|
||||
#advancedoptions p:first-child {
|
||||
font-weight: 700;
|
||||
font-size: 1.2em;
|
||||
}
|
||||
|
||||
#savebar {
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
#savebar:first-child {
|
||||
flex-grow: 2;
|
||||
}
|
||||
|
||||
#cfgmore {
|
||||
padding: 2px 5px;
|
||||
font-weight: 700;
|
||||
font-size: 1.2em;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#apconfigbox {
|
||||
background-color: #e6f0d3;
|
||||
}
|
||||
|
||||
#aptable {
|
||||
width: 100%;
|
||||
border-spacing: 0;
|
||||
}
|
||||
|
||||
#aptable th {
|
||||
text-align: left;
|
||||
background-color: #00000020;
|
||||
padding: 0px 3px;
|
||||
}
|
||||
|
||||
#aptable th, #aptable td {
|
||||
border-right: 1px solid #000010;
|
||||
padding: 0px 3px;
|
||||
}
|
||||
|
||||
#aptable td:nth-child(1), #aptable th:nth-child(1) {
|
||||
border-left: 1px solid #000010;
|
||||
}
|
||||
|
||||
#aptable td:nth-child(3),
|
||||
#aptable td:nth-child(4) {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
#apupdatebox {
|
||||
background-color: #f0d0c8;
|
||||
width: 700px;
|
||||
padding-bottom: 20px;
|
||||
border: 1px solid #d0b0a8;
|
||||
}
|
||||
|
||||
#cfgdelete {
|
||||
cursor: pointer;
|
||||
padding: 2px 10px;
|
||||
}
|
||||
|
||||
.closebtn {
|
||||
border: 1px solid black;
|
||||
float: right;
|
||||
width: 19px;
|
||||
height: 20px;
|
||||
font-size: 1.1em;
|
||||
text-align: center;
|
||||
margin: 5px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.logbox {
|
||||
margin: 5px;
|
||||
}
|
||||
|
||||
.logbox p {
|
||||
background-color: #ffffff;
|
||||
padding: 5px 10px;
|
||||
}
|
||||
|
||||
.logbox img {
|
||||
vertical-align: bottom;
|
||||
cursor:pointer;
|
||||
}
|
||||
|
||||
.logbox #sysinfo {
|
||||
float: right;
|
||||
}
|
||||
|
||||
.taglist {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
#tagtemplate {
|
||||
display:none;
|
||||
}
|
||||
|
||||
.tagcard {
|
||||
width: 225px;
|
||||
position: relative;
|
||||
min-height: 170px;
|
||||
margin: 5px;
|
||||
padding: 5px;
|
||||
background-color: #ffffff;
|
||||
border: 1px solid #cccccc;
|
||||
transition: box-shadow 0.3s ease;
|
||||
}
|
||||
|
||||
.tagcard:hover {
|
||||
cursor:pointer;
|
||||
box-shadow: 7px 10px 52px -19px rgba(0, 0, 0, 0.63);
|
||||
filter: brightness(1.02);
|
||||
}
|
||||
|
||||
.tagflash {
|
||||
animation: tagflash 1s;
|
||||
}
|
||||
|
||||
.tagpending {
|
||||
animation: pending 1.5s ease infinite;
|
||||
}
|
||||
|
||||
.currimg {
|
||||
float: right;
|
||||
}
|
||||
|
||||
.currimg img, .currimg canvas {
|
||||
max-width: 50px;
|
||||
border: 1px solid #c0c0c0;
|
||||
}
|
||||
|
||||
.mac {
|
||||
font-size: 0.9em;
|
||||
cursor:pointer;
|
||||
}
|
||||
|
||||
.alias {
|
||||
font-size: 1.4em;
|
||||
font-weight: bold;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.received {
|
||||
display: flex;
|
||||
font-size: .85em;
|
||||
padding-bottom: 5px;
|
||||
}
|
||||
.received div {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.contentmode {
|
||||
font-weight: bold;
|
||||
font-size: 1.4em;
|
||||
padding-bottom: 5px;
|
||||
}
|
||||
|
||||
.lastseen, .nextcheckin, .nextupdate {
|
||||
font-size: 0.9em;
|
||||
}
|
||||
|
||||
.lastseen span,
|
||||
.nextcheckin span,
|
||||
.nextupdate span {
|
||||
width:105px;
|
||||
display:inline-block;
|
||||
}
|
||||
.corner {
|
||||
position: absolute;
|
||||
right: 0px;
|
||||
bottom: 0px;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
.pendingicon {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
background-color: rgb(7, 174, 230);
|
||||
font-size: 1.2em;
|
||||
text-align: center;
|
||||
font-weight: bold;
|
||||
display: none;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
.warningicon {
|
||||
display:none;
|
||||
font-size: 1.3em;
|
||||
background-color: yellow;
|
||||
color: black;
|
||||
height: 20px;
|
||||
width: 20px;
|
||||
vertical-align: top;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
ul.messages {
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
ul.messages li {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
ul.messages li.new {
|
||||
animation-name: new;
|
||||
animation-duration: 1400ms;
|
||||
animation-iteration-count: 1;
|
||||
animation-timing-function: ease-in-out;
|
||||
}
|
||||
|
||||
.error {
|
||||
color: red;
|
||||
}
|
||||
|
||||
#paintbutton {
|
||||
padding: 1px 3px;
|
||||
border: 1px solid black;
|
||||
font-size: 1.3em;
|
||||
vertical-align: top;
|
||||
margin-left:12px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#paintbutton:hover {
|
||||
background-color: #aaaaaa;
|
||||
}
|
||||
|
||||
/* painter */
|
||||
|
||||
#canvasdiv {
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
#canvasdiv canvas {
|
||||
border: 1px solid black;
|
||||
}
|
||||
|
||||
#buttonbar {
|
||||
padding: 5px;
|
||||
display: flex;
|
||||
gap: 5px;
|
||||
}
|
||||
|
||||
#buttonbar button,
|
||||
#layersdiv button {
|
||||
padding: 1px 2px;
|
||||
border: 1px solid #cccccc;
|
||||
background-color: #dddddd;
|
||||
width: 40px;
|
||||
}
|
||||
#buttonbar button {
|
||||
font-size: 1.2em;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
#buttonbar .active {
|
||||
background-color: #ffffff;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#buttonbar button:hover,
|
||||
#layersdiv button:hover {
|
||||
background-color: #cccccc;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#layersdiv {
|
||||
padding: 0px 5px;
|
||||
}
|
||||
|
||||
#layersdiv>div {
|
||||
display: flex;
|
||||
gap: 5px;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
#layersdiv input,
|
||||
#layersdiv select {
|
||||
padding: 2px;
|
||||
}
|
||||
|
||||
#font-select {
|
||||
width: 150px;
|
||||
}
|
||||
|
||||
#savebar button {
|
||||
border: solid 1px #666666;
|
||||
}
|
||||
|
||||
/* updatescreens */
|
||||
|
||||
#easyupdate{
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
#easyupdate button {
|
||||
display: block;
|
||||
margin: 15px 40px;
|
||||
padding: 10px 20px;
|
||||
}
|
||||
|
||||
#easyupdate a {
|
||||
cursor: pointer;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
#advanceddiv {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#releasetable {
|
||||
margin: 10px 0px;
|
||||
}
|
||||
|
||||
#releasetable table {
|
||||
border-spacing: 1px;
|
||||
}
|
||||
|
||||
#releasetable th {
|
||||
text-align: left;
|
||||
background-color: #ffffff;
|
||||
padding: 1px 5px;
|
||||
}
|
||||
|
||||
#releasetable td {
|
||||
background-color: #ffffff;
|
||||
padding: 1px 5px;
|
||||
min-width: 70px;
|
||||
}
|
||||
|
||||
#releasetable button {
|
||||
padding: 3px 10px;
|
||||
background-color: #e0e0e0;
|
||||
}
|
||||
|
||||
#releasetable button:hover {
|
||||
background-color: #a0a0a0;
|
||||
}
|
||||
|
||||
.console {
|
||||
width: 100%;
|
||||
background-color: black;
|
||||
font-family: 'lucida console','ui-monospace';
|
||||
color: white;
|
||||
padding: 5px 10px;
|
||||
margin: 20px 0px;
|
||||
padding-bottom: 25px;
|
||||
height: 400px;
|
||||
overflow-y: scroll;
|
||||
white-space: break-spaces;
|
||||
}
|
||||
.console div {
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
/* media */
|
||||
|
||||
@media(max-width: 460px) {
|
||||
.messages li div, ul.messages li div.date, ul.messages li div.message {
|
||||
display:block;
|
||||
position:relative;
|
||||
padding: 0;
|
||||
left: auto;
|
||||
}
|
||||
.messages li div.message, li.pending {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
ul.messages {
|
||||
padding-bottom: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes new {
|
||||
0% { background-color: rgba(255, 255, 204, 1); }
|
||||
50% { background-color: rgba(255, 255, 204, .5); }
|
||||
100% { background-color: rgba(255, 255, 204, 0); }
|
||||
}
|
||||
|
||||
@keyframes tagflash {
|
||||
0% { opacity: 1; }
|
||||
50% { opacity: 0; }
|
||||
100% { opacity: 1; }
|
||||
}
|
||||
|
||||
@keyframes pending {
|
||||
0% { }
|
||||
50% { background-color: #d4d4f5;}
|
||||
100% { }
|
||||
}
|
||||
|
||||
@media screen and (max-width: 480px) {
|
||||
/* styles for mobile devices in portrait mode */
|
||||
|
||||
body {
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.tagcard {
|
||||
width: 100%;
|
||||
min-height: 200px;
|
||||
}
|
||||
|
||||
.logbox #sysinfo {
|
||||
float: none;
|
||||
display: block;
|
||||
}
|
||||
|
||||
#configbox {
|
||||
top: 0px;
|
||||
left: 0px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.currimg {
|
||||
float: none;
|
||||
position: absolute;
|
||||
right: 5px;
|
||||
}
|
||||
|
||||
.currimg img,
|
||||
.currimg canvas {
|
||||
max-width: 60px;
|
||||
}
|
||||
|
||||
.logo {
|
||||
padding-top: 10px;
|
||||
text-indent: 0px;
|
||||
font-size: 1.8em;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.actionbox>div {
|
||||
gap: 5px;
|
||||
flex-flow: wrap;
|
||||
}
|
||||
|
||||
.actionbox>div:first-child>div:first-child {
|
||||
flex-basis: 100%;
|
||||
}
|
||||
}
|
||||
707
ESP32_AP-Flasher/data/www/main.js
Normal file
707
ESP32_AP-Flasher/data/www/main.js
Normal file
@@ -0,0 +1,707 @@
|
||||
const $ = document.querySelector.bind(document);
|
||||
|
||||
const WAKEUP_REASON_TIMED = 0;
|
||||
const WAKEUP_REASON_BOOT = 1;
|
||||
const WAKEUP_REASON_GPIO = 2;
|
||||
const WAKEUP_REASON_NFC = 3;
|
||||
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)"
|
||||
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];
|
||||
const colorTable = { 0: [255, 255, 255], 1: [0, 0, 0], 2: [255, 0, 0], 3: [150, 150, 150] };
|
||||
|
||||
const apstate = [
|
||||
{ state: "offline", color: "red" },
|
||||
{ state: "online", color: "green" },
|
||||
{ state: "flashing", color: "orange" },
|
||||
{ state: "wait for reset", color: "blue" },
|
||||
{ state: "requires power cycle", color: "purple" },
|
||||
{ state: "failed", color: "red" },
|
||||
{ state: "coming online", color: "yellow" }
|
||||
];
|
||||
const runstate = [
|
||||
{ state: "⏹︎ stopped" },
|
||||
{ state: "⏸pause" },
|
||||
{ state: "" }, // hide running
|
||||
{ state: "⏳︎ init" }
|
||||
];
|
||||
|
||||
const imageQueue = [];
|
||||
let isProcessing = false;
|
||||
let servertimediff = 0;
|
||||
let paintLoaded = false, paintShow = false;
|
||||
var 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 => {
|
||||
cardconfig = data;
|
||||
loadTags(0);
|
||||
connect();
|
||||
setInterval(updatecards, 1000);
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error:', error);
|
||||
alert("I can\'t load /www/content_cards.json.\r\nHave you upload it to the data partition?");
|
||||
});
|
||||
});
|
||||
|
||||
let socket;
|
||||
|
||||
function loadTags(pos) {
|
||||
fetch("/get_db?pos=" + pos)
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
processTags(data.tags);
|
||||
if (data.continu && data.continu > pos) loadTags(data.continu);
|
||||
})
|
||||
//.catch(error => showMessage('loadTags error: ' + error));
|
||||
}
|
||||
|
||||
function connect() {
|
||||
socket = new WebSocket("ws://" + location.host + "/ws");
|
||||
|
||||
socket.addEventListener("open", (event) => {
|
||||
showMessage("websocket connected");
|
||||
});
|
||||
|
||||
socket.addEventListener("message", (event) => {
|
||||
console.log(event.data)
|
||||
const msg = JSON.parse(event.data);
|
||||
if (msg.logMsg) {
|
||||
showMessage(msg.logMsg, false);
|
||||
}
|
||||
if (msg.errMsg) {
|
||||
showMessage(msg.errMsg, true);
|
||||
}
|
||||
if (msg.tags) {
|
||||
processTags(msg.tags);
|
||||
}
|
||||
if (msg.sys) {
|
||||
$('#sysinfo').innerHTML = 'free heap: ' + msg.sys.heap + ' bytes ┇ db size: ' + msg.sys.dbsize + ' bytes ┇ db record count: ' + msg.sys.recordcount + ' ┇ active filesystem free: ' + msg.sys.littlefsfree + ' bytes ' + convertSize(msg.sys.littlefsfree);
|
||||
if (msg.sys.apstate) {
|
||||
$("#apstatecolor").style.color = apstate[msg.sys.apstate].color;
|
||||
$("#apstate").innerHTML = apstate[msg.sys.apstate].state;
|
||||
$("#runstate").innerHTML = runstate[msg.sys.runstate].state;
|
||||
}
|
||||
servertimediff = (Date.now() / 1000) - msg.sys.currtime;
|
||||
}
|
||||
if (msg.apitem) {
|
||||
var 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;
|
||||
row.insertCell(3).innerHTML = msg.apitem.channel;
|
||||
row.insertCell(4).innerHTML = msg.apitem.version;
|
||||
}
|
||||
if (msg.console) {
|
||||
if (otamodule && typeof(otamodule.print) === "function") {
|
||||
let color = "#c0c0c0";
|
||||
if (msg.console.startsWith("Fail") || msg.console.startsWith("Err")) {
|
||||
color = "red";
|
||||
}
|
||||
otamodule.print(msg.console, color);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
socket.addEventListener("close", (event) => {
|
||||
showMessage(`websocket closed ${event.code}`);
|
||||
setTimeout(connect, 5000);
|
||||
});
|
||||
}
|
||||
|
||||
// Copied from
|
||||
// https://stackoverflow.com/questions/15900485/correct-way-to-convert-size-in-bytes-to-kb-mb-gb-in-javascript
|
||||
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"; }
|
||||
return "(" + bytes + ")";
|
||||
}
|
||||
|
||||
function processTags(tagArray) {
|
||||
for (const element of tagArray) {
|
||||
tagmac = element.mac;
|
||||
|
||||
var div = $('#tag' + tagmac);
|
||||
if (div == null) {
|
||||
div = $('#tagtemplate').cloneNode(true);
|
||||
div.setAttribute('id', 'tag' + tagmac);
|
||||
div.dataset.mac = tagmac;
|
||||
div.dataset.hwtype = -1;
|
||||
$('#taglist').appendChild(div);
|
||||
}
|
||||
|
||||
div.style.display = 'block';
|
||||
|
||||
if (element.contentMode == 255) {
|
||||
div.remove();
|
||||
showMessage(tagmac + " removed by remote AP");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (element.isexternal) {
|
||||
$('#tag' + tagmac + ' .mac').innerHTML = tagmac + " via ext AP";
|
||||
} else {
|
||||
$('#tag' + tagmac + ' .mac').innerHTML = tagmac;
|
||||
}
|
||||
let alias = element.alias;
|
||||
if (!alias) alias = tagmac.replace(/^0{1,4}/, '');
|
||||
if ($('#tag' + tagmac + ' .alias').innerHTML != alias) {
|
||||
$('#tag' + tagmac + ' .alias').innerHTML = alias;
|
||||
sortGrid();
|
||||
}
|
||||
|
||||
let contentDefObj = getContentDefById(element.contentMode);
|
||||
if (contentDefObj) $('#tag' + tagmac + ' .contentmode').innerHTML = contentDefObj.name;
|
||||
if (element.RSSI) {
|
||||
div.dataset.hwtype = element.hwType;
|
||||
$('#tag' + tagmac + ' .model').innerHTML = models[element.hwType];
|
||||
$('#tag' + tagmac + ' .rssi').innerHTML = element.RSSI;
|
||||
$('#tag' + tagmac + ' .lqi').innerHTML = element.LQI;
|
||||
$('#tag' + tagmac + ' .temperature').innerHTML = (element.temperature > 0 ? ", " + element.temperature + "°C": "");
|
||||
if (element.batteryMv == 0 || element.batteryMv == 1337) {
|
||||
$('#tag' + tagmac + ' .batt').innerHTML = "";
|
||||
} else {
|
||||
$('#tag' + tagmac + ' .batt').innerHTML = ", " + (element.batteryMv >= 2600 ? "≥" : "") + (element.batteryMv / 1000) + "V";
|
||||
}
|
||||
$('#tag' + tagmac + ' .received').style.opacity = "1";
|
||||
} else {
|
||||
$('#tag' + tagmac + ' .model').innerHTML = "waiting for hardware type";
|
||||
$('#tag' + tagmac + ' .received').style.opacity = "0";
|
||||
}
|
||||
|
||||
if (div.dataset.hash != element.hash && div.dataset.hwtype > -1 && (element.isexternal == false || element.contentMode != 12)) {
|
||||
loadImage(tagmac, '/current/' + tagmac + '.raw?' + (new Date()).getTime());
|
||||
div.dataset.hash = element.hash;
|
||||
}
|
||||
if (element.isexternal == true && 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 };
|
||||
$('#tag' + tagmac + ' .nextupdate').innerHTML = "<span>next update</span>" + date.toLocaleString('nl-NL', options);
|
||||
} else {
|
||||
$('#tag' + tagmac + ' .nextupdate').innerHTML = "";
|
||||
}
|
||||
|
||||
if (element.nextcheckin > 1672531200) {
|
||||
div.dataset.nextcheckin = element.nextcheckin;
|
||||
} else {
|
||||
div.dataset.nextcheckin = element.lastseen + 1800;
|
||||
}
|
||||
|
||||
div.style.opacity = '1';
|
||||
$('#tag' + tagmac + ' .lastseen').style.color = "black";
|
||||
div.classList.remove("tagpending");
|
||||
div.dataset.lastseen = element.lastseen;
|
||||
div.dataset.wakeupreason = element.wakeupReason;
|
||||
$('#tag' + tagmac + ' .warningicon').style.display = 'none';
|
||||
$('#tag' + tagmac).style.background = "#ffffff";
|
||||
if (element.contentMode == 12) $('#tag' + tagmac).style.background = "#e4e4e0";
|
||||
switch (parseInt(element.wakeupReason)) {
|
||||
case WAKEUP_REASON_TIMED:
|
||||
break;
|
||||
case WAKEUP_REASON_BOOT:
|
||||
$('#tag' + tagmac + ' .nextcheckin').innerHTML = "<font color=yellow>First boot</font>"
|
||||
$('#tag' + tagmac).style.background = "#b0d0b0";
|
||||
break;
|
||||
case WAKEUP_REASON_GPIO:
|
||||
$('#tag' + tagmac + ' .nextcheckin').innerHTML = "GPIO wakeup"
|
||||
$('#tag' + tagmac).style.background = "#c8f1bb";
|
||||
break;
|
||||
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";
|
||||
break;
|
||||
case WAKEUP_REASON_WDT_RESET:
|
||||
$('#tag' + tagmac + ' .nextcheckin').innerHTML = "Watchdog reset!"
|
||||
$('#tag' + tagmac).style.background = "#d0a0a0";
|
||||
break;
|
||||
}
|
||||
$('#tag' + tagmac + ' .pendingicon').style.display = (element.pending ? 'inline-block' : 'none');
|
||||
div.classList.add("tagflash");
|
||||
(function (tagmac) {
|
||||
setTimeout(function () { $('#tag' + tagmac).classList.remove("tagflash"); }, 1400);
|
||||
})(tagmac);
|
||||
if (element.pending) div.classList.add("tagpending");
|
||||
}
|
||||
}
|
||||
|
||||
function updatecards() {
|
||||
$('#taglist').querySelectorAll('[data-mac]').forEach(item => {
|
||||
let tagmac = item.dataset.mac;
|
||||
|
||||
if (item.dataset.lastseen && item.dataset.lastseen > 1672531200) {
|
||||
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) {
|
||||
$('#tag' + tagmac + ' .warningicon').style.display = 'inline-block';
|
||||
$('#tag' + tagmac).classList.remove("tagpending")
|
||||
$('#tag' + tagmac).style.background = '#e0e0a0';
|
||||
}
|
||||
if (idletime > 24 * 3600) {
|
||||
$('#tag' + tagmac).style.opacity = '.5';
|
||||
$('#tag' + tagmac + ' .lastseen').style.color = "red";
|
||||
}
|
||||
} else {
|
||||
if ($('#tag' + tagmac + ' .lastseen')) {
|
||||
$('#tag' + tagmac + ' .lastseen').innerHTML = ""
|
||||
} else {
|
||||
console.log(tagmac + " not found")
|
||||
}
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
$('#clearlog').onclick = function () {
|
||||
$('#messages').innerHTML = '';
|
||||
}
|
||||
|
||||
document.querySelectorAll('.closebtn').forEach(button => {
|
||||
button.addEventListener('click', (event) => {
|
||||
event.target.parentNode.style.display = 'none';
|
||||
$('#advancedoptions').style.height = '0px';
|
||||
});
|
||||
});
|
||||
|
||||
//clicking on a tag: load config dialog for tag
|
||||
$('#taglist').addEventListener("click", (event) => {
|
||||
let currentElement = event.target;
|
||||
while (currentElement !== $('#taglist')) {
|
||||
if (currentElement.classList.contains("tagcard")) {
|
||||
break;
|
||||
}
|
||||
currentElement = currentElement.parentNode;
|
||||
}
|
||||
if (!currentElement.classList.contains("tagcard")) {
|
||||
return;
|
||||
}
|
||||
const mac = currentElement.dataset.mac;
|
||||
$('#cfgmac').innerHTML = mac;
|
||||
$('#cfgmac').dataset.mac = mac;
|
||||
fetch("/get_db?mac=" + mac)
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
var tagdata = data.tags[0];
|
||||
$('#cfgalias').value = tagdata.alias;
|
||||
$('#cfgmore').style.display = "none";
|
||||
if (populateSelectTag(tagdata.hwType, tagdata.capabilities)) {
|
||||
$('#cfgcontent').parentNode.style.display = "flex";
|
||||
$('#cfgcontent').value = tagdata.contentMode;
|
||||
$('#cfgcontent').dataset.json = tagdata.modecfgjson;
|
||||
contentselected();
|
||||
if (tagdata.contentMode != 12) $('#cfgmore').style.display = 'block';
|
||||
} else {
|
||||
$('#customoptions').innerHTML = "";
|
||||
$('#cfgcontent').parentNode.style.display = "none";
|
||||
}
|
||||
$('#cfgrotate').value = tagdata.rotate;
|
||||
$('#cfglut').value = tagdata.lut;
|
||||
$('#cfgmore').innerHTML = '🞃';
|
||||
$('#configbox').style.display = 'block';
|
||||
})
|
||||
})
|
||||
|
||||
$('#cfgmore').onclick = function () {
|
||||
$('#cfgmore').innerHTML = $('#advancedoptions').style.height == '0px' ? '🞁' : '🞃';
|
||||
$('#advancedoptions').style.height = $('#advancedoptions').style.height == '0px' ? $('#advancedoptions').scrollHeight + 'px' : '0px';
|
||||
};
|
||||
|
||||
$('#cfgsave').onclick = function () {
|
||||
let contentMode = $('#cfgcontent').value;
|
||||
let contentDef = getContentDefById(contentMode);
|
||||
let extraoptions = contentDef?.param ?? null;
|
||||
let obj = {};
|
||||
|
||||
let formData = new FormData();
|
||||
formData.append("mac", $('#cfgmac').dataset.mac);
|
||||
formData.append("alias", $('#cfgalias').value);
|
||||
|
||||
if (contentMode) {
|
||||
extraoptions.forEach(element => {
|
||||
if ($('#opt' + element.key)) {
|
||||
obj[element.key] = $('#opt' + element.key).value;
|
||||
}
|
||||
});
|
||||
|
||||
formData.append("contentmode", contentMode);
|
||||
formData.append("modecfgjson", JSON.stringify(obj));
|
||||
} else {
|
||||
formData.append("contentmode", "0");
|
||||
formData.append("modecfgjson", String());
|
||||
}
|
||||
|
||||
formData.append("rotate", $('#cfgrotate').value);
|
||||
formData.append("lut", $('#cfglut').value);
|
||||
|
||||
fetch("/save_cfg", {
|
||||
method: "POST",
|
||||
body: formData
|
||||
})
|
||||
.then(response => response.text())
|
||||
.then(data => showMessage(data))
|
||||
.catch(error => showMessage('Error: ' + error));
|
||||
|
||||
$('#advancedoptions').style.height = '0px';
|
||||
$('#configbox').style.display = 'none';
|
||||
}
|
||||
|
||||
function sendCmd(mac, cmd) {
|
||||
let formData = new FormData();
|
||||
formData.append("mac", mac);
|
||||
formData.append("cmd", cmd);
|
||||
fetch("/tag_cmd", {
|
||||
method: "POST",
|
||||
body: formData
|
||||
})
|
||||
.then(response => response.text())
|
||||
.then(data => {
|
||||
var div = $('#tag' + $('#cfgmac').dataset.mac);
|
||||
if (cmd == "del") div.remove();
|
||||
showMessage(data);
|
||||
})
|
||||
.catch(error => showMessage('Error: ' + error));
|
||||
$('#advancedoptions').style.height = '0px';
|
||||
$('#configbox').style.display = 'none';
|
||||
}
|
||||
|
||||
$('#cfgdelete').onclick = function () {
|
||||
sendCmd($('#cfgmac').dataset.mac, "del");
|
||||
}
|
||||
|
||||
$('#cfgclrpending').onclick = function () {
|
||||
sendCmd($('#cfgmac').dataset.mac, "clear");
|
||||
}
|
||||
|
||||
$('#cfgrefresh').onclick = function () {
|
||||
sendCmd($('#cfgmac').dataset.mac, "refresh");
|
||||
}
|
||||
|
||||
$('#rebootbutton').onclick = function () {
|
||||
showMessage("rebooting AP....", true);
|
||||
fetch("/reboot", {
|
||||
method: "POST"
|
||||
});
|
||||
socket.close();
|
||||
}
|
||||
|
||||
$('#apconfigbutton').onclick = function () {
|
||||
var table = document.getElementById("aptable");
|
||||
var rowCount = table.rows.length;
|
||||
for (var i = rowCount - 1; i > 0; i--) {
|
||||
table.deleteRow(i);
|
||||
}
|
||||
fetch("/get_ap_config")
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
$('#apcfgalias').value = data.alias;
|
||||
$('#apcfgchid').value = data.channel;
|
||||
$("#apcfgledbrightness").value = data.led;
|
||||
$("#apcfglanguage").value = data.language;
|
||||
$("#apclatency").value = data.maxsleep;
|
||||
$("#apcpreventsleep").value = data.stopsleep;
|
||||
})
|
||||
$('#apconfigbox').style.display = 'block'
|
||||
}
|
||||
|
||||
$('#apcfgsave').onclick = function () {
|
||||
let formData = new FormData();
|
||||
formData.append("alias", $('#apcfgalias').value);
|
||||
formData.append("channel", $('#apcfgchid').value);
|
||||
formData.append('led', $('#apcfgledbrightness').value);
|
||||
formData.append('language', $('#apcfglanguage').value);
|
||||
formData.append('maxsleep', $('#apclatency').value);
|
||||
formData.append('stopsleep', $('#apcpreventsleep').value);
|
||||
fetch("/save_apcfg", {
|
||||
method: "POST",
|
||||
body: formData
|
||||
})
|
||||
.then(response => response.text())
|
||||
.then(data => showMessage(data))
|
||||
.catch(error => showMessage('Error: ' + error));
|
||||
$(".logo").innerHTML = $('#apcfgalias').value;
|
||||
$('#apconfigbox').style.display = 'none';
|
||||
}
|
||||
|
||||
$('#updatebutton').onclick = function () {
|
||||
$('#apconfigbox').style.display = 'none';
|
||||
$('#apupdatebox').style.display = 'block';
|
||||
loadOTA();
|
||||
}
|
||||
|
||||
async function loadOTA() {
|
||||
otamodule = await import('./ota.js?v=' + Date.now());
|
||||
otamodule.initUpdate();
|
||||
}
|
||||
|
||||
$('#paintbutton').onclick = function () {
|
||||
if (paintShow) {
|
||||
paintShow = false;
|
||||
$('#cfgsave').parentNode.style.display = 'block';
|
||||
contentselected();
|
||||
} else {
|
||||
paintShow = true;
|
||||
$('#cfgsave').parentNode.style.display = 'none';
|
||||
$('#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];
|
||||
if (paintLoaded) {
|
||||
startPainter(mac, width, height);
|
||||
} else {
|
||||
loadScript('painter.js', function () {
|
||||
startPainter(mac, width, height);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function loadScript(url, callback) {
|
||||
var script = document.createElement('script');
|
||||
script.src = url;
|
||||
script.onload = function () {
|
||||
if (callback) {
|
||||
callback();
|
||||
}
|
||||
};
|
||||
document.head.appendChild(script);
|
||||
}
|
||||
|
||||
function contentselected() {
|
||||
let contentMode = $('#cfgcontent').value;
|
||||
$('#customoptions').innerHTML = "";
|
||||
var obj = {};
|
||||
if ($('#cfgcontent').dataset.json && ($('#cfgcontent').dataset.json != "null")) {
|
||||
obj = JSON.parse($('#cfgcontent').dataset.json);
|
||||
}
|
||||
$('#paintbutton').style.display = 'none';
|
||||
if (contentMode) {
|
||||
let contentDef = getContentDefById(contentMode);
|
||||
if (contentDef) {
|
||||
$('#customoptions').innerHTML = "<p>" + contentDef?.desc + "</p>"
|
||||
}
|
||||
$('#paintbutton').style.display = (contentMode == 0 ? 'inline-block' : 'none');
|
||||
let extraoptions = contentDef?.param ?? null;
|
||||
extraoptions.forEach(element => {
|
||||
var 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");
|
||||
switch (element.type) {
|
||||
case 'text':
|
||||
input.type = "text";
|
||||
break;
|
||||
case 'int':
|
||||
input.type = "number";
|
||||
break;
|
||||
case 'ro':
|
||||
input.type = "text";
|
||||
input.disabled = true;
|
||||
break;
|
||||
case 'select':
|
||||
input = document.createElement("select");
|
||||
for (const key in element.options) {
|
||||
const optionElement = document.createElement("option");
|
||||
optionElement.value = key;
|
||||
optionElement.text = element.options[key];
|
||||
if (element.options[key].substring(0,1)=="-") {
|
||||
optionElement.text = element.options[key].substring(1);
|
||||
optionElement.selected = true;
|
||||
} else {
|
||||
optionElement.selected = false;
|
||||
}
|
||||
input.appendChild(optionElement);
|
||||
}
|
||||
break;
|
||||
}
|
||||
input.id = 'opt' + element.key;
|
||||
input.title = element.desc;
|
||||
if (obj[element.key]) input.value = obj[element.key];
|
||||
var p = document.createElement("p");
|
||||
p.appendChild(label);
|
||||
p.appendChild(input);
|
||||
$('#customoptions').appendChild(p);
|
||||
});
|
||||
}
|
||||
paintShow = false;
|
||||
$('#cfgsave').parentNode.style.display = 'block';
|
||||
}
|
||||
|
||||
function populateSelectTag(hwtype, capabilities) {
|
||||
var selectTag = $("#cfgcontent");
|
||||
selectTag.innerHTML = "";
|
||||
var optionsAdded = false;
|
||||
var option;
|
||||
cardconfig.forEach(item => {
|
||||
var capcheck = item.capabilities ?? 0;
|
||||
var hwtypeArray = item.hwtype;
|
||||
if (hwtypeArray.includes(hwtype) && (capabilities & capcheck || capcheck == 0)) {
|
||||
option = document.createElement("option");
|
||||
option.value = item.id;
|
||||
option.text = item.name;
|
||||
selectTag.appendChild(option);
|
||||
optionsAdded = true;
|
||||
}
|
||||
});
|
||||
|
||||
var rotateTag = $("#cfgrotate");
|
||||
rotateTag.innerHTML = "";
|
||||
|
||||
for (let i = 0; i < 4; i++) {
|
||||
if (i == 0 || displaySizeLookup[hwtype][2] == 4 || (i == 2 && displaySizeLookup[hwtype][2] == 2)) {
|
||||
option = document.createElement("option");
|
||||
option.value = i;
|
||||
option.text = (i * 90) + " degrees";
|
||||
rotateTag.appendChild(option);
|
||||
}
|
||||
}
|
||||
|
||||
var lutTag = $("#cfglut");
|
||||
lutTag.innerHTML = "";
|
||||
|
||||
option = document.createElement("option");
|
||||
option.value = "0";
|
||||
option.text = "auto";
|
||||
lutTag.appendChild(option);
|
||||
|
||||
if (hwtype != 240) {
|
||||
option = document.createElement("option");
|
||||
option.value = "1";
|
||||
option.text = "Always full refresh";
|
||||
lutTag.appendChild(option);
|
||||
}
|
||||
|
||||
return optionsAdded;
|
||||
}
|
||||
|
||||
function getContentDefById(id) {
|
||||
if (id == null) return null;
|
||||
var obj = cardconfig.find(item => item.id == id);
|
||||
return obj ? 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' });
|
||||
if (iserr) {
|
||||
messages.insertAdjacentHTML("afterbegin", '<li class="new error">' + htmlEncode(time + ' ' + message) + '</li>');
|
||||
} else {
|
||||
messages.insertAdjacentHTML("afterbegin", '<li class="new">' + htmlEncode(time + ' ' + message) + '</li>');
|
||||
}
|
||||
}
|
||||
|
||||
function htmlEncode(input) {
|
||||
const textArea = document.createElement("textarea");
|
||||
textArea.innerText = input;
|
||||
return textArea.innerHTML.split("<br>").join("\n");
|
||||
}
|
||||
|
||||
function loadImage(id, imageSrc) {
|
||||
imageQueue.push({ id, imageSrc });
|
||||
if (!isProcessing) {
|
||||
processQueue();
|
||||
}
|
||||
}
|
||||
|
||||
function processQueue() {
|
||||
if (imageQueue.length === 0) {
|
||||
isProcessing = false;
|
||||
return;
|
||||
}
|
||||
isProcessing = true;
|
||||
const { id, imageSrc } = imageQueue.shift();
|
||||
const canvas = $('#tag' + id + ' .tagimg');
|
||||
canvas.style.display = 'block';
|
||||
const hwtype = $('#tag' + id).dataset.hwtype;
|
||||
|
||||
fetch(imageSrc)
|
||||
.then(response => response.arrayBuffer())
|
||||
.then(buffer => {
|
||||
[canvas.width, canvas.height] = displaySizeLookup[hwtype] || [0, 0];
|
||||
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);
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
ctx.putImageData(imageData, 0, 0);
|
||||
processQueue();
|
||||
})
|
||||
.catch(error => {
|
||||
processQueue();
|
||||
});
|
||||
}
|
||||
|
||||
function displayTime(seconds) {
|
||||
let hours = Math.floor(Math.abs(seconds) / 3600);
|
||||
let minutes = Math.floor((Math.abs(seconds) % 3600) / 60);
|
||||
let remainingSeconds = Math.abs(seconds) % 60;
|
||||
return (seconds < 0 ? '-' : '') + (hours > 0 ? `${hours}:${String(minutes).padStart(2, '0')}` : `${minutes}`) + `:${String(remainingSeconds).padStart(2, '0')}`;
|
||||
}
|
||||
|
||||
function sortGrid() {
|
||||
const sortableGrid = $('#taglist');
|
||||
const gridItems = Array.from(sortableGrid.getElementsByClassName('tagcard'));
|
||||
gridItems.sort((a, b) => {
|
||||
const macA = a.querySelector('.alias').innerHTML;
|
||||
const macB = b.querySelector('.alias').innerHTML;
|
||||
if (macA < macB) return -1;
|
||||
if (macA > macB) return 1;
|
||||
return 0;
|
||||
});
|
||||
gridItems.forEach((item) => sortableGrid.appendChild(item));
|
||||
}
|
||||
|
||||
419
ESP32_AP-Flasher/data/www/ota.js
Normal file
419
ESP32_AP-Flasher/data/www/ota.js
Normal file
@@ -0,0 +1,419 @@
|
||||
const repoUrl = 'https://api.github.com/repos/jjwbruijn/OpenEPaperLink/releases';
|
||||
|
||||
const $ = document.querySelector.bind(document);
|
||||
|
||||
let running = false;
|
||||
let errors = 0;
|
||||
let env = '', currentVer = '', currentBuildtime = 0;
|
||||
let buttonState = false;
|
||||
|
||||
export async function initUpdate() {
|
||||
if (!$("#updateconsole")) {
|
||||
const consoleDiv = document.createElement('div');
|
||||
consoleDiv.classList.add('console');
|
||||
consoleDiv.id = "updateconsole";
|
||||
$('#apupdatebox').appendChild(consoleDiv);
|
||||
}
|
||||
$("#updateconsole").innerHTML = "";
|
||||
|
||||
const response = await fetch("/version.txt");
|
||||
let filesystemversion = await response.text();
|
||||
if (!filesystemversion) filesystemversion = "unknown";
|
||||
|
||||
fetch("/sysinfo")
|
||||
.then(response => {
|
||||
if (response.status != 200) {
|
||||
print("Error fetching sysinfo: " + response.status, "red");
|
||||
if (response.status == 404) {
|
||||
print("Your current firmware version is not yet capable of updating OTA.");
|
||||
print("Update it manually one last time.");
|
||||
disableButtons(true);
|
||||
}
|
||||
return "{}";
|
||||
} else {
|
||||
return response.json();
|
||||
}
|
||||
})
|
||||
.then(data => {
|
||||
if (data.env) {
|
||||
let matchtest='';
|
||||
if (data.buildversion != filesystemversion && filesystemversion != "custom" && data.buildversion != "custom") matchtest = " <- not matching!"
|
||||
print(`env: ${data.env}`);
|
||||
print(`build date: ${formatEpoch(data.buildtime)}`);
|
||||
print(`esp32 version: ${data.buildversion}`);
|
||||
print(`filesystem version: ${filesystemversion}` + matchtest);
|
||||
print(`sha: ${data.sha}`);
|
||||
print(`psram size: ${data.psramsize}`);
|
||||
print(`flash size: ${data.flashsize}`);
|
||||
print("--------------------------","gray");
|
||||
env = data.env;
|
||||
currentVer = data.buildversion;
|
||||
currentBuildtime = data.buildtime;
|
||||
if (data.rollback) $("#rollbackOption").style.display = 'block';
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
print('Error fetching sysinfo: ' + error, "red");
|
||||
});
|
||||
|
||||
fetch(repoUrl)
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
const releaseDetails = data.map(release => {
|
||||
const assets = release.assets;
|
||||
let fileUrl = null;
|
||||
const filesJsonAsset = assets.find(asset => asset.name === 'files.json');
|
||||
if (filesJsonAsset) {
|
||||
fileUrl = filesJsonAsset.browser_download_url;
|
||||
return {
|
||||
html_url: release.html_url,
|
||||
tag_name: release.tag_name,
|
||||
name: release.name,
|
||||
date: formatDateTime(release.published_at),
|
||||
author: release.author.login,
|
||||
file_url: fileUrl
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
const easyupdate = $('#easyupdate');
|
||||
if (releaseDetails.length === 0) {
|
||||
easyupdate.innerHTML = ("No releases found.");
|
||||
} else {
|
||||
const release = releaseDetails[0];
|
||||
if (release && release.tag_name) {
|
||||
if (release.tag_name == currentVer) {
|
||||
easyupdate.innerHTML = `Version ${currentVer}. You are up to date`;
|
||||
} else if (release.date < formatEpoch(currentBuildtime)) {
|
||||
easyupdate.innerHTML = `Your version is newer than the latest release date.<br>Are you the developer? :-)`;
|
||||
} else {
|
||||
easyupdate.innerHTML = `An update from version ${currentVer} to version ${release.tag_name} is available.<button onclick="otamodule.updateAll('${release.file_url}','${release.tag_name}')">Update now!</button>`;
|
||||
}
|
||||
}
|
||||
}
|
||||
easyupdate.innerHTML += "<br><a onclick=\"$('#advanceddiv').style.display='block'\">advanced options</a>"
|
||||
|
||||
const table = document.createElement('table');
|
||||
const tableHeader = document.createElement('tr');
|
||||
tableHeader.innerHTML = '<th>Release</th><th>Date</th><th>Name</th><th>Author</th><th colspan="2">Update:</th><th>Remark</th>';
|
||||
table.appendChild(tableHeader);
|
||||
|
||||
releaseDetails.forEach(release => {
|
||||
if (release && 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>${release.author}</td><td><button onclick="otamodule.updateESP('${release.file_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) {
|
||||
tablerow += "<td>current version</td>";
|
||||
} else if (release.date < formatEpoch(currentBuildtime)) {
|
||||
tablerow += "<td>older</td>";
|
||||
} else {
|
||||
tablerow += "<td>newer</td>";
|
||||
}
|
||||
tableRow.innerHTML = tablerow;
|
||||
table.appendChild(tableRow);
|
||||
}
|
||||
});
|
||||
|
||||
$('#releasetable').innerHTML = "";
|
||||
$('#releasetable').appendChild(table);
|
||||
disableButtons(buttonState);
|
||||
})
|
||||
.catch(error => {
|
||||
print('Error fetching releases:' + error, "red");
|
||||
});
|
||||
}
|
||||
|
||||
export function updateAll(fileUrl, tagname) {
|
||||
updateWebpage(fileUrl, tagname, false)
|
||||
.then(() => {
|
||||
updateESP(fileUrl, false);
|
||||
})
|
||||
.catch(error => {
|
||||
console.error(error);
|
||||
});
|
||||
}
|
||||
|
||||
export async function updateWebpage(fileUrl, tagname, showReload) {
|
||||
return new Promise((resolve, reject) => {
|
||||
(async function () {
|
||||
try {
|
||||
if (running) return;
|
||||
if (showReload) {
|
||||
if (!confirm("Confirm updating the filesystem")) return;
|
||||
} else {
|
||||
if (!confirm("Confirm updating the esp32 and filesystem")) return;
|
||||
}
|
||||
|
||||
disableButtons(true);
|
||||
running = true;
|
||||
errors = 0;
|
||||
const consoleDiv = document.getElementById('updateconsole');
|
||||
consoleDiv.scrollTop = consoleDiv.scrollHeight;
|
||||
|
||||
print("Updating littleFS partition...");
|
||||
|
||||
try {
|
||||
const response = await fetch("/update_actions", {
|
||||
method: "POST",
|
||||
body: ''
|
||||
});
|
||||
if (response.ok) {
|
||||
const data = await response.text();
|
||||
} else {
|
||||
print(`error performing update actions: ${response.status}`, "red");
|
||||
errors++;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`error calling update actions:` + error, "red");
|
||||
errors++;
|
||||
}
|
||||
|
||||
fetch("/getexturl?url=" + fileUrl)
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
checkfiles(data.files);
|
||||
})
|
||||
.catch(error => {
|
||||
print('Error fetching data:' + error, "red");
|
||||
});
|
||||
|
||||
const checkfiles = async (files) => {
|
||||
for (const file of files) {
|
||||
try {
|
||||
const url = "/check_file?path=" + encodeURIComponent(file.path);
|
||||
const response = await fetch(url);
|
||||
if (response.ok) {
|
||||
const data = await response.json();
|
||||
if (data.filesize == file.size && data.md5 == file.md5) {
|
||||
print(`file ${file.path} is up to date`, "green");
|
||||
} else if (data.filesize == 0) {
|
||||
await fetchAndPost(file.url, file.name, file.path);
|
||||
} else {
|
||||
await fetchAndPost(file.url, file.name, file.path);
|
||||
}
|
||||
} else {
|
||||
print(`error checking file ${file.path}: ${response.status}`, "red");
|
||||
errors++;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`error checking file ${file.path}:` + error, "red");
|
||||
errors++;
|
||||
}
|
||||
}
|
||||
writeVersion(tagname, "version.txt", "/www/version.txt")
|
||||
running = false;
|
||||
if (errors) {
|
||||
print("------", "gray");
|
||||
print(`Finished updating with ${errors} errors.`, "red");
|
||||
reject(error);
|
||||
} else {
|
||||
print("------", "gray");
|
||||
print("Update succesful.");
|
||||
resolve();
|
||||
}
|
||||
disableButtons(false);
|
||||
|
||||
if (showReload) {
|
||||
const newLine = document.createElement('div');
|
||||
newLine.innerHTML = "<button onclick=\"location.reload()\">Reload this page</button>";
|
||||
consoleDiv.appendChild(newLine);
|
||||
consoleDiv.scrollTop = consoleDiv.scrollHeight;
|
||||
}
|
||||
};
|
||||
} catch (error) {
|
||||
print('Error: ' + error, "red");
|
||||
errors++;
|
||||
reject(error);
|
||||
}
|
||||
})();
|
||||
});
|
||||
}
|
||||
|
||||
export async function updateESP(fileUrl, showConfirm) {
|
||||
if (running) return;
|
||||
if (showConfirm) {
|
||||
if (!confirm("Confirm updating the esp32")) return;
|
||||
}
|
||||
|
||||
disableButtons(true);
|
||||
running = true;
|
||||
errors = 0;
|
||||
const consoleDiv = document.getElementById('updateconsole');
|
||||
consoleDiv.scrollTop = consoleDiv.scrollHeight;
|
||||
|
||||
print("Updating firmware...");
|
||||
|
||||
let binurl, binmd5, binsize;
|
||||
|
||||
try {
|
||||
const response = await fetch("/getexturl?url=" + fileUrl);
|
||||
const data = await response.json();
|
||||
const file = data.binaries.find((entry) => entry.name == env + '.bin');
|
||||
if (file) {
|
||||
binurl = file.url;
|
||||
binmd5 = file.md5;
|
||||
binsize = file.size;
|
||||
console.log(`URL for "${file.name}": ${binurl}`);
|
||||
|
||||
try {
|
||||
const response = await fetch('/update_ota', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded'
|
||||
},
|
||||
body: new URLSearchParams({
|
||||
url: binurl,
|
||||
md5: binmd5,
|
||||
size: binsize
|
||||
})
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
const result = await response.text();
|
||||
print('OTA update initiated.');
|
||||
} else {
|
||||
print('Failed to initiate OTA update: ' + response.status, "red");
|
||||
}
|
||||
} catch (error) {
|
||||
print('Error during OTA update: ' + error, "red");
|
||||
}
|
||||
} else {
|
||||
print(`File "${fileName}" not found.`, "red");
|
||||
}
|
||||
} catch (error) {
|
||||
print('Error: ' + error, "red");
|
||||
print("Something went wrong, try again.");
|
||||
}
|
||||
|
||||
running = false;
|
||||
disableButtons(false);
|
||||
}
|
||||
|
||||
$('#rollbackBtn').onclick = function () {
|
||||
if (running) return;
|
||||
|
||||
disableButtons(true);
|
||||
running = true;
|
||||
errors = 0;
|
||||
const consoleDiv = document.getElementById('updateconsole');
|
||||
consoleDiv.scrollTop = consoleDiv.scrollHeight;
|
||||
|
||||
print("Rolling back...");
|
||||
|
||||
fetch("/rollback", {
|
||||
method: "POST",
|
||||
body: ''
|
||||
})
|
||||
|
||||
running = false;
|
||||
disableButtons(false);
|
||||
|
||||
}
|
||||
|
||||
export function print(line, color = "white") {
|
||||
const consoleDiv = document.getElementById('updateconsole');
|
||||
if (consoleDiv) {
|
||||
const isScrolledToBottom = consoleDiv.scrollHeight - consoleDiv.clientHeight <= consoleDiv.scrollTop;
|
||||
const newLine = document.createElement('div');
|
||||
newLine.style.color = color;
|
||||
if (line == "[reboot]") {
|
||||
newLine.innerHTML = "<button onclick=\"otamodule.reboot()\">Reboot</button>";
|
||||
} else {
|
||||
newLine.textContent = line;
|
||||
}
|
||||
consoleDiv.appendChild(newLine);
|
||||
if (isScrolledToBottom) {
|
||||
consoleDiv.scrollTop = consoleDiv.scrollHeight;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function reboot() {
|
||||
print("Rebooting now... Reloading webpage in 5 seconds...", "yellow");
|
||||
fetch("/reboot",{method: "POST"});
|
||||
setTimeout(() => {
|
||||
location.reload();
|
||||
}, 5000);
|
||||
}
|
||||
|
||||
function formatEpoch(epochTime) {
|
||||
const date = new Date(epochTime * 1000); // Convert seconds to milliseconds
|
||||
|
||||
const year = date.getFullYear();
|
||||
const month = String(date.getMonth() + 1).padStart(2, '0'); // Months are zero-based
|
||||
const day = String(date.getDate()).padStart(2, '0');
|
||||
const hours = String(date.getHours()).padStart(2, '0');
|
||||
const minutes = String(date.getMinutes()).padStart(2, '0');
|
||||
|
||||
return `${year}-${month}-${day} ${hours}:${minutes}`;
|
||||
}
|
||||
|
||||
function formatDateTime(utcDateString) {
|
||||
const localTimeZoneOffset = new Date().getTimezoneOffset();
|
||||
const date = new Date(utcDateString);
|
||||
date.setMinutes(date.getMinutes() - localTimeZoneOffset);
|
||||
const year = date.getUTCFullYear();
|
||||
const month = String(date.getUTCMonth() + 1).padStart(2, '0');
|
||||
const day = String(date.getUTCDate()).padStart(2, '0');
|
||||
const hours = String(date.getUTCHours()).padStart(2, '0');
|
||||
const minutes = String(date.getUTCMinutes()).padStart(2, '0');
|
||||
|
||||
const formattedDate = `${year}-${month}-${day} ${hours}:${minutes}`;
|
||||
return formattedDate;
|
||||
}
|
||||
|
||||
const fetchAndPost = async (url, name, path) => {
|
||||
try {
|
||||
print("updating " + path);
|
||||
const response = await fetch(url);
|
||||
const fileContent = await response.blob();
|
||||
|
||||
const formData = new FormData();
|
||||
formData.append('path', path);
|
||||
formData.append('file', fileContent, name);
|
||||
|
||||
const uploadResponse = await fetch('/littlefs_put', {
|
||||
method: 'POST',
|
||||
body: formData
|
||||
});
|
||||
|
||||
if (!uploadResponse.ok) {
|
||||
print(`${response.status} ${response.body}`, "red");
|
||||
errors++;
|
||||
}
|
||||
} catch (error) {
|
||||
print('error: ' + error, "red");
|
||||
errors++;
|
||||
}
|
||||
};
|
||||
|
||||
const writeVersion = async (content, name, path) => {
|
||||
try {
|
||||
print("uploading " + path);
|
||||
|
||||
const formData = new FormData();
|
||||
formData.append('path', path);
|
||||
const blob = new Blob([content]);
|
||||
formData.append('file', blob, name);
|
||||
|
||||
const uploadResponse = await fetch('/littlefs_put', {
|
||||
method: 'POST',
|
||||
body: formData
|
||||
});
|
||||
|
||||
if (!uploadResponse.ok) {
|
||||
print(`${response.status} ${response.body}`, "red");
|
||||
errors++;
|
||||
}
|
||||
} catch (error) {
|
||||
print('error: ' + error, "red");
|
||||
errors++;
|
||||
}
|
||||
};
|
||||
|
||||
function disableButtons(active) {
|
||||
$("#apupdatebox").querySelectorAll('button').forEach(button => {
|
||||
button.disabled = active;
|
||||
});
|
||||
buttonState = active;
|
||||
}
|
||||
360
ESP32_AP-Flasher/data/www/painter.js
Normal file
360
ESP32_AP-Flasher/data/www/painter.js
Normal file
@@ -0,0 +1,360 @@
|
||||
function startPainter(mac, width, height) {
|
||||
let isDrawing = false;
|
||||
let lastX = 0;
|
||||
let lastY = 0;
|
||||
let color = 'black';
|
||||
let linewidth = 3;
|
||||
let cursor = 'auto';
|
||||
let isAddingText = false;
|
||||
let layerDiv, intervalId, showCursor, input, textX, textY, font, sizeSelect, isDragging;
|
||||
|
||||
var fonts = ['Roboto', 'Open Sans', 'Lato', 'Montserrat', 'PT Sans', 'Barlow Condensed', 'Headland One', 'Sofia Sans Extra Condensed', 'Mynerve', 'Lilita One', 'Passion One', 'Big Shoulders Display'];
|
||||
|
||||
loadGoogleFonts(fonts);
|
||||
|
||||
const canvas = document.createElement('canvas');
|
||||
canvas.width = width;
|
||||
canvas.height = height;
|
||||
|
||||
const ctx = canvas.getContext('2d');
|
||||
|
||||
ctx.fillStyle = 'white';
|
||||
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
||||
ctx.imageSmoothingEnabled = false;
|
||||
|
||||
$("#canvasdiv").appendChild(canvas);
|
||||
canvas.style.imageRendering = 'pixelated';
|
||||
|
||||
canvas.addEventListener('mousedown', startDrawing);
|
||||
canvas.addEventListener('mouseup', stopDrawing);
|
||||
canvas.addEventListener('mousemove', draw);
|
||||
|
||||
canvas.addEventListener('touchstart', startDrawing, { passive: true });
|
||||
canvas.addEventListener('touchend', stopDrawing);
|
||||
canvas.addEventListener('touchmove', draw, { passive: true });
|
||||
|
||||
const bgCanvas = document.createElement('canvas');
|
||||
bgCanvas.width = canvas.width;
|
||||
bgCanvas.height = canvas.height;
|
||||
const bgCtx = bgCanvas.getContext('2d');
|
||||
|
||||
bgCtx.fillStyle = '#ffffff';
|
||||
bgCtx.fillRect(0, 0, bgCanvas.width, bgCanvas.height);
|
||||
|
||||
const txtButton = document.createElement('button');
|
||||
txtButton.innerHTML = 'tT';
|
||||
txtButton.style.fontStyle = 'italic';
|
||||
txtButton.addEventListener('click', addText);
|
||||
|
||||
const blackButton = document.createElement('button');
|
||||
blackButton.innerHTML = '﹏🖌';
|
||||
blackButton.style.color = 'black';
|
||||
blackButton.addEventListener('click', () => {
|
||||
color = 'black';
|
||||
linewidth = 3;
|
||||
cursor = 'url("data:image/svg+xml;utf8,<svg xmlns=\'http://www.w3.org/2000/svg\'><circle cx=\'2\' cy=\'2\' r=\'2\' opacity=\'0.5\'/></svg>") 2 2, auto';
|
||||
blackButton.classList.add('active');
|
||||
redButton.classList.remove('active');
|
||||
whiteButton.classList.remove('active');
|
||||
});
|
||||
blackButton.classList.add('active');
|
||||
|
||||
const redButton = document.createElement('button');
|
||||
redButton.innerHTML = '﹏🖌';
|
||||
redButton.style.color = 'red';
|
||||
redButton.addEventListener('click', () => {
|
||||
color = 'red';
|
||||
linewidth = 3;
|
||||
cursor = 'url("data:image/svg+xml;utf8,<svg xmlns=\'http://www.w3.org/2000/svg\'><circle cx=\'2\' cy=\'2\' r=\'2\' opacity=\'0.5\'/></svg>") 2 2, auto';
|
||||
blackButton.classList.remove('active');
|
||||
redButton.classList.add('active');
|
||||
whiteButton.classList.remove('active');
|
||||
});
|
||||
|
||||
const whiteButton = document.createElement('button');
|
||||
whiteButton.innerHTML = '⬤';
|
||||
whiteButton.style.color = 'white';
|
||||
whiteButton.addEventListener('click', () => {
|
||||
color = 'white';
|
||||
linewidth = 20;
|
||||
cursor = 'url("data:image/svg+xml;utf8,<svg xmlns=\'http://www.w3.org/2000/svg\'><circle cx=\'10\' cy=\'10\' r=\'10\' opacity=\'0.5\'/></svg>") 10 10, auto';
|
||||
blackButton.classList.remove('active');
|
||||
redButton.classList.remove('active');
|
||||
whiteButton.classList.add('active');
|
||||
});
|
||||
|
||||
const clearButton = document.createElement('button');
|
||||
clearButton.innerHTML = '🖵';
|
||||
clearButton.addEventListener('click', () => {
|
||||
if (isAddingText) handleFinish(false);
|
||||
ctx.fillStyle = 'white';
|
||||
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
||||
});
|
||||
|
||||
const uploadButton = document.createElement('button');
|
||||
uploadButton.innerHTML = 'Upload';
|
||||
uploadButton.addEventListener('click', () => {
|
||||
if (isAddingText) handleFinish(true);
|
||||
const dataURL = canvas.toDataURL('image/jpeg');
|
||||
const binaryImage = dataURLToBlob(dataURL);
|
||||
const formData = new FormData();
|
||||
formData.append('mac', mac);
|
||||
formData.append('dither', '0');
|
||||
formData.append('file', binaryImage, 'image.jpg');
|
||||
const xhr = new XMLHttpRequest();
|
||||
xhr.open('POST', '/imgupload');
|
||||
xhr.send(formData);
|
||||
$('#configbox').style.display = 'none';
|
||||
});
|
||||
|
||||
$("#buttonbar").appendChild(blackButton);
|
||||
$("#buttonbar").appendChild(redButton);
|
||||
$("#buttonbar").appendChild(whiteButton);
|
||||
$("#buttonbar").appendChild(txtButton);
|
||||
$("#buttonbar").appendChild(clearButton);
|
||||
$("#savebar").appendChild(uploadButton);
|
||||
|
||||
canvas.addEventListener('mouseenter', function () {
|
||||
if (!isAddingText) {
|
||||
canvas.style.cursor = cursor;
|
||||
} else {
|
||||
canvas.style.cursor = 'move';
|
||||
}
|
||||
});
|
||||
|
||||
canvas.addEventListener('mouseleave', function () {
|
||||
canvas.style.cursor = 'auto';
|
||||
});
|
||||
|
||||
function startDrawing(e) {
|
||||
if (isAddingText) return;
|
||||
isDrawing = true;
|
||||
var rect = canvas.getBoundingClientRect();
|
||||
lastX = e.pageX - rect.left - window.pageXOffset;
|
||||
lastY = e.pageY - rect.top - window.pageYOffset;
|
||||
}
|
||||
|
||||
function stopDrawing() {
|
||||
if (isAddingText) return;
|
||||
isDrawing = false;
|
||||
}
|
||||
|
||||
function draw(e) {
|
||||
if (isAddingText) return;
|
||||
if (!isDrawing) return;
|
||||
var rect = canvas.getBoundingClientRect();
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(lastX, lastY);
|
||||
ctx.lineTo(e.pageX - rect.left - window.pageXOffset, e.pageY - rect.top - window.pageYOffset);
|
||||
ctx.strokeStyle = color;
|
||||
ctx.lineWidth = linewidth;
|
||||
ctx.lineCap = "round";
|
||||
ctx.stroke();
|
||||
lastX = e.pageX - rect.left - window.pageXOffset;
|
||||
lastY = e.pageY - rect.top - window.pageYOffset;
|
||||
}
|
||||
|
||||
function addText() {
|
||||
if (isAddingText) {
|
||||
handleFinish(true);
|
||||
return;
|
||||
}
|
||||
txtButton.classList.add('active');
|
||||
bgCtx.drawImage(canvas, 0, 0);
|
||||
|
||||
const defaultX = 5;
|
||||
const defaultY = 40;
|
||||
isDragging = false;
|
||||
let startX, startY;
|
||||
showCursor = true;
|
||||
|
||||
textX = defaultX;
|
||||
textY = defaultY;
|
||||
font = '24px ' + fonts[0];
|
||||
|
||||
input = document.createElement('textarea');
|
||||
input.type = 'text';
|
||||
input.placeholder = 'Type text here';
|
||||
input.style.opacity = '0';
|
||||
input.style.position = 'absolute';
|
||||
input.style.left = '-200px'
|
||||
|
||||
input.addEventListener('input', () => {
|
||||
drawText(input.value, textX, textY);
|
||||
});
|
||||
input.addEventListener('keyup', () => {
|
||||
input.selectionStart = input.selectionEnd = input.value.length;
|
||||
});
|
||||
|
||||
input.addEventListener('blur', function () {
|
||||
input.focus();
|
||||
});
|
||||
|
||||
intervalId = setInterval(function () {
|
||||
showCursor = !showCursor;
|
||||
drawText(input.value, textX, textY);
|
||||
}, 300);
|
||||
|
||||
canvas.addEventListener('mouseup', handleMouseUp);
|
||||
canvas.addEventListener('mousedown', handleMouseDown);
|
||||
canvas.addEventListener('mousemove', handleMouseMove);
|
||||
|
||||
canvas.addEventListener('touchstart', handleTouchStart, { passive: true });
|
||||
canvas.addEventListener('touchend', handleTouchEnd);
|
||||
canvas.addEventListener('touchmove', handleTouchMove, { passive: true });
|
||||
|
||||
var sizes = [10,11,12,13,14,16,18,20,24,28,32,36,40,48,56,64,72,84];
|
||||
|
||||
const fontSelect = document.createElement('select');
|
||||
fontSelect.id = 'font-select';
|
||||
for (var i = 0; i < fonts.length; i++) {
|
||||
const option = document.createElement('option');
|
||||
option.value = fonts[i];
|
||||
option.text = fonts[i];
|
||||
option.style.fontFamily = fonts[i];
|
||||
fontSelect.appendChild(option);
|
||||
}
|
||||
|
||||
sizeSelect = document.createElement('select');
|
||||
sizeSelect.id = 'size-select';
|
||||
for (var i = 0; i < sizes.length; i++) {
|
||||
const option = document.createElement('option');
|
||||
option.value = sizes[i];
|
||||
option.text = sizes[i] + ' px';
|
||||
sizeSelect.appendChild(option);
|
||||
}
|
||||
|
||||
function updateFont() {
|
||||
var selectedFont = fontSelect.value;
|
||||
var selectedSize = sizeSelect.value;
|
||||
fontSelect.style.fontFamily = selectedFont;
|
||||
font = selectedSize + 'px ' + selectedFont;
|
||||
drawText(input.value, textX, textY);
|
||||
}
|
||||
|
||||
fontSelect.value = fonts[0];
|
||||
sizeSelect.value = '24';
|
||||
fontSelect.addEventListener('change', updateFont);
|
||||
sizeSelect.addEventListener('change', updateFont);
|
||||
|
||||
const finishButton = document.createElement('button');
|
||||
finishButton.innerHTML = '✔';
|
||||
finishButton.addEventListener('click', clickHandleFinish);
|
||||
|
||||
layerDiv = document.createElement('div');
|
||||
|
||||
layerDiv.appendChild(input);
|
||||
layerDiv.appendChild(fontSelect);
|
||||
layerDiv.appendChild(sizeSelect);
|
||||
layerDiv.appendChild(finishButton);
|
||||
$("#layersdiv").appendChild(layerDiv);
|
||||
input.focus();
|
||||
|
||||
isAddingText = true;
|
||||
//cursor = 'move';
|
||||
blackButton.innerHTML = 'aA'
|
||||
redButton.innerHTML = 'aA'
|
||||
if (color=='white') {
|
||||
whiteButton.classList.remove('active');
|
||||
blackButton.classList.add('active');
|
||||
color='black';
|
||||
}
|
||||
}
|
||||
|
||||
function handleFinish(apply) {
|
||||
canvas.removeEventListener('mousedown', handleMouseDown);
|
||||
canvas.removeEventListener('mouseup', handleMouseUp);
|
||||
canvas.removeEventListener('mousemove', handleMouseMove);
|
||||
|
||||
canvas.removeEventListener('touchstart', handleTouchStart);
|
||||
canvas.removeEventListener('touchend', handleTouchEnd);
|
||||
canvas.removeEventListener('touchmove', handleTouchMove);
|
||||
isAddingText = false;
|
||||
cursor = 'auto';
|
||||
layerDiv.remove();
|
||||
clearInterval(intervalId);
|
||||
showCursor = false;
|
||||
if (apply) drawText(input.value, textX, textY);
|
||||
txtButton.classList.remove('active');
|
||||
blackButton.innerHTML = '﹏🖌'
|
||||
redButton.innerHTML = '﹏🖌'
|
||||
}
|
||||
|
||||
function drawText(text, x, y) {
|
||||
ctx.drawImage(bgCanvas, 0, 0);
|
||||
ctx.save();
|
||||
ctx.translate(x, y);
|
||||
ctx.font = font;
|
||||
ctx.fillStyle = color;
|
||||
const lines = text.split('\n');
|
||||
lines.forEach((line, index) => {
|
||||
ctx.fillText(line + (showCursor && index === lines.length - 1 ? '|' : ''), 0, index * (sizeSelect.value * 1.1));
|
||||
});
|
||||
ctx.restore();
|
||||
}
|
||||
|
||||
function handleMouseDown(e) {
|
||||
isDragging = true;
|
||||
startX = textX;
|
||||
startY = textY;
|
||||
({ clientX: lastMouseX, clientY: lastMouseY } = e);
|
||||
}
|
||||
|
||||
function handleMouseMove(e) {
|
||||
if (isDragging) {
|
||||
const { clientX, clientY } = e;
|
||||
textX = startX + clientX - lastMouseX;
|
||||
textY = startY + clientY - lastMouseY;
|
||||
drawText(input.value, textX, textY);
|
||||
}
|
||||
}
|
||||
|
||||
function handleTouchStart(e) {
|
||||
isDragging = true;
|
||||
startX = textX;
|
||||
startY = textY;
|
||||
({ clientX: lastTouchX, clientY: lastTouchY } = e.touches[0]);
|
||||
}
|
||||
|
||||
function handleTouchMove(e) {
|
||||
if (isDragging) {
|
||||
const { clientX, clientY } = e.touches[0];
|
||||
textX = startX + clientX - lastTouchX;
|
||||
textY = startY + clientY - lastTouchY;
|
||||
drawText(input.value, textX, textY);
|
||||
}
|
||||
}
|
||||
|
||||
function handleMouseUp(e) {
|
||||
isDragging = false;
|
||||
}
|
||||
|
||||
function handleTouchEnd(e) {
|
||||
isDragging = false;
|
||||
}
|
||||
|
||||
function clickHandleFinish() {
|
||||
handleFinish(true);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function loadGoogleFonts(fonts) {
|
||||
var link = document.createElement('link');
|
||||
link.rel = 'stylesheet';
|
||||
link.href = 'https://fonts.googleapis.com/css?family=' + fonts.join('|');
|
||||
document.head.appendChild(link);
|
||||
}
|
||||
|
||||
function dataURLToBlob(dataURL) {
|
||||
const byteString = atob(dataURL.split(',')[1]);
|
||||
const mimeString = dataURL.split(',')[0].split(':')[1].split(';')[0];
|
||||
const arrayBuffer = new ArrayBuffer(byteString.length);
|
||||
const uint8Array = new Uint8Array(arrayBuffer);
|
||||
for (let i = 0; i < byteString.length; i++) {
|
||||
uint8Array[i] = byteString.charCodeAt(i);
|
||||
}
|
||||
return new Blob([arrayBuffer], { type: mimeString });
|
||||
}
|
||||
|
||||
paintLoaded = true;
|
||||
38
ESP32_AP-Flasher/data/www/upload-demo.html
Normal file
38
ESP32_AP-Flasher/data/www/upload-demo.html
Normal file
@@ -0,0 +1,38 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Image Upload Form</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h3>demo upload form</h3>
|
||||
<p>You can use this as an example how to push images to a tag by an external server/script.</p>
|
||||
<p>
|
||||
<form method="POST" enctype="multipart/form-data" action="/imgupload">
|
||||
|
||||
<p>
|
||||
<label for="mac">Enter a 6 or 8 byte MAC address:</label><br>
|
||||
<input type="text" id="mac" name="mac">
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<label for="dither">Floyd Steinberg dithering (0=off, 1=on)</label><br>
|
||||
<input type="text" id="dither" name="dither">
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<label for="image">Select an image to upload. Must have the correct resolution for the tag (rotating is allowed):</label><br>
|
||||
<input type="file" id="image" name="file">
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<input type="submit" value="Upload">
|
||||
</p>
|
||||
|
||||
</form>
|
||||
</p>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
9
ESP32_AP-Flasher/dependencies.lock
Normal file
9
ESP32_AP-Flasher/dependencies.lock
Normal file
@@ -0,0 +1,9 @@
|
||||
dependencies:
|
||||
idf:
|
||||
component_hash: null
|
||||
source:
|
||||
type: idf
|
||||
version: 4.4.4
|
||||
manifest_hash: dcf4d39b94252de130019eadceb989d72b0dbc26b552cfdcbb50f6da531d2b92
|
||||
target: esp32s3
|
||||
version: 1.0.0
|
||||
7
ESP32_AP-Flasher/esp32_sdcard.csv
Normal file
7
ESP32_AP-Flasher/esp32_sdcard.csv
Normal 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, 0x150000,
|
||||
app1, app, ota_1, 0x160000,0x150000,
|
||||
spiffs, data, spiffs, 0x2B0000,0x140000,
|
||||
coredump, data, coredump,0x3F0000,0x10000,
|
||||
|
7
ESP32_AP-Flasher/esp32_squeeze_ota.csv
Normal file
7
ESP32_AP-Flasher/esp32_squeeze_ota.csv
Normal 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, 0x150000,
|
||||
app1, app, ota_1, 0x160000,0x150000,
|
||||
spiffs, data, spiffs, 0x2B0000,0x140000,
|
||||
coredump, data, coredump,0x3F0000,0x10000,
|
||||
|
24
ESP32_AP-Flasher/include/SPIFFSEditor.h
Normal file
24
ESP32_AP-Flasher/include/SPIFFSEditor.h
Normal file
@@ -0,0 +1,24 @@
|
||||
#ifndef SPIFFSEditor_H_
|
||||
#define SPIFFSEditor_H_
|
||||
#include <ESPAsyncWebServer.h>
|
||||
|
||||
class SPIFFSEditor: public AsyncWebHandler {
|
||||
private:
|
||||
fs::FS _fs;
|
||||
String _username;
|
||||
String _password;
|
||||
bool _authenticated;
|
||||
uint32_t _startTime;
|
||||
public:
|
||||
#ifdef ESP32
|
||||
SPIFFSEditor(const fs::FS& fs, const String& username=String(), const String& password=String());
|
||||
#else
|
||||
SPIFFSEditor(const String& username=String(), const String& password=String(), const fs::FS& fs=SPIFFS);
|
||||
#endif
|
||||
virtual bool canHandle(AsyncWebServerRequest *request) override final;
|
||||
virtual void handleRequest(AsyncWebServerRequest *request) override final;
|
||||
virtual void handleUpload(AsyncWebServerRequest *request, const String& filename, size_t index, uint8_t *data, size_t len, bool final) override final;
|
||||
virtual bool isRequestHandlerTrivial() override final {return false;}
|
||||
};
|
||||
|
||||
#endif
|
||||
125
ESP32_AP-Flasher/include/commstructs.h
Normal file
125
ESP32_AP-Flasher/include/commstructs.h
Normal file
@@ -0,0 +1,125 @@
|
||||
#include <Arduino.h>
|
||||
#pragma pack(push, 1)
|
||||
|
||||
#include "../../tag_types.h"
|
||||
|
||||
struct espBlockRequest {
|
||||
uint8_t checksum;
|
||||
uint64_t ver;
|
||||
uint8_t blockId;
|
||||
uint8_t src[8];
|
||||
} __packed;
|
||||
|
||||
struct espXferComplete {
|
||||
uint8_t checksum;
|
||||
uint8_t src[8];
|
||||
} __packed;
|
||||
|
||||
struct espSetChannelPower {
|
||||
uint8_t checksum;
|
||||
uint8_t channel;
|
||||
uint8_t power;
|
||||
} __packed;
|
||||
|
||||
struct blockData {
|
||||
uint16_t size;
|
||||
uint16_t checksum;
|
||||
uint8_t data[];
|
||||
} __packed;
|
||||
|
||||
struct AvailDataReq {
|
||||
uint8_t checksum;
|
||||
uint8_t lastPacketLQI;
|
||||
int8_t lastPacketRSSI;
|
||||
int8_t temperature;
|
||||
uint16_t batteryMv;
|
||||
uint8_t hwType;
|
||||
uint8_t wakeupReason;
|
||||
uint8_t capabilities;
|
||||
} __packed;
|
||||
|
||||
struct espAvailDataReq {
|
||||
uint8_t checksum;
|
||||
uint8_t src[8];
|
||||
struct AvailDataReq adr;
|
||||
} __packed;
|
||||
|
||||
#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_LUT_OTA 0x10
|
||||
|
||||
struct AvailDataInfo {
|
||||
uint8_t checksum;
|
||||
uint64_t dataVer; // MD5 of potential traffic
|
||||
uint32_t dataSize;
|
||||
uint8_t dataType; // allows for 16 different datatypes
|
||||
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
|
||||
} __packed;
|
||||
|
||||
struct pendingData {
|
||||
struct AvailDataInfo availdatainfo;
|
||||
uint16_t attemptsLeft;
|
||||
uint8_t targetMac[8];
|
||||
} __packed;
|
||||
|
||||
#define BLOCK_DATA_SIZE 4096
|
||||
#define BLOCK_XFER_BUFFER_SIZE BLOCK_DATA_SIZE + sizeof(struct blockData)
|
||||
|
||||
#define PKT_AVAIL_DATA_REQ 0xE5
|
||||
#define PKT_AVAIL_DATA_INFO 0xE6
|
||||
#define PKT_XFER_COMPLETE 0xEA
|
||||
#define PKT_XFER_TIMEOUT 0xED
|
||||
#define PKT_CANCEL_XFER 0xEC
|
||||
#define PKT_APLIST_REQ 0x80
|
||||
#define PKT_APLIST_REPLY 0x81
|
||||
#define PKT_TAGINFO 0x82
|
||||
|
||||
struct APlist {
|
||||
uint32_t src;
|
||||
char alias[32];
|
||||
uint8_t channelId;
|
||||
uint8_t tagCount;
|
||||
uint16_t version;
|
||||
} __packed;
|
||||
|
||||
#define SYNC_NOSYNC 0
|
||||
#define SYNC_USERCFG 1
|
||||
#define SYNC_TAGSTATUS 2
|
||||
#define SYNC_DELETE 3
|
||||
#define SYNC_VERSION 0xAA00
|
||||
|
||||
struct TagInfo {
|
||||
uint16_t structVersion = SYNC_VERSION;
|
||||
uint8_t mac[8];
|
||||
uint8_t syncMode;
|
||||
char alias[32];
|
||||
uint32_t lastseen;
|
||||
uint32_t nextupdate;
|
||||
bool pending;
|
||||
uint32_t expectedNextCheckin;
|
||||
uint8_t hwType;
|
||||
uint8_t wakeupReason;
|
||||
uint8_t capabilities;
|
||||
uint16_t pendingIdle;
|
||||
uint8_t contentMode;
|
||||
} __packed;
|
||||
|
||||
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
|
||||
} __packed;
|
||||
|
||||
#pragma pack(pop)
|
||||
42
ESP32_AP-Flasher/include/contentmanager.h
Normal file
42
ESP32_AP-Flasher/include/contentmanager.h
Normal file
@@ -0,0 +1,42 @@
|
||||
#include <Arduino.h>
|
||||
#include <LittleFS.h>
|
||||
#include <TFT_eSPI.h>
|
||||
|
||||
#include "U8g2_for_TFT_eSPI.h"
|
||||
#include "makeimage.h"
|
||||
#include "tag_db.h"
|
||||
|
||||
struct contentTypes {
|
||||
uint16_t id;
|
||||
String name;
|
||||
uint16_t tagTypes;
|
||||
void (*functionname)();
|
||||
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, uint16_t posx, uint16_t posy, String font, byte align = 0, uint16_t color = TFT_BLACK);
|
||||
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);
|
||||
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);
|
||||
void drawBuienradar(String &filename, JsonObject &cfgobj, tagRecord *&taginfo, imgParam &imageParams);
|
||||
char *formatHttpDate(time_t t);
|
||||
String urlEncode(const char *msg);
|
||||
int windSpeedToBeaufort(float windSpeed);
|
||||
String windDirectionIcon(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 setU8G2Font(const String &title, U8g2_for_TFT_eSPI &u8f);
|
||||
13
ESP32_AP-Flasher/include/flasher.h
Normal file
13
ESP32_AP-Flasher/include/flasher.h
Normal file
@@ -0,0 +1,13 @@
|
||||
#include <Arduino.h>
|
||||
|
||||
uint16_t getAPUpdateVersion(uint8_t type);
|
||||
bool checkForcedAPFlash();
|
||||
bool doForcedAPFlash();
|
||||
bool doAPFlash();
|
||||
bool doAPUpdate(uint8_t type);
|
||||
void flashCountDown(uint8_t c);
|
||||
|
||||
#ifdef OPENEPAPERLINK_PCB
|
||||
bool extTagConnected();
|
||||
bool doTagFlash();
|
||||
#endif
|
||||
33
ESP32_AP-Flasher/include/language.h
Normal file
33
ESP32_AP-Flasher/include/language.h
Normal file
@@ -0,0 +1,33 @@
|
||||
#pragma once
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
static int defaultLanguage = 0;
|
||||
|
||||
static String languageList[] = {"EN - English", "NL - Nederlands", "DE - Deutsch"};
|
||||
|
||||
/*EN English language section*/
|
||||
static String languageEnDaysShort[] = {"SU", "MO", "TU", "WE", "TH", "FR", "SA"};
|
||||
static String languageEnDays[] = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"};
|
||||
static String languageEnMonth[] = {"January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"};
|
||||
/*END English language section END*/
|
||||
|
||||
/*NL Dutch language section*/
|
||||
static String languageNlDaysShort[] = {"ZO", "MA", "DI", "WO", "DO", "VR", "ZA"};
|
||||
static String languageNlDays[] = {"zondag", "maandag", "dinsdag", "woensdag", "donderdag", "vrijdag", "zaterdag"};
|
||||
static String languageNlMonth[] = {"januari", "februari", "maart", "april", "mei", "juni", "juli", "augustus", "september", "oktober", "november", "december"};
|
||||
/*END Dutch language section END*/
|
||||
|
||||
/*DE German language section*/
|
||||
static String languageDeDaysShort[] = {"SO", "MO", "DI", "MI", "DO", "FR", "SA"};
|
||||
static String languageDeDays[] = {"Sonntag", "Montag", "Dienstag", "Mittwoch", "Donnerstag", "Freitag", "Samstag"};
|
||||
static String languageDeMonth[] = {"Januar", "Februar", "März", "April", "Mai", "Juni", "Juli", "August", "September", "Oktober", "November", "Dezember"};
|
||||
/*END German language section END*/
|
||||
|
||||
static String* languageDaysShort[] = {languageEnDaysShort, languageNlDaysShort, languageDeDaysShort};
|
||||
static String* languageDays[] = {languageEnDays, languageNlDays, languageDeDays};
|
||||
static String* languageMonth[] = {languageEnMonth, languageNlMonth, languageDeMonth};
|
||||
|
||||
void updateLanguageFromConfig();
|
||||
int getDefaultLanguage();
|
||||
int getCurrentLanguage();
|
||||
19
ESP32_AP-Flasher/include/leds.h
Normal file
19
ESP32_AP-Flasher/include/leds.h
Normal file
@@ -0,0 +1,19 @@
|
||||
#include <Arduino.h>
|
||||
|
||||
#ifdef HAS_RGB_LED
|
||||
#define FASTLED_INTERNAL
|
||||
#include <FastLED.h>
|
||||
#endif
|
||||
|
||||
void ledTask(void* parameter);
|
||||
void setBrightness(int brightness);
|
||||
void updateBrightnessFromConfig();
|
||||
|
||||
#ifdef HAS_RGB_LED
|
||||
void shortBlink(CRGB cname);
|
||||
void showColorPattern(CRGB colorone, CRGB colortwo, CRGB colorthree);
|
||||
void rgbIdle();
|
||||
void addFadeColor(CRGB cname);
|
||||
#endif
|
||||
|
||||
void quickBlink(uint8_t repeat);
|
||||
20
ESP32_AP-Flasher/include/makeimage.h
Normal file
20
ESP32_AP-Flasher/include/makeimage.h
Normal file
@@ -0,0 +1,20 @@
|
||||
#include <Arduino.h>
|
||||
#include <TFT_eSPI.h>
|
||||
|
||||
#pragma once
|
||||
|
||||
struct imgParam {
|
||||
bool hasRed;
|
||||
uint8_t dataType;
|
||||
bool dither;
|
||||
bool grayLut = false;
|
||||
uint8_t bpp = 8;
|
||||
uint8_t rotate = 0;
|
||||
|
||||
char segments[12];
|
||||
uint16_t symbols;
|
||||
bool invert;
|
||||
};
|
||||
|
||||
void spr2buffer(TFT_eSprite &spr, String &fileout, imgParam &imageParams);
|
||||
void jpg2buffer(String filein, String fileout, imgParam &imageParams);
|
||||
22
ESP32_AP-Flasher/include/newproto.h
Normal file
22
ESP32_AP-Flasher/include/newproto.h
Normal file
@@ -0,0 +1,22 @@
|
||||
#include <Arduino.h>
|
||||
|
||||
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 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 void updateTaginfoitem(struct TagInfo* taginfoitem);
|
||||
|
||||
void refreshAllPending();
|
||||
void updateContent(uint8_t* dst);
|
||||
void setAPchannel();
|
||||
13
ESP32_AP-Flasher/include/ota.h
Normal file
13
ESP32_AP-Flasher/include/ota.h
Normal file
@@ -0,0 +1,13 @@
|
||||
#include <Arduino.h>
|
||||
|
||||
#include "web.h"
|
||||
|
||||
void handleSysinfoRequest(AsyncWebServerRequest* request);
|
||||
void handleCheckFile(AsyncWebServerRequest* request);
|
||||
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 handleRollback(AsyncWebServerRequest* request);
|
||||
void handleUpdateActions(AsyncWebServerRequest* request);
|
||||
5
ESP32_AP-Flasher/include/powermgt.h
Normal file
5
ESP32_AP-Flasher/include/powermgt.h
Normal file
@@ -0,0 +1,5 @@
|
||||
#include <Arduino.h>
|
||||
|
||||
|
||||
//void doLeds();
|
||||
void powerControl(bool powerState, uint8_t* pin, uint8_t pincount);
|
||||
35
ESP32_AP-Flasher/include/serialap.h
Normal file
35
ESP32_AP-Flasher/include/serialap.h
Normal file
@@ -0,0 +1,35 @@
|
||||
#include <Arduino.h>
|
||||
|
||||
extern struct espSetChannelPower curChannel;
|
||||
|
||||
#define AP_STATE_OFFLINE 0
|
||||
#define AP_STATE_ONLINE 1
|
||||
#define AP_STATE_FLASHING 2
|
||||
#define AP_STATE_WAIT_RESET 3
|
||||
#define AP_STATE_REQUIRED_POWER_CYCLE 4
|
||||
#define AP_STATE_FAILED 5
|
||||
#define AP_STATE_COMING_ONLINE 6
|
||||
|
||||
struct APInfoS {
|
||||
bool isOnline = false;
|
||||
uint8_t state = AP_STATE_OFFLINE;
|
||||
uint8_t type;
|
||||
uint16_t version = 0;
|
||||
uint8_t channel;
|
||||
uint8_t mac[8];
|
||||
uint8_t power;
|
||||
uint8_t pending;
|
||||
uint8_t nop;
|
||||
};
|
||||
|
||||
extern struct APInfoS apInfo;
|
||||
|
||||
void APTask(void* parameter);
|
||||
|
||||
bool sendCancelPending(struct pendingData* pending);
|
||||
bool sendDataAvail(struct pendingData* pending);
|
||||
bool sendPing();
|
||||
|
||||
void APEnterEarlyReset();
|
||||
|
||||
bool sendChannelPower(struct espSetChannelPower* scp);
|
||||
8
ESP32_AP-Flasher/include/serialconsole.h
Normal file
8
ESP32_AP-Flasher/include/serialconsole.h
Normal file
@@ -0,0 +1,8 @@
|
||||
#include <Arduino.h>
|
||||
extern QueueHandle_t consoleCmdQueue;
|
||||
extern TaskHandle_t consoleTaskHandle;
|
||||
|
||||
|
||||
void consoleStopTask();
|
||||
void consoleTask(void* parameter);
|
||||
void consoleUartHandler(uint8_t* data, uint8_t len);
|
||||
16
ESP32_AP-Flasher/include/settings.h
Normal file
16
ESP32_AP-Flasher/include/settings.h
Normal file
@@ -0,0 +1,16 @@
|
||||
#include <Arduino.h>
|
||||
|
||||
#define FLASHER_AP_PORT 0
|
||||
#ifdef OPENEPAPERLINK_PCB
|
||||
#define FLASHER_EXT_PORT 1
|
||||
#define FLASHER_ALTRADIO_PORT 2
|
||||
#endif
|
||||
|
||||
// Which port we should use for the AP process
|
||||
// (useful for testing, can only be FLASHER_AP_PORT for any other board than the OpenEPaperLinkPCB board)
|
||||
#define AP_PROCESS_PORT FLASHER_AP_PORT
|
||||
|
||||
// flasher options
|
||||
#define CUSTOM_MAC_HDR 0x0000
|
||||
|
||||
#define MAX_WRITE_ATTEMPTS 5
|
||||
39
ESP32_AP-Flasher/include/storage.h
Normal file
39
ESP32_AP-Flasher/include/storage.h
Normal file
@@ -0,0 +1,39 @@
|
||||
#ifndef _DYN_STORAGE_H_
|
||||
#define _DYN_STORAGE_H_
|
||||
|
||||
#include "FS.h"
|
||||
|
||||
#ifdef HAS_SDCARD
|
||||
#ifndef SD_CARD_SS
|
||||
#error SD_CARD_SS UNDEFINED
|
||||
#endif
|
||||
|
||||
#ifndef SD_CARD_CLK
|
||||
#define SD_CARD_CLK 18
|
||||
#endif
|
||||
|
||||
#ifndef SD_CARD_MISO
|
||||
#define SD_CARD_MISO 19
|
||||
#endif
|
||||
|
||||
#ifndef SD_CARD_MOSI
|
||||
#define SD_CARD_MOSI 23
|
||||
#endif
|
||||
#endif
|
||||
|
||||
class DynStorage {
|
||||
public:
|
||||
DynStorage();
|
||||
void begin();
|
||||
void end();
|
||||
void listFiles();
|
||||
size_t freeSpace();
|
||||
|
||||
private:
|
||||
bool isInited;
|
||||
};
|
||||
|
||||
extern DynStorage Storage;
|
||||
extern fs::FS *contentFS;
|
||||
|
||||
#endif
|
||||
14
ESP32_AP-Flasher/include/system.h
Normal file
14
ESP32_AP-Flasher/include/system.h
Normal file
@@ -0,0 +1,14 @@
|
||||
#include <Arduino.h>
|
||||
|
||||
#define WAKEUP_REASON_TIMED 0
|
||||
#define WAKEUP_REASON_BOOT 1
|
||||
#define WAKEUP_REASON_GPIO 2
|
||||
#define WAKEUP_REASON_NFC 3
|
||||
#define WAKEUP_REASON_FIRSTBOOT 0xFC
|
||||
#define WAKEUP_REASON_NETWORK_SCAN 0xFD
|
||||
#define WAKEUP_REASON_WDT_RESET 0xFE
|
||||
|
||||
void init_time();
|
||||
void logLine(char* buffer);
|
||||
void logLine(String text);
|
||||
void logStartUp();
|
||||
83
ESP32_AP-Flasher/include/tag_db.h
Normal file
83
ESP32_AP-Flasher/include/tag_db.h
Normal file
@@ -0,0 +1,83 @@
|
||||
#include <Arduino.h>
|
||||
#include <ArduinoJson.h>
|
||||
|
||||
#include <vector>
|
||||
|
||||
#pragma pack(push, 1)
|
||||
#pragma once
|
||||
|
||||
#define WAKEUP_REASON_TIMED 0
|
||||
#define WAKEUP_REASON_BOOTUP 1
|
||||
#define WAKEUP_REASON_GPIO 2
|
||||
#define WAKEUP_REASON_NFC 3
|
||||
|
||||
#define RUNSTATUS_STOP 0
|
||||
#define RUNSTATUS_PAUSE 1
|
||||
#define RUNSTATUS_RUN 2
|
||||
#define RUNSTATUS_INIT 3
|
||||
|
||||
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),
|
||||
dataType(0), filename(""), data(nullptr), len(0) {}
|
||||
|
||||
uint8_t mac[8];
|
||||
String alias;
|
||||
uint32_t lastseen;
|
||||
uint32_t nextupdate;
|
||||
uint8_t contentMode;
|
||||
bool pending;
|
||||
uint8_t md5[16];
|
||||
uint8_t md5pending[16];
|
||||
uint32_t expectedNextCheckin;
|
||||
String modeConfigJson;
|
||||
uint8_t LQI;
|
||||
int8_t RSSI;
|
||||
int8_t temperature;
|
||||
uint16_t batteryMv;
|
||||
uint8_t hwType;
|
||||
uint8_t wakeupReason;
|
||||
uint8_t capabilities;
|
||||
uint32_t lastfullupdate;
|
||||
bool isExternal;
|
||||
uint16_t pendingIdle;
|
||||
bool hasCustomLUT;
|
||||
uint8_t rotate;
|
||||
uint8_t lut;
|
||||
|
||||
uint8_t dataType;
|
||||
String filename;
|
||||
uint8_t* data;
|
||||
uint32_t len;
|
||||
|
||||
static tagRecord* findByMAC(uint8_t mac[8]);
|
||||
};
|
||||
|
||||
struct Config {
|
||||
uint8_t channel;
|
||||
char alias[32];
|
||||
int16_t led;
|
||||
uint8_t language;
|
||||
uint8_t maxsleep;
|
||||
uint8_t stopsleep;
|
||||
uint8_t runStatus;
|
||||
};
|
||||
|
||||
// extern SemaphoreHandle_t tagDBOwner;
|
||||
extern Config config;
|
||||
extern std::vector<tagRecord*> tagDB;
|
||||
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);
|
||||
void saveDB(String filename);
|
||||
void loadDB(String filename);
|
||||
void destroyDB();
|
||||
uint8_t getTagCount();
|
||||
void mac2hex(uint8_t* mac, char* hexBuffer);
|
||||
bool hex2mac(const String& hexString, uint8_t* mac);
|
||||
void clearPending(tagRecord* taginfo);
|
||||
void initAPconfig();
|
||||
void saveAPconfig();
|
||||
|
||||
#pragma pack(pop)
|
||||
26
ESP32_AP-Flasher/include/udp.h
Normal file
26
ESP32_AP-Flasher/include/udp.h
Normal file
@@ -0,0 +1,26 @@
|
||||
#include <Arduino.h>
|
||||
|
||||
#include "AsyncUDP.h"
|
||||
|
||||
#ifndef defudpcomm
|
||||
#define defudpcomm
|
||||
|
||||
class UDPcomm {
|
||||
public:
|
||||
UDPcomm();
|
||||
~UDPcomm();
|
||||
void init();
|
||||
void getAPList();
|
||||
void netProcessDataReq(struct espAvailDataReq* eadr);
|
||||
void netProcessXferComplete(struct espXferComplete* xfc);
|
||||
void netProcessXferTimeout(struct espXferComplete* xfc);
|
||||
void netSendDataAvail(struct pendingData* pending);
|
||||
void netTaginfo(struct TagInfo* taginfoitem);
|
||||
private:
|
||||
AsyncUDP udp;
|
||||
void processPacket(AsyncUDPPacket packet);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
void init_udp();
|
||||
4
ESP32_AP-Flasher/include/usbflasher.h
Normal file
4
ESP32_AP-Flasher/include/usbflasher.h
Normal file
@@ -0,0 +1,4 @@
|
||||
#include <Arduino.h>
|
||||
|
||||
|
||||
void usbFlasherTask(void* parameter);
|
||||
@@ -1,5 +1,5 @@
|
||||
|
||||
#include<Arduino.h>
|
||||
#include <Arduino.h>
|
||||
#include <AsyncTCP.h>
|
||||
#include <ESPAsyncWebServer.h>
|
||||
|
||||
@@ -9,11 +9,12 @@ void doImageUpload(AsyncWebServerRequest *request, String filename, size_t index
|
||||
extern void webSocketSendProcess(void *parameter);
|
||||
void wsLog(String text);
|
||||
void wsErr(String text);
|
||||
void wsSendTaginfo(uint8_t mac[6]);
|
||||
void wsSendTaginfo(uint8_t *mac, uint8_t syncMode);
|
||||
void wsSendSysteminfo();
|
||||
void wsSendAPitem(struct APlist* apitem);
|
||||
void wsSerial(String text);
|
||||
uint8_t wsClientCount();
|
||||
|
||||
extern uint64_t swap64(uint64_t x);
|
||||
extern AsyncWebSocket ws; //("/ws");
|
||||
extern AsyncWebSocket ws;
|
||||
|
||||
extern SemaphoreHandle_t wsMutex;
|
||||
extern TaskHandle_t websocketUpdater;
|
||||
@@ -1,15 +1,16 @@
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <SPI.h>
|
||||
|
||||
/* Autor: Aaron Christophel ATCnetz.de */
|
||||
#include <Arduino.h>
|
||||
|
||||
void simplePowerOn();
|
||||
|
||||
class ZBS_interface {
|
||||
public:
|
||||
uint8_t begin();
|
||||
class ZBS_interface
|
||||
{
|
||||
public:
|
||||
uint8_t begin(uint8_t SS, uint8_t CLK, uint8_t MOSI, uint8_t MISO, uint8_t RESET, uint8_t* POWER = nullptr, uint8_t powerPins = 1, uint32_t spi_speed = 8000000);
|
||||
void setSpeed(uint32_t speed);
|
||||
void set_power(uint8_t state);
|
||||
void enable_debug();
|
||||
void reset();
|
||||
@@ -27,17 +28,24 @@ class ZBS_interface {
|
||||
uint8_t select_flash(uint8_t page);
|
||||
void erase_flash();
|
||||
void erase_infoblock();
|
||||
~ZBS_interface();
|
||||
|
||||
private:
|
||||
private:
|
||||
SPIClass *spi = NULL;
|
||||
SPISettings spiSettings;
|
||||
uint8_t _SS_PIN = -1;
|
||||
uint8_t _CLK_PIN = -1;
|
||||
uint8_t _MOSI_PIN = -1;
|
||||
uint8_t _MISO_PIN = -1;
|
||||
uint8_t _RESET_PIN = -1;
|
||||
uint8_t _POWER_PIN = -1;
|
||||
uint8_t* _POWER_PIN = nullptr;
|
||||
uint8_t _POWER_PINS = 1;
|
||||
int ZBS_spi_delay = 1;
|
||||
uint8_t spi_ready = 0;
|
||||
uint32_t after_byte_delay = 10;
|
||||
|
||||
typedef enum {
|
||||
typedef enum
|
||||
{
|
||||
ZBS_CMD_W_RAM = 0x02,
|
||||
ZBS_CMD_R_RAM = 0x03,
|
||||
ZBS_CMD_W_FLASH = 0x08,
|
||||
@@ -48,10 +56,9 @@ class ZBS_interface {
|
||||
ZBS_CMD_ERASE_INFOBLOCK = 0x48,
|
||||
} ZBS_CMD_LIST;
|
||||
|
||||
typedef enum {
|
||||
typedef enum
|
||||
{
|
||||
ZBS_ON = 1,
|
||||
ZBS_OFF = 0,
|
||||
} ZBS_POWER_STATE;
|
||||
};
|
||||
|
||||
extern ZBS_interface zbs;
|
||||
};
|
||||
274
ESP32_AP-Flasher/platformio.ini
Normal file
274
ESP32_AP-Flasher/platformio.ini
Normal file
@@ -0,0 +1,274 @@
|
||||
; PlatformIO Project Configuration File
|
||||
;
|
||||
; Build options: build flags, source filter
|
||||
; Upload options: custom upload port, speed and extra flags
|
||||
; Library options: dependencies, extra library storages
|
||||
; Advanced options: extra scripting
|
||||
;
|
||||
; Please visit documentation for the other options and examples
|
||||
; https://docs.platformio.org/page/projectconf.html
|
||||
|
||||
[env]
|
||||
platform = espressif32
|
||||
framework = arduino
|
||||
lib_deps =
|
||||
https://github.com/me-no-dev/ESPAsyncWebServer
|
||||
https://github.com/tzapu/WiFiManager.git#feature_asyncwebserver
|
||||
bblanchon/ArduinoJson
|
||||
bodmer/TFT_eSPI
|
||||
https://github.com/Bodmer/TJpg_Decoder.git
|
||||
https://github.com/garretlab/shoddyxml2
|
||||
https://github.com/Bodmer/U8g2_for_TFT_eSPI
|
||||
https://github.com/ricmoo/qrcode
|
||||
fastled/FastLED
|
||||
https://github.com/MajenkoLibraries/SoftSPI
|
||||
platform_packages =
|
||||
board_build.filesystem = littlefs
|
||||
monitor_filters = esp32_exception_decoder
|
||||
monitor_speed = 115200
|
||||
board_build.f_cpu = 240000000L
|
||||
|
||||
; ----------------------------------------------------------------------------------------
|
||||
; !!! this configuration expects the Mini_AP
|
||||
;
|
||||
; ----------------------------------------------------------------------------------------
|
||||
[env:OpenEPaperLink_Mini_AP]
|
||||
platform = https://github.com/platformio/platform-espressif32.git
|
||||
board=lolin_s2_mini
|
||||
board_build.partitions = default.csv
|
||||
build_unflags =
|
||||
-D CONFIG_MBEDTLS_INTERNAL_MEM_ALLOC=y
|
||||
build_flags =
|
||||
-D BUILD_ENV_NAME=$PIOENV
|
||||
-D BUILD_TIME=$UNIX_TIME
|
||||
-D OPENEPAPERLINK_MINI_AP_PCB
|
||||
-D ARDUINO_USB_MODE=0
|
||||
-D CONFIG_SPIRAM_USE_MALLOC=1
|
||||
-D CONFIG_MBEDTLS_EXTERNAL_MEM_ALLOC=y
|
||||
-D HAS_RGB_LED
|
||||
-D BOARD_HAS_PSRAM
|
||||
-D POWER_NO_SOFT_POWER
|
||||
-D FLASHER_AP_SS=11
|
||||
-D FLASHER_AP_CLK=9
|
||||
-D FLASHER_AP_MOSI=10
|
||||
-D FLASHER_AP_MISO=8
|
||||
-D FLASHER_AP_RESET=13
|
||||
-D FLASHER_AP_POWER={-1} ;this board has no soft power control
|
||||
-D FLASHER_AP_TXD=7
|
||||
-D FLASHER_AP_RXD=6
|
||||
-D FLASHER_AP_TEST=12
|
||||
-D FLASHER_LED=15
|
||||
-D FLASHER_RGB_LED=33
|
||||
-D USER_SETUP_LOADED
|
||||
-D DISABLE_ALL_LIBRARY_WARNINGS
|
||||
-D ILI9341_DRIVER
|
||||
-D SMOOTH_FONT
|
||||
-D LOAD_FONT2
|
||||
build_src_filter =
|
||||
+<*>-<usbflasher.cpp>-<serialconsole.cpp>
|
||||
board_build.psram_type=qspi_opi
|
||||
board_upload.maximum_size = 4194304
|
||||
board_upload.maximum_ram_size = 327680
|
||||
board_upload.flash_size = 4MB
|
||||
|
||||
; ----------------------------------------------------------------------------------------
|
||||
; !!! this configuration expects the Nano_AP
|
||||
;
|
||||
; ----------------------------------------------------------------------------------------
|
||||
[env:OpenEPaperLink_Nano_AP]
|
||||
platform = https://github.com/platformio/platform-espressif32.git
|
||||
board=lolin_s2_mini
|
||||
board_build.partitions = default.csv
|
||||
build_unflags =
|
||||
-D CONFIG_MBEDTLS_INTERNAL_MEM_ALLOC=y
|
||||
build_flags =
|
||||
-D BUILD_ENV_NAME=$PIOENV
|
||||
-D BUILD_TIME=$UNIX_TIME
|
||||
-D OPENEPAPERLINK_NANO_AP_PCB
|
||||
-D ARDUINO_USB_MODE=0
|
||||
-D CONFIG_SPIRAM_USE_MALLOC=1
|
||||
-D CONFIG_MBEDTLS_EXTERNAL_MEM_ALLOC=y
|
||||
-D BOARD_HAS_PSRAM
|
||||
-D FLASHER_AP_SS=38
|
||||
-D FLASHER_AP_CLK=40
|
||||
-D FLASHER_AP_MOSI=39
|
||||
-D FLASHER_AP_MISO=33
|
||||
-D FLASHER_AP_RESET=37
|
||||
-D FLASHER_AP_POWER={16,17,18,21}
|
||||
-D FLASHER_AP_TXD=35
|
||||
-D FLASHER_AP_RXD=34
|
||||
-D FLASHER_AP_TEST=36
|
||||
-D FLASHER_LED=15
|
||||
-D FLASHER_RGB_LED=-1
|
||||
-D USER_SETUP_LOADED
|
||||
-D DISABLE_ALL_LIBRARY_WARNINGS
|
||||
-D ILI9341_DRIVER
|
||||
-D SMOOTH_FONT
|
||||
-D LOAD_FONT2
|
||||
build_src_filter =
|
||||
+<*>-<usbflasher.cpp>-<serialconsole.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 16MB Flash / 8MB Ram version of the ESP32-S3-DevkitC1
|
||||
;
|
||||
; ----------------------------------------------------------------------------------------
|
||||
[env:OpenEPaperLink_AP_and_Flasher]
|
||||
platform = https://github.com/platformio/platform-espressif32.git
|
||||
board = esp32-s3-devkitc-1
|
||||
board_build.partitions = default_16MB.csv
|
||||
build_unflags =
|
||||
-D ARDUINO_USB_MODE=1
|
||||
-D CONFIG_MBEDTLS_INTERNAL_MEM_ALLOC=y
|
||||
build_flags =
|
||||
-D BUILD_ENV_NAME=$PIOENV
|
||||
-D BUILD_TIME=$UNIX_TIME
|
||||
-D OPENEPAPERLINK_PCB
|
||||
-D ARDUINO_USB_MODE=0
|
||||
-D CONFIG_ESP32S3_SPIRAM_SUPPORT=1
|
||||
-D CONFIG_SPIRAM_USE_MALLOC=1
|
||||
-D HAS_USB
|
||||
-D HAS_RGB_LED
|
||||
-D BOARD_HAS_PSRAM
|
||||
-D CONFIG_MBEDTLS_EXTERNAL_MEM_ALLOC=y
|
||||
-D POWER_RAMPING
|
||||
-D POWER_HIGH_SIDE_DRIVER
|
||||
-D FLASHER_AP_SS=4
|
||||
-D FLASHER_AP_CLK=5
|
||||
-D FLASHER_AP_MOSI=7
|
||||
-D FLASHER_AP_MISO=6
|
||||
-D FLASHER_AP_RESET=15
|
||||
-D FLASHER_AP_POWER={0}
|
||||
-D FLASHER_AP_TXD=16
|
||||
-D FLASHER_AP_RXD=18
|
||||
-D FLASHER_AP_TEST=17
|
||||
-D FLASHER_EXT_SS=40
|
||||
-D FLASHER_EXT_CLK=41
|
||||
-D FLASHER_EXT_MOSI=2
|
||||
-D FLASHER_EXT_MISO=42
|
||||
-D FLASHER_EXT_RESET=1
|
||||
-D FLASHER_EXT_POWER={8}
|
||||
-D FLASHER_EXT_TXD=38
|
||||
-D FLASHER_EXT_RXD=39
|
||||
-D FLASHER_EXT_TEST=47
|
||||
-D FLASHER_ALT_SS=3
|
||||
-D FLASHER_ALT_CLK=46
|
||||
-D FLASHER_ALT_MOSI=10
|
||||
-D FLASHER_ALT_MISO=9
|
||||
-D FLASHER_ALT_RESET=11
|
||||
-D FLASHER_ALT_POWER={-1}
|
||||
-D FLASHER_ALT_TXD=12
|
||||
-D FLASHER_ALT_RXD=14
|
||||
-D FLASHER_ALT_TEST=13
|
||||
-D FLASHER_LED=21
|
||||
-D FLASHER_RGB_LED=48
|
||||
-D USER_SETUP_LOADED
|
||||
-D DISABLE_ALL_LIBRARY_WARNINGS
|
||||
-D ILI9341_DRIVER
|
||||
-D SMOOTH_FONT
|
||||
-D LOAD_FONT2
|
||||
board_build.flash_mode=qio
|
||||
board_build.arduino.memory_type = qio_opi
|
||||
board_build.psram_type=qspi_opi
|
||||
board_upload.maximum_size = 16777216
|
||||
board_upload.maximum_ram_size = 327680
|
||||
board_upload.flash_size = 16MB
|
||||
|
||||
; ----------------------------------------------------------------------------------------
|
||||
; !!! this configuration expects an esp32
|
||||
;
|
||||
; ----------------------------------------------------------------------------------------
|
||||
[env:Simple_AP]
|
||||
board = esp32dev
|
||||
board_build.partitions = default.csv
|
||||
build_flags =
|
||||
-D BUILD_ENV_NAME=$PIOENV
|
||||
-D BUILD_TIME=$UNIX_TIME
|
||||
-D CORE_DEBUG_LEVEL=0
|
||||
-D SIMPLE_AP
|
||||
-D FLASHER_AP_SS=5
|
||||
-D FLASHER_AP_CLK=18
|
||||
-D FLASHER_AP_MOSI=23
|
||||
-D FLASHER_AP_MISO=19
|
||||
-D FLASHER_AP_RESET=2
|
||||
-D FLASHER_AP_POWER={13,15}
|
||||
-D FLASHER_AP_TEST=-1
|
||||
-D FLASHER_AP_TXD=17
|
||||
-D FLASHER_AP_RXD=16
|
||||
-D FLASHER_LED=22
|
||||
-D USER_SETUP_LOADED
|
||||
-D DISABLE_ALL_LIBRARY_WARNINGS
|
||||
-D ILI9341_DRIVER
|
||||
-D SMOOTH_FONT
|
||||
-D LOAD_FONT2
|
||||
build_src_filter =
|
||||
+<*>-<usbflasher.cpp>-<serialconsole.cpp>
|
||||
|
||||
; ----------------------------------------------------------------------------------------
|
||||
; !!! this configuration expects an wemos_d1_mini32
|
||||
;
|
||||
; ----------------------------------------------------------------------------------------
|
||||
[env:Weemos_AP]
|
||||
board = wemos_d1_mini32
|
||||
board_build.partitions = default.csv
|
||||
build_flags =
|
||||
-D BUILD_ENV_NAME=$PIOENV
|
||||
-D BUILD_TIME=$UNIX_TIME
|
||||
-D CORE_DEBUG_LEVEL=0
|
||||
-D FLASHER_AP_SS=5
|
||||
-D FLASHER_AP_CLK=18
|
||||
-D FLASHER_AP_MOSI=23
|
||||
-D FLASHER_AP_MISO=19
|
||||
-D FLASHER_AP_RESET=14
|
||||
-D FLASHER_AP_POWER={-1}
|
||||
-D FLASHER_AP_TEST=-1
|
||||
-D FLASHER_AP_TXD=16
|
||||
-D FLASHER_AP_RXD=17
|
||||
-D FLASHER_LED=22
|
||||
-D USER_SETUP_LOADED
|
||||
-D DISABLE_ALL_LIBRARY_WARNINGS
|
||||
-D ILI9341_DRIVER
|
||||
-D SMOOTH_FONT
|
||||
-D LOAD_FONT2
|
||||
build_src_filter =
|
||||
+<*>-<usbflasher.cpp>-<serialconsole.cpp>
|
||||
|
||||
; ----------------------------------------------------------------------------------------
|
||||
; !!! this configuration expects an m5stack esp32
|
||||
;
|
||||
; ----------------------------------------------------------------------------------------
|
||||
[env:M5Stack_Core_ONE_AP]
|
||||
platform = espressif32
|
||||
board = m5stack-core-esp32
|
||||
board_build.partitions = esp32_sdcard.csv
|
||||
build_flags =
|
||||
-D BUILD_ENV_NAME=$PIOENV
|
||||
-D BUILD_TIME=$UNIX_TIME
|
||||
-D CORE_DEBUG_LEVEL=0
|
||||
-D HAS_SDCARD
|
||||
-D USE_SOFTSPI
|
||||
-D SD_CARD_SS=4
|
||||
-D SD_CARD_CLK=18
|
||||
-D SD_CARD_MISO=19
|
||||
-D SD_CARD_MOSI=23
|
||||
-D FLASHER_AP_SS=5
|
||||
-D FLASHER_AP_CLK=36
|
||||
-D FLASHER_AP_MOSI=26
|
||||
-D FLASHER_AP_MISO=35
|
||||
-D FLASHER_AP_RESET=2
|
||||
-D FLASHER_AP_POWER={-1}
|
||||
-D FLASHER_AP_TEST=-1
|
||||
-D FLASHER_AP_TXD=16
|
||||
-D FLASHER_AP_RXD=17
|
||||
-D FLASHER_LED=-1
|
||||
-D FLASH_TIMEOUT=10
|
||||
-D USER_SETUP_LOADED
|
||||
-D DISABLE_ALL_LIBRARY_WARNINGS
|
||||
-D ILI9341_DRIVER
|
||||
-D SMOOTH_FONT
|
||||
-D LOAD_FONT2
|
||||
build_src_filter =
|
||||
+<*>-<usbflasher.cpp>-<serialconsole.cpp>
|
||||
6
ESP32_AP-Flasher/src/CMakeLists.txt
Normal file
6
ESP32_AP-Flasher/src/CMakeLists.txt
Normal file
@@ -0,0 +1,6 @@
|
||||
# This file was automatically generated for projects
|
||||
# without default 'CMakeLists.txt' file.
|
||||
|
||||
FILE(GLOB_RECURSE app_sources ${CMAKE_SOURCE_DIR}/src/*.*)
|
||||
|
||||
idf_component_register(SRCS ${app_sources})
|
||||
1060
ESP32_AP-Flasher/src/contentmanager.cpp
Normal file
1060
ESP32_AP-Flasher/src/contentmanager.cpp
Normal file
File diff suppressed because it is too large
Load Diff
725
ESP32_AP-Flasher/src/flasher.cpp
Normal file
725
ESP32_AP-Flasher/src/flasher.cpp
Normal file
@@ -0,0 +1,725 @@
|
||||
#include "flasher.h"
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <ArduinoJson.h>
|
||||
#include "storage.h"
|
||||
#include "LittleFS.h"
|
||||
#include <MD5Builder.h>
|
||||
// #include <FS.h>
|
||||
|
||||
#include "leds.h"
|
||||
#include "settings.h"
|
||||
#include "time.h"
|
||||
#include "zbs_interface.h"
|
||||
|
||||
#define FINGERPRINT_FLASH_SIZE 10240
|
||||
|
||||
#ifdef OPENEPAPERLINK_PCB
|
||||
bool extTagConnected() {
|
||||
// checks if the TEST (P1.0) pin on the ZBS243 will come up high. If it doesn't, there's probably a tag connected.
|
||||
pinMode(FLASHER_EXT_TEST, INPUT_PULLDOWN);
|
||||
vTaskDelay(5 / portTICK_PERIOD_MS);
|
||||
pinMode(FLASHER_EXT_TEST, INPUT_PULLUP);
|
||||
vTaskDelay(5 / portTICK_PERIOD_MS);
|
||||
return !digitalRead(FLASHER_EXT_TEST);
|
||||
}
|
||||
#endif
|
||||
|
||||
void dump(uint8_t *a, uint16_t l) {
|
||||
if (a == nullptr) {
|
||||
Serial.print("Tried to dump the contents of a nullptr, this is probably not what you want.\n");
|
||||
}
|
||||
Serial.printf(" ");
|
||||
#define ROWS 16
|
||||
for (uint8_t c = 0; c < ROWS; c++) {
|
||||
Serial.printf(" %02X", c);
|
||||
}
|
||||
Serial.printf("\n--------");
|
||||
for (uint8_t c = 0; c < ROWS; c++) {
|
||||
Serial.printf("---");
|
||||
}
|
||||
for (uint16_t c = 0; c < l; c++) {
|
||||
if ((c % ROWS) == 0) {
|
||||
Serial.printf("\n0x%04X | ", c);
|
||||
}
|
||||
Serial.printf("%02X ", a[c]);
|
||||
}
|
||||
Serial.printf("\n--------");
|
||||
for (uint8_t c = 0; c < ROWS; c++) {
|
||||
Serial.printf("---");
|
||||
}
|
||||
Serial.printf("\n");
|
||||
}
|
||||
|
||||
int8_t powerPinsAP[] = FLASHER_AP_POWER;
|
||||
int8_t pinsAP[] = {FLASHER_AP_CLK, FLASHER_AP_MISO, FLASHER_AP_MOSI, FLASHER_AP_RESET, FLASHER_AP_RXD, FLASHER_AP_SS, FLASHER_AP_TEST, FLASHER_AP_TXD};
|
||||
|
||||
#ifdef OPENEPAPERLINK_PCB
|
||||
int8_t powerPinsExt[] = FLASHER_EXT_POWER;
|
||||
int8_t powerPinsAlt[] = FLASHER_ALT_POWER;
|
||||
uint8_t pinsExt[] = {FLASHER_EXT_CLK, FLASHER_EXT_MISO, FLASHER_EXT_MOSI, FLASHER_EXT_RESET, FLASHER_EXT_RXD, FLASHER_EXT_SS, FLASHER_EXT_TEST, FLASHER_EXT_TXD};
|
||||
|
||||
#endif
|
||||
|
||||
class flasher {
|
||||
public:
|
||||
class ZBS_interface *zbs = nullptr;
|
||||
uint8_t md5[16] = {0};
|
||||
char md5char[34];
|
||||
uint8_t tagtype;
|
||||
uint8_t *infoblock = nullptr;
|
||||
|
||||
// Infoblock structure:
|
||||
// 0x00-0x0F - Calibration data
|
||||
// 0x10-0x17 - MAC
|
||||
// 0x19 - OpenEPaperLink Type
|
||||
// 0x30 - Original firmware MD5
|
||||
|
||||
uint8_t mac[8] = {0};
|
||||
uint8_t mac_format = 0;
|
||||
uint16_t mac_suffix = 0;
|
||||
uint16_t mac_offset = 0;
|
||||
|
||||
flasher();
|
||||
~flasher();
|
||||
bool connectTag(uint8_t port);
|
||||
void getFirmwareMD5();
|
||||
bool getFirmwareMac();
|
||||
bool findTagByMD5();
|
||||
bool findTagByType(uint8_t type);
|
||||
bool getInfoBlockMD5();
|
||||
bool getInfoBlockMac();
|
||||
bool getInfoBlockType();
|
||||
void getMacFromWiFi();
|
||||
bool prepareInfoBlock();
|
||||
|
||||
bool backupFlash();
|
||||
|
||||
bool writeFlash(uint8_t *flashbuffer, uint16_t size);
|
||||
bool writeFlashFromPack(String filename, uint8_t type);
|
||||
bool writeFlashFromPackOffset(fs::File *file, uint16_t length);
|
||||
|
||||
bool readInfoBlock();
|
||||
bool writeInfoBlock();
|
||||
|
||||
protected:
|
||||
bool writeBlock256(uint16_t offset, uint8_t *flashbuffer);
|
||||
void get_mac_format1();
|
||||
void get_mac_format2();
|
||||
};
|
||||
|
||||
flasher::flasher() {
|
||||
zbs = new ZBS_interface;
|
||||
Storage.end();
|
||||
}
|
||||
flasher::~flasher() {
|
||||
delete zbs;
|
||||
Storage.begin();
|
||||
}
|
||||
|
||||
#ifndef FLASHER_AP_SPEED
|
||||
#define FLASHER_AP_SPEED 4000000
|
||||
#endif
|
||||
|
||||
bool flasher::connectTag(uint8_t port) {
|
||||
bool result;
|
||||
switch (port) {
|
||||
case 0:
|
||||
result = zbs->begin(FLASHER_AP_SS, FLASHER_AP_CLK, FLASHER_AP_MOSI, FLASHER_AP_MISO, FLASHER_AP_RESET, (uint8_t *)powerPinsAP, sizeof(powerPinsAP), FLASHER_AP_SPEED);
|
||||
break;
|
||||
#ifdef OPENEPAPERLINK_PCB
|
||||
case 1:
|
||||
result = zbs->begin(FLASHER_EXT_SS, FLASHER_EXT_CLK, FLASHER_EXT_MOSI, FLASHER_EXT_MISO, FLASHER_EXT_RESET, (uint8_t *)powerPinsExt, sizeof(powerPinsExt), FLASHER_AP_SPEED);
|
||||
break;
|
||||
case 2:
|
||||
result = zbs->begin(FLASHER_ALT_SS, FLASHER_ALT_CLK, FLASHER_ALT_MOSI, FLASHER_ALT_MISO, FLASHER_ALT_RESET, (uint8_t *)powerPinsAlt, sizeof(powerPinsAlt), FLASHER_AP_SPEED);
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
Serial.printf("Tried to connect to port %d, but this port isn't available. Some dev borked it up, probably Jelmer.\n", port);
|
||||
return false;
|
||||
}
|
||||
if (!result) Serial.printf("I tried connecting to port %d, but I couldn't establish a link to the tag. That's all I know.\n", port);
|
||||
return result;
|
||||
}
|
||||
|
||||
void flasher::getFirmwareMD5() {
|
||||
uint8_t *buffer = (uint8_t *)malloc(FINGERPRINT_FLASH_SIZE);
|
||||
if (buffer == nullptr) {
|
||||
Serial.print("couldn't malloc bytes for firmware MD5\n");
|
||||
return;
|
||||
}
|
||||
|
||||
zbs->select_flash(0);
|
||||
for (uint16_t c = 0; c < FINGERPRINT_FLASH_SIZE; c++) {
|
||||
buffer[c] = zbs->read_flash(c);
|
||||
}
|
||||
|
||||
{
|
||||
MD5Builder md5calc;
|
||||
md5calc.begin();
|
||||
md5calc.add(buffer, FINGERPRINT_FLASH_SIZE);
|
||||
md5calc.calculate();
|
||||
md5calc.getBytes(md5);
|
||||
}
|
||||
|
||||
for (uint8_t c = 0; c < 16; c++) {
|
||||
sprintf(md5char + (2 * c), "%02X", md5[c]);
|
||||
}
|
||||
Serial.printf("MD5=%s\n", md5char);
|
||||
free(buffer);
|
||||
}
|
||||
|
||||
bool flasher::getInfoBlockMac() {
|
||||
if (!zbs->select_flash(1)) return false;
|
||||
for (uint16_t c = 7; c < 8; c--) {
|
||||
mac[7 - c] = zbs->read_flash(c + 0x10);
|
||||
}
|
||||
Serial.printf("Infopage mac=");
|
||||
uint16_t macsum = 0;
|
||||
for (uint8_t c = 0; c < 8; c++) {
|
||||
macsum += mac[c];
|
||||
Serial.printf("%02X", mac[c]);
|
||||
}
|
||||
Serial.printf("\n");
|
||||
if (macsum == 0) return false;
|
||||
if (macsum > 0x5F9) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool flasher::getInfoBlockMD5() {
|
||||
if (!zbs->select_flash(1)) return false;
|
||||
for (uint16_t c = 0; c < 16; c++) {
|
||||
md5[c] = zbs->read_flash(c + 0x30);
|
||||
}
|
||||
uint16_t macsum = 0;
|
||||
for (uint8_t c = 0; c < 16; c++) {
|
||||
macsum += md5[c];
|
||||
sprintf(md5char + (2 * c), "%02X", md5[c]);
|
||||
}
|
||||
Serial.printf("Infoblock MD5=%s\n", md5char);
|
||||
if (macsum == 0) return false; // invalid mac
|
||||
if (macsum > 0xF00) return false; // *probably* an invalid mac
|
||||
return true;
|
||||
}
|
||||
|
||||
bool flasher::getInfoBlockType() {
|
||||
if (!zbs->select_flash(1)) return false;
|
||||
tagtype = zbs->read_flash(0x19);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool flasher::findTagByMD5() {
|
||||
StaticJsonDocument<3000> doc;
|
||||
DynamicJsonDocument APconfig(600);
|
||||
fs::File readfile = contentFS->open("/tag_md5_db.json", "r");
|
||||
DeserializationError err = deserializeJson(doc, readfile);
|
||||
if (!err) {
|
||||
for (JsonObject elem : doc.as<JsonArray>()) {
|
||||
const char *jsonmd5 = elem["MD5"];
|
||||
if (jsonmd5 != nullptr) {
|
||||
if (strncmp(md5char, jsonmd5, 32) == 0) {
|
||||
Serial.print("MD5 Matches > ");
|
||||
const char *name = elem["name"];
|
||||
Serial.println(name);
|
||||
mac_suffix = strtoul(elem["mac_suffix"], 0, 16);
|
||||
mac_format = elem["mac_format"];
|
||||
mac_offset = elem["mac_offset"];
|
||||
tagtype = elem["type"];
|
||||
readfile.close();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
Serial.print("Failed to find this tag's current firmware MD5 in the json database. If this tag is already OpenEpaperLink, this is to be expected.\n");
|
||||
} else {
|
||||
Serial.print("Failed to read json file\n");
|
||||
}
|
||||
readfile.close();
|
||||
return false;
|
||||
}
|
||||
|
||||
bool flasher::findTagByType(uint8_t type) {
|
||||
StaticJsonDocument<3000> doc;
|
||||
DynamicJsonDocument APconfig(600);
|
||||
fs::File readfile = contentFS->open("/tag_md5_db.json", "r");
|
||||
DeserializationError err = deserializeJson(doc, readfile);
|
||||
if (!err) {
|
||||
for (JsonObject elem : doc.as<JsonArray>()) {
|
||||
if (elem["type"] != nullptr) {
|
||||
uint8_t jtype = elem["type"];
|
||||
if (jtype == type) {
|
||||
Serial.print("Type Matches > ");
|
||||
const char *name = elem["name"];
|
||||
Serial.println(name);
|
||||
const char *jsonmd5 = elem["MD5"];
|
||||
|
||||
for (uint8_t c = 0; c < 16; c++) {
|
||||
uint32_t n = 0;
|
||||
sscanf(jsonmd5 + (2 * c), "%02X", &n);
|
||||
md5[c] = (uint8_t)n;
|
||||
}
|
||||
|
||||
for (uint8_t c = 0; c < 16; c++) {
|
||||
sprintf(md5char + (2 * c), "%02X", md5[c]);
|
||||
}
|
||||
|
||||
mac_suffix = strtoul(elem["mac_suffix"], 0, 16);
|
||||
mac_format = elem["mac_format"];
|
||||
mac_offset = elem["mac_offset"];
|
||||
tagtype = elem["type"];
|
||||
readfile.close();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
Serial.print("Failed to find this tag's type in the json database.\n");
|
||||
} else {
|
||||
Serial.print("Failed to read json file\n");
|
||||
}
|
||||
readfile.close();
|
||||
return false;
|
||||
}
|
||||
|
||||
bool flasher::getFirmwareMac() {
|
||||
if (!mac_offset) return false;
|
||||
switch (mac_format) {
|
||||
case 1:
|
||||
get_mac_format1();
|
||||
break;
|
||||
case 2:
|
||||
get_mac_format2();
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void flasher::getMacFromWiFi() {
|
||||
mac[0] = 0x00;
|
||||
mac[1] = 0x00;
|
||||
esp_read_mac(mac + 2, ESP_MAC_WIFI_SOFTAP);
|
||||
}
|
||||
|
||||
bool flasher::backupFlash() {
|
||||
getFirmwareMD5();
|
||||
if (!zbs->select_flash(0)) return false;
|
||||
md5char[16] = 0x00;
|
||||
fs::File backup = contentFS->open("/" + (String)md5char + "_backup.bin", "w", true);
|
||||
for (uint32_t c = 0; c < 65535; c++) {
|
||||
backup.write(zbs->read_flash(c));
|
||||
}
|
||||
backup.close();
|
||||
return true;
|
||||
}
|
||||
|
||||
// extract original mac from firmware (1.54" and 2.9") and make it 2 bytes longer based on info in settings.h
|
||||
void flasher::get_mac_format1() {
|
||||
zbs->select_flash(0);
|
||||
for (uint8_t c = 0; c < 6; c++) {
|
||||
mac[c + 2] = zbs->read_flash(mac_offset + c); // 0xFC06
|
||||
}
|
||||
mac[0] = (uint8_t)(CUSTOM_MAC_HDR >> 8);
|
||||
mac[1] = (uint8_t)CUSTOM_MAC_HDR;
|
||||
|
||||
mac[6] = (uint8_t)(mac_suffix >> 8);
|
||||
mac[7] = (uint8_t)(mac_suffix & 0xFF);
|
||||
|
||||
uint8_t xorchk = 0;
|
||||
for (uint8_t c = 2; c < 8; c++) {
|
||||
xorchk ^= (mac[c] & 0x0F);
|
||||
xorchk ^= (mac[c] >> 4);
|
||||
}
|
||||
mac[7] |= xorchk;
|
||||
}
|
||||
|
||||
// extract original mac from segmented tag
|
||||
void flasher::get_mac_format2() {
|
||||
zbs->select_flash(0);
|
||||
|
||||
for (uint8_t c = 0; c < 3; c++) {
|
||||
mac[c] = zbs->read_flash(mac_offset + c); // 0x7802
|
||||
}
|
||||
for (uint8_t c = 3; c < 6; c++) {
|
||||
mac[c] = zbs->read_flash(mac_offset + 2 + c);
|
||||
}
|
||||
|
||||
uint16_t type = mac_suffix;
|
||||
mac[6] = (uint8_t)(type >> 8);
|
||||
mac[7] = (uint8_t)(type & 0xFF);
|
||||
}
|
||||
|
||||
// erase flash and program from flash buffer
|
||||
bool flasher::writeFlash(uint8_t *flashbuffer, uint16_t size) {
|
||||
if (!zbs->select_flash(0)) return false;
|
||||
zbs->erase_flash();
|
||||
if (!zbs->select_flash(0)) return false;
|
||||
Serial.printf("Starting flash, size=%d\n", size);
|
||||
for (uint16_t c = 0; c < size; c++) {
|
||||
if (flashbuffer[c] == 0xFF) goto flashWriteSuccess;
|
||||
for (uint8_t i = 0; i < MAX_WRITE_ATTEMPTS; i++) {
|
||||
zbs->write_flash(c, flashbuffer[c]);
|
||||
if (zbs->read_flash(c) == flashbuffer[c]) {
|
||||
goto flashWriteSuccess;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
flashWriteSuccess:
|
||||
if (c % 256 == 0) {
|
||||
#ifdef HAS_RGB_LED
|
||||
shortBlink(CRGB::Yellow);
|
||||
#else
|
||||
quickBlink(2);
|
||||
#endif
|
||||
Serial.printf("\rNow flashing, %d/%d ", c, size);
|
||||
vTaskDelay(1 / portTICK_PERIOD_MS);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool flasher::writeBlock256(uint16_t offset, uint8_t *flashbuffer) {
|
||||
for (uint16_t c = 0; c < 256; c++) {
|
||||
if (flashbuffer[c] == 0xFF) goto flashWriteSuccess;
|
||||
for (uint8_t i = 0; i < MAX_WRITE_ATTEMPTS; i++) {
|
||||
zbs->write_flash(offset + c, flashbuffer[c]);
|
||||
if (zbs->read_flash(offset + c) == flashbuffer[c]) {
|
||||
goto flashWriteSuccess;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
flashWriteSuccess:
|
||||
continue;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// get info from infoblock (eeprom flash, kinda)
|
||||
bool flasher::readInfoBlock() {
|
||||
if (!zbs->select_flash(1)) return false;
|
||||
if (infoblock == nullptr) {
|
||||
infoblock = (uint8_t *)malloc(1024);
|
||||
if (infoblock == nullptr) return false;
|
||||
}
|
||||
for (uint16_t c = 0; c < 1024; c++) {
|
||||
infoblock[c] = zbs->read_flash(c);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// write info to infoblock
|
||||
bool flasher::writeInfoBlock() {
|
||||
if (infoblock == nullptr) return false;
|
||||
if (!zbs->select_flash(1)) return false;
|
||||
zbs->erase_infoblock();
|
||||
if (!zbs->select_flash(1)) return false;
|
||||
// select info page
|
||||
|
||||
for (uint16_t c = 0; c < 1024; c++) {
|
||||
if (infoblock[c] == 0xFF) goto ifBlockWriteSuccess;
|
||||
for (uint8_t i = 0; i < MAX_WRITE_ATTEMPTS; i++) {
|
||||
zbs->write_flash(c, infoblock[c]);
|
||||
if (zbs->read_flash(c) == infoblock[c]) {
|
||||
goto ifBlockWriteSuccess;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
ifBlockWriteSuccess:
|
||||
continue;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool flasher::prepareInfoBlock() {
|
||||
if (infoblock == nullptr) return false;
|
||||
for (uint8_t c = 7; c < 8; c--) {
|
||||
infoblock[0x10 + (7 - c)] = mac[c];
|
||||
}
|
||||
infoblock[0x19] = tagtype;
|
||||
for (uint8_t c = 0; c < 16; c++) {
|
||||
infoblock[0x30 + c] = md5[c];
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool flasher::writeFlashFromPackOffset(fs::File *file, uint16_t length) {
|
||||
if (!zbs->select_flash(0)) return false;
|
||||
zbs->erase_flash();
|
||||
if (!zbs->select_flash(0)) return false;
|
||||
Serial.printf("Starting flash, size=%d\n", length);
|
||||
|
||||
uint8_t *buf = (uint8_t *)malloc(256);
|
||||
uint16_t offset = 0;
|
||||
while (length) {
|
||||
if (length > 256) {
|
||||
file->read(buf, 256);
|
||||
length -= 256;
|
||||
} else {
|
||||
file->read(buf, length);
|
||||
length = 0;
|
||||
}
|
||||
#ifdef HAS_RGB_LED
|
||||
shortBlink(CRGB::Yellow);
|
||||
#else
|
||||
quickBlink(2);
|
||||
#endif
|
||||
Serial.printf("\rFlashing, %d bytes left ", length);
|
||||
bool res = writeBlock256(offset, buf);
|
||||
offset += 256;
|
||||
if (!res) {
|
||||
Serial.printf("Failed writing block to tag, probably a hardware failure\n");
|
||||
return false;
|
||||
}
|
||||
vTaskDelay(1 / portTICK_PERIOD_MS);
|
||||
}
|
||||
Serial.printf("\nFlashing done\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
bool flasher::writeFlashFromPack(String filename, uint8_t type) {
|
||||
StaticJsonDocument<512> doc;
|
||||
DynamicJsonDocument APconfig(512);
|
||||
fs::File readfile = contentFS->open(filename, "r");
|
||||
DeserializationError err = deserializeJson(doc, readfile);
|
||||
if (!err) {
|
||||
for (JsonObject elem : doc.as<JsonArray>()) {
|
||||
if (elem["type"] != nullptr) {
|
||||
uint8_t jtype = elem["type"];
|
||||
if (jtype == type) {
|
||||
const char *name = elem["name"];
|
||||
Serial.print("Flashing from FW pack: ");
|
||||
Serial.println(name);
|
||||
|
||||
uint32_t offset = elem["offset"];
|
||||
uint16_t length = elem["length"];
|
||||
readfile.seek(offset);
|
||||
bool result = writeFlashFromPackOffset(&readfile, length);
|
||||
readfile.close();
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
Serial.print("Failed to find this tag's type in the FW pack database.\n");
|
||||
} else {
|
||||
Serial.print("Failed to read json header from FW pack\n");
|
||||
}
|
||||
readfile.close();
|
||||
return false;
|
||||
}
|
||||
|
||||
uint16_t getAPUpdateVersion(uint8_t type) {
|
||||
StaticJsonDocument<512> doc;
|
||||
DynamicJsonDocument APconfig(512);
|
||||
fs::File readfile = contentFS->open("/AP_FW_Pack.bin", "r");
|
||||
DeserializationError err = deserializeJson(doc, readfile);
|
||||
if (!err) {
|
||||
for (JsonObject elem : doc.as<JsonArray>()) {
|
||||
if (elem["type"] != nullptr) {
|
||||
uint8_t jtype = elem["type"];
|
||||
if (jtype == type) {
|
||||
const char *name = elem["name"];
|
||||
uint32_t version = elem["version"];
|
||||
Serial.printf("AP FW version %04X - %s found in FW pack\n", version, name);
|
||||
readfile.close();
|
||||
return version;
|
||||
}
|
||||
}
|
||||
}
|
||||
Serial.print("Failed to find this tag's type in the AP FW pack database.\n");
|
||||
} else {
|
||||
Serial.print("Failed to read json header from FW pack\n");
|
||||
}
|
||||
readfile.close();
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool checkForcedAPFlash() {
|
||||
return contentFS->exists("/AP_force_flash.bin");
|
||||
}
|
||||
|
||||
bool doForcedAPFlash() {
|
||||
class flasher *f = new flasher();
|
||||
if (!f->connectTag(AP_PROCESS_PORT)) {
|
||||
delete f;
|
||||
return false;
|
||||
}
|
||||
|
||||
// we're going to overwrite the contents of the tag, so if we haven't set the mac already, we can forget about it. We'll set the mac to the wifi mac
|
||||
if (!f->getInfoBlockMac()) {
|
||||
f->readInfoBlock();
|
||||
f->getMacFromWiFi();
|
||||
f->prepareInfoBlock();
|
||||
f->writeInfoBlock();
|
||||
}
|
||||
|
||||
fs::File readfile = contentFS->open("/AP_force_flash.bin", "r");
|
||||
bool res = f->writeFlashFromPackOffset(&readfile, readfile.size());
|
||||
#ifdef HAS_RGB_LED
|
||||
if (res) addFadeColor(CRGB::Green);
|
||||
if (!res) addFadeColor(CRGB::Red);
|
||||
#endif
|
||||
readfile.close();
|
||||
if (res) contentFS->remove("/AP_force_flash.bin");
|
||||
f->zbs->reset();
|
||||
delete f;
|
||||
return res;
|
||||
}
|
||||
|
||||
bool doAPFlash() {
|
||||
// This function expects a tag in stock configuration, to be used as an AP. It can also work with 'dead' AP's.
|
||||
class flasher *f = new flasher();
|
||||
if (!f->connectTag(AP_PROCESS_PORT)) {
|
||||
Serial.printf("Sorry, failed to connect to this tag...\n");
|
||||
delete f;
|
||||
return false;
|
||||
}
|
||||
|
||||
f->getFirmwareMD5();
|
||||
|
||||
if (f->findTagByMD5()) {
|
||||
// fresh tag for AP
|
||||
Serial.printf("Found an original fw tag, flashing it for use with OpenEPaperLink\n");
|
||||
f->readInfoBlock();
|
||||
f->getFirmwareMac();
|
||||
f->prepareInfoBlock();
|
||||
f->writeInfoBlock();
|
||||
} else if (f->getInfoBlockMD5() && f->findTagByMD5()) {
|
||||
// used tag, but recognized
|
||||
} else {
|
||||
// unknown tag, bailing out.
|
||||
f->backupFlash();
|
||||
|
||||
Serial.printf("Found a tag, but don't know what to do with it. Consider flashing using a file called \"AP_force_flash.bin\"\n");
|
||||
delete f;
|
||||
return false;
|
||||
}
|
||||
bool res = f->writeFlashFromPack("/AP_FW_Pack.bin", f->tagtype);
|
||||
f->zbs->reset();
|
||||
delete f;
|
||||
return res;
|
||||
}
|
||||
|
||||
bool doAPUpdate(uint8_t type) {
|
||||
// this function expects the tag to be already flashed with some version of the OpenEpaperLink Firmware, and that it correctly reported its type
|
||||
class flasher *f = new flasher();
|
||||
if (!f->connectTag(AP_PROCESS_PORT)) {
|
||||
Serial.printf("Sorry, failed to connect to this tag...\n");
|
||||
delete f;
|
||||
return false;
|
||||
}
|
||||
|
||||
f->readInfoBlock();
|
||||
if (f->getInfoBlockMD5() && f->findTagByMD5()) {
|
||||
// header (MD5) was correctly set. We'll use the type set there, instead of the provided 'type' in the argument
|
||||
type = f->tagtype;
|
||||
} else {
|
||||
f->readInfoBlock();
|
||||
// Couldn't recognize the firmware, maybe it was already used for OpenEPaperLink?
|
||||
if (!f->getInfoBlockMac()) {
|
||||
// infoblock mac was incorrectly configured, we skipped it in an earlier version. We'll consider it lost, and overwrite it with the wifi mac.
|
||||
f->getMacFromWiFi();
|
||||
}
|
||||
// we'll try to update the MD5, searching for it by type.
|
||||
f->findTagByType(type);
|
||||
f->writeInfoBlock();
|
||||
}
|
||||
bool res = f->writeFlashFromPack("/AP_FW_Pack.bin", f->tagtype);
|
||||
#ifdef HAS_RGB_LED
|
||||
if (res) addFadeColor(CRGB::Green);
|
||||
if (!res) addFadeColor(CRGB::Red);
|
||||
#endif
|
||||
if (res) f->zbs->reset();
|
||||
delete f;
|
||||
return res;
|
||||
}
|
||||
|
||||
void flashCountDown(uint8_t c) {
|
||||
Serial.printf("\r%d ", c);
|
||||
for (c -= 1; c < 254; c--) {
|
||||
vTaskDelay(1000 / portTICK_PERIOD_MS);
|
||||
Serial.printf("\r%d ", c);
|
||||
}
|
||||
}
|
||||
|
||||
void pinTest() {
|
||||
uint8_t *pintest;
|
||||
pintest = (uint8_t *)pinsAP;
|
||||
for (uint8_t c = 0; c < 8; c++) {
|
||||
if (pintest[c] != -1) {
|
||||
pinMode(pintest[c], INPUT_PULLDOWN);
|
||||
vTaskDelay(10 / portTICK_PERIOD_MS);
|
||||
if (digitalRead(pintest[c])) {
|
||||
Serial.printf("Pin %d failed to become low\n", c);
|
||||
} else {
|
||||
pinMode(pintest[c], INPUT_PULLUP);
|
||||
bool pinChange = false;
|
||||
uint16_t pinTime = 0;
|
||||
for (uint16_t t = 0; t < 65535; t++) {
|
||||
if (digitalRead(pintest[c])) {
|
||||
pinChange = true;
|
||||
pinTime = t;
|
||||
break;
|
||||
}
|
||||
ets_delay_us(1);
|
||||
}
|
||||
if (pinChange) {
|
||||
Serial.printf("Pin %d went high in %d µS\n", pintest[c], pinTime);
|
||||
} else {
|
||||
Serial.printf("Pin %d timeout becoming high\n", pintest[c]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for (uint8_t c = 0; c < 8; c++) {
|
||||
if (pintest[c] != -1) {
|
||||
pinMode(pintest[c], INPUT_PULLDOWN);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef OPENEPAPERLINK_PCB
|
||||
// perform device flash, save mac, everything
|
||||
bool doTagFlash() {
|
||||
class flasher *f = new flasher();
|
||||
if (!f->connectTag(FLASHER_EXT_PORT)) {
|
||||
Serial.printf("Sorry, failed to connect to this tag...\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
f->getFirmwareMD5();
|
||||
|
||||
if (f->findTagByMD5()) {
|
||||
// this tag currently contains original firmware, found its fingerprint
|
||||
Serial.printf("Found original firmware tag, recognized its fingerprint (%s)\n", f->md5char);
|
||||
f->readInfoBlock();
|
||||
f->getFirmwareMac();
|
||||
f->prepareInfoBlock();
|
||||
f->writeInfoBlock();
|
||||
f->writeFlashFromPack("/Tag_FW_Pack.bin", f->tagtype);
|
||||
f->zbs->reset();
|
||||
} else if (f->getInfoBlockMD5()) {
|
||||
// did find an infoblock MD5 that looks valid
|
||||
if (f->findTagByMD5()) {
|
||||
// did find the md5 in the database
|
||||
Serial.printf("Found an already-flashed tag, recognized its fingerprint (%s)\n", f->md5char);
|
||||
f->getInfoBlockMac();
|
||||
f->getInfoBlockType();
|
||||
f->readInfoBlock();
|
||||
f->writeFlashFromPack("/Tag_FW_Pack.bin", f->tagtype);
|
||||
f->zbs->reset();
|
||||
} else {
|
||||
// couldn't find the md5 from the infoblock
|
||||
Serial.printf("Found an already-flashed tag, but we couldn't find its fingerprint (%s) in the database\n", f->md5char);
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
// We couldn't recognize the tag from it's fingerprint...
|
||||
Serial.printf("Found a tag but didn't recognize its fingerprint\n", f->md5char);
|
||||
f->backupFlash();
|
||||
Serial.printf("Saved this MD5 binary to filesystem\n");
|
||||
}
|
||||
delete f;
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
25
ESP32_AP-Flasher/src/language.cpp
Normal file
25
ESP32_AP-Flasher/src/language.cpp
Normal file
@@ -0,0 +1,25 @@
|
||||
#include "language.h"
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
#include "settings.h"
|
||||
#include "tag_db.h"
|
||||
|
||||
int currentLanguage = defaultLanguage;
|
||||
|
||||
void updateLanguageFromConfig() {
|
||||
int tempLang = config.language;
|
||||
if (tempLang < 0 || tempLang >= sizeof(languageList)) {
|
||||
Serial.println("Language not supported");
|
||||
return;
|
||||
}
|
||||
currentLanguage = tempLang;
|
||||
}
|
||||
|
||||
int getDefaultLanguage() {
|
||||
return defaultLanguage;
|
||||
}
|
||||
|
||||
int getCurrentLanguage() {
|
||||
return currentLanguage;
|
||||
}
|
||||
355
ESP32_AP-Flasher/src/leds.cpp
Normal file
355
ESP32_AP-Flasher/src/leds.cpp
Normal file
@@ -0,0 +1,355 @@
|
||||
#include <Arduino.h>
|
||||
|
||||
#ifdef HAS_RGB_LED
|
||||
#define FASTLED_INTERNAL
|
||||
#include <FastLED.h>
|
||||
#endif
|
||||
|
||||
#include "settings.h"
|
||||
#include "tag_db.h"
|
||||
|
||||
QueueHandle_t ledQueue;
|
||||
int maxledbrightness = 255;
|
||||
|
||||
#ifdef HAS_RGB_LED
|
||||
QueueHandle_t rgbLedQueue;
|
||||
|
||||
struct ledInstructionRGB {
|
||||
CRGB ledColor;
|
||||
uint16_t fadeTime;
|
||||
uint16_t length;
|
||||
bool reQueue = false;
|
||||
};
|
||||
|
||||
CRGB leds[1];
|
||||
volatile bool rgbQueueFlush = false;
|
||||
#endif
|
||||
|
||||
struct ledInstruction {
|
||||
uint16_t value;
|
||||
uint16_t fadeTime;
|
||||
uint16_t length;
|
||||
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) {
|
||||
rgb->reQueue = requeue;
|
||||
if (!rgbLedQueue) {
|
||||
delete rgb;
|
||||
return;
|
||||
}
|
||||
BaseType_t queuestatus = xQueueSend(rgbLedQueue, &rgb, 0);
|
||||
if (queuestatus == pdFALSE) {
|
||||
delete rgb;
|
||||
}
|
||||
}
|
||||
|
||||
void addFadeColor(CRGB cname) {
|
||||
struct ledInstructionRGB* rgb = new struct ledInstructionRGB;
|
||||
rgb->ledColor = cname;
|
||||
rgb->fadeTime = 750;
|
||||
rgb->length = 0;
|
||||
addToRGBQueue(rgb, false);
|
||||
}
|
||||
|
||||
void shortBlink(CRGB cname) {
|
||||
struct ledInstructionRGB* rgb = new struct ledInstructionRGB;
|
||||
rgb->ledColor = CRGB::Black;
|
||||
rgb->fadeTime = 0;
|
||||
rgb->length = 3;
|
||||
addToRGBQueue(rgb, false);
|
||||
rgb = new struct ledInstructionRGB;
|
||||
rgb->ledColor = cname;
|
||||
rgb->ledColor.maximizeBrightness(0x80);
|
||||
rgb->fadeTime = 0;
|
||||
rgb->length = 10;
|
||||
addToRGBQueue(rgb, false);
|
||||
rgb = new struct ledInstructionRGB;
|
||||
rgb->ledColor = CRGB::Black;
|
||||
rgb->fadeTime = 0;
|
||||
rgb->length = 3;
|
||||
addToRGBQueue(rgb, false);
|
||||
}
|
||||
|
||||
void flushRGBQueue() {
|
||||
rgbQueueFlush = true;
|
||||
}
|
||||
|
||||
void rgbIdle() {
|
||||
flushRGBQueue();
|
||||
}
|
||||
|
||||
void showColorPattern(CRGB colorone, CRGB colortwo, CRGB colorthree) {
|
||||
struct ledInstructionRGB* rgb = new struct ledInstructionRGB;
|
||||
rgb->ledColor = CRGB::Black;
|
||||
rgb->fadeTime = 0;
|
||||
rgb->length = 600;
|
||||
addToRGBQueue(rgb, true);
|
||||
rgb = new struct ledInstructionRGB;
|
||||
rgb->ledColor = colorone;
|
||||
rgb->fadeTime = 0;
|
||||
rgb->length = 120;
|
||||
addToRGBQueue(rgb, true);
|
||||
rgb = new struct ledInstructionRGB;
|
||||
rgb->ledColor = CRGB::Black;
|
||||
rgb->fadeTime = 0;
|
||||
rgb->length = 200;
|
||||
addToRGBQueue(rgb, true);
|
||||
rgb = new struct ledInstructionRGB;
|
||||
rgb->ledColor = colortwo;
|
||||
rgb->fadeTime = 0;
|
||||
rgb->length = 120;
|
||||
addToRGBQueue(rgb, true);
|
||||
rgb = new struct ledInstructionRGB;
|
||||
rgb->ledColor = CRGB::Black;
|
||||
rgb->fadeTime = 0;
|
||||
rgb->length = 200;
|
||||
addToRGBQueue(rgb, true);
|
||||
rgb = new struct ledInstructionRGB;
|
||||
rgb->ledColor = colorthree;
|
||||
rgb->fadeTime = 0;
|
||||
rgb->length = 120;
|
||||
addToRGBQueue(rgb, true);
|
||||
}
|
||||
|
||||
void showRGB() {
|
||||
FastLED.show();
|
||||
}
|
||||
|
||||
volatile CRGB rgbIdleColor = CRGB::Green;
|
||||
volatile uint16_t rgbIdlePeriod = 800;
|
||||
|
||||
void rgbIdleStep() {
|
||||
static bool dirUp = true;
|
||||
static uint16_t step = 0;
|
||||
|
||||
if (dirUp) {
|
||||
// up
|
||||
step++;
|
||||
if (step == rgbIdlePeriod) {
|
||||
dirUp = false;
|
||||
}
|
||||
} else {
|
||||
// down
|
||||
step--;
|
||||
if (step == 0) {
|
||||
dirUp = true;
|
||||
}
|
||||
}
|
||||
CRGB newvalue = blend(CRGB::Black, (const CRGB&)rgbIdleColor, gamma8[map(step, 0, rgbIdlePeriod, 0, 255)]);
|
||||
if (newvalue != leds[0]) {
|
||||
leds[0] = newvalue;
|
||||
showRGB();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void setBrightness(int brightness) {
|
||||
maxledbrightness = brightness;
|
||||
#ifdef HAS_RGB_LED
|
||||
FastLED.setBrightness(maxledbrightness);
|
||||
#endif
|
||||
}
|
||||
|
||||
void updateBrightnessFromConfig() {
|
||||
if (config.led != 0) {
|
||||
int newbrightness = config.led;
|
||||
if (newbrightness < 0) newbrightness = 0;
|
||||
if (newbrightness != maxledbrightness) {
|
||||
setBrightness(newbrightness);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void addToMonoQueue(struct ledInstruction* mono) {
|
||||
BaseType_t queuestatus = xQueueSend(ledQueue, &mono, 0);
|
||||
if (queuestatus == pdFALSE) {
|
||||
delete mono;
|
||||
}
|
||||
}
|
||||
|
||||
void addFadeMono(uint8_t value) {
|
||||
struct ledInstruction* mono = new struct ledInstruction;
|
||||
mono->value = value;
|
||||
mono->fadeTime = 750;
|
||||
mono->length = 0;
|
||||
addToMonoQueue(mono);
|
||||
}
|
||||
|
||||
void showMono(uint8_t brightness) {
|
||||
ledcWrite(7, 255 - gamma8[brightness]);
|
||||
}
|
||||
|
||||
void quickBlink(uint8_t repeat) {
|
||||
for (int i = 0; i < repeat; i++) {
|
||||
struct ledInstruction* mono = new struct ledInstruction;
|
||||
mono->value = maxledbrightness;
|
||||
mono->fadeTime = 120 / repeat;
|
||||
mono->length = 0;
|
||||
addToMonoQueue(mono);
|
||||
mono = new struct ledInstruction;
|
||||
mono->value = 0;
|
||||
mono->fadeTime = 120 / repeat;
|
||||
mono->length = 0;
|
||||
addToMonoQueue(mono);
|
||||
}
|
||||
}
|
||||
|
||||
volatile uint16_t monoIdlePeriod = 900;
|
||||
|
||||
uint8_t monoValue = 0;
|
||||
|
||||
void monoIdleStep() {
|
||||
static bool dirUp = true;
|
||||
static uint16_t step = 0;
|
||||
if (dirUp) {
|
||||
// up
|
||||
step++;
|
||||
if (step == monoIdlePeriod) {
|
||||
dirUp = false;
|
||||
}
|
||||
} else {
|
||||
// down
|
||||
step--;
|
||||
if (step == 0) {
|
||||
dirUp = true;
|
||||
}
|
||||
}
|
||||
uint8_t newvalue = map(step, 0, monoIdlePeriod, 0, maxledbrightness);
|
||||
if (newvalue != monoValue) {
|
||||
monoValue = newvalue;
|
||||
showMono(newvalue);
|
||||
}
|
||||
}
|
||||
|
||||
void ledTask(void* parameter) {
|
||||
#ifdef HAS_RGB_LED
|
||||
FastLED.addLeds<WS2812B, FLASHER_RGB_LED, GRB>(leds, 1); // GRB ordering is typical
|
||||
leds[0] = CRGB::Blue;
|
||||
showRGB();
|
||||
rgbLedQueue = xQueueCreate(30, sizeof(struct ledInstructionRGB*));
|
||||
|
||||
struct ledInstructionRGB* rgb = nullptr;
|
||||
// open with a nice RGB crossfade
|
||||
addFadeColor(CRGB::Red);
|
||||
addFadeColor(CRGB::Green);
|
||||
addFadeColor(CRGB::Blue);
|
||||
addFadeColor(CRGB::Red);
|
||||
addFadeColor(CRGB::Green);
|
||||
addFadeColor(CRGB::Blue);
|
||||
CRGB oldColor = CRGB::Black;
|
||||
uint16_t rgbInstructionFadeTime = 0;
|
||||
#endif
|
||||
|
||||
ledQueue = xQueueCreate(30, sizeof(struct ledInstruction*));
|
||||
|
||||
ledcSetup(7, 5000, 8);
|
||||
if (FLASHER_LED != -1) {
|
||||
digitalWrite(FLASHER_LED, HIGH);
|
||||
pinMode(FLASHER_LED, OUTPUT);
|
||||
ledcAttachPin(FLASHER_LED, 7);
|
||||
}
|
||||
|
||||
struct ledInstruction* monoled = nullptr;
|
||||
|
||||
addFadeMono(0);
|
||||
addFadeMono(maxledbrightness);
|
||||
addFadeMono(0);
|
||||
|
||||
uint8_t oldBrightness = 0;
|
||||
|
||||
uint16_t monoInstructionFadeTime = 0;
|
||||
|
||||
while (1) {
|
||||
#ifdef HAS_RGB_LED
|
||||
// handle RGB led instructions
|
||||
if (rgb == nullptr) {
|
||||
// fetch a led instruction
|
||||
BaseType_t q = xQueueReceive(rgbLedQueue, &rgb, 1);
|
||||
if (q == pdTRUE) {
|
||||
if (rgb->reQueue && !rgbQueueFlush) {
|
||||
// requeue this instruction at the end of the queue, caveman style.
|
||||
struct ledInstructionRGB* requeue = new ledInstructionRGB;
|
||||
requeue->fadeTime = rgb->fadeTime;
|
||||
requeue->ledColor = rgb->ledColor;
|
||||
requeue->length = rgb->length;
|
||||
addToRGBQueue(requeue, true);
|
||||
}
|
||||
|
||||
if (rgbQueueFlush) {
|
||||
delete rgb;
|
||||
rgb = nullptr;
|
||||
} else {
|
||||
rgbInstructionFadeTime = rgb->fadeTime;
|
||||
if (rgb->fadeTime <= 1) {
|
||||
leds[0] = rgb->ledColor;
|
||||
showRGB();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
rgbQueueFlush = false;
|
||||
// no commands, run idle led task
|
||||
rgbIdleStep();
|
||||
}
|
||||
} else {
|
||||
// process instruction
|
||||
if (rgb->fadeTime) {
|
||||
rgb->fadeTime--;
|
||||
leds[0] = blend(rgb->ledColor, oldColor, map(rgb->fadeTime, 0, rgbInstructionFadeTime, 0, 255));
|
||||
showRGB();
|
||||
} else if (rgb->length) {
|
||||
rgb->length--;
|
||||
} else {
|
||||
oldColor = rgb->ledColor;
|
||||
delete rgb;
|
||||
rgb = nullptr;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
// handle flasher LED (single color)
|
||||
if (monoled == nullptr) {
|
||||
BaseType_t q = xQueueReceive(ledQueue, &monoled, 1);
|
||||
if (q == pdTRUE) {
|
||||
monoInstructionFadeTime = monoled->fadeTime;
|
||||
if (monoled->fadeTime <= 1) {
|
||||
showMono(monoled->value);
|
||||
}
|
||||
} else {
|
||||
//monoIdleStep();
|
||||
}
|
||||
} else {
|
||||
if (monoled->fadeTime) {
|
||||
monoled->fadeTime--;
|
||||
showMono(map(monoled->fadeTime, 0, monoInstructionFadeTime, monoled->value, oldBrightness));
|
||||
} else if (monoled->length) {
|
||||
monoled->length--;
|
||||
} else {
|
||||
oldBrightness = monoled->value;
|
||||
delete monoled;
|
||||
monoled = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
vTaskDelay(1 / portTICK_PERIOD_MS);
|
||||
}
|
||||
}
|
||||
186
ESP32_AP-Flasher/src/main.cpp
Normal file
186
ESP32_AP-Flasher/src/main.cpp
Normal file
@@ -0,0 +1,186 @@
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <WiFi.h>
|
||||
#include <WiFiManager.h>
|
||||
#include <time.h>
|
||||
|
||||
#include "storage.h"
|
||||
#include "contentmanager.h"
|
||||
#include "flasher.h"
|
||||
#include "makeimage.h"
|
||||
#include "serialap.h"
|
||||
#include "settings.h"
|
||||
#include "system.h"
|
||||
#include "tag_db.h"
|
||||
|
||||
#ifdef HAS_USB
|
||||
#include "usbflasher.h"
|
||||
#endif
|
||||
|
||||
#include "language.h"
|
||||
#include "leds.h"
|
||||
#include "udp.h"
|
||||
#include "web.h"
|
||||
|
||||
void pinTest();
|
||||
|
||||
void delayedStart(void* parameter) {
|
||||
vTaskDelay(30000 / portTICK_PERIOD_MS);
|
||||
Serial.println("Resuming content generation");
|
||||
wsLog("resuming content generation");
|
||||
config.runStatus = RUNSTATUS_RUN;
|
||||
vTaskDelay(10 / portTICK_PERIOD_MS);
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
void timeTask(void* parameter) {
|
||||
wsSendSysteminfo();
|
||||
Serial.printf("Free heap: %.2f KB\n", ESP.getFreeHeap() / 1024.0f);
|
||||
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) Serial.printf("Free heap: %.2f KB\n", ESP.getFreeHeap() / 1024.0f);
|
||||
if (now % 300 == 6 && config.runStatus != RUNSTATUS_STOP) saveDB("/current/tagDB.json");
|
||||
|
||||
if (apInfo.state == AP_STATE_ONLINE) contentRunner();
|
||||
|
||||
vTaskDelay(1000 / portTICK_PERIOD_MS);
|
||||
}
|
||||
}
|
||||
|
||||
void setup() {
|
||||
// starts the led task/state machine
|
||||
xTaskCreate(ledTask, "ledhandler", 2000, NULL, 2, NULL);
|
||||
vTaskDelay(10 / portTICK_PERIOD_MS);
|
||||
|
||||
// show a nice pattern to indicate the AP is booting / waiting for WiFi setup
|
||||
#ifdef HAS_RGB_LED
|
||||
showColorPattern(CRGB::Aqua, CRGB::Green, CRGB::Blue);
|
||||
#endif
|
||||
|
||||
#if defined(OPENEPAPERLINK_MINI_AP_PCB) || defined(OPENEPAPERLINK_NANO_AP_PCB)
|
||||
APEnterEarlyReset();
|
||||
// this allows us to view the booting process. After the device showing up, you have 3 seconds to open a terminal on the COM port
|
||||
vTaskDelay(3000 / portTICK_PERIOD_MS);
|
||||
#ifdef DEBUG_VERSION
|
||||
// Specifically for the Mini-version (using an ESP32-S2), use another serial port for debug output. Makes it possible to see core dumps
|
||||
Serial0.begin(115200, SERIAL_8N1, 38, 37);
|
||||
Serial0.printf("Started debug output...\n");
|
||||
Serial0.setDebugOutput(true);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
Serial.begin(115200);
|
||||
Serial.print(">\n");
|
||||
|
||||
pinTest();
|
||||
#ifdef BOARD_HAS_PSRAM
|
||||
if (!psramInit()) {
|
||||
Serial.printf("This build of the AP expects PSRAM, but we couldn't find/init any. Something is terribly wrong here! System halted.");
|
||||
#ifdef HAS_RGB_LED
|
||||
showColorPattern(CRGB::Yellow, CRGB::Red, CRGB::Red);
|
||||
#endif
|
||||
while (1) {
|
||||
vTaskDelay(1000 / portTICK_PERIOD_MS);
|
||||
}
|
||||
};
|
||||
heap_caps_malloc_extmem_enable(64);
|
||||
#endif
|
||||
|
||||
Storage.begin();
|
||||
|
||||
/*
|
||||
Serial.println("\n\n##################################");
|
||||
Serial.printf("Internal Total heap %d, internal Free Heap %d\n", ESP.getHeapSize(), ESP.getFreeHeap());
|
||||
Serial.printf("SPIRam Total heap %d, SPIRam Free Heap %d\n", ESP.getPsramSize(), ESP.getFreePsram());
|
||||
Serial.printf("ChipRevision %d, Cpu Freq %d, SDK Version %s\n", ESP.getChipRevision(), ESP.getCpuFreqMHz(), ESP.getSdkVersion());
|
||||
Serial.printf("Flash Size %d, Flash Speed %d\n", ESP.getFlashChipSize(), ESP.getFlashChipSpeed());
|
||||
Serial.println("##################################\n\n");
|
||||
|
||||
Serial.printf("Total heap: %d\n", ESP.getHeapSize());
|
||||
Serial.printf("Free heap: %d\n", ESP.getFreeHeap());
|
||||
Serial.printf("Total PSRAM: %d\n", ESP.getPsramSize());
|
||||
Serial.printf("Free PSRAM: %d\n\n", ESP.getFreePsram());
|
||||
|
||||
Serial.printf("ESP32 Partition table:\n");
|
||||
Serial.printf("| Type | Sub | Offset | Size | Label |\n");
|
||||
Serial.printf("| ---- | --- | -------- | -------- | ---------------- |\n");
|
||||
esp_partition_iterator_t pi = esp_partition_find(ESP_PARTITION_TYPE_ANY, ESP_PARTITION_SUBTYPE_ANY, NULL);
|
||||
if (pi != NULL) {
|
||||
do {
|
||||
const esp_partition_t* p = esp_partition_get(pi);
|
||||
Serial.printf("| %02x | %02x | 0x%06X | 0x%06X | %-16s |\r\n",
|
||||
p->type, p->subtype, p->address, p->size, p->label);
|
||||
} while (pi = (esp_partition_next(pi)));
|
||||
}
|
||||
*/
|
||||
|
||||
#ifdef HAS_USB
|
||||
// We'll need to start the 'usbflasher' task for boards with a second (USB) port. This can be used as a 'flasher' interface, using a python script on the host
|
||||
xTaskCreate(usbFlasherTask, "usbflasher", 10000, NULL, configMAX_PRIORITIES - 10, NULL);
|
||||
#endif
|
||||
|
||||
configTzTime("CET-1CEST,M3.5.0,M10.5.0/3", "0.nl.pool.ntp.org", "europe.pool.ntp.org", "time.nist.gov");
|
||||
// https://github.com/nayarsystems/posix_tz_db/blob/master/zones.csv
|
||||
|
||||
initAPconfig();
|
||||
updateLanguageFromConfig();
|
||||
updateBrightnessFromConfig();
|
||||
|
||||
init_web();
|
||||
init_udp();
|
||||
|
||||
#ifdef HAS_RGB_LED
|
||||
rgbIdle();
|
||||
#endif
|
||||
loadDB("/current/tagDB.json");
|
||||
// tagDBOwner = xSemaphoreCreateMutex();
|
||||
xTaskCreate(APTask, "AP Process", 6000, NULL, 2, NULL);
|
||||
xTaskCreate(webSocketSendProcess, "ws", 2000, NULL, configMAX_PRIORITIES - 10, NULL);
|
||||
vTaskDelay(10 / portTICK_PERIOD_MS);
|
||||
|
||||
config.runStatus = RUNSTATUS_INIT;
|
||||
|
||||
xTaskCreate(timeTask, "timed tasks", 12000, NULL, 2, NULL);
|
||||
|
||||
init_time();
|
||||
logStartUp();
|
||||
|
||||
esp_reset_reason_t resetReason = esp_reset_reason();
|
||||
if (resetReason == ESP_RST_PANIC) {
|
||||
Serial.println("Panic! Pausing content generation for 30 seconds");
|
||||
config.runStatus = RUNSTATUS_PAUSE;
|
||||
xTaskCreate(delayedStart, "delaystart", 2000, NULL, 2, NULL);
|
||||
} else {
|
||||
config.runStatus = RUNSTATUS_RUN;
|
||||
}
|
||||
}
|
||||
|
||||
void loop() {
|
||||
vTaskDelay(10000 / portTICK_PERIOD_MS);
|
||||
// performDeviceFlash();
|
||||
while (1) {
|
||||
// pinTest();
|
||||
while (1) {
|
||||
vTaskDelay(10000 / portTICK_PERIOD_MS);
|
||||
// pinTest();
|
||||
}
|
||||
#ifdef OPENEPAPERLINK_PCB
|
||||
if (extTagConnected()) {
|
||||
flashCountDown(3);
|
||||
|
||||
pinMode(FLASHER_EXT_TEST, OUTPUT);
|
||||
digitalWrite(FLASHER_EXT_TEST, LOW);
|
||||
|
||||
doTagFlash();
|
||||
|
||||
vTaskDelay(10000 / portTICK_PERIOD_MS);
|
||||
pinMode(FLASHER_EXT_TEST, INPUT);
|
||||
vTaskDelay(1000 / portTICK_PERIOD_MS);
|
||||
}
|
||||
#endif
|
||||
vTaskDelay(100 / portTICK_PERIOD_MS);
|
||||
}
|
||||
}
|
||||
230
ESP32_AP-Flasher/src/makeimage.cpp
Normal file
230
ESP32_AP-Flasher/src/makeimage.cpp
Normal file
@@ -0,0 +1,230 @@
|
||||
#include <Arduino.h>
|
||||
#include <FS.h>
|
||||
#include "storage.h"
|
||||
#include <TFT_eSPI.h>
|
||||
#include <TJpg_Decoder.h>
|
||||
#include <makeimage.h>
|
||||
#include <web.h>
|
||||
|
||||
TFT_eSPI tft = TFT_eSPI();
|
||||
TFT_eSprite spr = TFT_eSprite(&tft);
|
||||
|
||||
bool spr_output(int16_t x, int16_t y, uint16_t w, uint16_t h, uint16_t *bitmap) {
|
||||
spr.pushImage(x, y, w, h, bitmap);
|
||||
return 1;
|
||||
}
|
||||
|
||||
void jpg2buffer(String filein, String fileout, imgParam &imageParams) {
|
||||
Storage.begin();
|
||||
TJpgDec.setSwapBytes(true);
|
||||
TJpgDec.setJpgScale(1);
|
||||
TJpgDec.setCallback(spr_output);
|
||||
uint16_t w = 0, h = 0;
|
||||
if (filein.c_str()[0] != '/') {
|
||||
filein = "/" + filein;
|
||||
}
|
||||
TJpgDec.getFsJpgSize(&w, &h, filein, *contentFS);
|
||||
if (w==0 && h==0) {
|
||||
wsErr("invalid jpg");
|
||||
return;
|
||||
}
|
||||
Serial.println("jpeg conversion " + String(w) + "x" + String(h));
|
||||
|
||||
#ifdef BOARD_HAS_PSRAM
|
||||
spr.setColorDepth(16);
|
||||
#else
|
||||
spr.setColorDepth(8);
|
||||
#endif
|
||||
spr.createSprite(w, h);
|
||||
if (spr.getPointer() == nullptr) {
|
||||
wsErr("low on memory. Fallback to 1bpp");
|
||||
spr.setColorDepth(1);
|
||||
spr.setBitmapColor(TFT_WHITE, TFT_BLACK);
|
||||
imageParams.bpp = 1;
|
||||
spr.createSprite(w, h);
|
||||
}
|
||||
if (spr.getPointer() == nullptr) {
|
||||
wsErr("Failed to create sprite in jpg2buffer");
|
||||
} else {
|
||||
spr.fillSprite(TFT_WHITE);
|
||||
TJpgDec.drawFsJpg(0, 0, filein, *contentFS);
|
||||
|
||||
spr2buffer(spr, fileout, imageParams);
|
||||
spr.deleteSprite();
|
||||
}
|
||||
}
|
||||
|
||||
struct Color {
|
||||
uint8_t r, g, b;
|
||||
Color() : r(0), g(0), b(0) {}
|
||||
Color(uint16_t value_) : r((value_ >> 8) & 0xF8 | (value_ >> 13) & 0x07), g((value_ >> 3) & 0xFC | (value_ >> 9) & 0x03), b((value_ << 3) & 0xF8 | (value_ >> 2) & 0x07) {}
|
||||
Color(uint8_t r_, uint8_t g_, uint8_t b_) : r(r_), g(g_), b(b_) {}
|
||||
};
|
||||
|
||||
struct Error {
|
||||
float r;
|
||||
float g;
|
||||
float 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;
|
||||
}
|
||||
|
||||
uint8_t *spr2color(TFT_eSprite &spr, imgParam &imageParams, size_t *buffer_size, bool is_red) {
|
||||
|
||||
bool dither = true;
|
||||
uint8_t rotate = imageParams.rotate;
|
||||
long bufw = spr.width(), bufh = spr.height();
|
||||
|
||||
if (bufw > bufh && bufw!=400 && bufh!=300) {
|
||||
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("Fallied to allocated buffer");
|
||||
return nullptr;
|
||||
}
|
||||
memset(buffer, 0, *buffer_size);
|
||||
|
||||
std::vector<Color> palette = {
|
||||
{255, 255, 255}, // White
|
||||
{0, 0, 0}, // Black
|
||||
{255, 0, 0} // Red
|
||||
};
|
||||
if (imageParams.grayLut) {
|
||||
Color newColor = {160, 160, 160};
|
||||
palette.push_back(newColor);
|
||||
Serial.println("rendering with gray");
|
||||
}
|
||||
int num_colors = palette.size();
|
||||
if (imageParams.bpp == 1) num_colors = 2;
|
||||
Color color;
|
||||
Error *error_bufferold = new Error[bufw + 4];
|
||||
Error *error_buffernew = new Error[bufw + 4];
|
||||
|
||||
memset(error_bufferold, 0, bufw * sizeof(Error));
|
||||
for (uint16_t y = 0; y < bufh; y++) {
|
||||
memset(error_buffernew, 0, bufw * sizeof(Error));
|
||||
for (uint16_t x = 0; x < bufw; x++) {
|
||||
switch (rotate) {
|
||||
case 0:
|
||||
color = Color(spr.readPixel(x, y));
|
||||
break;
|
||||
case 1:
|
||||
color = Color(spr.readPixel(y, bufw - 1 - x));
|
||||
break;
|
||||
case 2:
|
||||
color = Color(spr.readPixel(bufw - 1 - x, bufh - 1 - y));
|
||||
break;
|
||||
case 3:
|
||||
color = Color(spr.readPixel(bufh - 1 - y, x));
|
||||
break;
|
||||
}
|
||||
|
||||
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++) {
|
||||
uint32_t distance = colorDistance(color, palette[i], error_bufferold[x]);
|
||||
if (distance < best_color_distance) {
|
||||
best_color_distance = distance;
|
||||
best_color_index = i;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t bitIndex = 7 - (x % 8);
|
||||
uint16_t byteIndex = (y * bufw + x) / 8;
|
||||
|
||||
// this looks a bit ugly, but it's performing better than shorter notations
|
||||
switch (best_color_index) {
|
||||
case 1:
|
||||
if(!is_red)
|
||||
buffer[byteIndex] |= (1 << bitIndex);
|
||||
break;
|
||||
case 2:
|
||||
imageParams.hasRed = true;
|
||||
if(is_red)
|
||||
buffer[byteIndex] |= (1 << bitIndex);
|
||||
break;
|
||||
case 3:
|
||||
buffer[byteIndex] |= (1 << bitIndex);
|
||||
imageParams.hasRed = true;
|
||||
break;
|
||||
}
|
||||
|
||||
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) };
|
||||
|
||||
// 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;
|
||||
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;
|
||||
}
|
||||
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 + 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_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_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_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;
|
||||
}
|
||||
}
|
||||
memcpy(error_bufferold, error_buffernew, bufw * sizeof(Error));
|
||||
}
|
||||
|
||||
delete[] error_buffernew;
|
||||
delete[] error_bufferold;
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
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)
|
||||
return;
|
||||
f_out.write(blackBuffer, bufferSize);
|
||||
free(blackBuffer);
|
||||
if (imageParams.hasRed) {
|
||||
uint8_t *redBuffer = (uint8_t*) spr2color(spr, imageParams, &bufferSize, true);
|
||||
if(!redBuffer)
|
||||
return;
|
||||
f_out.write(redBuffer, bufferSize);
|
||||
free(redBuffer);
|
||||
}
|
||||
|
||||
f_out.close();
|
||||
Serial.println("finished writing buffer " + String(millis() - t) + "ms");
|
||||
}
|
||||
660
ESP32_AP-Flasher/src/newproto.cpp
Normal file
660
ESP32_AP-Flasher/src/newproto.cpp
Normal file
@@ -0,0 +1,660 @@
|
||||
#include "newproto.h"
|
||||
|
||||
#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 "system.h"
|
||||
#include "tag_db.h"
|
||||
#include "udp.h"
|
||||
#include "web.h"
|
||||
|
||||
extern uint16_t sendBlock(const void* data, const uint16_t len);
|
||||
extern UDPcomm udpsync;
|
||||
|
||||
void addCRC(void* p, uint8_t len) {
|
||||
uint8_t total = 0;
|
||||
for (uint8_t c = 1; c < len; c++) {
|
||||
total += ((uint8_t*)p)[c];
|
||||
}
|
||||
((uint8_t*)p)[0] = total;
|
||||
// pr("%d",total);
|
||||
}
|
||||
bool checkCRC(void* p, uint8_t len) {
|
||||
uint8_t total = 0;
|
||||
for (uint8_t c = 1; c < len; c++) {
|
||||
total += ((uint8_t*)p)[c];
|
||||
}
|
||||
return ((uint8_t*)p)[0] == total;
|
||||
}
|
||||
|
||||
uint8_t* getDataForFile(fs::File* file) {
|
||||
uint8_t* ret = nullptr;
|
||||
ret = (uint8_t*)malloc(file->size());
|
||||
if (ret) {
|
||||
file->seek(0);
|
||||
file->readBytes((char*)ret, file->size());
|
||||
} else {
|
||||
Serial.print("malloc failed for file...\n");
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void prepareCancelPending(uint8_t dst[8]) {
|
||||
struct pendingData pending = {0};
|
||||
memcpy(pending.targetMac, dst, 8);
|
||||
sendCancelPending(&pending);
|
||||
|
||||
tagRecord* taginfo = nullptr;
|
||||
taginfo = tagRecord::findByMAC(dst);
|
||||
if (taginfo == nullptr) {
|
||||
wsErr("Tag not found, this shouldn't happen.");
|
||||
return;
|
||||
}
|
||||
clearPending(taginfo);
|
||||
|
||||
wsSendTaginfo(dst, SYNC_TAGSTATUS);
|
||||
}
|
||||
|
||||
void prepareIdleReq(uint8_t* dst, uint16_t nextCheckin) {
|
||||
if (nextCheckin > config.maxsleep) nextCheckin = config.maxsleep;
|
||||
if (nextCheckin > 0) {
|
||||
struct pendingData pending = {0};
|
||||
memcpy(pending.targetMac, dst, 8);
|
||||
pending.availdatainfo.dataType = DATATYPE_NOUPDATE;
|
||||
pending.availdatainfo.nextCheckIn = nextCheckin;
|
||||
pending.attemptsLeft = 10 + config.maxsleep;
|
||||
|
||||
char buffer[64];
|
||||
sprintf(buffer, ">SDA %02X%02X%02X%02X%02X%02X%02X%02X NOP\n", dst[7], dst[6], dst[5], dst[4], dst[3], dst[2], dst[1], dst[0]);
|
||||
Serial.print(buffer);
|
||||
sendDataAvail(&pending);
|
||||
}
|
||||
}
|
||||
|
||||
void prepareDataAvail(uint8_t* data, uint16_t len, uint8_t dataType, uint8_t* dst) {
|
||||
tagRecord* taginfo = nullptr;
|
||||
taginfo = tagRecord::findByMAC(dst);
|
||||
if (taginfo == nullptr) {
|
||||
wsErr("Tag not found, this shouldn't happen.");
|
||||
return;
|
||||
}
|
||||
|
||||
clearPending(taginfo);
|
||||
taginfo->data = (uint8_t*)malloc(len);
|
||||
if (taginfo->data == nullptr) {
|
||||
wsErr("no memory allocation for data");
|
||||
return;
|
||||
}
|
||||
memcpy(taginfo->data, data, len);
|
||||
taginfo->pending = true;
|
||||
taginfo->len = len;
|
||||
taginfo->expectedNextCheckin = 0;
|
||||
taginfo->filename = String();
|
||||
taginfo->dataType = dataType;
|
||||
memset(taginfo->md5pending, 0, 16 * sizeof(uint8_t));
|
||||
|
||||
struct pendingData pending = {0};
|
||||
memcpy(pending.targetMac, dst, 8);
|
||||
pending.availdatainfo.dataSize = len;
|
||||
pending.availdatainfo.dataType = dataType;
|
||||
pending.availdatainfo.nextCheckIn = 0;
|
||||
pending.availdatainfo.dataVer = millis();
|
||||
pending.attemptsLeft = 10;
|
||||
|
||||
if (taginfo->isExternal) {
|
||||
udpsync.netSendDataAvail(&pending);
|
||||
} else {
|
||||
sendDataAvail(&pending);
|
||||
}
|
||||
wsSendTaginfo(dst, SYNC_TAGSTATUS);
|
||||
}
|
||||
|
||||
bool prepareDataAvail(String* filename, uint8_t dataType, uint8_t* dst, uint16_t nextCheckin) {
|
||||
if (nextCheckin > config.maxsleep) nextCheckin = config.maxsleep;
|
||||
if (wsClientCount() && config.stopsleep == 1) nextCheckin=0;
|
||||
|
||||
tagRecord* taginfo = nullptr;
|
||||
taginfo = tagRecord::findByMAC(dst);
|
||||
if (taginfo == nullptr) {
|
||||
wsErr("Tag not found, this shouldn't happen.");
|
||||
return true;
|
||||
}
|
||||
|
||||
*filename = "/" + *filename;
|
||||
Storage.begin();
|
||||
|
||||
if (!contentFS->exists(*filename)) {
|
||||
wsErr("File not found. " + *filename);
|
||||
return false;
|
||||
}
|
||||
|
||||
fs::File file = contentFS->open(*filename);
|
||||
uint32_t filesize = file.size();
|
||||
if (filesize == 0) {
|
||||
file.close();
|
||||
wsErr("File has size 0. " + *filename);
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t md5bytes[16];
|
||||
{
|
||||
MD5Builder md5;
|
||||
md5.begin();
|
||||
md5.addStream(file, filesize);
|
||||
md5.calculate();
|
||||
md5.getBytes(md5bytes);
|
||||
}
|
||||
|
||||
file.close();
|
||||
uint16_t attempts = 60 * 24;
|
||||
uint8_t lut = EPD_LUT_NO_REPEATS;
|
||||
|
||||
if (memcmp(md5bytes, taginfo->md5pending, 16) == 0) {
|
||||
wsLog("new image is the same as current or already pending image. not updating tag.");
|
||||
wsSendTaginfo(dst, SYNC_TAGSTATUS);
|
||||
if (contentFS->exists(*filename)) {
|
||||
contentFS->remove(*filename);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
time_t now;
|
||||
time(&now);
|
||||
time_t last_midnight = now - now % (24 * 60 * 60) + 3 * 3600; // somewhere in the middle of the night
|
||||
if (taginfo->lastfullupdate < last_midnight || taginfo->hwType == SOLUM_29_UC8151 || taginfo->lut == 1) {
|
||||
lut = EPD_LUT_DEFAULT; // full update once a day
|
||||
taginfo->lastfullupdate = now;
|
||||
}
|
||||
if (taginfo->hasCustomLUT && taginfo->capabilities & CAPABILITY_SUPPORTS_CUSTOM_LUTS && taginfo->lut != 1) {
|
||||
Serial.println("using custom LUT");
|
||||
lut = EPD_LUT_OTA;
|
||||
}
|
||||
|
||||
if (dataType != DATATYPE_FW_UPDATE) {
|
||||
char dst_path[64];
|
||||
sprintf(dst_path, "/current/%02X%02X%02X%02X%02X%02X%02X%02X.pending\0", dst[7], dst[6], dst[5], dst[4], dst[3], dst[2], dst[1], dst[0]);
|
||||
if (contentFS->exists(dst_path)) {
|
||||
contentFS->remove(dst_path);
|
||||
}
|
||||
contentFS->rename(*filename, dst_path);
|
||||
*filename = String(dst_path);
|
||||
|
||||
wsLog("new image: " + String(dst_path));
|
||||
time_t now;
|
||||
time(&now);
|
||||
taginfo->expectedNextCheckin = now + nextCheckin * 60 + 60;
|
||||
clearPending(taginfo);
|
||||
taginfo->filename = *filename;
|
||||
taginfo->len = filesize;
|
||||
taginfo->dataType = dataType;
|
||||
taginfo->pending = true;
|
||||
memcpy(taginfo->md5pending, md5bytes, sizeof(md5bytes));
|
||||
} else {
|
||||
wsLog("firmware upload pending");
|
||||
clearPending(taginfo);
|
||||
taginfo->filename = *filename;
|
||||
taginfo->len = filesize;
|
||||
taginfo->dataType = dataType;
|
||||
taginfo->pending = true;
|
||||
}
|
||||
|
||||
struct pendingData pending = {0};
|
||||
memcpy(pending.targetMac, dst, 8);
|
||||
pending.availdatainfo.dataType = dataType;
|
||||
pending.availdatainfo.dataVer = *((uint64_t*)md5bytes);
|
||||
pending.availdatainfo.dataSize = filesize;
|
||||
pending.availdatainfo.dataTypeArgument = lut;
|
||||
pending.availdatainfo.nextCheckIn = nextCheckin;
|
||||
pending.attemptsLeft = attempts;
|
||||
if (taginfo->isExternal == false) {
|
||||
char buffer[64];
|
||||
sprintf(buffer, ">SDA %02X%02X%02X%02X%02X%02X%02X%02X TYPE 0x%02X\n\0", dst[7], dst[6], dst[5], dst[4], dst[3], dst[2], dst[1], dst[0], pending.availdatainfo.dataType);
|
||||
Serial.print(buffer);
|
||||
sendDataAvail(&pending);
|
||||
} else {
|
||||
udpsync.netSendDataAvail(&pending);
|
||||
}
|
||||
|
||||
wsSendTaginfo(dst, SYNC_TAGSTATUS);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void prepareExternalDataAvail(struct pendingData* pending, IPAddress remoteIP) {
|
||||
tagRecord* taginfo = nullptr;
|
||||
taginfo = tagRecord::findByMAC(pending->targetMac);
|
||||
if (taginfo == nullptr) {
|
||||
return;
|
||||
}
|
||||
if (taginfo->isExternal == false) {
|
||||
switch (pending->availdatainfo.dataType) {
|
||||
case DATATYPE_IMG_DIFF:
|
||||
case DATATYPE_IMG_RAW_1BPP:
|
||||
case DATATYPE_IMG_RAW_2BPP:
|
||||
case DATATYPE_IMG_RAW_1BPP_DIRECT: {
|
||||
Storage.begin();
|
||||
|
||||
char hexmac[17];
|
||||
mac2hex(pending->targetMac, hexmac);
|
||||
String filename = "/current/" + String(hexmac) + ".pending";
|
||||
String imageUrl = "http://" + remoteIP.toString() + filename;
|
||||
wsLog("GET " + imageUrl);
|
||||
HTTPClient http;
|
||||
http.begin(imageUrl);
|
||||
int httpCode = http.GET();
|
||||
if (httpCode == 200) {
|
||||
File file = contentFS->open(filename, "w");
|
||||
http.writeToStream(&file);
|
||||
file.close();
|
||||
}
|
||||
http.end();
|
||||
|
||||
fs::File file = contentFS->open(filename);
|
||||
uint32_t filesize = file.size();
|
||||
if (filesize == 0) {
|
||||
file.close();
|
||||
wsErr("Remote file not found. " + filename);
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t md5bytes[16];
|
||||
{
|
||||
MD5Builder md5;
|
||||
md5.begin();
|
||||
md5.addStream(file, filesize);
|
||||
md5.calculate();
|
||||
md5.getBytes(md5bytes);
|
||||
}
|
||||
|
||||
file.close();
|
||||
clearPending(taginfo);
|
||||
taginfo->filename = filename;
|
||||
taginfo->len = filesize;
|
||||
taginfo->dataType = pending->availdatainfo.dataType;
|
||||
taginfo->pending = true;
|
||||
memcpy(taginfo->md5pending, md5bytes, sizeof(md5bytes));
|
||||
break;
|
||||
}
|
||||
case DATATYPE_NFC_RAW_CONTENT:
|
||||
case DATATYPE_NFC_URL_DIRECT:
|
||||
case DATATYPE_CUSTOM_LUT_OTA: {
|
||||
char hexmac[17];
|
||||
mac2hex(pending->targetMac, hexmac);
|
||||
String dataUrl = "http://" + remoteIP.toString() + "/getdata?mac=" + String(hexmac);
|
||||
wsLog("GET " + dataUrl);
|
||||
HTTPClient http;
|
||||
http.begin(dataUrl);
|
||||
int httpCode = http.GET();
|
||||
if (httpCode == 200) {
|
||||
size_t len = http.getSize();
|
||||
if (len > 0) {
|
||||
clearPending(taginfo);
|
||||
taginfo->data = new uint8_t[len];
|
||||
WiFiClient* stream = http.getStreamPtr();
|
||||
stream->readBytes(taginfo->data, len);
|
||||
taginfo->dataType = pending->availdatainfo.dataType;
|
||||
taginfo->pending = true;
|
||||
taginfo->len = len;
|
||||
}
|
||||
}
|
||||
http.end();
|
||||
break;
|
||||
}
|
||||
case DATATYPE_FW_UPDATE: {
|
||||
return;
|
||||
}
|
||||
}
|
||||
taginfo->contentMode = 12;
|
||||
taginfo->nextupdate = 3216153600;
|
||||
sendDataAvail(pending);
|
||||
|
||||
wsSendTaginfo(pending->targetMac, SYNC_NOSYNC);
|
||||
}
|
||||
}
|
||||
|
||||
void processBlockRequest(struct espBlockRequest* br) {
|
||||
if (config.runStatus == RUNSTATUS_STOP) return;
|
||||
if (!checkCRC(br, sizeof(struct espBlockRequest))) {
|
||||
Serial.print("Failed CRC on a blockrequest received by the AP");
|
||||
return;
|
||||
}
|
||||
|
||||
tagRecord* taginfo = nullptr;
|
||||
taginfo = tagRecord::findByMAC(br->src);
|
||||
if (taginfo == nullptr) {
|
||||
prepareCancelPending(br->src);
|
||||
Serial.printf("blockrequest: couldn't find taginfo %02X%02X%02X%02X%02X%02X%02X%02X\n", br->src[7], br->src[6], br->src[5], br->src[4], br->src[3], br->src[2], br->src[1], br->src[0]);
|
||||
return;
|
||||
}
|
||||
|
||||
if (taginfo->data == nullptr) {
|
||||
// not cached. open file, cache the data
|
||||
fs::File file = contentFS->open(taginfo->filename);
|
||||
if (!file) {
|
||||
Serial.print("No current file. Canceling request\n");
|
||||
prepareCancelPending(br->src);
|
||||
return;
|
||||
}
|
||||
taginfo->data = getDataForFile(&file);
|
||||
file.close();
|
||||
}
|
||||
|
||||
// check if we're not exceeding max blocks (to prevent sendBlock from exceeding its boundary)
|
||||
uint8_t totalblocks = (taginfo->len / BLOCK_DATA_SIZE);
|
||||
if (taginfo->len % BLOCK_DATA_SIZE) totalblocks++;
|
||||
if (br->blockId >= totalblocks) {
|
||||
br->blockId = totalblocks - 1;
|
||||
}
|
||||
|
||||
uint32_t len = taginfo->len - (BLOCK_DATA_SIZE * br->blockId);
|
||||
if (len > BLOCK_DATA_SIZE) len = BLOCK_DATA_SIZE;
|
||||
uint16_t checksum = sendBlock(taginfo->data + (br->blockId * BLOCK_DATA_SIZE), len);
|
||||
char buffer[150];
|
||||
sprintf(buffer, "< Block Request received for file %s block %d, len %d checksum %u\0", taginfo->filename.c_str(), br->blockId, len, checksum);
|
||||
wsLog((String)buffer);
|
||||
Serial.printf("<RQB file %s block %d, len %d checksum %u\n\0", taginfo->filename.c_str(), br->blockId, len, checksum);
|
||||
}
|
||||
|
||||
void processXferComplete(struct espXferComplete* xfc, bool local) {
|
||||
if (config.runStatus == RUNSTATUS_STOP) return;
|
||||
char buffer[64];
|
||||
sprintf(buffer, "< %02X%02X%02X%02X%02X%02X%02X%02X reports xfer complete\n\0", xfc->src[7], xfc->src[6], xfc->src[5], xfc->src[4], xfc->src[3], xfc->src[2], xfc->src[1], xfc->src[0]);
|
||||
wsLog((String)buffer);
|
||||
|
||||
if (local) {
|
||||
Serial.printf("<XFC %02X%02X%02X%02X%02X%02X%02X%02X\n", xfc->src[7], xfc->src[6], xfc->src[5], xfc->src[4], xfc->src[3], xfc->src[2], xfc->src[1], xfc->src[0]);
|
||||
} else {
|
||||
Serial.printf("<REMOTE XFC %02X%02X%02X%02X%02X%02X%02X%02X\n", xfc->src[7], xfc->src[6], xfc->src[5], xfc->src[4], xfc->src[3], xfc->src[2], xfc->src[1], xfc->src[0]);
|
||||
}
|
||||
|
||||
char src_path[64];
|
||||
char dst_path[64];
|
||||
sprintf(src_path, "/current/%02X%02X%02X%02X%02X%02X%02X%02X.pending\0", xfc->src[7], xfc->src[6], xfc->src[5], xfc->src[4], xfc->src[3], xfc->src[2], xfc->src[1], xfc->src[0]);
|
||||
sprintf(dst_path, "/current/%02X%02X%02X%02X%02X%02X%02X%02X.raw\0", xfc->src[7], xfc->src[6], xfc->src[5], xfc->src[4], xfc->src[3], xfc->src[2], xfc->src[1], xfc->src[0]);
|
||||
if (contentFS->exists(dst_path) && contentFS->exists(src_path)) {
|
||||
contentFS->remove(dst_path);
|
||||
}
|
||||
if (contentFS->exists(src_path)) {
|
||||
#ifndef REMOVE_RAW
|
||||
contentFS->rename(src_path, dst_path);
|
||||
#else
|
||||
contentFS->remove(src_path);
|
||||
#endif
|
||||
}
|
||||
|
||||
time_t now;
|
||||
time(&now);
|
||||
tagRecord* taginfo = nullptr;
|
||||
taginfo = tagRecord::findByMAC(xfc->src);
|
||||
if (taginfo != nullptr) {
|
||||
memcpy(taginfo->md5, taginfo->md5pending, sizeof(taginfo->md5pending));
|
||||
clearPending(taginfo);
|
||||
taginfo->wakeupReason = 0;
|
||||
if (taginfo->contentMode == 12 && local == false) {
|
||||
if (contentFS->exists(dst_path)) {
|
||||
contentFS->remove(dst_path);
|
||||
}
|
||||
}
|
||||
}
|
||||
wsSendTaginfo(xfc->src, SYNC_TAGSTATUS);
|
||||
if (local) udpsync.netProcessXferComplete(xfc);
|
||||
}
|
||||
|
||||
void processXferTimeout(struct espXferComplete* xfc, bool local) {
|
||||
if (config.runStatus == RUNSTATUS_STOP) return;
|
||||
char buffer[64];
|
||||
sprintf(buffer, "< %02X%02X%02X%02X%02X%02X%02X%02X xfer timeout\n\0", xfc->src[7], xfc->src[6], xfc->src[5], xfc->src[4], xfc->src[3], xfc->src[2], xfc->src[1], xfc->src[0]);
|
||||
wsErr((String)buffer);
|
||||
|
||||
if (local) {
|
||||
Serial.printf("<XTO %02X%02X%02X%02X%02X%02X%02X%02X\n", xfc->src[7], xfc->src[6], xfc->src[5], xfc->src[4], xfc->src[3], xfc->src[2], xfc->src[1], xfc->src[0]);
|
||||
} else {
|
||||
Serial.printf("<REMOTE XTO %02X%02X%02X%02X%02X%02X%02X%02X\n", xfc->src[7], xfc->src[6], xfc->src[5], xfc->src[4], xfc->src[3], xfc->src[2], xfc->src[1], xfc->src[0]);
|
||||
}
|
||||
|
||||
time_t now;
|
||||
time(&now);
|
||||
tagRecord* taginfo = nullptr;
|
||||
taginfo = tagRecord::findByMAC(xfc->src);
|
||||
if (taginfo != nullptr) {
|
||||
taginfo->expectedNextCheckin = now + 60;
|
||||
memset(taginfo->md5pending, 0, 16 * sizeof(uint8_t));
|
||||
clearPending(taginfo);
|
||||
}
|
||||
wsSendTaginfo(xfc->src, SYNC_TAGSTATUS);
|
||||
if (local) udpsync.netProcessXferTimeout(xfc);
|
||||
}
|
||||
|
||||
void processDataReq(struct espAvailDataReq* eadr, bool local) {
|
||||
if (config.runStatus == RUNSTATUS_STOP) return;
|
||||
char buffer[64];
|
||||
|
||||
tagRecord* taginfo = nullptr;
|
||||
taginfo = tagRecord::findByMAC(eadr->src);
|
||||
if (taginfo == nullptr) {
|
||||
taginfo = new tagRecord;
|
||||
memcpy(taginfo->mac, eadr->src, sizeof(taginfo->mac));
|
||||
taginfo->pending = false;
|
||||
tagDB.push_back(taginfo);
|
||||
}
|
||||
time_t now;
|
||||
time(&now);
|
||||
|
||||
char hexmac[17];
|
||||
mac2hex(eadr->src, hexmac);
|
||||
|
||||
if (!local) {
|
||||
if (taginfo->isExternal == false) {
|
||||
wsLog("moved AP from local to external " + String(hexmac));
|
||||
}
|
||||
taginfo->isExternal = true;
|
||||
} else {
|
||||
if (taginfo->isExternal == true) {
|
||||
wsLog("moved AP from external to local " + String(hexmac));
|
||||
}
|
||||
taginfo->isExternal = false;
|
||||
}
|
||||
|
||||
if (taginfo->pendingIdle == 0) {
|
||||
taginfo->expectedNextCheckin = now + 60;
|
||||
} else {
|
||||
taginfo->expectedNextCheckin = now + 60 * taginfo->pendingIdle;
|
||||
taginfo->pendingIdle = 0;
|
||||
}
|
||||
taginfo->lastseen = now;
|
||||
|
||||
if (eadr->adr.lastPacketRSSI != 0) {
|
||||
if (eadr->adr.wakeupReason >= 0xF0) {
|
||||
if (!taginfo->pending) taginfo->nextupdate = 0;
|
||||
memset(taginfo->md5, 0, 16 * sizeof(uint8_t));
|
||||
memset(taginfo->md5pending, 0, 16 * sizeof(uint8_t));
|
||||
|
||||
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";
|
||||
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);
|
||||
}
|
||||
if (taginfo->batteryMv != eadr->adr.batteryMv) {
|
||||
sprintf(buffer, "%02X%02X%02X%02X%02X%02X%02X%02X battery went from %.2fV to %.2fV", eadr->src[7], eadr->src[6], eadr->src[5], eadr->src[4], eadr->src[3], eadr->src[2], eadr->src[1], eadr->src[0], static_cast<float>(taginfo->batteryMv) / 1000.0, static_cast<float>(eadr->adr.batteryMv) / 1000.0);
|
||||
logLine(buffer);
|
||||
}
|
||||
|
||||
taginfo->LQI = eadr->adr.lastPacketLQI;
|
||||
taginfo->hwType = eadr->adr.hwType;
|
||||
taginfo->RSSI = eadr->adr.lastPacketRSSI;
|
||||
taginfo->temperature = eadr->adr.temperature;
|
||||
taginfo->batteryMv = eadr->adr.batteryMv;
|
||||
taginfo->hwType = eadr->adr.hwType;
|
||||
taginfo->wakeupReason = eadr->adr.wakeupReason;
|
||||
taginfo->capabilities = eadr->adr.capabilities;
|
||||
}
|
||||
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]);
|
||||
} 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]);
|
||||
}
|
||||
|
||||
Serial.print(buffer);
|
||||
wsSendTaginfo(eadr->src, SYNC_TAGSTATUS);
|
||||
if (local) {
|
||||
udpsync.netProcessDataReq(eadr);
|
||||
}
|
||||
}
|
||||
|
||||
void refreshAllPending() {
|
||||
for (int16_t c = 0; c < tagDB.size(); c++) {
|
||||
tagRecord* taginfo = nullptr;
|
||||
taginfo = tagDB.at(c);
|
||||
if (taginfo->pending) {
|
||||
clearPending(taginfo);
|
||||
taginfo->nextupdate = 0;
|
||||
memset(taginfo->md5, 0, 16 * sizeof(uint8_t));
|
||||
memset(taginfo->md5pending, 0, 16 * sizeof(uint8_t));
|
||||
wsSendTaginfo(taginfo->mac, SYNC_TAGSTATUS);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
void updateContent(uint8_t* dst) {
|
||||
tagRecord* taginfo = nullptr;
|
||||
taginfo = tagRecord::findByMAC(dst);
|
||||
if (taginfo != nullptr) {
|
||||
clearPending(taginfo);
|
||||
taginfo->nextupdate = 0;
|
||||
memset(taginfo->md5, 0, 16 * sizeof(uint8_t));
|
||||
memset(taginfo->md5pending, 0, 16 * sizeof(uint8_t));
|
||||
wsSendTaginfo(taginfo->mac, SYNC_TAGSTATUS);
|
||||
}
|
||||
}
|
||||
|
||||
void setAPchannel() {
|
||||
if (config.channel == 0) {
|
||||
// trigger channel autoselect
|
||||
UDPcomm udpsync;
|
||||
udpsync.getAPList();
|
||||
} else {
|
||||
if (curChannel.channel != config.channel) {
|
||||
curChannel.channel = config.channel;
|
||||
sendChannelPower(&curChannel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool sendAPSegmentedData(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;
|
||||
pending.availdatainfo.dataSize = icons << 16;
|
||||
memcpy((void*)&(pending.availdatainfo.dataVer), data.c_str(), 10);
|
||||
pending.availdatainfo.dataTypeArgument = inverted;
|
||||
pending.availdatainfo.nextCheckIn = 0;
|
||||
pending.attemptsLeft = 120;
|
||||
char buffer[64];
|
||||
sprintf(buffer, ">AP Segmented Data %02X%02X%02X%02X%02X%02X%02X%02X\n\0", dst[7], dst[6], dst[5], dst[4], dst[3], dst[2], dst[1], dst[0]);
|
||||
Serial.print(buffer);
|
||||
if (local) {
|
||||
return sendDataAvail(&pending);
|
||||
} else {
|
||||
udpsync.netSendDataAvail(&pending);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool showAPSegmentedInfo(uint8_t* dst, bool local) {
|
||||
struct pendingData pending = {0};
|
||||
memcpy(pending.targetMac, dst, 8);
|
||||
pending.availdatainfo.dataType = DATATYPE_UK_SEGMENTED;
|
||||
pending.availdatainfo.dataSize = 0x00;
|
||||
pending.availdatainfo.dataVer = 0x00;
|
||||
pending.availdatainfo.dataTypeArgument = 0;
|
||||
pending.availdatainfo.nextCheckIn = 0;
|
||||
pending.attemptsLeft = 120;
|
||||
char buffer[64];
|
||||
sprintf(buffer, ">SDA %02X%02X%02X%02X%02X%02X%02X%02X\n\0", dst[7], dst[6], dst[5], dst[4], dst[3], dst[2], dst[1], dst[0]);
|
||||
Serial.print(buffer);
|
||||
if (local) {
|
||||
return sendDataAvail(&pending);
|
||||
} else {
|
||||
udpsync.netSendDataAvail(&pending);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool sendTagCommand(uint8_t* dst, uint8_t cmd, bool local) {
|
||||
struct pendingData pending = {0};
|
||||
memcpy(pending.targetMac, dst, 8);
|
||||
pending.availdatainfo.dataType = 0xAF;
|
||||
pending.availdatainfo.dataTypeArgument = cmd;
|
||||
pending.availdatainfo.nextCheckIn = 0;
|
||||
pending.attemptsLeft = 120;
|
||||
char buffer[64];
|
||||
sprintf(buffer, ">Tag CMD %02X%02X%02X%02X%02X%02X%02X%02X\n\0", dst[7], dst[6], dst[5], dst[4], dst[3], dst[2], dst[1], dst[0]);
|
||||
Serial.print(buffer);
|
||||
if (local) {
|
||||
return sendDataAvail(&pending);
|
||||
} else {
|
||||
udpsync.netSendDataAvail(&pending);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void updateTaginfoitem(struct TagInfo* taginfoitem) {
|
||||
tagRecord* taginfo = nullptr;
|
||||
taginfo = tagRecord::findByMAC(taginfoitem->mac);
|
||||
|
||||
if (taginfo == nullptr) {
|
||||
taginfo = new tagRecord;
|
||||
memcpy(taginfo->mac, taginfoitem->mac, sizeof(taginfo->mac));
|
||||
taginfo->pending = false;
|
||||
tagDB.push_back(taginfo);
|
||||
}
|
||||
tagRecord initialTagInfo = *taginfo;
|
||||
|
||||
switch (taginfoitem->syncMode) {
|
||||
case SYNC_USERCFG:
|
||||
taginfo->alias = String(taginfoitem->alias);
|
||||
taginfo->nextupdate = taginfoitem->nextupdate;
|
||||
break;
|
||||
case SYNC_TAGSTATUS:
|
||||
taginfo->lastseen = taginfoitem->lastseen;
|
||||
taginfo->nextupdate = taginfoitem->nextupdate;
|
||||
taginfo->pending = taginfoitem->pending;
|
||||
taginfo->expectedNextCheckin = taginfoitem->expectedNextCheckin;
|
||||
taginfo->hwType = taginfoitem->hwType;
|
||||
taginfo->wakeupReason = taginfoitem->wakeupReason;
|
||||
taginfo->capabilities = taginfoitem->capabilities;
|
||||
taginfo->pendingIdle = taginfoitem->pendingIdle;
|
||||
break;
|
||||
}
|
||||
|
||||
char hexmac[17];
|
||||
mac2hex(taginfo->mac, hexmac);
|
||||
if (taginfo->contentMode != 12 && taginfoitem->contentMode != 12) {
|
||||
wsLog("Remote AP takes control over tag " + String(hexmac));
|
||||
taginfo->contentMode = 12;
|
||||
}
|
||||
|
||||
if (taginfoitem->syncMode == SYNC_DELETE) {
|
||||
taginfo->contentMode = 255;
|
||||
wsSendTaginfo(taginfo->mac, SYNC_NOSYNC);
|
||||
deleteRecord(taginfoitem->mac);
|
||||
} else {
|
||||
bool hasChanges = (memcmp(&initialTagInfo, taginfo, sizeof(tagRecord)) != 0);
|
||||
if (hasChanges) {
|
||||
wsSendTaginfo(taginfo->mac, SYNC_NOSYNC);
|
||||
}
|
||||
}
|
||||
}
|
||||
289
ESP32_AP-Flasher/src/ota.cpp
Normal file
289
ESP32_AP-Flasher/src/ota.cpp
Normal file
@@ -0,0 +1,289 @@
|
||||
#include "ota.h"
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <ArduinoJson.h>
|
||||
#include <FS.h>
|
||||
#include <HTTPClient.h>
|
||||
#include "storage.h"
|
||||
#include <MD5Builder.h>
|
||||
#include <Update.h>
|
||||
|
||||
#include "tag_db.h"
|
||||
#include "web.h"
|
||||
|
||||
#ifndef BUILD_ENV_NAME
|
||||
#define BUILD_ENV_NAME unknown
|
||||
#endif
|
||||
#ifndef BUILD_TIME
|
||||
#define BUILD_TIME 0
|
||||
#endif
|
||||
#ifndef BUILD_VERSION
|
||||
#define BUILD_VERSION custom
|
||||
#endif
|
||||
#ifndef SHA
|
||||
#define SHA 0
|
||||
#endif
|
||||
|
||||
#define STR_IMPL(x) #x
|
||||
#define STR(x) STR_IMPL(x)
|
||||
|
||||
void handleSysinfoRequest(AsyncWebServerRequest* request) {
|
||||
StaticJsonDocument<250> doc;
|
||||
doc["alias"] = config.alias;
|
||||
doc["env"] = STR(BUILD_ENV_NAME);
|
||||
doc["buildtime"] = STR(BUILD_TIME);
|
||||
doc["buildversion"] = STR(BUILD_VERSION);
|
||||
doc["sha"] = STR(SHA);
|
||||
doc["psramsize"] = ESP.getPsramSize();
|
||||
doc["flashsize"] = ESP.getFlashChipSize();
|
||||
doc["rollback"] = Update.canRollBack();
|
||||
|
||||
size_t bufferSize = measureJson(doc) + 1;
|
||||
AsyncResponseStream* response = request->beginResponseStream("application/json", bufferSize);
|
||||
serializeJson(doc, *response);
|
||||
request->send(response);
|
||||
};
|
||||
|
||||
void handleCheckFile(AsyncWebServerRequest* request) {
|
||||
if (!request->hasParam("path")) {
|
||||
request->send(400);
|
||||
return;
|
||||
}
|
||||
|
||||
String filePath = request->getParam("path")->value();
|
||||
File file = contentFS->open(filePath, "r");
|
||||
if (!file) {
|
||||
StaticJsonDocument<64> doc;
|
||||
doc["filesize"] = 0;
|
||||
doc["md5"] = "";
|
||||
String jsonResponse;
|
||||
serializeJson(doc, jsonResponse);
|
||||
request->send(200, "application/json", jsonResponse);
|
||||
return;
|
||||
}
|
||||
|
||||
size_t fileSize = file.size();
|
||||
|
||||
MD5Builder md5;
|
||||
md5.begin();
|
||||
md5.addStream(file, fileSize);
|
||||
md5.calculate();
|
||||
String md5Hash = md5.toString();
|
||||
|
||||
file.close();
|
||||
|
||||
StaticJsonDocument<128> doc;
|
||||
doc["filesize"] = fileSize;
|
||||
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();
|
||||
HTTPClient http;
|
||||
http.begin(url);
|
||||
http.setConnectTimeout(5000);
|
||||
http.setFollowRedirects(HTTPC_FORCE_FOLLOW_REDIRECTS);
|
||||
int httpResponseCode = http.GET();
|
||||
if (httpResponseCode > 0) {
|
||||
Serial.println(httpResponseCode);
|
||||
String contentType = http.header("Content-Type");
|
||||
size_t contentLength = http.getSize();
|
||||
if (contentLength > 0) {
|
||||
String content = http.getString();
|
||||
AsyncWebServerResponse* response = request->beginResponse(200, contentType, content);
|
||||
request->send(response);
|
||||
} else {
|
||||
request->send(500, "text/plain", "no size header");
|
||||
}
|
||||
} else {
|
||||
request->send(httpResponseCode, "text/plain", "Failed to fetch URL");
|
||||
wsSerial(http.errorToString(httpResponseCode));
|
||||
}
|
||||
http.end();
|
||||
} else {
|
||||
request->send(400, "text/plain", "Missing 'url' parameter");
|
||||
}
|
||||
}
|
||||
|
||||
void handleLittleFSUpload(AsyncWebServerRequest* request, String filename, size_t index, uint8_t* data, size_t len, bool final) {
|
||||
bool error = false;
|
||||
if (!index) {
|
||||
String path;
|
||||
if (!request->hasParam("path", true)) {
|
||||
path = "/temp/null.bin";
|
||||
final = true;
|
||||
error = true;
|
||||
} else {
|
||||
path = request->getParam("path", true)->value();
|
||||
Serial.println("update " + path);
|
||||
request->_tempFile = contentFS->open(path, "w", true);
|
||||
}
|
||||
}
|
||||
if (len) {
|
||||
if (!request->_tempFile.write(data, len)) {
|
||||
error = true;
|
||||
final = true;
|
||||
}
|
||||
}
|
||||
if (final) {
|
||||
request->_tempFile.close();
|
||||
if (error) {
|
||||
request->send(507, "text/plain", "Error. Disk full?");
|
||||
} else {
|
||||
request->send(200, "text/plain", "Ok, file written");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct FirmwareUpdateParams {
|
||||
String url;
|
||||
String md5;
|
||||
size_t size;
|
||||
};
|
||||
|
||||
void handleUpdateOTA(AsyncWebServerRequest* request) {
|
||||
if (request->hasParam("url", true) && request->hasParam("md5", true) && request->hasParam("size", true)) {
|
||||
FirmwareUpdateParams* params = new FirmwareUpdateParams;
|
||||
params->url = request->getParam("url", true)->value();
|
||||
params->md5 = request->getParam("md5", true)->value();
|
||||
params->size = request->getParam("size", true)->value().toInt();
|
||||
xTaskCreate(firmwareUpdateTask, "OTAUpdateTask", 6144, params, 10, NULL);
|
||||
|
||||
request->send(200, "text/plain", "In progress");
|
||||
} else {
|
||||
request->send(400, "Bad request");
|
||||
}
|
||||
}
|
||||
|
||||
void firmwareUpdateTask(void* parameter) {
|
||||
FirmwareUpdateParams* params = reinterpret_cast<FirmwareUpdateParams*>(parameter);
|
||||
|
||||
if (ESP.getMaxAllocHeap() < 22000) {
|
||||
wsSerial("Error: Not enough memory left. Restart the esp32 and try updating again.");
|
||||
wsSerial("[reboot]");
|
||||
} else {
|
||||
const char* url = params->url.c_str();
|
||||
const char* md5 = params->md5.c_str();
|
||||
size_t size = params->size;
|
||||
updateFirmware(url, md5, size);
|
||||
}
|
||||
|
||||
delete params;
|
||||
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);
|
||||
|
||||
config.runStatus = RUNSTATUS_STOP;
|
||||
vTaskDelay(3000 / portTICK_PERIOD_MS);
|
||||
// xSemaphoreTake(tagDBOwner, portMAX_DELAY);
|
||||
saveDB("/current/tagDB.json");
|
||||
// destroyDB();
|
||||
|
||||
HTTPClient httpClient;
|
||||
|
||||
wsSerial("start downloading");
|
||||
wsSerial(url);
|
||||
|
||||
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);
|
||||
|
||||
if (httpCode == HTTP_CODE_OK) {
|
||||
if (Update.begin(size)) {
|
||||
Update.setMD5(expectedMd5);
|
||||
|
||||
unsigned long progressTimer = millis();
|
||||
Update.onProgress([&progressTimer](size_t progress, size_t total) {
|
||||
if (millis() - progressTimer > 500 || progress == total) {
|
||||
char buffer[50];
|
||||
sprintf(buffer, "Progress: %u%% %d %d", progress * 100 / total, progress, total);
|
||||
wsSerial(String(buffer));
|
||||
progressTimer = millis();
|
||||
vTaskDelay(1 / portTICK_PERIOD_MS);
|
||||
}
|
||||
});
|
||||
|
||||
size_t written = Update.writeStream(httpClient.getStream());
|
||||
if (written == httpClient.getSize()) {
|
||||
if (Update.end(true)) {
|
||||
wsSerial("Firmware update successful");
|
||||
wsSerial("Reboot system now");
|
||||
wsSerial("[reboot]");
|
||||
vTaskDelay(1000 / portTICK_PERIOD_MS);
|
||||
// ESP.restart();
|
||||
} else {
|
||||
wsSerial("Error updating firmware:");
|
||||
wsSerial(Update.errorString());
|
||||
}
|
||||
} else {
|
||||
wsSerial("Error writing firmware data:");
|
||||
wsSerial(Update.errorString());
|
||||
}
|
||||
} else {
|
||||
wsSerial("Failed to begin firmware update");
|
||||
wsSerial(Update.errorString());
|
||||
}
|
||||
} else {
|
||||
wsSerial("Failed to download firmware file (HTTP code " + String(httpCode) + ")");
|
||||
wsSerial(httpClient.errorToString(httpCode));
|
||||
}
|
||||
|
||||
httpClient.end();
|
||||
// loadDB("/current/tagDB.json");
|
||||
config.runStatus = RUNSTATUS_RUN;
|
||||
// xSemaphoreGive(tagDBOwner);
|
||||
}
|
||||
|
||||
void handleRollback(AsyncWebServerRequest* request) {
|
||||
if (Update.canRollBack()) {
|
||||
bool rollbackSuccess = Update.rollBack();
|
||||
if (rollbackSuccess) {
|
||||
request->send(200, "Rollback successful");
|
||||
wsSerial("Rollback successful");
|
||||
wsSerial("Reboot system now");
|
||||
wsSerial("[reboot]");
|
||||
vTaskDelay(1000 / portTICK_PERIOD_MS);
|
||||
// ESP.restart();
|
||||
} else {
|
||||
wsSerial("Rollback failed");
|
||||
request->send(400, "Rollback failed");
|
||||
}
|
||||
} else {
|
||||
wsSerial("Rollback not allowed");
|
||||
request->send(400, "Rollback not allowed");
|
||||
}
|
||||
}
|
||||
|
||||
void handleUpdateActions(AsyncWebServerRequest* request) {
|
||||
wsSerial("Performing cleanup");
|
||||
File file = contentFS->open("/update_actions.json", "r");
|
||||
if (!file) {
|
||||
wsSerial("No update_actions.json present");
|
||||
request->send(200, "No update actions needed");
|
||||
return;
|
||||
}
|
||||
StaticJsonDocument<1000> doc;
|
||||
DeserializationError error = deserializeJson(doc, file);
|
||||
JsonArray deleteFiles = doc["deletefile"].as<JsonArray>();
|
||||
for (const auto& filePath : deleteFiles) {
|
||||
if (contentFS->remove(filePath.as<const char*>())) {
|
||||
wsSerial("deleted file: " + filePath.as<String>());
|
||||
}
|
||||
}
|
||||
file.close();
|
||||
wsSerial("Cleanup finished");
|
||||
request->send(200, "Clean up finished");
|
||||
contentFS->remove("/update_actions.json");
|
||||
}
|
||||
100
ESP32_AP-Flasher/src/powermgt.cpp
Normal file
100
ESP32_AP-Flasher/src/powermgt.cpp
Normal file
@@ -0,0 +1,100 @@
|
||||
#include "powermgt.h"
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
#include "settings.h"
|
||||
|
||||
#ifdef OPENEPAPERLINK_PCB
|
||||
#include "soc/rtc_cntl_reg.h"
|
||||
#include "soc/soc.h"
|
||||
#endif
|
||||
|
||||
void simpleAPPower(uint8_t* pin, uint8_t pincount, bool state) {
|
||||
for (uint8_t c = 0; c < pincount; c++) {
|
||||
pinMode(pin[c], INPUT);
|
||||
}
|
||||
for (uint8_t c = 0; c < pincount; c++) {
|
||||
#ifdef POWER_HIGH_SIDE_DRIVER
|
||||
digitalWrite(pin[c], !state);
|
||||
#else
|
||||
digitalWrite(pin[c], state);
|
||||
#endif
|
||||
}
|
||||
for (uint8_t c = 0; c < pincount; c++) {
|
||||
pinMode(pin[c], OUTPUT);
|
||||
}
|
||||
}
|
||||
|
||||
// On the OpenEPaperLink board, there is no in-rush current limiting. The tags that can be connected to the board can have significant capacity, which,
|
||||
// when drained if the board applies power, will cause the 3v3 rail to sag enough to reset the ESP32. This is obviously not great. To prevent this from happening,
|
||||
// we ramp up/down the voltage with PWM. Ramping down really is unnecessary, as the board has a resistor to dump the charge into.
|
||||
void rampTagPower(uint8_t* pin, bool up) {
|
||||
#ifdef OPENEPAPERLINK_PCB
|
||||
WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0);
|
||||
#endif
|
||||
if (up) {
|
||||
ledcSetup(0, 152000, 8); // 141251 okay // 101251 okay
|
||||
ledcWrite(0, 254);
|
||||
vTaskDelay(1 / portTICK_PERIOD_MS);
|
||||
ledcAttachPin(pin[0], 0);
|
||||
pinMode(pin[0], OUTPUT);
|
||||
vTaskDelay(10 / portTICK_PERIOD_MS);
|
||||
for (uint8_t c = 254; c != 0xFF; c--) {
|
||||
ledcWrite(0, c);
|
||||
if (c > 250) {
|
||||
vTaskDelay(2 / portTICK_PERIOD_MS);
|
||||
} else {
|
||||
delayMicroseconds(100);
|
||||
}
|
||||
}
|
||||
digitalWrite(pin[0], LOW);
|
||||
ledcDetachPin(pin[0]);
|
||||
digitalWrite(pin[0], LOW);
|
||||
} else {
|
||||
ledcSetup(0, 152000, 8); // 141251 okay // 101251 okay
|
||||
ledcWrite(0, 0);
|
||||
vTaskDelay(1 / portTICK_PERIOD_MS);
|
||||
ledcAttachPin(pin[0], 0);
|
||||
pinMode(pin[0], OUTPUT);
|
||||
vTaskDelay(10 / portTICK_PERIOD_MS);
|
||||
for (uint8_t c = 0; c < 0xFF; c++) {
|
||||
ledcWrite(0, c);
|
||||
if (c > 250) {
|
||||
vTaskDelay(2 / portTICK_PERIOD_MS);
|
||||
} else {
|
||||
delayMicroseconds(100);
|
||||
}
|
||||
}
|
||||
digitalWrite(pin[0], HIGH);
|
||||
ledcDetachPin(pin[0]);
|
||||
digitalWrite(pin[0], HIGH);
|
||||
}
|
||||
#ifdef OPENEPAPERLINK_PCB
|
||||
WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 1);
|
||||
#endif
|
||||
}
|
||||
|
||||
void powerControl(bool powerState, uint8_t* pin, uint8_t pincount) {
|
||||
if (pin[0] == -1) return;
|
||||
|
||||
#ifdef POWER_RAMPING
|
||||
if (powerState == true) {
|
||||
#ifdef POWER_HIGH_SIDE_DRIVER
|
||||
rampTagPower(pin, true);
|
||||
#else
|
||||
rampTagPower(pin, false);
|
||||
#endif
|
||||
} else {
|
||||
pinMode(pin[0], OUTPUT);
|
||||
#ifdef POWER_HIGH_SIDE_DRIVER
|
||||
digitalWrite(pin[0], HIGH);
|
||||
#else
|
||||
digitalWrite(pin[0], LOW);
|
||||
#endif
|
||||
}
|
||||
#else
|
||||
simpleAPPower(pin, pincount, false);
|
||||
delay(500);
|
||||
simpleAPPower(pin, pincount, true);
|
||||
#endif
|
||||
}
|
||||
794
ESP32_AP-Flasher/src/serialap.cpp
Normal file
794
ESP32_AP-Flasher/src/serialap.cpp
Normal file
@@ -0,0 +1,794 @@
|
||||
#include "serialap.h"
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <HardwareSerial.h>
|
||||
|
||||
#include "commstructs.h"
|
||||
#include "flasher.h"
|
||||
#include "leds.h"
|
||||
#include "newproto.h"
|
||||
#include "powermgt.h"
|
||||
#include "settings.h"
|
||||
#include "storage.h"
|
||||
#include "web.h"
|
||||
#include "zbs_interface.h"
|
||||
|
||||
QueueHandle_t rxCmdQueue;
|
||||
SemaphoreHandle_t txActive;
|
||||
|
||||
// If a command is sent, it will wait for a reply here
|
||||
#define CMD_REPLY_WAIT 0x00
|
||||
#define CMD_REPLY_ACK 0x01
|
||||
#define CMD_REPLY_NOK 0x02
|
||||
#define CMD_REPLY_NOQ 0x03
|
||||
volatile uint8_t cmdReplyValue = CMD_REPLY_WAIT;
|
||||
|
||||
#define AP_SERIAL_PORT Serial1
|
||||
|
||||
uint8_t channelList[6];
|
||||
struct espSetChannelPower curChannel = {0, 11, 10};
|
||||
|
||||
#define RX_CMD_RQB 0x01
|
||||
#define RX_CMD_ADR 0x02
|
||||
#define RX_CMD_XFC 0x03
|
||||
#define RX_CMD_XTO 0x04
|
||||
#define RX_CMD_RDY 0x05
|
||||
#define RX_CMD_RSET 0x06
|
||||
|
||||
#define AP_ACTIVITY_MAX_INTERVAL 30 * 1000
|
||||
volatile uint32_t lastAPActivity = 0;
|
||||
struct APInfoS apInfo;
|
||||
|
||||
extern uint8_t* getDataForFile(File* file);
|
||||
|
||||
struct rxCmd {
|
||||
uint8_t* data;
|
||||
uint8_t len;
|
||||
uint8_t type;
|
||||
};
|
||||
|
||||
#define ZBS_RX_WAIT_HEADER 0
|
||||
#define ZBS_RX_WAIT_PKT_LEN 1
|
||||
#define ZBS_RX_WAIT_PKT_RX 2
|
||||
#define ZBS_RX_WAIT_SEP1 3
|
||||
#define ZBS_RX_WAIT_SEP2 4
|
||||
#define ZBS_RX_WAIT_VER 6
|
||||
#define ZBS_RX_BLOCK_REQUEST 7
|
||||
#define ZBS_RX_WAIT_XFERCOMPLETE 8
|
||||
#define ZBS_RX_WAIT_DATA_REQ 9
|
||||
#define ZBS_RX_WAIT_JOINNETWORK 10
|
||||
#define ZBS_RX_WAIT_XFERTIMEOUT 11
|
||||
#define ZBS_RX_WAIT_MAC 12
|
||||
#define ZBS_RX_WAIT_CHANNEL 13
|
||||
#define ZBS_RX_WAIT_POWER 14
|
||||
#define ZBS_RX_WAIT_PENDING 15
|
||||
#define ZBS_RX_WAIT_NOP 16
|
||||
#define ZBS_RX_WAIT_TYPE 17
|
||||
|
||||
bool txStart() {
|
||||
while (1) {
|
||||
if (xPortInIsrContext()) {
|
||||
if (xSemaphoreTakeFromISR(txActive, NULL) == pdTRUE) return true;
|
||||
} else {
|
||||
if (xSemaphoreTake(txActive, portTICK_PERIOD_MS)) return true;
|
||||
}
|
||||
vTaskDelay(10 / portTICK_PERIOD_MS);
|
||||
Serial.println("wait... tx busy");
|
||||
}
|
||||
// this never happens. Should we make a timeout?
|
||||
return false;
|
||||
}
|
||||
void txEnd() {
|
||||
if (xPortInIsrContext()) {
|
||||
xSemaphoreGiveFromISR(txActive, NULL);
|
||||
} else {
|
||||
xSemaphoreGive(txActive);
|
||||
}
|
||||
}
|
||||
bool waitCmdReply() {
|
||||
uint32_t val = millis();
|
||||
while (millis() < val + 100) {
|
||||
switch (cmdReplyValue) {
|
||||
case CMD_REPLY_WAIT:
|
||||
break;
|
||||
case CMD_REPLY_ACK:
|
||||
lastAPActivity = millis();
|
||||
return true;
|
||||
break;
|
||||
case CMD_REPLY_NOK:
|
||||
lastAPActivity = millis();
|
||||
return false;
|
||||
break;
|
||||
case CMD_REPLY_NOQ:
|
||||
lastAPActivity = millis();
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
vTaskDelay(1 / portTICK_RATE_MS);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
#if (AP_PROCESS_PORT == FLASHER_AP_PORT)
|
||||
int8_t APpowerPins[] = FLASHER_AP_POWER;
|
||||
#define AP_RESET_PIN FLASHER_AP_RESET
|
||||
#define AP_POWER_PIN FLASHER_AP_POWER
|
||||
#endif
|
||||
#ifdef OPENEPAPERLINK_PCB
|
||||
#if (AP_PROCESS_PORT == FLASHER_EXT_PORT)
|
||||
int8_t APpowerPins[] = FLASHER_EXT_POWER;
|
||||
#define AP_RESET_PIN FLASHER_EXT_RESET
|
||||
#define AP_POWER_PIN FLASHER_EXT_POWER
|
||||
#endif
|
||||
#if (AP_PROCESS_PORT == FLASHER_ALTRADIO_PORT)
|
||||
int8_t APpowerPins[] = FLASHER_ALT_POWER;
|
||||
#define AP_RESET_PIN FLASHER_ALT_RESET
|
||||
#define AP_POWER_PIN FLASHER_ALT_POWER
|
||||
#endif
|
||||
#endif
|
||||
|
||||
void APEnterEarlyReset() {
|
||||
pinMode(AP_RESET_PIN, OUTPUT);
|
||||
digitalWrite(AP_RESET_PIN, LOW);
|
||||
}
|
||||
|
||||
// Reset the tag
|
||||
void APTagReset() {
|
||||
pinMode(AP_RESET_PIN, OUTPUT);
|
||||
digitalWrite(AP_RESET_PIN, LOW);
|
||||
vTaskDelay(50 / portTICK_PERIOD_MS);
|
||||
powerControl(false, (uint8_t*)APpowerPins, sizeof(APpowerPins));
|
||||
vTaskDelay(300 / portTICK_PERIOD_MS);
|
||||
powerControl(true, (uint8_t*)APpowerPins, sizeof(APpowerPins));
|
||||
vTaskDelay(100 / portTICK_PERIOD_MS);
|
||||
digitalWrite(AP_RESET_PIN, HIGH);
|
||||
vTaskDelay(100 / portTICK_PERIOD_MS);
|
||||
}
|
||||
|
||||
// Send data to the AP
|
||||
uint16_t sendBlock(const void* data, const uint16_t len) {
|
||||
if (!apInfo.isOnline) return false;
|
||||
if (!txStart()) return 0;
|
||||
for (uint8_t attempt = 0; attempt < 5; attempt++) {
|
||||
cmdReplyValue = CMD_REPLY_WAIT;
|
||||
AP_SERIAL_PORT.print(">D>");
|
||||
if (waitCmdReply()) goto blksend;
|
||||
Serial.printf("block send failed in try %d\n", attempt);
|
||||
}
|
||||
Serial.print("Failed sending block...\n");
|
||||
txEnd();
|
||||
return 0;
|
||||
blksend:
|
||||
uint8_t blockbuffer[sizeof(struct blockData)];
|
||||
struct blockData* bd = (struct blockData*)blockbuffer;
|
||||
bd->size = len;
|
||||
bd->checksum = 0;
|
||||
|
||||
// calculate checksum
|
||||
for (uint16_t c = 0; c < len; c++) {
|
||||
bd->checksum += ((uint8_t*)data)[c];
|
||||
}
|
||||
|
||||
// send blockData header
|
||||
for (uint8_t c = 0; c < sizeof(struct blockData); c++) {
|
||||
AP_SERIAL_PORT.write(0xAA ^ blockbuffer[c]);
|
||||
}
|
||||
|
||||
// send an entire block of data
|
||||
uint16_t c;
|
||||
for (c = 0; c < len; c++) {
|
||||
AP_SERIAL_PORT.write(0xAA ^ ((uint8_t*)data)[c]);
|
||||
}
|
||||
|
||||
// fill the rest of the block-length filled with something else (will end up as 0xFF in the buffer)
|
||||
for (; c < BLOCK_DATA_SIZE; c++) {
|
||||
AP_SERIAL_PORT.write(0x55);
|
||||
}
|
||||
|
||||
// dummy bytes in case some bytes were missed, makes sure the AP gets kicked out of data-loading mode
|
||||
for (c = 0; c < 32; c++) {
|
||||
AP_SERIAL_PORT.write(0xF5);
|
||||
}
|
||||
delay(10);
|
||||
txEnd();
|
||||
return bd->checksum;
|
||||
}
|
||||
bool sendDataAvail(struct pendingData* pending) {
|
||||
if (!apInfo.isOnline) return false;
|
||||
if (!txStart()) return false;
|
||||
addCRC(pending, sizeof(struct pendingData));
|
||||
for (uint8_t attempt = 0; attempt < 5; attempt++) {
|
||||
cmdReplyValue = CMD_REPLY_WAIT;
|
||||
AP_SERIAL_PORT.print("SDA>");
|
||||
for (uint8_t c = 0; c < sizeof(struct pendingData); c++) {
|
||||
AP_SERIAL_PORT.write(((uint8_t*)pending)[c]);
|
||||
}
|
||||
if (waitCmdReply()) goto sdasend;
|
||||
Serial.printf("SDA send failed in try %d\n", attempt);
|
||||
delay(200);
|
||||
}
|
||||
Serial.print("SDA failed to send...\n");
|
||||
txEnd();
|
||||
return false;
|
||||
sdasend:
|
||||
txEnd();
|
||||
return true;
|
||||
}
|
||||
bool sendCancelPending(struct pendingData* pending) {
|
||||
if (!apInfo.isOnline) return false;
|
||||
if (!txStart()) return false;
|
||||
addCRC(pending, sizeof(struct pendingData));
|
||||
for (uint8_t attempt = 0; attempt < 5; attempt++) {
|
||||
cmdReplyValue = CMD_REPLY_WAIT;
|
||||
AP_SERIAL_PORT.print("CXD>");
|
||||
for (uint8_t c = 0; c < sizeof(struct pendingData); c++) {
|
||||
AP_SERIAL_PORT.write(((uint8_t*)pending)[c]);
|
||||
}
|
||||
if (waitCmdReply()) goto cxdsent;
|
||||
Serial.printf("CXD send failed in try %d\n", attempt);
|
||||
}
|
||||
Serial.print("CXD failed to send...\n");
|
||||
txEnd();
|
||||
return false;
|
||||
cxdsent:
|
||||
txEnd();
|
||||
return true;
|
||||
}
|
||||
bool sendChannelPower(struct espSetChannelPower* scp) {
|
||||
if ((apInfo.state != AP_STATE_ONLINE) && (apInfo.state != AP_STATE_COMING_ONLINE)) return false;
|
||||
if (!txStart()) return false;
|
||||
addCRC(scp, sizeof(struct espSetChannelPower));
|
||||
for (uint8_t attempt = 0; attempt < 5; attempt++) {
|
||||
cmdReplyValue = CMD_REPLY_WAIT;
|
||||
AP_SERIAL_PORT.print("SCP>");
|
||||
for (uint8_t c = 0; c < sizeof(struct espSetChannelPower); c++) {
|
||||
AP_SERIAL_PORT.write(((uint8_t*)scp)[c]);
|
||||
}
|
||||
if (waitCmdReply()) goto scpSent;
|
||||
Serial.printf("SCP send failed in try %d\n", attempt);
|
||||
}
|
||||
Serial.print("SCP failed to send...\n");
|
||||
txEnd();
|
||||
return false;
|
||||
scpSent:
|
||||
txEnd();
|
||||
return true;
|
||||
}
|
||||
bool sendPing() {
|
||||
if (!txStart()) return false;
|
||||
for (uint8_t attempt = 0; attempt < 5; attempt++) {
|
||||
cmdReplyValue = CMD_REPLY_WAIT;
|
||||
AP_SERIAL_PORT.print("RDY?");
|
||||
if (waitCmdReply()) goto pingSent;
|
||||
}
|
||||
txEnd();
|
||||
return false;
|
||||
pingSent:
|
||||
txEnd();
|
||||
return true;
|
||||
}
|
||||
bool sendGetInfo() {
|
||||
if (!txStart()) return false;
|
||||
for (uint8_t attempt = 0; attempt < 5; attempt++) {
|
||||
cmdReplyValue = CMD_REPLY_WAIT;
|
||||
AP_SERIAL_PORT.print("NFO?");
|
||||
if (waitCmdReply()) goto nfoRequested;
|
||||
}
|
||||
txEnd();
|
||||
return false;
|
||||
nfoRequested:
|
||||
txEnd();
|
||||
return true;
|
||||
}
|
||||
|
||||
// add RX'd request from the AP to the processor queue
|
||||
void addRXQueue(uint8_t* data, uint8_t len, uint8_t type) {
|
||||
struct rxCmd* rxcmd = new struct rxCmd;
|
||||
rxcmd->data = data;
|
||||
rxcmd->len = len;
|
||||
rxcmd->type = type;
|
||||
BaseType_t queuestatus = xQueueSend(rxCmdQueue, &rxcmd, 0);
|
||||
if (queuestatus == pdFALSE) {
|
||||
if (data) free(data);
|
||||
free(rxcmd);
|
||||
}
|
||||
}
|
||||
|
||||
// Asynchronous command processor
|
||||
void rxCmdProcessor(void* parameter) {
|
||||
rxCmdQueue = xQueueCreate(30, sizeof(struct rxCmd*));
|
||||
txActive = xSemaphoreCreateBinary();
|
||||
xSemaphoreGive(txActive);
|
||||
while (1) {
|
||||
struct rxCmd* rxcmd = nullptr;
|
||||
BaseType_t q = xQueueReceive(rxCmdQueue, &rxcmd, 10);
|
||||
if (q == pdTRUE) {
|
||||
switch (rxcmd->type) {
|
||||
case RX_CMD_RQB:
|
||||
processBlockRequest((struct espBlockRequest*)rxcmd->data);
|
||||
#ifdef HAS_RGB_LED
|
||||
shortBlink(CRGB::Blue);
|
||||
#else
|
||||
quickBlink(3);
|
||||
#endif
|
||||
break;
|
||||
case RX_CMD_ADR:
|
||||
processDataReq((struct espAvailDataReq*)rxcmd->data, true);
|
||||
#ifdef HAS_RGB_LED
|
||||
shortBlink(CRGB::Aqua);
|
||||
#else
|
||||
quickBlink(1);
|
||||
#endif
|
||||
break;
|
||||
case RX_CMD_XFC:
|
||||
processXferComplete((struct espXferComplete*)rxcmd->data, true);
|
||||
#ifdef HAS_RGB_LED
|
||||
shortBlink(CRGB::Purple);
|
||||
#endif
|
||||
break;
|
||||
case RX_CMD_XTO:
|
||||
processXferTimeout((struct espXferComplete*)rxcmd->data, true);
|
||||
break;
|
||||
}
|
||||
if (rxcmd->data) free(rxcmd->data);
|
||||
if (rxcmd) free(rxcmd);
|
||||
}
|
||||
}
|
||||
}
|
||||
void rxSerialTask(void* parameter) {
|
||||
static char cmdbuffer[4] = {0};
|
||||
static uint8_t* packetp = nullptr;
|
||||
// static uint8_t pktlen = 0;
|
||||
static uint8_t pktindex = 0; // length of the command
|
||||
static uint8_t RXState = ZBS_RX_WAIT_HEADER;
|
||||
static char lastchar = 0;
|
||||
static uint8_t charindex = 0;
|
||||
|
||||
while (1) {
|
||||
while (AP_SERIAL_PORT.available()) {
|
||||
lastchar = AP_SERIAL_PORT.read();
|
||||
switch (RXState) {
|
||||
case ZBS_RX_WAIT_HEADER:
|
||||
// Serial.write(lastchar);
|
||||
// shift characters in
|
||||
for (uint8_t c = 0; c < 3; c++) {
|
||||
cmdbuffer[c] = cmdbuffer[c + 1];
|
||||
}
|
||||
cmdbuffer[3] = lastchar;
|
||||
|
||||
if ((strncmp(cmdbuffer, "ACK>", 4) == 0)) cmdReplyValue = CMD_REPLY_ACK;
|
||||
if ((strncmp(cmdbuffer, "NOK>", 4) == 0)) cmdReplyValue = CMD_REPLY_NOK;
|
||||
if ((strncmp(cmdbuffer, "NOQ>", 4) == 0)) cmdReplyValue = CMD_REPLY_NOQ;
|
||||
|
||||
if ((strncmp(cmdbuffer, "VER>", 4) == 0)) {
|
||||
pktindex = 0;
|
||||
RXState = ZBS_RX_WAIT_VER;
|
||||
charindex = 0;
|
||||
memset(cmdbuffer, 0x00, 4);
|
||||
}
|
||||
if ((strncmp(cmdbuffer, "MAC>", 4) == 0)) {
|
||||
RXState = ZBS_RX_WAIT_MAC;
|
||||
charindex = 0;
|
||||
memset(cmdbuffer, 0x00, 4);
|
||||
}
|
||||
if ((strncmp(cmdbuffer, "ZCH>", 4) == 0)) {
|
||||
RXState = ZBS_RX_WAIT_CHANNEL;
|
||||
charindex = 0;
|
||||
memset(cmdbuffer, 0x00, 4);
|
||||
}
|
||||
if ((strncmp(cmdbuffer, "ZPW>", 4) == 0)) {
|
||||
RXState = ZBS_RX_WAIT_POWER;
|
||||
charindex = 0;
|
||||
memset(cmdbuffer, 0x00, 4);
|
||||
}
|
||||
if ((strncmp(cmdbuffer, "PEN>", 4) == 0)) {
|
||||
RXState = ZBS_RX_WAIT_PENDING;
|
||||
charindex = 0;
|
||||
memset(cmdbuffer, 0x00, 4);
|
||||
}
|
||||
if ((strncmp(cmdbuffer, "NOP>", 4) == 0)) {
|
||||
RXState = ZBS_RX_WAIT_NOP;
|
||||
charindex = 0;
|
||||
memset(cmdbuffer, 0x00, 4);
|
||||
}
|
||||
if ((strncmp(cmdbuffer, "TYP>", 4) == 0)) {
|
||||
RXState = ZBS_RX_WAIT_TYPE;
|
||||
charindex = 0;
|
||||
memset(cmdbuffer, 0x00, 4);
|
||||
}
|
||||
if (strncmp(cmdbuffer, "RES>", 4) == 0) {
|
||||
addRXQueue(NULL, 0, RX_CMD_RSET);
|
||||
}
|
||||
if (strncmp(cmdbuffer, "RQB>", 4) == 0) {
|
||||
RXState = ZBS_RX_BLOCK_REQUEST;
|
||||
charindex = 0;
|
||||
pktindex = 0;
|
||||
packetp = (uint8_t*)calloc(sizeof(struct espBlockRequest) + 8, 1);
|
||||
memset(cmdbuffer, 0x00, 4);
|
||||
}
|
||||
if (strncmp(cmdbuffer, "ADR>", 4) == 0) {
|
||||
RXState = ZBS_RX_WAIT_DATA_REQ;
|
||||
charindex = 0;
|
||||
pktindex = 0;
|
||||
packetp = (uint8_t*)calloc(sizeof(struct espAvailDataReq) + 8, 1);
|
||||
memset(cmdbuffer, 0x00, 4);
|
||||
}
|
||||
if (strncmp(cmdbuffer, "XFC>", 4) == 0) {
|
||||
RXState = ZBS_RX_WAIT_XFERCOMPLETE;
|
||||
pktindex = 0;
|
||||
packetp = (uint8_t*)calloc(sizeof(struct espXferComplete) + 8, 1);
|
||||
memset(cmdbuffer, 0x00, 4);
|
||||
}
|
||||
if (strncmp(cmdbuffer, "XTO>", 4) == 0) {
|
||||
RXState = ZBS_RX_WAIT_XFERTIMEOUT;
|
||||
pktindex = 0;
|
||||
packetp = (uint8_t*)calloc(sizeof(struct espXferComplete) + 8, 1);
|
||||
memset(cmdbuffer, 0x00, 4);
|
||||
}
|
||||
if (strncmp(cmdbuffer, "RDY>", 4) == 0) {
|
||||
addRXQueue(NULL, 0, RX_CMD_RDY);
|
||||
}
|
||||
break;
|
||||
case ZBS_RX_BLOCK_REQUEST:
|
||||
packetp[pktindex] = lastchar;
|
||||
pktindex++;
|
||||
if (pktindex == sizeof(struct espBlockRequest)) {
|
||||
addRXQueue(packetp, pktindex, RX_CMD_RQB);
|
||||
RXState = ZBS_RX_WAIT_HEADER;
|
||||
}
|
||||
break;
|
||||
case ZBS_RX_WAIT_XFERCOMPLETE:
|
||||
packetp[pktindex] = lastchar;
|
||||
pktindex++;
|
||||
if (pktindex == sizeof(struct espXferComplete)) {
|
||||
addRXQueue(packetp, pktindex, RX_CMD_XFC);
|
||||
RXState = ZBS_RX_WAIT_HEADER;
|
||||
}
|
||||
break;
|
||||
case ZBS_RX_WAIT_XFERTIMEOUT:
|
||||
packetp[pktindex] = lastchar;
|
||||
pktindex++;
|
||||
if (pktindex == sizeof(struct espXferComplete)) {
|
||||
addRXQueue(packetp, pktindex, RX_CMD_XTO);
|
||||
RXState = ZBS_RX_WAIT_HEADER;
|
||||
}
|
||||
break;
|
||||
case ZBS_RX_WAIT_DATA_REQ:
|
||||
packetp[pktindex] = lastchar;
|
||||
pktindex++;
|
||||
if (pktindex == sizeof(struct espAvailDataReq)) {
|
||||
addRXQueue(packetp, pktindex, RX_CMD_ADR);
|
||||
RXState = ZBS_RX_WAIT_HEADER;
|
||||
}
|
||||
break;
|
||||
case ZBS_RX_WAIT_VER:
|
||||
cmdbuffer[charindex] = lastchar;
|
||||
charindex++;
|
||||
if (charindex == 4) {
|
||||
charindex = 0;
|
||||
apInfo.version = (uint16_t)strtoul(cmdbuffer, NULL, 16);
|
||||
RXState = ZBS_RX_WAIT_HEADER;
|
||||
}
|
||||
break;
|
||||
case ZBS_RX_WAIT_MAC:
|
||||
cmdbuffer[charindex] = lastchar;
|
||||
charindex++;
|
||||
if (charindex == 2) {
|
||||
charindex = 0;
|
||||
apInfo.mac[pktindex] = (uint8_t)strtoul(cmdbuffer, NULL, 16);
|
||||
pktindex++;
|
||||
}
|
||||
if (pktindex == 8) {
|
||||
RXState = ZBS_RX_WAIT_HEADER;
|
||||
}
|
||||
break;
|
||||
case ZBS_RX_WAIT_CHANNEL:
|
||||
cmdbuffer[charindex] = lastchar;
|
||||
charindex++;
|
||||
if (charindex == 2) {
|
||||
RXState = ZBS_RX_WAIT_HEADER;
|
||||
apInfo.channel = (uint8_t)strtoul(cmdbuffer, NULL, 16);
|
||||
}
|
||||
break;
|
||||
case ZBS_RX_WAIT_POWER:
|
||||
cmdbuffer[charindex] = lastchar;
|
||||
charindex++;
|
||||
if (charindex == 2) {
|
||||
RXState = ZBS_RX_WAIT_HEADER;
|
||||
apInfo.power = (uint8_t)strtoul(cmdbuffer, NULL, 16);
|
||||
}
|
||||
break;
|
||||
case ZBS_RX_WAIT_PENDING:
|
||||
cmdbuffer[charindex] = lastchar;
|
||||
charindex++;
|
||||
if (charindex == 2) {
|
||||
RXState = ZBS_RX_WAIT_HEADER;
|
||||
apInfo.pending = (uint8_t)strtoul(cmdbuffer, NULL, 16);
|
||||
}
|
||||
break;
|
||||
case ZBS_RX_WAIT_NOP:
|
||||
cmdbuffer[charindex] = lastchar;
|
||||
charindex++;
|
||||
if (charindex == 2) {
|
||||
RXState = ZBS_RX_WAIT_HEADER;
|
||||
apInfo.nop = (uint8_t)strtoul(cmdbuffer, NULL, 16);
|
||||
}
|
||||
break;
|
||||
case ZBS_RX_WAIT_TYPE:
|
||||
cmdbuffer[charindex] = lastchar;
|
||||
charindex++;
|
||||
if (charindex == 2) {
|
||||
RXState = ZBS_RX_WAIT_HEADER;
|
||||
apInfo.type = (uint8_t)strtoul(cmdbuffer, NULL, 16);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
vTaskDelay(1 / portTICK_PERIOD_MS);
|
||||
} // end of while(1)
|
||||
}
|
||||
|
||||
void ShowAPInfo() {
|
||||
Serial.printf("+----------------------------+\n");
|
||||
Serial.printf("| AP Information - type %02X |\n", apInfo.type);
|
||||
Serial.printf("+----------------------------+\n");
|
||||
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);
|
||||
Serial.printf("+----------------------------+\n");
|
||||
}
|
||||
|
||||
void notifySegmentedFlash() {
|
||||
sendAPSegmentedData(apInfo.mac, (String) "Fl ash", 0x0800, false, true);
|
||||
vTaskDelay(2000 / portTICK_PERIOD_MS);
|
||||
#ifdef POWER_NO_SOFT_POWER
|
||||
sendAPSegmentedData(apInfo.mac, (String) "If done", 0x0800, false, true);
|
||||
vTaskDelay(2000 / portTICK_PERIOD_MS);
|
||||
sendAPSegmentedData(apInfo.mac, (String) "RE boot", 0x0800, false, true);
|
||||
vTaskDelay(1000 / portTICK_PERIOD_MS);
|
||||
#endif
|
||||
}
|
||||
void checkWaitPowerCycle() {
|
||||
// check if we should wait for a power cycle. If we do, try to inform the user the best we can, and hang.
|
||||
#ifdef POWER_NO_SOFT_POWER
|
||||
apInfo.isOnline = false;
|
||||
apInfo.state = AP_STATE_REQUIRED_POWER_CYCLE;
|
||||
// If we have no soft power control, we'll now wait until the device is power-cycled
|
||||
Serial.printf("Please power-cycle your AP/device\n");
|
||||
#ifdef HAS_RGB_LED
|
||||
showColorPattern(CRGB::Aqua, CRGB::Aqua, CRGB::Red);
|
||||
#endif
|
||||
while (1) {
|
||||
vTaskDelay(3000 / portTICK_PERIOD_MS);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
void segmentedShowIp() {
|
||||
IPAddress IP = WiFi.localIP();
|
||||
char temp[12];
|
||||
vTaskDelay(2000 / portTICK_PERIOD_MS);
|
||||
sendAPSegmentedData(apInfo.mac, (String) "IP Addr", 0x0200, true, true);
|
||||
vTaskDelay(2000 / portTICK_PERIOD_MS);
|
||||
sprintf(temp, "%03d IP %03d", IP[0], IP[1]);
|
||||
sendAPSegmentedData(apInfo.mac, (String)temp, 0x0200, true, true);
|
||||
vTaskDelay(2000 / portTICK_PERIOD_MS);
|
||||
sprintf(temp, "%03d IP %03d", IP[2], IP[3]);
|
||||
sendAPSegmentedData(apInfo.mac, (String)temp, 0x0200, true, true);
|
||||
vTaskDelay(2000 / portTICK_PERIOD_MS);
|
||||
}
|
||||
|
||||
bool bringAPOnline() {
|
||||
apInfo.isOnline = false;
|
||||
apInfo.state = AP_STATE_OFFLINE;
|
||||
APTagReset();
|
||||
vTaskDelay(500 / portTICK_PERIOD_MS);
|
||||
uint32_t bootTimeout = millis();
|
||||
bool APrdy = false;
|
||||
while ((!APrdy) && (millis() - bootTimeout < 10 * 1000)) {
|
||||
APrdy = sendPing();
|
||||
vTaskDelay(300 / portTICK_PERIOD_MS);
|
||||
}
|
||||
if (!APrdy) {
|
||||
return false;
|
||||
} else {
|
||||
apInfo.state = AP_STATE_COMING_ONLINE;
|
||||
sendChannelPower(&curChannel);
|
||||
vTaskDelay(200 / portTICK_PERIOD_MS);
|
||||
if (!sendGetInfo()) {
|
||||
apInfo.state = AP_STATE_OFFLINE;
|
||||
return false;
|
||||
}
|
||||
vTaskDelay(200 / portTICK_PERIOD_MS);
|
||||
apInfo.isOnline = true;
|
||||
apInfo.state = AP_STATE_ONLINE;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
void APTask(void* parameter) {
|
||||
xTaskCreate(rxCmdProcessor, "rxCmdProcessor", 4000, NULL, configMAX_PRIORITIES - 10, NULL);
|
||||
xTaskCreate(rxSerialTask, "rxSerialTask", 1750, NULL, configMAX_PRIORITIES - 4, NULL);
|
||||
|
||||
#if (AP_PROCESS_PORT == FLASHER_AP_PORT)
|
||||
AP_SERIAL_PORT.begin(115200, SERIAL_8N1, FLASHER_AP_RXD, FLASHER_AP_TXD);
|
||||
#endif
|
||||
#ifdef OPENEPAPERLINK_PCB
|
||||
#if (AP_PROCESS_PORT == FLASHER_EXT_PORT)
|
||||
AP_SERIAL_PORT.begin(115200, SERIAL_8N1, FLASHER_EXT_RXD, FLASHER_EXT_TXD);
|
||||
#endif
|
||||
#if (AP_PROCESS_PORT == FLASHER_ALTRADIO_PORT)
|
||||
AP_SERIAL_PORT.begin(115200, SERIAL_8N1, FLASHER_AP_RXD, FLASHER_AP_TXD);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
bringAPOnline();
|
||||
|
||||
if (checkForcedAPFlash()) {
|
||||
if (apInfo.type == SOLUM_SEG_UK && apInfo.isOnline) {
|
||||
Serial.printf("Showing some stuff on the segmented display about our intentions to flash...\n");
|
||||
notifySegmentedFlash();
|
||||
}
|
||||
Serial.printf("We're going to try to perform an 'AP forced flash' in\n");
|
||||
flashCountDown(10);
|
||||
Serial.printf("\nPerforming force flash of the AP\n");
|
||||
apInfo.isOnline = false;
|
||||
apInfo.state = AP_STATE_FLASHING;
|
||||
doForcedAPFlash();
|
||||
checkWaitPowerCycle();
|
||||
bringAPOnline();
|
||||
}
|
||||
|
||||
if (apInfo.isOnline) {
|
||||
// AP works!
|
||||
ShowAPInfo();
|
||||
|
||||
if (apInfo.type == SOLUM_SEG_UK) {
|
||||
apInfo.state = AP_STATE_COMING_ONLINE;
|
||||
segmentedShowIp();
|
||||
showAPSegmentedInfo(apInfo.mac, true);
|
||||
apInfo.state = AP_STATE_ONLINE;
|
||||
updateContent(apInfo.mac);
|
||||
}
|
||||
|
||||
uint16_t fsversion;
|
||||
fsversion = getAPUpdateVersion(apInfo.type);
|
||||
if ((fsversion) && (apInfo.version != fsversion)) {
|
||||
Serial.printf("Firmware version on LittleFS: %04X\n", fsversion);
|
||||
|
||||
Serial.printf("We're going to try to update the AP's FW in\n");
|
||||
flashCountDown(30);
|
||||
Serial.printf("\n");
|
||||
notifySegmentedFlash();
|
||||
apInfo.isOnline = false;
|
||||
apInfo.state = AP_STATE_FLASHING;
|
||||
if (doAPUpdate(apInfo.type)) {
|
||||
checkWaitPowerCycle();
|
||||
Serial.printf("Flash completed, let's try to boot the AP!\n");
|
||||
if (bringAPOnline()) {
|
||||
// AP works
|
||||
ShowAPInfo();
|
||||
setAPchannel();
|
||||
} else {
|
||||
Serial.printf("Failed to bring up the AP after flashing seemed successful... That's not supposed to happen!\n");
|
||||
Serial.printf("This can be caused by a bad AP firmware, failed or failing hardware, or the inability to fully power-cycle the AP\n");
|
||||
apInfo.state = AP_STATE_FAILED;
|
||||
#ifdef HAS_RGB_LED
|
||||
showColorPattern(CRGB::Red, CRGB::Yellow, CRGB::Red);
|
||||
#endif
|
||||
}
|
||||
} else {
|
||||
apInfo.state = AP_STATE_FAILED;
|
||||
checkWaitPowerCycle();
|
||||
Serial.println("Failed to update version on the AP :(\n");
|
||||
#ifdef HAS_RGB_LED
|
||||
showColorPattern(CRGB::Red, CRGB::Red, CRGB::Red);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
refreshAllPending();
|
||||
} else {
|
||||
#ifndef FLASH_TIMEOUT
|
||||
#define FLASH_TIMEOUT 30
|
||||
#endif
|
||||
|
||||
// AP unavailable, maybe time to flash?
|
||||
apInfo.isOnline = false;
|
||||
apInfo.state = AP_STATE_OFFLINE;
|
||||
Serial.println("I wasn't able to connect to a ZBS (AP) tag.\n");
|
||||
Serial.printf("This could be the first time this AP is booted and the AP-tag may be unflashed. We'll try to flash it!\n");
|
||||
Serial.printf("If this tag was previously flashed succesfully but this message still shows up, there's probably something wrong with the serial connections.\n");
|
||||
Serial.printf("The build of this firmware expects an AP tag with TXD/RXD on ESP32 pins %d and %d, does this match with your wiring?\n", FLASHER_AP_RXD, FLASHER_AP_TXD);
|
||||
Serial.printf("Performing firmware flash in about %d seconds!\n", FLASH_TIMEOUT);
|
||||
flashCountDown(FLASH_TIMEOUT);
|
||||
if (doAPFlash()) {
|
||||
checkWaitPowerCycle();
|
||||
if (bringAPOnline()) {
|
||||
// AP works
|
||||
ShowAPInfo();
|
||||
if (apInfo.type == SOLUM_SEG_UK) {
|
||||
segmentedShowIp();
|
||||
showAPSegmentedInfo(apInfo.mac, true);
|
||||
}
|
||||
refreshAllPending();
|
||||
} else {
|
||||
Serial.printf("Failed to bring up the AP after successful flashing... That's not supposed to happen!\n");
|
||||
Serial.printf("This generally means that the flasher connections (MISO/MOSI/CLK/RESET/CS) are okay,\n");
|
||||
Serial.printf("but we can't (yet) talk to the AP over serial lines. Verify the pins mentioned above.\n\n");
|
||||
|
||||
#ifndef POWER_NO_SOFT_POWER
|
||||
Serial.printf("The firmware you're using expects soft power control over the AP tag; if it can't\n");
|
||||
Serial.printf("power-cycle the AP-tag using GPIO pin %d, this can cause this very same issue.\n", APpowerPins[0]);
|
||||
#endif
|
||||
|
||||
#ifdef HAS_RGB_LED
|
||||
showColorPattern(CRGB::Red, CRGB::Yellow, CRGB::Red);
|
||||
#endif
|
||||
apInfo.isOnline = false;
|
||||
apInfo.state = AP_STATE_FAILED;
|
||||
}
|
||||
} else {
|
||||
// failed to flash
|
||||
#ifdef HAS_RGB_LED
|
||||
showColorPattern(CRGB::Red, CRGB::Red, CRGB::Red);
|
||||
#endif
|
||||
apInfo.isOnline = false;
|
||||
apInfo.state = AP_STATE_FAILED;
|
||||
Serial.println("Failed to flash the AP :(");
|
||||
Serial.println("Seems like you're running into some issues with the wiring, or (very small chance) the tag itself");
|
||||
Serial.println("This ESP32-build expects the following pins connected to the ZBS243:");
|
||||
Serial.println("--- ZBS243 based tag ESP32 ---");
|
||||
Serial.printf(" TXD ---------------- %02d\n", FLASHER_AP_RXD);
|
||||
Serial.printf(" RXD ---------------- %02d\n", FLASHER_AP_TXD);
|
||||
Serial.printf(" CS/SS ---------------- %02d\n", FLASHER_AP_SS);
|
||||
Serial.printf(" MOSI ---------------- %02d\n", FLASHER_AP_MOSI);
|
||||
Serial.printf(" MISO ---------------- %02d\n", FLASHER_AP_MISO);
|
||||
Serial.printf(" CLK ---------------- %02d\n", FLASHER_AP_CLK);
|
||||
Serial.printf(" RSET ---------------- %02d\n", FLASHER_AP_RESET);
|
||||
#ifdef POWER_NO_SOFT_POWER
|
||||
Serial.printf("Your firmware is configured without soft power control. This means you'll have to manually power-cycle the tag after flashing.\n");
|
||||
#else
|
||||
Serial.printf(" POWER ---------------- %02d\n", APpowerPins[0]);
|
||||
#endif
|
||||
Serial.println("Please verify your wiring and try again!");
|
||||
}
|
||||
#ifdef HAS_SDCARD
|
||||
if (SD_CARD_CLK == FLASHER_AP_CLK ||
|
||||
SD_CARD_MISO == FLASHER_AP_MISO ||
|
||||
SD_CARD_MOSI == FLASHER_AP_MOSI) {
|
||||
Serial.println("Reseting in 30 seconds to restore SPI state!\n");
|
||||
flashCountDown(30);
|
||||
ESP.restart();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
uint8_t attempts = 0;
|
||||
while (1) {
|
||||
if ((apInfo.isOnline) && (millis() - lastAPActivity > AP_ACTIVITY_MAX_INTERVAL)) {
|
||||
bool reply = sendPing();
|
||||
if (!reply) {
|
||||
attempts++;
|
||||
} else {
|
||||
attempts = 0;
|
||||
}
|
||||
if (attempts > 5) {
|
||||
apInfo.state = AP_STATE_WAIT_RESET;
|
||||
apInfo.isOnline = false;
|
||||
if (!bringAPOnline()) {
|
||||
// tried to reset the AP, but we failed... Maybe the AP-Tag died?
|
||||
apInfo.state = AP_STATE_FAILED;
|
||||
#ifdef HAS_RGB_LED
|
||||
showColorPattern(CRGB::Yellow, CRGB::Yellow, CRGB::Red);
|
||||
#endif
|
||||
} else {
|
||||
apInfo.state = AP_STATE_ONLINE;
|
||||
apInfo.isOnline = true;
|
||||
attempts = 0;
|
||||
refreshAllPending();
|
||||
}
|
||||
}
|
||||
}
|
||||
vTaskDelay(1000 / portTICK_PERIOD_MS);
|
||||
}
|
||||
}
|
||||
118
ESP32_AP-Flasher/src/serialconsole.cpp
Normal file
118
ESP32_AP-Flasher/src/serialconsole.cpp
Normal file
@@ -0,0 +1,118 @@
|
||||
#include "serialconsole.h"
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
#include "USB.h"
|
||||
|
||||
QueueHandle_t consoleCmdQueue;
|
||||
TaskHandle_t consoleTaskHandle;
|
||||
extern USBCDC USBSerial;
|
||||
|
||||
struct consoleCommand {
|
||||
uint8_t command = 0;
|
||||
uint8_t len = 0;
|
||||
uint8_t* data = nullptr;
|
||||
};
|
||||
|
||||
void consoleStopTask() {
|
||||
if (consoleTaskHandle) vTaskDelete(consoleTaskHandle);
|
||||
consoleTaskHandle = NULL;
|
||||
}
|
||||
/*
|
||||
.in>1B 5B 3C 30 3B 33 39 3B 39 4D
|
||||
,in>1B 5B 3C 30 3B 33 39 3B 39 6D
|
||||
,in>1B 5B 3C 32 3B 33 39 3B 39 4D
|
||||
,in>1B 5B 3C 32 3B 33 39 3B 39 6D
|
||||
|
||||
in>1B 5B 3C 30 3B 32 38 3B 31 32 4D
|
||||
in>1B 5B 3C 30 3B 32 38 3B 31 32 6D
|
||||
|
||||
in>1B 5B 3C 32 3B 32 38 3B 31 32 4D
|
||||
in>1B 5B 3C 32 3B 32 38 3B 31 32 6D
|
||||
|
||||
in>1B 5B 3C 36 34 3B 32 39 3B 31 32 4D
|
||||
in>1B 5B 3C 36 35 3B 32 39 3B 31 32 4D
|
||||
*/
|
||||
|
||||
bool escapeCommandComplete(struct consoleCommand* cmd) {
|
||||
return true;
|
||||
}
|
||||
|
||||
void consoleUartHandler(uint8_t* data, uint8_t len) {
|
||||
static struct consoleCommand* cmd = nullptr;
|
||||
static bool commandStarted = false;
|
||||
|
||||
while (len--) {
|
||||
uint8_t usbbyte = *(data++);
|
||||
|
||||
if (cmd == nullptr) {
|
||||
cmd = new struct consoleCommand;
|
||||
cmd->data = (uint8_t*)calloc(65, 1);
|
||||
cmd->len = 0;
|
||||
}
|
||||
|
||||
// check if we've started a command in this byte
|
||||
if ((!commandStarted) && (usbbyte == 0x1B)) {
|
||||
commandStarted = true;
|
||||
if (cmd->len != 0) {
|
||||
BaseType_t queuestatus = xQueueSend(consoleCmdQueue, &cmd, 0);
|
||||
if (queuestatus == pdFALSE) {
|
||||
if (cmd->data != nullptr) free(cmd->data);
|
||||
delete cmd;
|
||||
}
|
||||
cmd = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
if (cmd == nullptr) {
|
||||
cmd = new struct consoleCommand;
|
||||
cmd->data = (uint8_t*)calloc(65, 1);
|
||||
cmd->len = 0;
|
||||
}
|
||||
|
||||
cmd->data[cmd->len++] = usbbyte;
|
||||
cmd->len %= 64;
|
||||
|
||||
if (commandStarted) {
|
||||
if (escapeCommandComplete(cmd) || cmd->len == 0x00) {
|
||||
BaseType_t queuestatus = xQueueSend(consoleCmdQueue, &cmd, 0);
|
||||
if (queuestatus == pdFALSE) {
|
||||
if (cmd->data != nullptr) free(cmd->data);
|
||||
delete cmd;
|
||||
}
|
||||
commandStarted = false;
|
||||
cmd = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!commandStarted && cmd != nullptr) {
|
||||
BaseType_t queuestatus = xQueueSend(consoleCmdQueue, &cmd, 0);
|
||||
if (queuestatus == pdFALSE) {
|
||||
if (cmd->data != nullptr) free(cmd->data);
|
||||
delete cmd;
|
||||
}
|
||||
cmd = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void consoleTask(void* parameter) {
|
||||
struct consoleCommand* cmd;
|
||||
|
||||
USBSerial.print("\e[?1000;1006;1015h"); // works
|
||||
|
||||
while (true) {
|
||||
BaseType_t queuereceive = xQueueReceive(consoleCmdQueue, &cmd, 1500 / portTICK_PERIOD_MS);
|
||||
if (queuereceive == pdTRUE) {
|
||||
uint8_t c = 0;
|
||||
Serial.printf("queue>");
|
||||
while (cmd->len--) {
|
||||
Serial.printf(" %02X", cmd->data[c]);
|
||||
c++;
|
||||
}
|
||||
if (cmd->data != nullptr) free(cmd->data);
|
||||
delete cmd;
|
||||
Serial.printf("\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
215
ESP32_AP-Flasher/src/storage.cpp
Normal file
215
ESP32_AP-Flasher/src/storage.cpp
Normal file
@@ -0,0 +1,215 @@
|
||||
#include "storage.h"
|
||||
|
||||
#ifdef HAS_SDCARD
|
||||
#include "FS.h"
|
||||
#include "SD.h"
|
||||
#include "SPI.h"
|
||||
#endif
|
||||
|
||||
#include "LittleFS.h"
|
||||
|
||||
DynStorage::DynStorage() : isInited(0) {}
|
||||
|
||||
static void initLittleFS() {
|
||||
LittleFS.begin();
|
||||
contentFS = &LittleFS;
|
||||
}
|
||||
|
||||
#ifdef HAS_SDCARD
|
||||
static SPIClass* spi;
|
||||
|
||||
static void initSDCard() {
|
||||
uint8_t spi_bus = VSPI;
|
||||
|
||||
// SD.begin and spi.begin are allocating memory so we dont want to do that
|
||||
if(!spi) {
|
||||
spi = new SPIClass(spi_bus);
|
||||
spi->begin(SD_CARD_CLK, SD_CARD_MISO, SD_CARD_MOSI, SD_CARD_SS);
|
||||
|
||||
bool res = SD.begin(SD_CARD_SS, *spi, 40000000);
|
||||
if (!res) {
|
||||
Serial.println("Card Mount Failed");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t cardType = SD.cardType();
|
||||
|
||||
if (cardType == CARD_NONE) {
|
||||
Serial.println("No SD card attached");
|
||||
return;
|
||||
}
|
||||
|
||||
contentFS = &SD;
|
||||
}
|
||||
#endif
|
||||
|
||||
size_t DynStorage::freeSpace(){
|
||||
this->begin();
|
||||
#ifdef HAS_SDCARD
|
||||
return SD.totalBytes() - SD.usedBytes();
|
||||
#endif
|
||||
return LittleFS.totalBytes() - LittleFS.usedBytes();
|
||||
}
|
||||
|
||||
void copyFile(File in, File out) {
|
||||
Serial.print("Copying ");
|
||||
Serial.print(in.path());
|
||||
Serial.print(" to ");
|
||||
Serial.println(out.path());
|
||||
|
||||
size_t n;
|
||||
uint8_t buf[64];
|
||||
while ((n = in.read(buf, sizeof(buf))) > 0) {
|
||||
out.write(buf, n);
|
||||
}
|
||||
}
|
||||
|
||||
void copyBetweenFS(FS& sourceFS, const char* source_path, FS& targetFS) {
|
||||
File root = sourceFS.open(source_path);
|
||||
char next_path[128];
|
||||
|
||||
if (root.isDirectory()) {
|
||||
if (!contentFS->exists(root.path())) {
|
||||
if (!contentFS->mkdir(root.path())) {
|
||||
Serial.print("Failed to create directory ");
|
||||
Serial.println(root.path());
|
||||
return;
|
||||
}
|
||||
}
|
||||
File file = root.openNextFile();
|
||||
while (file) {
|
||||
if (file.isDirectory()) {
|
||||
sprintf(next_path, "%s/%s\0", root.path(), file.path());
|
||||
|
||||
copyBetweenFS(sourceFS, file.path(), targetFS);
|
||||
} else {
|
||||
File target = contentFS->open(file.path(), "w");
|
||||
if (target) {
|
||||
copyFile(file, target);
|
||||
target.close();
|
||||
file.close();
|
||||
} else {
|
||||
Serial.print("Couldn't create high target file");
|
||||
Serial.println(file.path());
|
||||
return;
|
||||
}
|
||||
}
|
||||
file = root.openNextFile();
|
||||
}
|
||||
} else {
|
||||
File target = contentFS->open(root.path(), "w");
|
||||
if (target) {
|
||||
copyFile(root, target);
|
||||
} else {
|
||||
Serial.print("Couldn't create target file ");
|
||||
Serial.println(root.path());
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef HAS_SDCARD
|
||||
void copyIfNeeded(const char* path) {
|
||||
if (!contentFS->exists(path) && LittleFS.exists(path)) {
|
||||
Serial.printf("SDCard does not contain %s, littleFS does, copying\n", path);
|
||||
copyBetweenFS(LittleFS, path, *contentFS);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void DynStorage::begin() {
|
||||
initLittleFS();
|
||||
|
||||
#ifdef HAS_SDCARD
|
||||
initSDCard();
|
||||
|
||||
copyIfNeeded("/index.html");
|
||||
copyIfNeeded("/fonts");
|
||||
copyIfNeeded("/www");
|
||||
copyIfNeeded("/AP_FW_Pack.bin");
|
||||
copyIfNeeded("/tag_md5_db.json");
|
||||
copyIfNeeded("/update_actions.json");
|
||||
copyIfNeeded("/content_template.json");
|
||||
#endif
|
||||
|
||||
if (!contentFS->exists("/current")) {
|
||||
contentFS->mkdir("/current");
|
||||
}
|
||||
if (!contentFS->exists("/temp")) {
|
||||
contentFS->mkdir("/temp");
|
||||
}
|
||||
}
|
||||
|
||||
void DynStorage::end() {
|
||||
#ifdef HAS_SDCARD
|
||||
initLittleFS();
|
||||
if (SD_CARD_CLK == FLASHER_AP_CLK ||
|
||||
SD_CARD_MISO == FLASHER_AP_MISO ||
|
||||
SD_CARD_MOSI == FLASHER_AP_MOSI) {
|
||||
Serial.println("Tearing down SD card connection");
|
||||
|
||||
copyBetweenFS(*contentFS, "/tag_md5_db.json", LittleFS);
|
||||
copyBetweenFS(*contentFS, "/AP_FW_Pack.bin", LittleFS);
|
||||
if (contentFS->exists("/AP_force_flash.bin")) {
|
||||
copyBetweenFS(*contentFS, "/AP_force_flash.bin", LittleFS);
|
||||
contentFS->remove("/AP_force_flash.bin");
|
||||
}
|
||||
Serial.println("Swapping to LittleFS");
|
||||
|
||||
contentFS = &LittleFS;
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
void listDir(fs::FS& fs, const char* dirname, uint8_t levels) {
|
||||
Storage.begin();
|
||||
// Print blank line on screen
|
||||
Serial.printf(" \n ");
|
||||
|
||||
Serial.printf("Listing directory: %s\n", dirname);
|
||||
|
||||
File root = fs.open(dirname);
|
||||
if (!root) {
|
||||
Serial.println("Failed to open directory");
|
||||
return;
|
||||
}
|
||||
if (!root.isDirectory()) {
|
||||
Serial.println("Not a directory");
|
||||
return;
|
||||
}
|
||||
|
||||
File file = root.openNextFile();
|
||||
while (file) {
|
||||
if (!strcmp("System Volume Information", file.name())) {
|
||||
file = root.openNextFile();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (file.isDirectory()) {
|
||||
Serial.print(" DIR : ");
|
||||
Serial.println(file.name());
|
||||
if (levels) {
|
||||
listDir(fs, file.path(), levels - 1);
|
||||
}
|
||||
Serial.println();
|
||||
} else {
|
||||
Serial.print(" FILE: ");
|
||||
Serial.print(file.name());
|
||||
Serial.print(" SIZE: ");
|
||||
Serial.println(file.size());
|
||||
}
|
||||
file = root.openNextFile();
|
||||
}
|
||||
}
|
||||
|
||||
void DynStorage::listFiles() {
|
||||
listDir(LittleFS, "/", 1);
|
||||
#ifdef HAS_SDCARD
|
||||
listDir(*contentFS, "/", 1);
|
||||
#endif
|
||||
}
|
||||
|
||||
fs::FS* contentFS;
|
||||
DynStorage Storage;
|
||||
80
ESP32_AP-Flasher/src/system.cpp
Normal file
80
ESP32_AP-Flasher/src/system.cpp
Normal file
@@ -0,0 +1,80 @@
|
||||
#include "system.h"
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <FS.h>
|
||||
|
||||
#include "storage.h"
|
||||
|
||||
void init_time() {
|
||||
struct tm timeinfo;
|
||||
while (true) {
|
||||
if (!getLocalTime(&timeinfo)) {
|
||||
Serial.println("Waiting for valid time from NTP-server");
|
||||
vTaskDelay(1000 / portTICK_PERIOD_MS);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void logLine(char* buffer) {
|
||||
logLine(String(buffer));
|
||||
}
|
||||
|
||||
void logLine(String text) {
|
||||
time_t now;
|
||||
time(&now);
|
||||
|
||||
char timeStr[24];
|
||||
strftime(timeStr, sizeof(timeStr), "%Y-%m-%d %H:%M:%S ", localtime(&now));
|
||||
|
||||
File logFile = contentFS->open("/log.txt", "a");
|
||||
if (logFile) {
|
||||
logFile.print(timeStr);
|
||||
logFile.println(text);
|
||||
logFile.close();
|
||||
}
|
||||
}
|
||||
|
||||
void logStartUp() {
|
||||
esp_reset_reason_t resetReason = esp_reset_reason();
|
||||
|
||||
String logEntry = "Reboot. Reason: ";
|
||||
switch (resetReason) {
|
||||
case ESP_RST_POWERON:
|
||||
logEntry += "Power-on";
|
||||
break;
|
||||
case ESP_RST_EXT:
|
||||
logEntry += "External";
|
||||
break;
|
||||
case ESP_RST_SW:
|
||||
logEntry += "Software";
|
||||
break;
|
||||
case ESP_RST_PANIC:
|
||||
logEntry += "Panic";
|
||||
break;
|
||||
case ESP_RST_INT_WDT:
|
||||
logEntry += "Watchdog";
|
||||
break;
|
||||
case ESP_RST_TASK_WDT:
|
||||
logEntry += "Task Watchdog";
|
||||
break;
|
||||
case ESP_RST_WDT:
|
||||
logEntry += "Other Watchdog";
|
||||
break;
|
||||
case ESP_RST_DEEPSLEEP:
|
||||
logEntry += "Deep Sleep";
|
||||
break;
|
||||
case ESP_RST_BROWNOUT:
|
||||
logEntry += "Brownout";
|
||||
break;
|
||||
case ESP_RST_SDIO:
|
||||
logEntry += "SDIO";
|
||||
break;
|
||||
default:
|
||||
logEntry += "Unknown";
|
||||
break;
|
||||
}
|
||||
|
||||
logLine(logEntry);
|
||||
}
|
||||
@@ -2,29 +2,37 @@
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <ArduinoJson.h>
|
||||
|
||||
#include <FS.h>
|
||||
#include <vector>
|
||||
|
||||
#include "LittleFS.h"
|
||||
#include "storage.h"
|
||||
#include "language.h"
|
||||
|
||||
std::vector<tagRecord*> tagDB;
|
||||
|
||||
tagRecord* tagRecord::findByMAC(uint8_t mac[6]) {
|
||||
Config config;
|
||||
// SemaphoreHandle_t tagDBOwner;
|
||||
|
||||
tagRecord* tagRecord::findByMAC(uint8_t mac[8]) {
|
||||
for (int16_t c = 0; c < tagDB.size(); c++) {
|
||||
tagRecord* tag = nullptr;
|
||||
tag = tagDB.at(c);
|
||||
if (memcmp(tag->mac, mac, 6) == 0) {
|
||||
if (memcmp(tag->mac, mac, 8) == 0) {
|
||||
return tag;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool deleteRecord(uint8_t mac[6]) {
|
||||
bool deleteRecord(uint8_t mac[8]) {
|
||||
for (int16_t c = 0; c < tagDB.size(); c++) {
|
||||
tagRecord* tag = nullptr;
|
||||
tag = tagDB.at(c);
|
||||
if (memcmp(tag->mac, mac, 6) == 0) {
|
||||
if (memcmp(tag->mac, mac, 8) == 0) {
|
||||
if (tag->data != nullptr) {
|
||||
free(tag->data);
|
||||
}
|
||||
tag->data = nullptr;
|
||||
delete tagDB[c];
|
||||
tagDB.erase(tagDB.begin() + c);
|
||||
return true;
|
||||
@@ -33,7 +41,28 @@ bool deleteRecord(uint8_t mac[6]) {
|
||||
return false;
|
||||
}
|
||||
|
||||
String tagDBtoJson(uint8_t mac[6], uint8_t startPos) {
|
||||
void mac2hex(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]);
|
||||
}
|
||||
|
||||
bool hex2mac(const String& hexString, uint8_t* mac) {
|
||||
size_t hexLength = hexString.length();
|
||||
if (hexLength != 12 && hexLength != 16) {
|
||||
return false;
|
||||
}
|
||||
if (hexLength / 2 == 6) {
|
||||
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);
|
||||
} 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);
|
||||
}
|
||||
}
|
||||
|
||||
String tagDBtoJson(uint8_t mac[8], uint8_t startPos) {
|
||||
DynamicJsonDocument doc(2500);
|
||||
JsonArray tags = doc.createNestedArray("tags");
|
||||
|
||||
@@ -43,7 +72,7 @@ String tagDBtoJson(uint8_t mac[6], uint8_t startPos) {
|
||||
|
||||
bool select = false;
|
||||
if (mac) {
|
||||
if (memcmp(taginfo->mac, mac, 6) == 0) {
|
||||
if (memcmp(taginfo->mac, mac, 8) == 0) {
|
||||
select = true;
|
||||
}
|
||||
} else {
|
||||
@@ -65,10 +94,9 @@ String tagDBtoJson(uint8_t mac[6], uint8_t startPos) {
|
||||
}
|
||||
|
||||
void fillNode(JsonObject &tag, tagRecord* &taginfo) {
|
||||
char buffer[16];
|
||||
sprintf(buffer, "%02X%02X%02X%02X%02X%02X\0", taginfo->mac[0], taginfo->mac[1], taginfo->mac[2], taginfo->mac[3], taginfo->mac[4], taginfo->mac[5]);
|
||||
tag["mac"] = (String)buffer;
|
||||
|
||||
char hexmac[17];
|
||||
mac2hex(taginfo->mac, hexmac);
|
||||
tag["mac"] = String(hexmac);
|
||||
char hex[33];
|
||||
for (uint8_t i = 0; i < 16; i++) {
|
||||
sprintf(hex + (i * 2), "%02x", taginfo->md5[i]);
|
||||
@@ -88,6 +116,9 @@ void fillNode(JsonObject &tag, tagRecord* &taginfo) {
|
||||
tag["wakeupReason"] = taginfo->wakeupReason;
|
||||
tag["capabilities"] = taginfo->capabilities;
|
||||
tag["modecfgjson"] = taginfo->modeConfigJson;
|
||||
tag["isexternal"] = taginfo->isExternal;
|
||||
tag["rotate"] = taginfo->rotate;
|
||||
tag["lut"] = taginfo->lut;
|
||||
}
|
||||
|
||||
void saveDB(String filename) {
|
||||
@@ -95,8 +126,8 @@ void saveDB(String filename) {
|
||||
|
||||
long t = millis();
|
||||
|
||||
LittleFS.begin();
|
||||
fs::File file = LittleFS.open(filename, "w");
|
||||
Storage.begin();
|
||||
fs::File file = contentFS->open(filename, "w");
|
||||
if (!file) {
|
||||
Serial.println("saveDB: Failed to open file");
|
||||
return;
|
||||
@@ -114,7 +145,7 @@ void saveDB(String filename) {
|
||||
if (c > 0) {
|
||||
file.write(',');
|
||||
}
|
||||
serializeJson(doc, file);
|
||||
serializeJsonPretty(doc, file);
|
||||
}
|
||||
file.write(']');
|
||||
|
||||
@@ -127,11 +158,11 @@ void saveDB(String filename) {
|
||||
void loadDB(String filename) {
|
||||
StaticJsonDocument<1000> doc;
|
||||
|
||||
Serial.println("start reading DB from file");
|
||||
Serial.println("reading DB from file");
|
||||
long t = millis();
|
||||
|
||||
LittleFS.begin();
|
||||
fs::File readfile = LittleFS.open(filename, "r");
|
||||
Storage.begin();
|
||||
fs::File readfile = contentFS->open(filename, "r");
|
||||
if (!readfile) {
|
||||
Serial.println("loadDB: Failed to open file");
|
||||
return;
|
||||
@@ -147,8 +178,8 @@ void loadDB(String filename) {
|
||||
if (!err) {
|
||||
JsonObject tag = doc[0];
|
||||
String dst = tag["mac"].as<String>();
|
||||
uint8_t mac[12];
|
||||
if (sscanf(dst.c_str(), "%02X%02X%02X%02X%02X%02X", &mac[0], &mac[1], &mac[2], &mac[3], &mac[4], &mac[5]) == 6) {
|
||||
uint8_t mac[8];
|
||||
if (hex2mac(dst, mac)) {
|
||||
tagRecord* taginfo = nullptr;
|
||||
taginfo = tagRecord::findByMAC(mac);
|
||||
if (taginfo == nullptr) {
|
||||
@@ -180,6 +211,9 @@ void loadDB(String filename) {
|
||||
taginfo->wakeupReason = tag["wakeupReason"];
|
||||
taginfo->capabilities = tag["capabilities"];
|
||||
taginfo->modeConfigJson = tag["modecfgjson"].as<String>();
|
||||
taginfo->isExternal = tag["isexternal"].as<bool>();
|
||||
taginfo->rotate = tag["rotate"] | 0;
|
||||
taginfo->lut = tag["lut"] | 0;
|
||||
}
|
||||
} else {
|
||||
Serial.print(F("deserializeJson() failed: "));
|
||||
@@ -191,8 +225,74 @@ void loadDB(String filename) {
|
||||
}
|
||||
|
||||
readfile.close();
|
||||
Serial.println(millis() - t);
|
||||
Serial.println("finished reading file");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void destroyDB() {
|
||||
Serial.println("destoying DB");
|
||||
Serial.printf("before, free heap: %d\n", ESP.getFreeHeap());
|
||||
for (int16_t c = 0; c < tagDB.size(); c++) {
|
||||
tagRecord* tag = nullptr;
|
||||
tag = tagDB.at(c);
|
||||
if (tag->data != nullptr) {
|
||||
free(tag->data);
|
||||
}
|
||||
tag->data = nullptr;
|
||||
delete tagDB[c];
|
||||
tagDB.erase(tagDB.begin() + c);
|
||||
}
|
||||
Serial.printf("after, free heap: %d\n", ESP.getFreeHeap());
|
||||
}
|
||||
|
||||
uint8_t getTagCount() {
|
||||
uint8_t tagcount = 0;
|
||||
for (int16_t c = 0; c < tagDB.size(); c++) {
|
||||
tagRecord* taginfo = nullptr;
|
||||
taginfo = tagDB.at(c);
|
||||
if (taginfo->isExternal == false) tagcount++;
|
||||
}
|
||||
return tagcount;
|
||||
}
|
||||
|
||||
void clearPending(tagRecord* taginfo) {
|
||||
taginfo->filename = String();
|
||||
if (taginfo->data != nullptr) {
|
||||
free(taginfo->data);
|
||||
taginfo->data = nullptr;
|
||||
}
|
||||
taginfo->pending = false;
|
||||
}
|
||||
|
||||
void initAPconfig() {
|
||||
Storage.begin();
|
||||
DynamicJsonDocument APconfig(500);
|
||||
File configFile = contentFS->open("/current/apconfig.json", "r");
|
||||
if (configFile) {
|
||||
DeserializationError error = deserializeJson(APconfig, configFile);
|
||||
if (error) {
|
||||
configFile.close();
|
||||
Serial.println("failed to read apconfig.json. Using default config");
|
||||
Serial.println(error.c_str());
|
||||
}
|
||||
configFile.close();
|
||||
}
|
||||
config.channel = APconfig["channel"] | 0;
|
||||
if (APconfig["alias"]) strlcpy(config.alias, APconfig["alias"], sizeof(config.alias));
|
||||
config.led = APconfig["led"] | 255;
|
||||
config.language = APconfig["language"] | getDefaultLanguage();
|
||||
config.maxsleep = APconfig["maxsleep"] | 10;
|
||||
config.stopsleep = APconfig["stopsleep"] | 1;
|
||||
}
|
||||
|
||||
void saveAPconfig() {
|
||||
fs::File configFile = contentFS->open("/current/apconfig.json", "w");
|
||||
DynamicJsonDocument APconfig(500);
|
||||
APconfig["channel"] = config.channel;
|
||||
APconfig["alias"] = config.alias;
|
||||
APconfig["led"] = config.led;
|
||||
APconfig["language"] = config.language;
|
||||
APconfig["maxsleep"] = config.maxsleep;
|
||||
APconfig["stopsleep"] = config.stopsleep;
|
||||
serializeJsonPretty(APconfig, configFile);
|
||||
configFile.close();
|
||||
}
|
||||
186
ESP32_AP-Flasher/src/udp.cpp
Normal file
186
ESP32_AP-Flasher/src/udp.cpp
Normal file
@@ -0,0 +1,186 @@
|
||||
#include <Arduino.h>
|
||||
#include <WiFi.h>
|
||||
|
||||
#include "AsyncUDP.h"
|
||||
#include "commstructs.h"
|
||||
#include "newproto.h"
|
||||
#include "tag_db.h"
|
||||
#include "web.h"
|
||||
#include "serialap.h"
|
||||
#include "udp.h"
|
||||
|
||||
#define UDPIP IPAddress(239, 10, 0, 1)
|
||||
#define UDPPORT 16033
|
||||
|
||||
UDPcomm udpsync;
|
||||
|
||||
extern uint8_t channelList[6];
|
||||
extern espSetChannelPower curChannel;
|
||||
|
||||
void init_udp() {
|
||||
udpsync.init();
|
||||
}
|
||||
|
||||
UDPcomm::UDPcomm() {
|
||||
// Constructor
|
||||
}
|
||||
|
||||
UDPcomm::~UDPcomm() {
|
||||
// Destructor
|
||||
}
|
||||
|
||||
void UDPcomm::init() {
|
||||
if (udp.listenMulticast(UDPIP, UDPPORT)) {
|
||||
udp.onPacket([this](AsyncUDPPacket packet) {
|
||||
if (packet.remoteIP() != WiFi.localIP()) {
|
||||
this->processPacket(packet);
|
||||
}
|
||||
});
|
||||
}
|
||||
setAPchannel();
|
||||
}
|
||||
|
||||
void UDPcomm::processPacket(AsyncUDPPacket packet) {
|
||||
|
||||
if (config.runStatus == RUNSTATUS_STOP) return;
|
||||
|
||||
switch (packet.data()[0]) {
|
||||
case PKT_AVAIL_DATA_INFO: {
|
||||
espAvailDataReq* adr = (espAvailDataReq*)&packet.data()[1];
|
||||
processDataReq(adr, false);
|
||||
break;
|
||||
}
|
||||
case PKT_XFER_COMPLETE: {
|
||||
espXferComplete* xfc = (espXferComplete*)&packet.data()[1];
|
||||
processXferComplete(xfc, false);
|
||||
break;
|
||||
}
|
||||
case PKT_XFER_TIMEOUT: {
|
||||
espXferComplete* xfc = (espXferComplete*)&packet.data()[1];
|
||||
processXferTimeout(xfc, false);
|
||||
break;
|
||||
}
|
||||
case PKT_AVAIL_DATA_REQ: {
|
||||
pendingData* pending = (pendingData*)&packet.data()[1];
|
||||
prepareExternalDataAvail(pending, packet.remoteIP());
|
||||
break;
|
||||
}
|
||||
case PKT_APLIST_REQ: {
|
||||
IPAddress senderIP = packet.remoteIP();
|
||||
|
||||
APlist APitem;
|
||||
APitem.src = WiFi.localIP();
|
||||
strcpy(APitem.alias, config.alias);
|
||||
APitem.channelId = curChannel.channel;
|
||||
APitem.tagCount = getTagCount();
|
||||
APitem.version = apInfo.version;
|
||||
|
||||
uint8_t buffer[sizeof(struct APlist) + 1];
|
||||
buffer[0] = PKT_APLIST_REPLY;
|
||||
memcpy(buffer + 1, &APitem, sizeof(struct APlist));
|
||||
udp.writeTo(buffer, sizeof(buffer), senderIP, UDPPORT);
|
||||
break;
|
||||
}
|
||||
case PKT_APLIST_REPLY: {
|
||||
APlist* APreply = (APlist*)&packet.data()[1];
|
||||
//remove active channel from list
|
||||
for (int i = 0; i < 6; i++) {
|
||||
if (channelList[i] == APreply->channelId) channelList[i] = 0;
|
||||
}
|
||||
wsSendAPitem(APreply);
|
||||
break;
|
||||
}
|
||||
case PKT_TAGINFO: {
|
||||
uint16_t syncversion = (packet.data()[2] << 8) | packet.data()[1];
|
||||
if (syncversion != SYNC_VERSION) {
|
||||
Serial.println("Got a packet from " + packet.remoteIP().toString() + " with mismatched udp sync version. Update firmware!");
|
||||
} else {
|
||||
TagInfo* taginfoitem = (TagInfo*)&packet.data()[1];
|
||||
updateTaginfoitem(taginfoitem);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void autoselect(void* pvParameters) {
|
||||
// reset channel list
|
||||
uint8_t values[] = {11, 15, 20, 25, 26, 27};
|
||||
memcpy(channelList, values, sizeof(values));
|
||||
// wait 5s for channelList to collect all AP's
|
||||
vTaskDelay(5000 / portTICK_PERIOD_MS);
|
||||
|
||||
curChannel.channel = 0;
|
||||
for (int i = 0; i < 6; i++) {
|
||||
if (channelList[i] > 0) {
|
||||
curChannel.channel = channelList[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (curChannel.channel == 0) {
|
||||
curChannel.channel = 11;
|
||||
}
|
||||
config.channel = curChannel.channel;
|
||||
do {
|
||||
vTaskDelay(1000 / portTICK_PERIOD_MS);
|
||||
} while (!apInfo.isOnline);
|
||||
|
||||
sendChannelPower(&curChannel);
|
||||
saveAPconfig();
|
||||
|
||||
vTaskDelay(1000 / portTICK_PERIOD_MS);
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
void UDPcomm::getAPList() {
|
||||
APlist APitem;
|
||||
APitem.src = WiFi.localIP();
|
||||
strcpy(APitem.alias, config.alias);
|
||||
APitem.channelId = curChannel.channel;
|
||||
APitem.tagCount = getTagCount();
|
||||
APitem.version = apInfo.version;
|
||||
wsSendAPitem(&APitem);
|
||||
|
||||
if (config.alias == 0) {
|
||||
xTaskCreate(autoselect, "autoselect", 5000, NULL, configMAX_PRIORITIES - 10, NULL);
|
||||
}
|
||||
|
||||
uint8_t buffer[sizeof(struct APlist) + 1];
|
||||
buffer[0] = PKT_APLIST_REQ;
|
||||
memcpy(buffer + 1, &APitem, sizeof(struct APlist));
|
||||
udp.writeTo(buffer, sizeof(buffer), UDPIP, UDPPORT);
|
||||
}
|
||||
|
||||
void UDPcomm::netProcessDataReq(struct espAvailDataReq* eadr) {
|
||||
uint8_t buffer[sizeof(struct espAvailDataReq) + 1];
|
||||
buffer[0] = PKT_AVAIL_DATA_INFO;
|
||||
memcpy(buffer + 1, eadr, sizeof(struct espAvailDataReq));
|
||||
udp.writeTo(buffer, sizeof(buffer), UDPIP, UDPPORT);
|
||||
}
|
||||
|
||||
void UDPcomm::netProcessXferComplete(struct espXferComplete* xfc) {
|
||||
uint8_t buffer[sizeof(struct espXferComplete) + 1];
|
||||
buffer[0] = PKT_XFER_COMPLETE;
|
||||
memcpy(buffer + 1, xfc, sizeof(struct espXferComplete));
|
||||
udp.writeTo(buffer, sizeof(buffer), UDPIP, UDPPORT);
|
||||
}
|
||||
|
||||
void UDPcomm::netProcessXferTimeout(struct espXferComplete* xfc) {
|
||||
uint8_t buffer[sizeof(struct espXferComplete) + 1];
|
||||
buffer[0] = PKT_XFER_TIMEOUT;
|
||||
memcpy(buffer + 1, xfc, sizeof(struct espXferComplete));
|
||||
udp.writeTo(buffer, sizeof(buffer), UDPIP, UDPPORT);
|
||||
}
|
||||
|
||||
void UDPcomm::netSendDataAvail(struct pendingData* pending) {
|
||||
uint8_t buffer[sizeof(struct pendingData) + 1];
|
||||
buffer[0] = PKT_AVAIL_DATA_REQ;
|
||||
memcpy(buffer + 1, pending, sizeof(struct pendingData));
|
||||
udp.writeTo(buffer, sizeof(buffer), UDPIP, UDPPORT);
|
||||
}
|
||||
|
||||
void UDPcomm::netTaginfo(struct TagInfo* taginfoitem) {
|
||||
uint8_t buffer[sizeof(struct TagInfo) + 1];
|
||||
buffer[0] = PKT_TAGINFO;
|
||||
memcpy(buffer + 1, taginfoitem, sizeof(struct TagInfo));
|
||||
udp.writeTo(buffer, sizeof(buffer), UDPIP, UDPPORT);
|
||||
}
|
||||
395
ESP32_AP-Flasher/src/usbflasher.cpp
Normal file
395
ESP32_AP-Flasher/src/usbflasher.cpp
Normal file
@@ -0,0 +1,395 @@
|
||||
#include <Arduino.h>
|
||||
|
||||
#include "USB.h"
|
||||
#include "powermgt.h"
|
||||
#include "serialconsole.h"
|
||||
#include "settings.h"
|
||||
#include "zbs_interface.h"
|
||||
|
||||
USBCDC USBSerial;
|
||||
|
||||
QueueHandle_t flasherCmdQueue;
|
||||
|
||||
uint32_t usbConnectedStartTime = 0;
|
||||
|
||||
#define FLASHER_WAIT_A 0
|
||||
#define FLASHER_WAIT_T 1
|
||||
#define FLASHER_WAIT_CMD 2
|
||||
#define FLASHER_WAIT_LEN 3
|
||||
#define FLASHER_WAIT_DATA 4
|
||||
#define FLASHER_WAIT_CRCH 5
|
||||
#define FLASHER_WAIT_CRCL 6
|
||||
|
||||
struct flasherCommand {
|
||||
uint8_t command = 0;
|
||||
uint8_t len = 0;
|
||||
uint8_t* data = nullptr;
|
||||
};
|
||||
|
||||
#define FLASHER_MODE_UNKNOWN 0
|
||||
#define FLASHER_MODE_FLASHER 1
|
||||
#define FLASHER_MODE_CONSOLE 2
|
||||
volatile uint8_t usbFlasherMode = FLASHER_MODE_UNKNOWN;
|
||||
|
||||
void enterConsoleMode() {
|
||||
usbFlasherMode = FLASHER_MODE_CONSOLE;
|
||||
xTaskCreate(consoleTask, "consoleTask", 10000, NULL, 2, &consoleTaskHandle);
|
||||
}
|
||||
|
||||
int8_t powerPins[] = FLASHER_AP_POWER;
|
||||
#ifdef OPENEPAPERLINK_PCB
|
||||
int8_t powerPins2[] = FLASHER_EXT_POWER;
|
||||
int8_t powerPins3[] = FLASHER_ALT_POWER;
|
||||
#endif
|
||||
|
||||
void sendFlasherAnswer(uint8_t answer_cmd, uint8_t* ans_buff, uint8_t len) {
|
||||
uint8_t* answer_buffer = (uint8_t*)calloc(2 + 2 + len + 2, 1);
|
||||
if (answer_buffer == nullptr) return;
|
||||
uint32_t CRC_value = 0xAB34;
|
||||
answer_buffer[0] = 'A';
|
||||
answer_buffer[1] = 'T';
|
||||
answer_buffer[2] = answer_cmd;
|
||||
CRC_value += answer_cmd;
|
||||
answer_buffer[3] = len;
|
||||
CRC_value += len;
|
||||
for (int i = 0; i < len; i++) {
|
||||
answer_buffer[4 + i] = ans_buff[i];
|
||||
CRC_value += ans_buff[i];
|
||||
}
|
||||
answer_buffer[2 + 2 + len] = CRC_value >> 8;
|
||||
answer_buffer[2 + 2 + len + 1] = CRC_value;
|
||||
USBSerial.write(answer_buffer, 2 + 2 + len + 2);
|
||||
free(answer_buffer);
|
||||
}
|
||||
|
||||
void flasherUartHandler(uint8_t* data, uint8_t len) {
|
||||
static struct flasherCommand* cmd;
|
||||
static uint8_t flasherSerialState = FLASHER_WAIT_A;
|
||||
static uint8_t flasherCmdDataIndex = 0;
|
||||
static uint16_t flasherCRC = 0xAB34;
|
||||
static uint32_t flasherLastCmd = 0;
|
||||
|
||||
if ((flasherSerialState != FLASHER_WAIT_A) && (millis() - flasherLastCmd >= 225)) {
|
||||
flasherSerialState = FLASHER_WAIT_A;
|
||||
}
|
||||
|
||||
while (len--) {
|
||||
uint8_t usbbyte = *(data++);
|
||||
switch (flasherSerialState) {
|
||||
case FLASHER_WAIT_A:
|
||||
if (usbbyte == 'A') {
|
||||
flasherSerialState = FLASHER_WAIT_T;
|
||||
flasherLastCmd = millis();
|
||||
} else {
|
||||
enterConsoleMode();
|
||||
}
|
||||
break;
|
||||
case FLASHER_WAIT_T:
|
||||
if (usbbyte == 'T') {
|
||||
flasherSerialState = FLASHER_WAIT_CMD;
|
||||
cmd = new flasherCommand;
|
||||
flasherCRC = 0xAB34;
|
||||
flasherCmdDataIndex = 0;
|
||||
} else {
|
||||
flasherSerialState = FLASHER_WAIT_A;
|
||||
}
|
||||
break;
|
||||
case FLASHER_WAIT_CMD:
|
||||
cmd->command = usbbyte;
|
||||
flasherCRC += usbbyte;
|
||||
flasherSerialState = FLASHER_WAIT_LEN;
|
||||
break;
|
||||
case FLASHER_WAIT_LEN:
|
||||
flasherCRC += usbbyte;
|
||||
if (usbbyte) {
|
||||
cmd->len = usbbyte;
|
||||
cmd->data = (uint8_t*)calloc(usbbyte, 1);
|
||||
flasherSerialState = FLASHER_WAIT_DATA;
|
||||
} else {
|
||||
flasherSerialState = FLASHER_WAIT_CRCH;
|
||||
}
|
||||
break;
|
||||
case FLASHER_WAIT_DATA:
|
||||
flasherCRC += usbbyte;
|
||||
cmd->data[flasherCmdDataIndex++] = usbbyte;
|
||||
if (flasherCmdDataIndex == cmd->len) {
|
||||
flasherSerialState = FLASHER_WAIT_CRCH;
|
||||
}
|
||||
break;
|
||||
case FLASHER_WAIT_CRCH:
|
||||
flasherCRC -= ((uint16_t)usbbyte << 8);
|
||||
flasherSerialState = FLASHER_WAIT_CRCL;
|
||||
break;
|
||||
case FLASHER_WAIT_CRCL:
|
||||
flasherCRC -= ((uint16_t)usbbyte);
|
||||
if (flasherCRC) {
|
||||
Serial.printf("CRC failed for flasher command :( %04X\n", flasherCRC);
|
||||
cmd = nullptr;
|
||||
} else {
|
||||
if (usbFlasherMode == FLASHER_MODE_UNKNOWN) usbFlasherMode = FLASHER_MODE_FLASHER;
|
||||
BaseType_t queuestatus = xQueueSend(flasherCmdQueue, &cmd, 0);
|
||||
if (queuestatus == pdFALSE) {
|
||||
if (cmd->data != nullptr) free(cmd->data);
|
||||
delete cmd;
|
||||
}
|
||||
cmd = nullptr;
|
||||
}
|
||||
flasherSerialState = FLASHER_WAIT_A;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void resetFlasherState() {
|
||||
if (usbFlasherMode != FLASHER_MODE_UNKNOWN) {
|
||||
if (usbFlasherMode == FLASHER_MODE_CONSOLE) consoleStopTask();
|
||||
Serial.print("Resetting flasher state");
|
||||
usbFlasherMode = FLASHER_MODE_UNKNOWN;
|
||||
usbConnectedStartTime = millis();
|
||||
}
|
||||
}
|
||||
|
||||
static void usbEventCallback(void* arg, esp_event_base_t event_base, int32_t event_id, void* event_data) {
|
||||
if (event_base == ARDUINO_USB_EVENTS) {
|
||||
arduino_usb_event_data_t* data = (arduino_usb_event_data_t*)event_data;
|
||||
switch (event_id) {
|
||||
case ARDUINO_USB_STARTED_EVENT:
|
||||
Serial.println("USB PLUGGED");
|
||||
resetFlasherState();
|
||||
break;
|
||||
case ARDUINO_USB_STOPPED_EVENT:
|
||||
Serial.println("USB UNPLUGGED");
|
||||
resetFlasherState();
|
||||
break;
|
||||
case ARDUINO_USB_SUSPEND_EVENT:
|
||||
Serial.printf("USB SUSPENDED: remote_wakeup_en: %u\n", data->suspend.remote_wakeup_en);
|
||||
break;
|
||||
case ARDUINO_USB_RESUME_EVENT:
|
||||
Serial.println("USB RESUMED");
|
||||
break;
|
||||
default:
|
||||
ets_printf("other USB event %d\n", event_id);
|
||||
break;
|
||||
}
|
||||
} else if (event_base == ARDUINO_USB_CDC_EVENTS) {
|
||||
arduino_usb_cdc_event_data_t* data = (arduino_usb_cdc_event_data_t*)event_data;
|
||||
switch (event_id) {
|
||||
case ARDUINO_USB_CDC_CONNECTED_EVENT:
|
||||
ets_printf("CDC CONNECTED\n");
|
||||
resetFlasherState();
|
||||
usbConnectedStartTime = millis();
|
||||
break;
|
||||
case ARDUINO_USB_CDC_DISCONNECTED_EVENT:
|
||||
ets_printf("CDC DISCONNECTED\n");
|
||||
resetFlasherState();
|
||||
break;
|
||||
case ARDUINO_USB_CDC_LINE_STATE_EVENT:
|
||||
ets_printf("CDC LINE STATE: dtr: %u, rts: %u\n", data->line_state.dtr, data->line_state.rts);
|
||||
if (data->line_state.dtr == 0) resetFlasherState();
|
||||
break;
|
||||
case ARDUINO_USB_CDC_LINE_CODING_EVENT:
|
||||
ets_printf("CDC LINE CODING: bit_rate: %u, data_bits: %u, stop_bits: %u, parity: %u\n", data->line_coding.bit_rate, data->line_coding.data_bits, data->line_coding.stop_bits, data->line_coding.parity);
|
||||
resetFlasherState();
|
||||
break;
|
||||
case ARDUINO_USB_CDC_RX_EVENT:
|
||||
// Serial.printf("CDC RX [%u]:", data->rx.len);
|
||||
{
|
||||
uint8_t buf[data->rx.len];
|
||||
size_t len = USBSerial.read(buf, data->rx.len);
|
||||
if (usbFlasherMode != FLASHER_MODE_CONSOLE) {
|
||||
flasherUartHandler(buf, len);
|
||||
} else {
|
||||
consoleUartHandler(buf, len);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ARDUINO_USB_CDC_RX_OVERFLOW_EVENT:
|
||||
Serial.printf("CDC RX Overflow of %d bytes", data->rx_overflow.dropped_bytes);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
typedef enum {
|
||||
CMD_GET_VERSION = 1,
|
||||
CMD_RESET_ESP = 2,
|
||||
CMD_ZBS_BEGIN = 10,
|
||||
CMD_RESET_ZBS = 11,
|
||||
CMD_SELECT_PAGE = 12,
|
||||
CMD_SET_POWER = 13,
|
||||
CMD_READ_RAM = 20,
|
||||
CMD_WRITE_RAM = 21,
|
||||
CMD_READ_FLASH = 22,
|
||||
CMD_WRITE_FLASH = 23,
|
||||
CMD_READ_SFR = 24,
|
||||
CMD_WRITE_SFR = 25,
|
||||
CMD_ERASE_FLASH = 26,
|
||||
CMD_ERASE_INFOBLOCK = 27,
|
||||
CMD_SAVE_MAC_FROM_FW = 40,
|
||||
CMD_PASS_THROUGH = 50,
|
||||
} ZBS_UART_PROTO;
|
||||
uint32_t FLASHER_VERSION = 0x0000002F;
|
||||
|
||||
static class ZBS_interface* zbs = nullptr;
|
||||
|
||||
void processFlasherCommand(struct flasherCommand* cmd) {
|
||||
uint8_t* tempbuffer;
|
||||
uint8_t temp_buff[16];
|
||||
uint32_t spi_speed = 0;
|
||||
static uint32_t curspeed = 0;
|
||||
|
||||
switch (cmd->command) {
|
||||
case CMD_GET_VERSION:
|
||||
temp_buff[0] = FLASHER_VERSION >> 24;
|
||||
temp_buff[1] = FLASHER_VERSION >> 16;
|
||||
temp_buff[2] = FLASHER_VERSION >> 8;
|
||||
temp_buff[3] = FLASHER_VERSION;
|
||||
sendFlasherAnswer(cmd->command, temp_buff, 4);
|
||||
break;
|
||||
case CMD_RESET_ESP:
|
||||
sendFlasherAnswer(cmd->command, NULL, 0);
|
||||
delay(100);
|
||||
ESP.restart();
|
||||
break;
|
||||
case CMD_ZBS_BEGIN:
|
||||
if (zbs != nullptr) {
|
||||
delete zbs;
|
||||
}
|
||||
zbs = new ZBS_interface;
|
||||
if (cmd->data[0] & 1) {
|
||||
spi_speed = 1000000;
|
||||
} else {
|
||||
spi_speed = 8000000;
|
||||
}
|
||||
curspeed = spi_speed;
|
||||
|
||||
if (cmd->data[0] & 2) {
|
||||
temp_buff[0] = zbs->begin(FLASHER_AP_SS, FLASHER_AP_CLK, FLASHER_AP_MOSI, FLASHER_AP_MISO, FLASHER_AP_RESET, (uint8_t*)powerPins, spi_speed);
|
||||
} else if (cmd->data[0] & 4) {
|
||||
#ifdef OPENEPAPERLINK_PCB
|
||||
temp_buff[0] = zbs->begin(FLASHER_ALT_SS, FLASHER_ALT_CLK, FLASHER_ALT_MOSI, FLASHER_ALT_MISO, FLASHER_ALT_RESET, (uint8_t*)powerPins3, spi_speed);
|
||||
#endif
|
||||
} else {
|
||||
#ifdef OPENEPAPERLINK_PCB
|
||||
temp_buff[0] = zbs->begin(FLASHER_EXT_SS, FLASHER_EXT_CLK, FLASHER_EXT_MOSI, FLASHER_EXT_MISO, FLASHER_EXT_RESET, (uint8_t*)powerPins2, spi_speed);
|
||||
#endif
|
||||
}
|
||||
sendFlasherAnswer(cmd->command, temp_buff, 1);
|
||||
break;
|
||||
case CMD_RESET_ZBS:
|
||||
zbs->reset();
|
||||
temp_buff[0] = 1;
|
||||
sendFlasherAnswer(cmd->command, temp_buff, 1);
|
||||
break;
|
||||
case CMD_SELECT_PAGE:
|
||||
temp_buff[0] = zbs->select_flash(cmd->data[0] ? 1 : 0);
|
||||
sendFlasherAnswer(cmd->command, temp_buff, 1);
|
||||
break;
|
||||
case CMD_SET_POWER:
|
||||
zbs->set_power(cmd->data[0] ? 1 : 0);
|
||||
temp_buff[0] = 1;
|
||||
sendFlasherAnswer(cmd->command, temp_buff, 1);
|
||||
break;
|
||||
case CMD_READ_RAM:
|
||||
temp_buff[0] = zbs->read_ram(cmd->data[0]);
|
||||
sendFlasherAnswer(cmd->command, temp_buff, 1);
|
||||
break;
|
||||
case CMD_WRITE_RAM:
|
||||
zbs->write_ram(cmd->data[0], cmd->data[1]);
|
||||
temp_buff[0] = 1;
|
||||
sendFlasherAnswer(cmd->command, temp_buff, 1);
|
||||
break;
|
||||
case CMD_READ_FLASH:
|
||||
tempbuffer = (uint8_t*)calloc(cmd->data[0], 1);
|
||||
// cmd_buff[0] = len
|
||||
// cmd_buff[1] << 8 | cmd_buff[2] = position
|
||||
// Serial.printf("Loading %d bytes from %04X \n", cmd->data[0], (cmd->data[1] << 8 | cmd->data[2]));
|
||||
for (int i = 0; i < cmd->data[0]; i++) {
|
||||
tempbuffer[i] = zbs->read_flash((cmd->data[1] << 8 | cmd->data[2]) + i);
|
||||
}
|
||||
sendFlasherAnswer(cmd->command, tempbuffer, cmd->data[0]);
|
||||
free(tempbuffer);
|
||||
break;
|
||||
case CMD_WRITE_FLASH:
|
||||
// cmd_buff[0] = len
|
||||
// cmd_buff[1] << 8 | cmd_buff[2] = position
|
||||
// cmd_buff[3+i] = data
|
||||
if (cmd->data[0] >= (0xff - 3)) { // Len too high, only 0xFF - header len possible
|
||||
temp_buff[0] = 0xEE;
|
||||
sendFlasherAnswer(cmd->command, temp_buff, 1);
|
||||
break;
|
||||
}
|
||||
// Serial.printf("Writing %d bytes to %04X \n", cmd->data[0], (cmd->data[1] << 8 | cmd->data[2]));
|
||||
for (int i = 0; i < cmd->data[0]; i++) {
|
||||
if (cmd->data[3 + i] != 0xff) {
|
||||
for (uint8_t attempts = 0; attempts < 10; attempts++) {
|
||||
zbs->write_flash((cmd->data[1] << 8 | cmd->data[2]) + i, cmd->data[3 + i]);
|
||||
if (zbs->read_flash((cmd->data[1] << 8 | cmd->data[2]) + i) == cmd->data[3 + i]) {
|
||||
goto flash_pass;
|
||||
}
|
||||
curspeed -= 100000;
|
||||
zbs->setSpeed(curspeed);
|
||||
}
|
||||
flash_fail:
|
||||
temp_buff[0] = 0;
|
||||
sendFlasherAnswer(cmd->command, temp_buff, 1);
|
||||
break;
|
||||
flash_pass:
|
||||
continue;
|
||||
}
|
||||
}
|
||||
temp_buff[0] = 1;
|
||||
sendFlasherAnswer(cmd->command, temp_buff, 1);
|
||||
break;
|
||||
case CMD_READ_SFR:
|
||||
temp_buff[0] = zbs->read_sfr(cmd->data[0]);
|
||||
sendFlasherAnswer(cmd->command, temp_buff, 1);
|
||||
break;
|
||||
case CMD_WRITE_SFR:
|
||||
zbs->write_sfr(cmd->data[0], cmd->data[1]);
|
||||
temp_buff[0] = 1;
|
||||
sendFlasherAnswer(cmd->command, temp_buff, 1);
|
||||
break;
|
||||
case CMD_ERASE_FLASH:
|
||||
zbs->erase_flash();
|
||||
temp_buff[0] = 1;
|
||||
sendFlasherAnswer(cmd->command, temp_buff, 1);
|
||||
break;
|
||||
case CMD_ERASE_INFOBLOCK:
|
||||
zbs->erase_infoblock();
|
||||
temp_buff[0] = 1;
|
||||
sendFlasherAnswer(cmd->command, temp_buff, 1);
|
||||
break;
|
||||
case CMD_SAVE_MAC_FROM_FW:
|
||||
case CMD_PASS_THROUGH:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void usbFlasherTask(void* parameter) {
|
||||
flasherCmdQueue = xQueueCreate(10, sizeof(struct flasherCommand*));
|
||||
consoleCmdQueue = xQueueCreate(10, sizeof(struct consoleCommand*));
|
||||
#if ARDUINO_USB_MODE
|
||||
#warning Wrong USB mode is in use, check settings in platformio.ini
|
||||
#endif
|
||||
USB.onEvent(usbEventCallback);
|
||||
USBSerial.onEvent(usbEventCallback);
|
||||
USBSerial.setTimeout(1000);
|
||||
USB.begin();
|
||||
USBSerial.begin();
|
||||
|
||||
struct flasherCommand* cmd;
|
||||
while (true) {
|
||||
BaseType_t queuereceive = xQueueReceive(flasherCmdQueue, &cmd, portMAX_DELAY);
|
||||
if (queuereceive == pdTRUE) {
|
||||
processFlasherCommand(cmd);
|
||||
if (cmd->data != nullptr) {
|
||||
free(cmd->data);
|
||||
}
|
||||
delete cmd;
|
||||
}
|
||||
}
|
||||
}
|
||||
477
ESP32_AP-Flasher/src/web.cpp
Normal file
477
ESP32_AP-Flasher/src/web.cpp
Normal file
@@ -0,0 +1,477 @@
|
||||
#include "web.h"
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <ArduinoJson.h>
|
||||
#include <AsyncTCP.h>
|
||||
#include <ESPAsyncWebServer.h>
|
||||
#include <ESPmDNS.h>
|
||||
#include <FS.h>
|
||||
#include "storage.h"
|
||||
#include "LittleFS.h"
|
||||
#include "SPIFFSEditor.h"
|
||||
#include <WiFi.h>
|
||||
#include <WiFiManager.h> // https://github.com/tzapu/WiFiManager/tree/feature_asyncwebserver
|
||||
|
||||
#include "commstructs.h"
|
||||
#include "language.h"
|
||||
#include "leds.h"
|
||||
#include "newproto.h"
|
||||
#include "ota.h"
|
||||
#include "serialap.h"
|
||||
#include "settings.h"
|
||||
#include "tag_db.h"
|
||||
#include "udp.h"
|
||||
|
||||
extern uint8_t data_to_send[];
|
||||
|
||||
// const char *http_username = "admin";
|
||||
// const char *http_password = "admin";
|
||||
AsyncWebServer server(80);
|
||||
AsyncWebSocket ws("/ws");
|
||||
|
||||
SemaphoreHandle_t wsMutex;
|
||||
|
||||
void webSocketSendProcess(void *parameter) {
|
||||
wsMutex = xSemaphoreCreateMutex();
|
||||
while (true) {
|
||||
ws.cleanupClients();
|
||||
vTaskDelay(1000 / portTICK_PERIOD_MS);
|
||||
}
|
||||
}
|
||||
|
||||
void onEvent(AsyncWebSocket *server, AsyncWebSocketClient *client, AwsEventType type, void *arg, uint8_t *data, size_t len) {
|
||||
#ifdef HAS_RGB_LED
|
||||
shortBlink(CRGB::BlueViolet);
|
||||
#endif
|
||||
switch (type) {
|
||||
case WS_EVT_CONNECT:
|
||||
ets_printf("ws[%s][%u] connect\n", server->url(), client->id());
|
||||
// client->ping();
|
||||
break;
|
||||
case WS_EVT_DISCONNECT:
|
||||
ets_printf("ws[%s][%u] disconnect: %u\n", server->url(), client->id());
|
||||
break;
|
||||
case WS_EVT_ERROR:
|
||||
ets_printf("WS Error received :(\n\n");
|
||||
// ets_printf("ws[%s][%u] error(%u): %s\n", server->url(), client->id(), *((uint16_t *)arg), (char *)data);
|
||||
break;
|
||||
case WS_EVT_PONG:
|
||||
ets_printf("ws[%s][%u] pong[%u]: %s\n", server->url(), client->id(), len, (len) ? (char *)data : "");
|
||||
break;
|
||||
case WS_EVT_DATA:
|
||||
/*
|
||||
AwsFrameInfo *info = (AwsFrameInfo *)arg;
|
||||
if (info->final && info->index == 0 && info->len == len) {
|
||||
// the whole message is in a single frame and we got all of it's data
|
||||
ets_printf("ws[%s][%u] %s-message[%llu]: ", server->url(), client->id(), (info->opcode == WS_TEXT) ? "text" : "binary", info->len);
|
||||
if (info->opcode == WS_TEXT) {
|
||||
data[len] = 0;
|
||||
ets_printf("%s\n", (char *)data);
|
||||
} else {
|
||||
for (size_t i = 0; i < info->len; i++) {
|
||||
ets_printf("%02x ", data[i]);
|
||||
}
|
||||
ets_printf("\n");
|
||||
}
|
||||
if (info->opcode == WS_TEXT)
|
||||
client->text("{\"status\":\"received\"}");
|
||||
else
|
||||
client->binary("{\"status\":\"received\"}");
|
||||
} else {
|
||||
// message is comprised of multiple frames or the frame is split into multiple packets
|
||||
if (info->index == 0) {
|
||||
if (info->num == 0)
|
||||
ets_printf("ws[%s][%u] %s-message start\n", server->url(), client->id(), (info->message_opcode == WS_TEXT) ? "text" : "binary");
|
||||
ets_printf("ws[%s][%u] frame[%u] start[%llu]\n", server->url(), client->id(), info->num, info->len);
|
||||
}
|
||||
|
||||
ets_printf("ws[%s][%u] frame[%u] %s[%llu - %llu]: ", server->url(), client->id(), info->num, (info->message_opcode == WS_TEXT) ? "text" : "binary", info->index, info->index + len);
|
||||
if (info->message_opcode == WS_TEXT) {
|
||||
data[len] = 0;
|
||||
ets_printf("%s\n", (char *)data);
|
||||
} else {
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
ets_printf("%02x ", data[i]);
|
||||
}
|
||||
ets_printf("\n");
|
||||
}
|
||||
|
||||
if ((info->index + len) == info->len) {
|
||||
ets_printf("ws[%s][%u] frame[%u] end[%llu]\n", server->url(), client->id(), info->num, info->len);
|
||||
if (info->final) {
|
||||
ets_printf("ws[%s][%u] %s-message end\n", server->url(), client->id(), (info->message_opcode == WS_TEXT) ? "text" : "binary");
|
||||
if (info->message_opcode == WS_TEXT)
|
||||
client->text("{\"status\":\"received\"}");
|
||||
else
|
||||
client->binary("{\"status\":\"received\"}");
|
||||
}
|
||||
}
|
||||
} */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void wsLog(String text) {
|
||||
StaticJsonDocument<250> doc;
|
||||
doc["logMsg"] = text;
|
||||
if (wsMutex) xSemaphoreTake(wsMutex, portMAX_DELAY);
|
||||
ws.textAll(doc.as<String>());
|
||||
if (wsMutex) xSemaphoreGive(wsMutex);
|
||||
}
|
||||
|
||||
void wsErr(String text) {
|
||||
StaticJsonDocument<250> doc;
|
||||
doc["errMsg"] = text;
|
||||
if (wsMutex) xSemaphoreTake(wsMutex, portMAX_DELAY);
|
||||
ws.textAll(doc.as<String>());
|
||||
if (wsMutex) xSemaphoreGive(wsMutex);
|
||||
}
|
||||
|
||||
void wsSendSysteminfo() {
|
||||
DynamicJsonDocument doc(150);
|
||||
JsonObject sys = doc.createNestedObject("sys");
|
||||
time_t now;
|
||||
time(&now);
|
||||
sys["currtime"] = now;
|
||||
sys["heap"] = ESP.getFreeHeap();
|
||||
sys["recordcount"] = tagDB.size();
|
||||
sys["dbsize"] = tagDB.size() * sizeof(tagRecord);
|
||||
sys["littlefsfree"] = Storage.freeSpace();
|
||||
sys["apstate"] = apInfo.state;
|
||||
sys["runstate"] = config.runStatus;
|
||||
|
||||
xSemaphoreTake(wsMutex, portMAX_DELAY);
|
||||
ws.textAll(doc.as<String>());
|
||||
xSemaphoreGive(wsMutex);
|
||||
}
|
||||
|
||||
void wsSendTaginfo(uint8_t *mac, uint8_t syncMode) {
|
||||
if (syncMode != SYNC_DELETE) {
|
||||
String json = "";
|
||||
json = tagDBtoJson(mac);
|
||||
xSemaphoreTake(wsMutex, portMAX_DELAY);
|
||||
ws.textAll(json);
|
||||
xSemaphoreGive(wsMutex);
|
||||
}
|
||||
if (syncMode > SYNC_NOSYNC) {
|
||||
tagRecord *taginfo = nullptr;
|
||||
taginfo = tagRecord::findByMAC(mac);
|
||||
if (taginfo != nullptr) {
|
||||
if (taginfo->contentMode != 12 || syncMode == SYNC_DELETE) {
|
||||
UDPcomm udpsync;
|
||||
struct TagInfo taginfoitem;
|
||||
memcpy(taginfoitem.mac, taginfo->mac, sizeof(taginfoitem.mac));
|
||||
taginfoitem.syncMode = syncMode;
|
||||
taginfoitem.contentMode = taginfo->contentMode;
|
||||
if (syncMode == SYNC_USERCFG) {
|
||||
strncpy(taginfoitem.alias, taginfo->alias.c_str(), sizeof(taginfoitem.alias) - 1);
|
||||
taginfoitem.alias[sizeof(taginfoitem.alias) - 1] = '\0';
|
||||
taginfoitem.nextupdate = taginfo->nextupdate;
|
||||
}
|
||||
if (syncMode == SYNC_TAGSTATUS) {
|
||||
taginfoitem.lastseen = taginfo->lastseen;
|
||||
taginfoitem.nextupdate = taginfo->nextupdate;
|
||||
taginfoitem.pending = taginfo->pending;
|
||||
taginfoitem.expectedNextCheckin = taginfo->expectedNextCheckin;
|
||||
taginfoitem.hwType = taginfo->hwType;
|
||||
taginfoitem.wakeupReason = taginfo->wakeupReason;
|
||||
taginfoitem.capabilities = taginfo->capabilities;
|
||||
taginfoitem.pendingIdle = taginfo->pendingIdle;
|
||||
}
|
||||
udpsync.netTaginfo(&taginfoitem);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void wsSendAPitem(struct APlist *apitem) {
|
||||
DynamicJsonDocument doc(250);
|
||||
JsonObject ap = doc.createNestedObject("apitem");
|
||||
|
||||
char version_str[6];
|
||||
sprintf(version_str, "%04X", apitem->version);
|
||||
|
||||
ap["ip"] = ((IPAddress)apitem->src).toString();
|
||||
ap["alias"] = apitem->alias;
|
||||
ap["count"] = apitem->tagCount;
|
||||
ap["channel"] = apitem->channelId;
|
||||
ap["version"] = version_str;
|
||||
|
||||
if (wsMutex) xSemaphoreTake(wsMutex, portMAX_DELAY);
|
||||
ws.textAll(doc.as<String>());
|
||||
if (wsMutex) xSemaphoreGive(wsMutex);
|
||||
}
|
||||
|
||||
void wsSerial(String text) {
|
||||
StaticJsonDocument<250> doc;
|
||||
doc["console"] = text;
|
||||
Serial.println(text);
|
||||
if (wsMutex) xSemaphoreTake(wsMutex, portMAX_DELAY);
|
||||
ws.textAll(doc.as<String>());
|
||||
if (wsMutex) xSemaphoreGive(wsMutex);
|
||||
}
|
||||
|
||||
uint8_t wsClientCount() {
|
||||
return ws.count();
|
||||
}
|
||||
|
||||
void init_web() {
|
||||
Storage.begin();
|
||||
WiFi.mode(WIFI_STA);
|
||||
|
||||
WiFiManager wm;
|
||||
bool res;
|
||||
#if defined(OPENEPAPERLINK_MINI_AP_PCB) || defined(OPENEPAPERLINK_NANO_AP_PCB)
|
||||
WiFi.setTxPower(WIFI_POWER_15dBm);
|
||||
#endif
|
||||
wm.setWiFiAutoReconnect(true);
|
||||
res = wm.autoConnect("OpenEPaperLink Setup");
|
||||
if (!res) {
|
||||
Serial.println("Failed to connect");
|
||||
ESP.restart();
|
||||
}
|
||||
#if defined(OPENEPAPERLINK_MINI_AP_PCB) || defined(OPENEPAPERLINK_NANO_AP_PCB)
|
||||
WiFi.setTxPower(WIFI_POWER_19_5dBm);
|
||||
#endif
|
||||
Serial.print("Connected! IP address: ");
|
||||
Serial.println(WiFi.localIP());
|
||||
|
||||
// server.addHandler(new SPIFFSEditor(*contentFS, http_username, http_password));
|
||||
server.addHandler(new SPIFFSEditor(*contentFS));
|
||||
|
||||
ws.onEvent(onEvent);
|
||||
server.addHandler(&ws);
|
||||
|
||||
server.on("/reboot", HTTP_POST, [](AsyncWebServerRequest *request) {
|
||||
request->send(200, "text/plain", "OK Reboot");
|
||||
wsErr("REBOOTING");
|
||||
ws.enable(false);
|
||||
refreshAllPending();
|
||||
saveDB("/current/tagDB.json");
|
||||
ws.closeAll();
|
||||
delay(100);
|
||||
ESP.restart();
|
||||
});
|
||||
|
||||
server.serveStatic("/current", *contentFS, "/current/");
|
||||
server.serveStatic("/", *contentFS, "/www/").setDefaultFile("index.html");
|
||||
|
||||
server.on(
|
||||
"/imgupload", HTTP_POST, [](AsyncWebServerRequest *request) {
|
||||
request->send(200);
|
||||
},
|
||||
doImageUpload);
|
||||
|
||||
server.on("/get_db", HTTP_GET, [](AsyncWebServerRequest *request) {
|
||||
String json = "";
|
||||
if (request->hasParam("mac")) {
|
||||
String dst = request->getParam("mac")->value();
|
||||
uint8_t mac[8];
|
||||
if (hex2mac(dst, mac)) {
|
||||
json = tagDBtoJson(mac);
|
||||
} else {
|
||||
json = "{\"error\": \"malformatted parameter\"}";
|
||||
}
|
||||
} else {
|
||||
uint8_t startPos = 0;
|
||||
if (request->hasParam("pos")) {
|
||||
startPos = atoi(request->getParam("pos")->value().c_str());
|
||||
}
|
||||
json = tagDBtoJson(nullptr, startPos);
|
||||
}
|
||||
request->send(200, "application/json", json);
|
||||
});
|
||||
|
||||
server.on("/getdata", HTTP_GET, [](AsyncWebServerRequest *request) {
|
||||
if (request->hasParam("mac")) {
|
||||
String dst = request->getParam("mac")->value();
|
||||
uint8_t mac[8];
|
||||
if (hex2mac(dst, mac)) {
|
||||
tagRecord *taginfo = nullptr;
|
||||
taginfo = tagRecord::findByMAC(mac);
|
||||
if (taginfo != nullptr) {
|
||||
if (taginfo->pending == true) {
|
||||
request->send_P(200, "application/octet-stream", taginfo->data, taginfo->len);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
request->send(400, "text/plain", "No data available");
|
||||
});
|
||||
|
||||
server.on("/save_cfg", HTTP_POST, [](AsyncWebServerRequest *request) {
|
||||
if (request->hasParam("mac", true)) {
|
||||
String dst = request->getParam("mac", true)->value();
|
||||
uint8_t mac[8];
|
||||
if (hex2mac(dst, mac)) {
|
||||
tagRecord *taginfo = nullptr;
|
||||
taginfo = tagRecord::findByMAC(mac);
|
||||
if (taginfo != nullptr) {
|
||||
taginfo->alias = request->getParam("alias", true)->value();
|
||||
taginfo->modeConfigJson = request->getParam("modecfgjson", true)->value();
|
||||
taginfo->contentMode = atoi(request->getParam("contentmode", true)->value().c_str());
|
||||
taginfo->nextupdate = 0;
|
||||
if (request->hasParam("rotate", true)) {
|
||||
taginfo->rotate = atoi(request->getParam("rotate", true)->value().c_str());
|
||||
}
|
||||
if (request->hasParam("lut", true)) {
|
||||
taginfo->lut = atoi(request->getParam("lut", true)->value().c_str());
|
||||
}
|
||||
// memset(taginfo->md5, 0, 16 * sizeof(uint8_t));
|
||||
// memset(taginfo->md5pending, 0, 16 * sizeof(uint8_t));
|
||||
wsSendTaginfo(mac, SYNC_USERCFG);
|
||||
saveDB("/current/tagDB.json");
|
||||
request->send(200, "text/plain", "Ok, saved");
|
||||
} else {
|
||||
request->send(200, "text/plain", "Error while saving: mac not found");
|
||||
}
|
||||
}
|
||||
}
|
||||
request->send(200, "text/plain", "Ok, saved");
|
||||
});
|
||||
|
||||
server.on("/tag_cmd", HTTP_POST, [](AsyncWebServerRequest *request) {
|
||||
if (request->hasParam("mac", true) && request->hasParam("cmd", true)) {
|
||||
uint8_t mac[8];
|
||||
if (hex2mac(request->getParam("mac", true)->value(), mac)) {
|
||||
tagRecord *taginfo = nullptr;
|
||||
taginfo = tagRecord::findByMAC(mac);
|
||||
if (taginfo != nullptr) {
|
||||
const char *cmdValue = request->getParam("cmd", true)->value().c_str();
|
||||
if (strcmp(cmdValue, "del") == 0) {
|
||||
wsSendTaginfo(mac, SYNC_DELETE);
|
||||
deleteRecord(mac);
|
||||
}
|
||||
if (strcmp(cmdValue, "clear") == 0) {
|
||||
clearPending(taginfo);
|
||||
memcpy(taginfo->md5pending, taginfo->md5, sizeof(taginfo->md5pending));
|
||||
wsSendTaginfo(mac, SYNC_TAGSTATUS);
|
||||
}
|
||||
if (strcmp(cmdValue, "refresh") == 0) {
|
||||
updateContent(mac);
|
||||
}
|
||||
request->send(200, "text/plain", "Ok, done");
|
||||
} else {
|
||||
request->send(200, "text/plain", "Error: mac not found");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
request->send(500, "text/plain", "param error");
|
||||
}
|
||||
});
|
||||
|
||||
server.on("/get_ap_config", HTTP_GET, [](AsyncWebServerRequest *request) {
|
||||
UDPcomm udpsync;
|
||||
udpsync.getAPList();
|
||||
File configFile = contentFS->open("/current/apconfig.json", "r");
|
||||
if (!configFile) {
|
||||
request->send(500, "text/plain", "Error opening apconfig.json file");
|
||||
return;
|
||||
}
|
||||
request->send(configFile, "application/json");
|
||||
configFile.close();
|
||||
});
|
||||
|
||||
server.on("/save_apcfg", HTTP_POST, [](AsyncWebServerRequest *request) {
|
||||
if (request->hasParam("alias", true) && request->hasParam("channel", true)) {
|
||||
String aliasValue = request->getParam("alias", true)->value();
|
||||
size_t aliasLength = aliasValue.length();
|
||||
if (aliasLength > 31) aliasLength = 31;
|
||||
aliasValue.toCharArray(config.alias, aliasLength + 1);
|
||||
config.alias[aliasLength] = '\0';
|
||||
|
||||
config.channel = static_cast<uint8_t>(request->getParam("channel", true)->value().toInt());
|
||||
if (request->hasParam("led", true)) {
|
||||
config.led = static_cast<int16_t>(request->getParam("led", true)->value().toInt());
|
||||
updateBrightnessFromConfig();
|
||||
}
|
||||
if (request->hasParam("language", true)) {
|
||||
config.language = static_cast<uint8_t>(request->getParam("language", true)->value().toInt());
|
||||
updateLanguageFromConfig();
|
||||
}
|
||||
if (request->hasParam("maxsleep", true)) {
|
||||
config.maxsleep = static_cast<uint8_t>(request->getParam("maxsleep", true)->value().toInt());
|
||||
}
|
||||
if (request->hasParam("stopsleep", true)) {
|
||||
config.stopsleep = static_cast<uint8_t>(request->getParam("stopsleep", true)->value().toInt());
|
||||
}
|
||||
saveAPconfig();
|
||||
setAPchannel();
|
||||
}
|
||||
request->send(200, "text/plain", "Ok, saved");
|
||||
});
|
||||
|
||||
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();
|
||||
});
|
||||
|
||||
server.on("/sysinfo", HTTP_GET, handleSysinfoRequest);
|
||||
server.on("/check_file", HTTP_GET, handleCheckFile);
|
||||
server.on("/getexturl", HTTP_GET, handleGetExtUrl);
|
||||
server.on("/rollback", HTTP_POST, handleRollback);
|
||||
server.on("/update_actions", HTTP_POST, handleUpdateActions);
|
||||
server.on("/update_ota", HTTP_POST, [](AsyncWebServerRequest *request) {
|
||||
handleUpdateOTA(request);
|
||||
});
|
||||
server.on(
|
||||
"/littlefs_put", HTTP_POST, [](AsyncWebServerRequest *request) {
|
||||
request->send(200);
|
||||
},
|
||||
handleLittleFSUpload);
|
||||
|
||||
server.onNotFound([](AsyncWebServerRequest *request) {
|
||||
if (request->url() == "/" || request->url() == "index.htm") {
|
||||
request->send(200, "text/html", "index.html not found. Did you forget to upload the littlefs partition?");
|
||||
return;
|
||||
}
|
||||
request->send(404);
|
||||
});
|
||||
|
||||
server.begin();
|
||||
}
|
||||
|
||||
void doImageUpload(AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final) {
|
||||
if (!index) {
|
||||
if (request->hasParam("mac", true)) {
|
||||
filename = request->getParam("mac", true)->value() + ".jpg";
|
||||
} else {
|
||||
filename = "unknown.jpg";
|
||||
}
|
||||
request->_tempFile = contentFS->open("/" + filename, "w");
|
||||
}
|
||||
if (len) {
|
||||
// stream the incoming chunk to the opened file
|
||||
request->_tempFile.write(data, len);
|
||||
}
|
||||
if (final) {
|
||||
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\"}";
|
||||
taginfo->contentMode = 0;
|
||||
taginfo->nextupdate = 0;
|
||||
wsSendTaginfo(mac, SYNC_USERCFG);
|
||||
request->send(200, "text/plain", "Ok, saved");
|
||||
} else {
|
||||
request->send(200, "text/plain", "Error while saving: mac not found");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
request->send(500, "text/plain", "parameters incomplete");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,55 +1,64 @@
|
||||
|
||||
/* Autor: Aaron Christophel ATCnetz.de */
|
||||
|
||||
#include "zbs_interface.h"
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <SPI.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "settings.h"
|
||||
#ifdef USE_SOFTSPI
|
||||
#include <SoftSPI.h>
|
||||
#endif
|
||||
|
||||
void simplePowerOn() {
|
||||
pinMode(ZBS_SS, INPUT);
|
||||
pinMode(ZBS_CLK, INPUT);
|
||||
pinMode(ZBS_MoSi, INPUT);
|
||||
pinMode(ZBS_MiSo, INPUT);
|
||||
pinMode(ZBS_Reset, OUTPUT);
|
||||
digitalWrite(ZBS_Reset, HIGH);
|
||||
zbs.set_power(0);
|
||||
delay(500);
|
||||
zbs.set_power(1);
|
||||
}
|
||||
|
||||
uint8_t ZBS_interface::begin() {
|
||||
_SS_PIN = ZBS_SS;
|
||||
_CLK_PIN = ZBS_CLK;
|
||||
_MOSI_PIN = ZBS_MoSi;
|
||||
_MISO_PIN = ZBS_MiSo;
|
||||
_RESET_PIN = ZBS_Reset;
|
||||
#include "powermgt.h"
|
||||
|
||||
uint8_t ZBS_interface::begin(uint8_t SS, uint8_t CLK, uint8_t MOSI, uint8_t MISO, uint8_t RESET, uint8_t* POWER, uint8_t powerPins, uint32_t spi_speed) {
|
||||
_SS_PIN = SS;
|
||||
_CLK_PIN = CLK;
|
||||
_MOSI_PIN = MOSI;
|
||||
_MISO_PIN = MISO;
|
||||
_RESET_PIN = RESET;
|
||||
_POWER_PIN = POWER;
|
||||
pinMode(_SS_PIN, OUTPUT);
|
||||
pinMode(_RESET_PIN, OUTPUT);
|
||||
digitalWrite(_SS_PIN, HIGH);
|
||||
digitalWrite(_RESET_PIN, HIGH);
|
||||
set_power(ZBS_ON);
|
||||
pinMode(_CLK_PIN, OUTPUT);
|
||||
pinMode(_MOSI_PIN, OUTPUT);
|
||||
pinMode(_MISO_PIN, INPUT);
|
||||
pinMode(_RESET_PIN, OUTPUT);
|
||||
digitalWrite(_SS_PIN, HIGH);
|
||||
digitalWrite(_CLK_PIN, LOW);
|
||||
digitalWrite(_MOSI_PIN, HIGH);
|
||||
digitalWrite(_RESET_PIN, HIGH);
|
||||
set_power(ZBS_ON);
|
||||
|
||||
#ifdef USE_SOFTSPI
|
||||
if (!spi) spi = new SoftSPI(_MOSI_PIN, _MISO_PIN, _CLK_PIN);
|
||||
#else
|
||||
if (!spi) spi = new SPIClass(HSPI);
|
||||
#endif
|
||||
|
||||
|
||||
spiSettings = SPISettings(spi_speed, MSBFIRST, SPI_MODE0);
|
||||
spi_ready = 0;
|
||||
|
||||
if (spi_speed != 8000000) {
|
||||
after_byte_delay = 10;
|
||||
} else {
|
||||
after_byte_delay = 10;
|
||||
}
|
||||
enable_debug();
|
||||
return check_connection();
|
||||
}
|
||||
|
||||
void ZBS_interface::setSpeed(uint32_t speed) {
|
||||
spiSettings = SPISettings(speed, MSBFIRST, SPI_MODE0);
|
||||
}
|
||||
|
||||
ZBS_interface::~ZBS_interface() {
|
||||
if(spi)delete spi;
|
||||
}
|
||||
void ZBS_interface::set_power(uint8_t state) {
|
||||
pinMode(ZBS_POWER1, INPUT);
|
||||
pinMode(ZBS_POWER2, INPUT);
|
||||
digitalWrite(ZBS_POWER1, state);
|
||||
digitalWrite(ZBS_POWER2, state);
|
||||
pinMode(ZBS_POWER1, OUTPUT);
|
||||
pinMode(ZBS_POWER2, OUTPUT);
|
||||
powerControl(state, _POWER_PIN, _POWER_PINS);
|
||||
}
|
||||
|
||||
void ZBS_interface::enable_debug() {
|
||||
@@ -81,6 +90,7 @@ void ZBS_interface::enable_debug() {
|
||||
}
|
||||
|
||||
void ZBS_interface::reset() {
|
||||
spi->end();
|
||||
pinMode(_SS_PIN, INPUT);
|
||||
pinMode(_CLK_PIN, INPUT);
|
||||
pinMode(_MOSI_PIN, INPUT);
|
||||
@@ -96,18 +106,14 @@ void ZBS_interface::reset() {
|
||||
void ZBS_interface::send_byte(uint8_t data) {
|
||||
digitalWrite(_SS_PIN, LOW);
|
||||
delayMicroseconds(5);
|
||||
for (int i = 0; i < 8; i++) {
|
||||
if (data & 0x80) {
|
||||
digitalWrite(_MOSI_PIN, HIGH);
|
||||
} else {
|
||||
digitalWrite(_MOSI_PIN, LOW);
|
||||
}
|
||||
delayMicroseconds(ZBS_spi_delay);
|
||||
digitalWrite(_CLK_PIN, HIGH);
|
||||
delayMicroseconds(ZBS_spi_delay);
|
||||
digitalWrite(_CLK_PIN, LOW);
|
||||
data <<= 1;
|
||||
if (!spi_ready) {
|
||||
spi_ready = 1;
|
||||
spi->begin(_CLK_PIN, _MISO_PIN, _MOSI_PIN);
|
||||
}
|
||||
spi->beginTransaction(spiSettings);
|
||||
spi->transfer(data);
|
||||
spi->endTransaction();
|
||||
|
||||
delayMicroseconds(2);
|
||||
digitalWrite(_SS_PIN, HIGH);
|
||||
}
|
||||
@@ -116,16 +122,13 @@ uint8_t ZBS_interface::read_byte() {
|
||||
uint8_t data = 0x00;
|
||||
digitalWrite(_SS_PIN, LOW);
|
||||
delayMicroseconds(5);
|
||||
for (int i = 0; i < 8; i++) {
|
||||
data <<= 1;
|
||||
if (digitalRead(_MISO_PIN)) {
|
||||
data |= 1;
|
||||
}
|
||||
delayMicroseconds(ZBS_spi_delay);
|
||||
digitalWrite(_CLK_PIN, HIGH);
|
||||
delayMicroseconds(ZBS_spi_delay);
|
||||
digitalWrite(_CLK_PIN, LOW);
|
||||
if (!spi_ready) {
|
||||
spi_ready = 1;
|
||||
spi->begin(_CLK_PIN, _MISO_PIN, _MOSI_PIN);
|
||||
}
|
||||
spi->beginTransaction(spiSettings);
|
||||
data = spi->transfer(0xff);
|
||||
spi->endTransaction();
|
||||
delayMicroseconds(2);
|
||||
digitalWrite(_SS_PIN, HIGH);
|
||||
return data;
|
||||
@@ -135,7 +138,7 @@ void ZBS_interface::write_byte(uint8_t cmd, uint8_t addr, uint8_t data) {
|
||||
send_byte(cmd);
|
||||
send_byte(addr);
|
||||
send_byte(data);
|
||||
delay(1);
|
||||
delayMicroseconds(after_byte_delay);
|
||||
}
|
||||
|
||||
uint8_t ZBS_interface::read_byte(uint8_t cmd, uint8_t addr) {
|
||||
@@ -143,7 +146,7 @@ uint8_t ZBS_interface::read_byte(uint8_t cmd, uint8_t addr) {
|
||||
send_byte(cmd);
|
||||
send_byte(addr);
|
||||
data = read_byte();
|
||||
delay(1);
|
||||
delayMicroseconds(after_byte_delay);
|
||||
return data;
|
||||
}
|
||||
|
||||
@@ -152,7 +155,7 @@ void ZBS_interface::write_flash(uint16_t addr, uint8_t data) {
|
||||
send_byte(addr >> 8);
|
||||
send_byte(addr);
|
||||
send_byte(data);
|
||||
delay(1);
|
||||
delayMicroseconds(after_byte_delay);
|
||||
}
|
||||
|
||||
uint8_t ZBS_interface::read_flash(uint16_t addr) {
|
||||
@@ -161,7 +164,7 @@ uint8_t ZBS_interface::read_flash(uint16_t addr) {
|
||||
send_byte(addr >> 8);
|
||||
send_byte(addr);
|
||||
data = read_byte();
|
||||
delay(1);
|
||||
delayMicroseconds(after_byte_delay);
|
||||
return data;
|
||||
}
|
||||
|
||||
@@ -210,5 +213,3 @@ void ZBS_interface::erase_infoblock() {
|
||||
send_byte(0x00);
|
||||
delay(100);
|
||||
}
|
||||
|
||||
ZBS_interface zbs;
|
||||
2
Hardware/2.9 Flasher by Jonas/ESLCONN.bak
Normal file
2
Hardware/2.9 Flasher by Jonas/ESLCONN.bak
Normal file
@@ -0,0 +1,2 @@
|
||||
(kicad_symbol_lib (version 20211014) (generator kicad_symbol_editor)
|
||||
)
|
||||
64
Hardware/2.9 Flasher by Jonas/ESLCONN.kicad_sym
Normal file
64
Hardware/2.9 Flasher by Jonas/ESLCONN.kicad_sym
Normal file
@@ -0,0 +1,64 @@
|
||||
(kicad_symbol_lib (version 20211014) (generator kicad_symbol_editor)
|
||||
(symbol "ESL-connector" (in_bom yes) (on_board yes)
|
||||
(property "Reference" "U" (id 0) (at 0 11.43 0)
|
||||
(effects (font (size 1.27 1.27)))
|
||||
)
|
||||
(property "Value" "ESL-connector" (id 1) (at 0 -11.43 0)
|
||||
(effects (font (size 1.27 1.27)))
|
||||
)
|
||||
(property "Footprint" "" (id 2) (at 0 0 0)
|
||||
(effects (font (size 1.27 1.27)) hide)
|
||||
)
|
||||
(property "Datasheet" "" (id 3) (at 0 0 0)
|
||||
(effects (font (size 1.27 1.27)) hide)
|
||||
)
|
||||
(symbol "ESL-connector_0_1"
|
||||
(rectangle (start -6.35 10.16) (end 6.35 -10.16)
|
||||
(stroke (width 0.1524) (type default) (color 0 0 0 0))
|
||||
(fill (type none))
|
||||
)
|
||||
)
|
||||
(symbol "ESL-connector_1_1"
|
||||
(pin power_in line (at -8.89 7.62 0) (length 2.54)
|
||||
(name "VCC" (effects (font (size 1.27 1.27))))
|
||||
(number "1" (effects (font (size 1.27 1.27))))
|
||||
)
|
||||
(pin bidirectional line (at 8.89 -7.62 180) (length 2.54)
|
||||
(name "P1.0" (effects (font (size 1.27 1.27))))
|
||||
(number "10" (effects (font (size 1.27 1.27))))
|
||||
)
|
||||
(pin input line (at -8.89 3.81 0) (length 2.54)
|
||||
(name "MoSi" (effects (font (size 1.27 1.27))))
|
||||
(number "2" (effects (font (size 1.27 1.27))))
|
||||
)
|
||||
(pin input line (at -8.89 0 0) (length 2.54)
|
||||
(name "SS" (effects (font (size 1.27 1.27))))
|
||||
(number "3" (effects (font (size 1.27 1.27))))
|
||||
)
|
||||
(pin output line (at -8.89 -3.81 0) (length 2.54)
|
||||
(name "TXD" (effects (font (size 1.27 1.27))))
|
||||
(number "4" (effects (font (size 1.27 1.27))))
|
||||
)
|
||||
(pin input line (at -8.89 -7.62 0) (length 2.54)
|
||||
(name "CLK" (effects (font (size 1.27 1.27))))
|
||||
(number "5" (effects (font (size 1.27 1.27))))
|
||||
)
|
||||
(pin output line (at 8.89 7.62 180) (length 2.54)
|
||||
(name "MiSo" (effects (font (size 1.27 1.27))))
|
||||
(number "6" (effects (font (size 1.27 1.27))))
|
||||
)
|
||||
(pin power_in line (at 8.89 3.81 180) (length 2.54)
|
||||
(name "GND" (effects (font (size 1.27 1.27))))
|
||||
(number "7" (effects (font (size 1.27 1.27))))
|
||||
)
|
||||
(pin input line (at 8.89 0 180) (length 2.54)
|
||||
(name "RST" (effects (font (size 1.27 1.27))))
|
||||
(number "8" (effects (font (size 1.27 1.27))))
|
||||
)
|
||||
(pin input line (at 8.89 -3.81 180) (length 2.54)
|
||||
(name "RXD" (effects (font (size 1.27 1.27))))
|
||||
(number "9" (effects (font (size 1.27 1.27))))
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
26
Hardware/2.9 Flasher by Jonas/README.md
Normal file
26
Hardware/2.9 Flasher by Jonas/README.md
Normal file
@@ -0,0 +1,26 @@
|
||||
# Alternative PCB for 2.9" flashing #
|
||||
|
||||
This is an alternative PCB for flashing 2.9" ESLs
|
||||
|
||||
<img width="600" alt="pcb" src="pcb.png">
|
||||
|
||||
<img width="600" alt="bot" src="jig_bot.jpg">
|
||||
|
||||
<img width="600" alt="top" src="jig_top.jpg">
|
||||
|
||||
### Parts (per board) ###
|
||||
* 10x 1mm pogo pin
|
||||
* 3x Switches
|
||||
* 3x 3mm LEDs
|
||||
* 3x 0805 SMD resistor (47 ohm or so, depending on LED color, personal taste, whatever)
|
||||
* ESP32-DevKit-Lipo form Olimex
|
||||
* please check the PCB for the rest
|
||||
|
||||
### Errata ###
|
||||
The ESP32 board needs to be solder upside down
|
||||
|
||||
## Getting PCB's ##
|
||||
You can order the boards from your favorite boardhouse, using the zip file in this repository.
|
||||
|
||||
## Disclaimer ##
|
||||
There is no guarantee or warranty whatsoever, nor is there any promise or insinuation that this board fill fullfill any particular purpose. This board may very well not work for you, set your hair and/or, but not limited to, pants on fire, incite violance or persuade other countries to invade your country. You're on your own, chief!
|
||||
@@ -0,0 +1,40 @@
|
||||
(footprint "SOLUM_ZBS_DEBUG" (version 20211014) (generator pcbnew)
|
||||
(layer "F.Cu")
|
||||
(tedit 0)
|
||||
(descr "<h3>Solum Debug header pinout for 1mm pogo pins</h3>")
|
||||
(fp_text reference "J19" (at 0 0) (layer "F.SilkS") hide
|
||||
(effects (font (size 1.27 1.27) (thickness 0.15)))
|
||||
(tstamp 82ec8fc7-4947-43e8-a28a-f87f8d8a424a)
|
||||
)
|
||||
(fp_text value "" (at 0 0) (layer "F.Fab") hide
|
||||
(effects (font (size 1.27 1.27) (thickness 0.15)))
|
||||
(tstamp 9be777ad-9f8e-434c-9925-b4f3cbcf667d)
|
||||
)
|
||||
(fp_line (start -3.745 -2.506) (end -3.745 2.494) (layer "F.SilkS") (width 0.127) (tstamp 230f73ed-7388-4311-bd67-896af89b3fcb))
|
||||
(fp_line (start -3.345 0.044) (end -2.745 -0.356) (layer "F.SilkS") (width 0.127) (tstamp 25b0ffe5-9f43-42b5-ad90-4a164f170e55))
|
||||
(fp_line (start 3.755 -2.506) (end 3.755 2.494) (layer "F.SilkS") (width 0.127) (tstamp 53faed3a-63cc-4487-8883-b5c4197265c2))
|
||||
(fp_line (start -3.345 0.044) (end -2.745 0.444) (layer "F.SilkS") (width 0.127) (tstamp 5a223cb8-3c61-4d1f-a412-d9c7d77f66b9))
|
||||
(fp_line (start -3.745 -2.506) (end 3.755 -2.506) (layer "F.SilkS") (width 0.127) (tstamp 85c102fc-8b65-4d83-a6fe-e043723d82a4))
|
||||
(fp_line (start -3.745 2.494) (end 3.755 2.494) (layer "F.SilkS") (width 0.127) (tstamp a1645c22-11d6-485a-92a9-3621b643513c))
|
||||
(fp_line (start -3.345 0.044) (end -1.495 0.044) (layer "F.SilkS") (width 0.127) (tstamp dab4d2de-3395-4723-9799-d0bb3a191c1b))
|
||||
(pad "1" thru_hole circle (at -2.995 1.744) (size 1.27 1.27) (drill 1) (layers *.Cu *.Mask)
|
||||
(solder_mask_margin 0.0508) (tstamp 71343d1a-b887-4c75-8f6e-3783c4cee9df))
|
||||
(pad "2" thru_hole circle (at -0.995 1.744) (size 1.27 1.27) (drill 1) (layers *.Cu *.Mask)
|
||||
(solder_mask_margin 0.0508) (tstamp a5bba08a-0aac-4518-8a18-8e86b2e9784c))
|
||||
(pad "3" thru_hole circle (at 1.005 1.744) (size 1.27 1.27) (drill 1) (layers *.Cu *.Mask)
|
||||
(solder_mask_margin 0.0508) (tstamp 64903969-f443-47c1-a03d-9412f3950a7f))
|
||||
(pad "4" thru_hole circle (at 3.005 1.794) (size 1.27 1.27) (drill 1) (layers *.Cu *.Mask)
|
||||
(solder_mask_margin 0.0508) (tstamp 53ec6283-c57d-491a-9c98-1df814704014))
|
||||
(pad "5" thru_hole circle (at 0.005 -0.006) (size 1.27 1.27) (drill 1) (layers *.Cu *.Mask)
|
||||
(solder_mask_margin 0.0508) (tstamp 6b11af81-f064-49d0-8c15-50a5af3bdce8))
|
||||
(pad "6" thru_hole circle (at 2.005 -0.006) (size 1.27 1.27) (drill 1) (layers *.Cu *.Mask)
|
||||
(solder_mask_margin 0.0508) (tstamp 048099e4-1d18-4e68-a98e-c376a592cc1d))
|
||||
(pad "7" thru_hole circle (at -2.995 -1.756) (size 1.27 1.27) (drill 1) (layers *.Cu *.Mask)
|
||||
(solder_mask_margin 0.0508) (tstamp 323c1c33-7532-46f9-b99c-f77f2344710a))
|
||||
(pad "8" thru_hole circle (at -0.995 -1.756) (size 1.27 1.27) (drill 1) (layers *.Cu *.Mask)
|
||||
(solder_mask_margin 0.0508) (tstamp e067c309-8b30-4efa-b5e6-088c3f57692f))
|
||||
(pad "9" thru_hole circle (at 1.005 -1.756) (size 1.27 1.27) (drill 1) (layers *.Cu *.Mask)
|
||||
(solder_mask_margin 0.0508) (tstamp f4cd57db-f82c-479a-9526-cfebc536273b))
|
||||
(pad "10" thru_hole circle (at 3.005 -1.756) (size 1.27 1.27) (drill 1) (layers *.Cu *.Mask)
|
||||
(solder_mask_margin 0.0508) (tstamp 2700ab94-06e0-45e2-9edc-3815ba09c9d6))
|
||||
)
|
||||
3
Hardware/2.9 Flasher by Jonas/fp-lib-table
Normal file
3
Hardware/2.9 Flasher by Jonas/fp-lib-table
Normal file
@@ -0,0 +1,3 @@
|
||||
(fp_lib_table
|
||||
(lib (name "esl")(type "KiCad")(uri "${KIPRJMOD}/esl.pretty")(options "")(descr ""))
|
||||
)
|
||||
BIN
Hardware/2.9 Flasher by Jonas/gerber.zip
Normal file
BIN
Hardware/2.9 Flasher by Jonas/gerber.zip
Normal file
Binary file not shown.
252
Hardware/2.9 Flasher by Jonas/gerber/jig2-B_Cu.gbl
Normal file
252
Hardware/2.9 Flasher by Jonas/gerber/jig2-B_Cu.gbl
Normal file
@@ -0,0 +1,252 @@
|
||||
G04 #@! TF.GenerationSoftware,KiCad,Pcbnew,(6.0.11)*
|
||||
G04 #@! TF.CreationDate,2023-03-04T17:24:56+01:00*
|
||||
G04 #@! TF.ProjectId,jig2,6a696732-2e6b-4696-9361-645f70636258,rev?*
|
||||
G04 #@! TF.SameCoordinates,Original*
|
||||
G04 #@! TF.FileFunction,Copper,L2,Bot*
|
||||
G04 #@! TF.FilePolarity,Positive*
|
||||
%FSLAX46Y46*%
|
||||
G04 Gerber Fmt 4.6, Leading zero omitted, Abs format (unit mm)*
|
||||
G04 Created by KiCad (PCBNEW (6.0.11)) date 2023-03-04 17:24:56*
|
||||
%MOMM*%
|
||||
%LPD*%
|
||||
G01*
|
||||
G04 APERTURE LIST*
|
||||
G04 #@! TA.AperFunction,ComponentPad*
|
||||
%ADD10C,2.000000*%
|
||||
G04 #@! TD*
|
||||
G04 #@! TA.AperFunction,ComponentPad*
|
||||
%ADD11R,1.700000X1.700000*%
|
||||
G04 #@! TD*
|
||||
G04 #@! TA.AperFunction,ComponentPad*
|
||||
%ADD12O,1.700000X1.700000*%
|
||||
G04 #@! TD*
|
||||
G04 #@! TA.AperFunction,ComponentPad*
|
||||
%ADD13R,1.800000X1.800000*%
|
||||
G04 #@! TD*
|
||||
G04 #@! TA.AperFunction,ComponentPad*
|
||||
%ADD14C,1.800000*%
|
||||
G04 #@! TD*
|
||||
G04 #@! TA.AperFunction,ComponentPad*
|
||||
%ADD15C,1.270000*%
|
||||
G04 #@! TD*
|
||||
G04 #@! TA.AperFunction,ViaPad*
|
||||
%ADD16C,0.600000*%
|
||||
G04 #@! TD*
|
||||
G04 #@! TA.AperFunction,Conductor*
|
||||
%ADD17C,1.000000*%
|
||||
G04 #@! TD*
|
||||
G04 #@! TA.AperFunction,Conductor*
|
||||
%ADD18C,0.500000*%
|
||||
G04 #@! TD*
|
||||
G04 APERTURE END LIST*
|
||||
D10*
|
||||
X9250000Y-4765564D03*
|
||||
X2750000Y-4765564D03*
|
||||
X2750000Y-9265564D03*
|
||||
X9250000Y-9265564D03*
|
||||
D11*
|
||||
X7620000Y-35560000D03*
|
||||
D12*
|
||||
X7620000Y-38100000D03*
|
||||
X7620000Y-40640000D03*
|
||||
X7620000Y-43180000D03*
|
||||
X7620000Y-45720000D03*
|
||||
X7620000Y-48260000D03*
|
||||
X7620000Y-50800000D03*
|
||||
X7620000Y-53340000D03*
|
||||
X7620000Y-55880000D03*
|
||||
X7620000Y-58420000D03*
|
||||
X7620000Y-60960000D03*
|
||||
X7620000Y-63500000D03*
|
||||
X7620000Y-66040000D03*
|
||||
X7620000Y-68580000D03*
|
||||
X7620000Y-71120000D03*
|
||||
X7620000Y-73660000D03*
|
||||
X7620000Y-76200000D03*
|
||||
X7620000Y-78740000D03*
|
||||
X7620000Y-81280000D03*
|
||||
D13*
|
||||
X23290000Y-5080000D03*
|
||||
D14*
|
||||
X25830000Y-5080000D03*
|
||||
D11*
|
||||
X32975000Y-35570000D03*
|
||||
D12*
|
||||
X32975000Y-38110000D03*
|
||||
X32975000Y-40650000D03*
|
||||
X32975000Y-43190000D03*
|
||||
X32975000Y-45730000D03*
|
||||
X32975000Y-48270000D03*
|
||||
X32975000Y-50810000D03*
|
||||
X32975000Y-53350000D03*
|
||||
X32975000Y-55890000D03*
|
||||
X32975000Y-58430000D03*
|
||||
X32975000Y-60970000D03*
|
||||
X32975000Y-63510000D03*
|
||||
X32975000Y-66050000D03*
|
||||
X32975000Y-68590000D03*
|
||||
X32975000Y-71130000D03*
|
||||
X32975000Y-73670000D03*
|
||||
X32975000Y-76210000D03*
|
||||
X32975000Y-78750000D03*
|
||||
X32975000Y-81290000D03*
|
||||
D15*
|
||||
X28256000Y-17005000D03*
|
||||
X28256000Y-19005000D03*
|
||||
X28256000Y-21005000D03*
|
||||
X28206000Y-23005000D03*
|
||||
X30006000Y-20005000D03*
|
||||
X30006000Y-22005000D03*
|
||||
X31756000Y-17005000D03*
|
||||
X31756000Y-19005000D03*
|
||||
X31756000Y-21005000D03*
|
||||
X31756000Y-23005000D03*
|
||||
D13*
|
||||
X28830000Y-5080000D03*
|
||||
D14*
|
||||
X31370000Y-5080000D03*
|
||||
D10*
|
||||
X19250000Y-4765564D03*
|
||||
X12750000Y-4765564D03*
|
||||
X19250000Y-9265564D03*
|
||||
X12750000Y-9265564D03*
|
||||
D13*
|
||||
X34370000Y-5080000D03*
|
||||
D14*
|
||||
X36910000Y-5080000D03*
|
||||
D10*
|
||||
X14750000Y-20820000D03*
|
||||
X21250000Y-20820000D03*
|
||||
X14750000Y-25320000D03*
|
||||
X21250000Y-25320000D03*
|
||||
D16*
|
||||
X23000000Y-35500000D03*
|
||||
X24000000Y-39700000D03*
|
||||
X11000000Y-29750000D03*
|
||||
X11500000Y-15000000D03*
|
||||
X14440000Y-79310000D03*
|
||||
X7620000Y-21620000D03*
|
||||
X20500000Y-17000000D03*
|
||||
X20500000Y-15250000D03*
|
||||
X21000000Y-13750000D03*
|
||||
X26750000Y-38250000D03*
|
||||
D17*
|
||||
X23000000Y-37093502D02*
|
||||
X23000000Y-35500000D01*
|
||||
X24828249Y-38921751D02*
|
||||
X24050000Y-39700000D01*
|
||||
X25606498Y-39700000D02*
|
||||
X24828249Y-38921751D01*
|
||||
X24828249Y-38921751D02*
|
||||
X23000000Y-37093502D01*
|
||||
X32975000Y-35570000D02*
|
||||
X28845000Y-39700000D01*
|
||||
X24050000Y-39700000D02*
|
||||
X24000000Y-39700000D01*
|
||||
D18*
|
||||
X11000000Y-15500000D02*
|
||||
X11500000Y-15000000D01*
|
||||
D17*
|
||||
X28845000Y-39700000D02*
|
||||
X25606498Y-39700000D01*
|
||||
D18*
|
||||
X11000000Y-29750000D02*
|
||||
X11000000Y-15500000D01*
|
||||
X32975000Y-40650000D02*
|
||||
X23150000Y-40650000D01*
|
||||
X19250000Y-36750000D02*
|
||||
X19250000Y-9265564D01*
|
||||
X23150000Y-40650000D02*
|
||||
X19250000Y-36750000D01*
|
||||
X21940000Y-43190000D02*
|
||||
X16750000Y-38000000D01*
|
||||
X32975000Y-43190000D02*
|
||||
X21940000Y-43190000D01*
|
||||
X16750000Y-38000000D02*
|
||||
X16750000Y-16765564D01*
|
||||
X16750000Y-16765564D02*
|
||||
X9250000Y-9265564D01*
|
||||
X14750000Y-40250000D02*
|
||||
X14750000Y-25320000D01*
|
||||
X20230000Y-45730000D02*
|
||||
X14750000Y-40250000D01*
|
||||
X32975000Y-45730000D02*
|
||||
X20230000Y-45730000D01*
|
||||
D17*
|
||||
X9200000Y-50800000D02*
|
||||
X10750000Y-49250000D01*
|
||||
X10750000Y-49250000D02*
|
||||
X10750000Y-37250000D01*
|
||||
D18*
|
||||
X25410000Y-68590000D02*
|
||||
X25160000Y-68590000D01*
|
||||
X6820000Y-20820000D02*
|
||||
X7620000Y-21620000D01*
|
||||
D17*
|
||||
X33505000Y-17005000D02*
|
||||
X31756000Y-17005000D01*
|
||||
X9060000Y-35560000D02*
|
||||
X7620000Y-35560000D01*
|
||||
X7620000Y-50800000D02*
|
||||
X9200000Y-50800000D01*
|
||||
D18*
|
||||
X4200000Y-6215564D02*
|
||||
X2750000Y-4765564D01*
|
||||
D17*
|
||||
X34525000Y-18025000D02*
|
||||
X33505000Y-17005000D01*
|
||||
X10750000Y-37250000D02*
|
||||
X9060000Y-35560000D01*
|
||||
X32975000Y-68590000D02*
|
||||
X25410000Y-68590000D01*
|
||||
D18*
|
||||
X25160000Y-68590000D02*
|
||||
X14440000Y-79310000D01*
|
||||
X6820000Y-20820000D02*
|
||||
X4200000Y-18200000D01*
|
||||
D17*
|
||||
X34525000Y-67040000D02*
|
||||
X34525000Y-18025000D01*
|
||||
X7620000Y-50800000D02*
|
||||
X25410000Y-68590000D01*
|
||||
D18*
|
||||
X7620000Y-21620000D02*
|
||||
X7620000Y-35560000D01*
|
||||
D17*
|
||||
X32975000Y-68590000D02*
|
||||
X34525000Y-67040000D01*
|
||||
D18*
|
||||
X4200000Y-18200000D02*
|
||||
X4200000Y-6215564D01*
|
||||
X29239950Y-7750000D02*
|
||||
X20500000Y-16489950D01*
|
||||
X31700000Y-7750000D02*
|
||||
X29239950Y-7750000D01*
|
||||
X34370000Y-5080000D02*
|
||||
X31700000Y-7750000D01*
|
||||
X20500000Y-16489950D02*
|
||||
X20500000Y-16750000D01*
|
||||
X20500000Y-16750000D02*
|
||||
X20500000Y-17000000D01*
|
||||
X28830000Y-7170000D02*
|
||||
X20750000Y-15250000D01*
|
||||
X20750000Y-15250000D02*
|
||||
X20500000Y-15250000D01*
|
||||
X28830000Y-5080000D02*
|
||||
X28830000Y-7170000D01*
|
||||
X23290000Y-5080000D02*
|
||||
X23290000Y-11460000D01*
|
||||
X23290000Y-11460000D02*
|
||||
X21000000Y-13750000D01*
|
||||
D17*
|
||||
X25250000Y-18250000D02*
|
||||
X26495000Y-17005000D01*
|
||||
X26495000Y-17005000D02*
|
||||
X28256000Y-17005000D01*
|
||||
X26750000Y-26250000D02*
|
||||
X25250000Y-24750000D01*
|
||||
X26750000Y-38250000D02*
|
||||
X26750000Y-26250000D01*
|
||||
X25250000Y-24750000D02*
|
||||
X25250000Y-18250000D01*
|
||||
M02*
|
||||
101
Hardware/2.9 Flasher by Jonas/gerber/jig2-B_Mask.gbs
Normal file
101
Hardware/2.9 Flasher by Jonas/gerber/jig2-B_Mask.gbs
Normal file
@@ -0,0 +1,101 @@
|
||||
G04 #@! TF.GenerationSoftware,KiCad,Pcbnew,(6.0.11)*
|
||||
G04 #@! TF.CreationDate,2023-03-04T17:24:57+01:00*
|
||||
G04 #@! TF.ProjectId,jig2,6a696732-2e6b-4696-9361-645f70636258,rev?*
|
||||
G04 #@! TF.SameCoordinates,Original*
|
||||
G04 #@! TF.FileFunction,Soldermask,Bot*
|
||||
G04 #@! TF.FilePolarity,Negative*
|
||||
%FSLAX46Y46*%
|
||||
G04 Gerber Fmt 4.6, Leading zero omitted, Abs format (unit mm)*
|
||||
G04 Created by KiCad (PCBNEW (6.0.11)) date 2023-03-04 17:24:57*
|
||||
%MOMM*%
|
||||
%LPD*%
|
||||
G01*
|
||||
G04 APERTURE LIST*
|
||||
%ADD10C,2.000000*%
|
||||
%ADD11R,1.700000X1.700000*%
|
||||
%ADD12O,1.700000X1.700000*%
|
||||
%ADD13R,1.800000X1.800000*%
|
||||
%ADD14C,1.800000*%
|
||||
%ADD15C,1.371600*%
|
||||
G04 APERTURE END LIST*
|
||||
D10*
|
||||
X9250000Y-4765564D03*
|
||||
X2750000Y-4765564D03*
|
||||
X2750000Y-9265564D03*
|
||||
X9250000Y-9265564D03*
|
||||
D11*
|
||||
X7620000Y-35560000D03*
|
||||
D12*
|
||||
X7620000Y-38100000D03*
|
||||
X7620000Y-40640000D03*
|
||||
X7620000Y-43180000D03*
|
||||
X7620000Y-45720000D03*
|
||||
X7620000Y-48260000D03*
|
||||
X7620000Y-50800000D03*
|
||||
X7620000Y-53340000D03*
|
||||
X7620000Y-55880000D03*
|
||||
X7620000Y-58420000D03*
|
||||
X7620000Y-60960000D03*
|
||||
X7620000Y-63500000D03*
|
||||
X7620000Y-66040000D03*
|
||||
X7620000Y-68580000D03*
|
||||
X7620000Y-71120000D03*
|
||||
X7620000Y-73660000D03*
|
||||
X7620000Y-76200000D03*
|
||||
X7620000Y-78740000D03*
|
||||
X7620000Y-81280000D03*
|
||||
D13*
|
||||
X23290000Y-5080000D03*
|
||||
D14*
|
||||
X25830000Y-5080000D03*
|
||||
D11*
|
||||
X32975000Y-35570000D03*
|
||||
D12*
|
||||
X32975000Y-38110000D03*
|
||||
X32975000Y-40650000D03*
|
||||
X32975000Y-43190000D03*
|
||||
X32975000Y-45730000D03*
|
||||
X32975000Y-48270000D03*
|
||||
X32975000Y-50810000D03*
|
||||
X32975000Y-53350000D03*
|
||||
X32975000Y-55890000D03*
|
||||
X32975000Y-58430000D03*
|
||||
X32975000Y-60970000D03*
|
||||
X32975000Y-63510000D03*
|
||||
X32975000Y-66050000D03*
|
||||
X32975000Y-68590000D03*
|
||||
X32975000Y-71130000D03*
|
||||
X32975000Y-73670000D03*
|
||||
X32975000Y-76210000D03*
|
||||
X32975000Y-78750000D03*
|
||||
X32975000Y-81290000D03*
|
||||
D15*
|
||||
X28256000Y-17005000D03*
|
||||
X28256000Y-19005000D03*
|
||||
X28256000Y-21005000D03*
|
||||
X28206000Y-23005000D03*
|
||||
X30006000Y-20005000D03*
|
||||
X30006000Y-22005000D03*
|
||||
X31756000Y-17005000D03*
|
||||
X31756000Y-19005000D03*
|
||||
X31756000Y-21005000D03*
|
||||
X31756000Y-23005000D03*
|
||||
D13*
|
||||
X28830000Y-5080000D03*
|
||||
D14*
|
||||
X31370000Y-5080000D03*
|
||||
D10*
|
||||
X19250000Y-4765564D03*
|
||||
X12750000Y-4765564D03*
|
||||
X19250000Y-9265564D03*
|
||||
X12750000Y-9265564D03*
|
||||
D13*
|
||||
X34370000Y-5080000D03*
|
||||
D14*
|
||||
X36910000Y-5080000D03*
|
||||
D10*
|
||||
X14750000Y-20820000D03*
|
||||
X21250000Y-20820000D03*
|
||||
X14750000Y-25320000D03*
|
||||
X21250000Y-25320000D03*
|
||||
M02*
|
||||
15
Hardware/2.9 Flasher by Jonas/gerber/jig2-B_Paste.gbp
Normal file
15
Hardware/2.9 Flasher by Jonas/gerber/jig2-B_Paste.gbp
Normal file
@@ -0,0 +1,15 @@
|
||||
G04 #@! TF.GenerationSoftware,KiCad,Pcbnew,(6.0.11)*
|
||||
G04 #@! TF.CreationDate,2023-03-04T17:24:57+01:00*
|
||||
G04 #@! TF.ProjectId,jig2,6a696732-2e6b-4696-9361-645f70636258,rev?*
|
||||
G04 #@! TF.SameCoordinates,Original*
|
||||
G04 #@! TF.FileFunction,Paste,Bot*
|
||||
G04 #@! TF.FilePolarity,Positive*
|
||||
%FSLAX46Y46*%
|
||||
G04 Gerber Fmt 4.6, Leading zero omitted, Abs format (unit mm)*
|
||||
G04 Created by KiCad (PCBNEW (6.0.11)) date 2023-03-04 17:24:57*
|
||||
%MOMM*%
|
||||
%LPD*%
|
||||
G01*
|
||||
G04 APERTURE LIST*
|
||||
G04 APERTURE END LIST*
|
||||
M02*
|
||||
153
Hardware/2.9 Flasher by Jonas/gerber/jig2-B_Silkscreen.gbo
Normal file
153
Hardware/2.9 Flasher by Jonas/gerber/jig2-B_Silkscreen.gbo
Normal file
@@ -0,0 +1,153 @@
|
||||
G04 #@! TF.GenerationSoftware,KiCad,Pcbnew,(6.0.11)*
|
||||
G04 #@! TF.CreationDate,2023-03-04T17:24:57+01:00*
|
||||
G04 #@! TF.ProjectId,jig2,6a696732-2e6b-4696-9361-645f70636258,rev?*
|
||||
G04 #@! TF.SameCoordinates,Original*
|
||||
G04 #@! TF.FileFunction,Legend,Bot*
|
||||
G04 #@! TF.FilePolarity,Positive*
|
||||
%FSLAX46Y46*%
|
||||
G04 Gerber Fmt 4.6, Leading zero omitted, Abs format (unit mm)*
|
||||
G04 Created by KiCad (PCBNEW (6.0.11)) date 2023-03-04 17:24:57*
|
||||
%MOMM*%
|
||||
%LPD*%
|
||||
G01*
|
||||
G04 APERTURE LIST*
|
||||
%ADD10C,0.150000*%
|
||||
%ADD11C,0.120000*%
|
||||
%ADD12C,2.000000*%
|
||||
%ADD13R,1.700000X1.700000*%
|
||||
%ADD14O,1.700000X1.700000*%
|
||||
%ADD15R,1.800000X1.800000*%
|
||||
%ADD16C,1.800000*%
|
||||
%ADD17C,1.371600*%
|
||||
G04 APERTURE END LIST*
|
||||
D10*
|
||||
X19333333Y-19224761D02*
|
||||
X19190476Y-19272380D01*
|
||||
X18952380Y-19272380D01*
|
||||
X18857142Y-19224761D01*
|
||||
X18809523Y-19177142D01*
|
||||
X18761904Y-19081904D01*
|
||||
X18761904Y-18986666D01*
|
||||
X18809523Y-18891428D01*
|
||||
X18857142Y-18843809D01*
|
||||
X18952380Y-18796190D01*
|
||||
X19142857Y-18748571D01*
|
||||
X19238095Y-18700952D01*
|
||||
X19285714Y-18653333D01*
|
||||
X19333333Y-18558095D01*
|
||||
X19333333Y-18462857D01*
|
||||
X19285714Y-18367619D01*
|
||||
X19238095Y-18320000D01*
|
||||
X19142857Y-18272380D01*
|
||||
X18904761Y-18272380D01*
|
||||
X18761904Y-18320000D01*
|
||||
X18428571Y-18272380D02*
|
||||
X18190476Y-19272380D01*
|
||||
X18000000Y-18558095D01*
|
||||
X17809523Y-19272380D01*
|
||||
X17571428Y-18272380D01*
|
||||
X17285714Y-18272380D02*
|
||||
X16666666Y-18272380D01*
|
||||
X17000000Y-18653333D01*
|
||||
X16857142Y-18653333D01*
|
||||
X16761904Y-18700952D01*
|
||||
X16714285Y-18748571D01*
|
||||
X16666666Y-18843809D01*
|
||||
X16666666Y-19081904D01*
|
||||
X16714285Y-19177142D01*
|
||||
X16761904Y-19224761D01*
|
||||
X16857142Y-19272380D01*
|
||||
X17142857Y-19272380D01*
|
||||
X17238095Y-19224761D01*
|
||||
X17285714Y-19177142D01*
|
||||
D11*
|
||||
X14500000Y-23820000D02*
|
||||
X14500000Y-22320000D01*
|
||||
X20250000Y-26320000D02*
|
||||
X15750000Y-26320000D01*
|
||||
X21500000Y-22320000D02*
|
||||
X21500000Y-23820000D01*
|
||||
X15750000Y-19820000D02*
|
||||
X20250000Y-19820000D01*
|
||||
%LPC*%
|
||||
D12*
|
||||
X9250000Y-4765564D03*
|
||||
X2750000Y-4765564D03*
|
||||
X2750000Y-9265564D03*
|
||||
X9250000Y-9265564D03*
|
||||
D13*
|
||||
X7620000Y-35560000D03*
|
||||
D14*
|
||||
X7620000Y-38100000D03*
|
||||
X7620000Y-40640000D03*
|
||||
X7620000Y-43180000D03*
|
||||
X7620000Y-45720000D03*
|
||||
X7620000Y-48260000D03*
|
||||
X7620000Y-50800000D03*
|
||||
X7620000Y-53340000D03*
|
||||
X7620000Y-55880000D03*
|
||||
X7620000Y-58420000D03*
|
||||
X7620000Y-60960000D03*
|
||||
X7620000Y-63500000D03*
|
||||
X7620000Y-66040000D03*
|
||||
X7620000Y-68580000D03*
|
||||
X7620000Y-71120000D03*
|
||||
X7620000Y-73660000D03*
|
||||
X7620000Y-76200000D03*
|
||||
X7620000Y-78740000D03*
|
||||
X7620000Y-81280000D03*
|
||||
D15*
|
||||
X23290000Y-5080000D03*
|
||||
D16*
|
||||
X25830000Y-5080000D03*
|
||||
D13*
|
||||
X32975000Y-35570000D03*
|
||||
D14*
|
||||
X32975000Y-38110000D03*
|
||||
X32975000Y-40650000D03*
|
||||
X32975000Y-43190000D03*
|
||||
X32975000Y-45730000D03*
|
||||
X32975000Y-48270000D03*
|
||||
X32975000Y-50810000D03*
|
||||
X32975000Y-53350000D03*
|
||||
X32975000Y-55890000D03*
|
||||
X32975000Y-58430000D03*
|
||||
X32975000Y-60970000D03*
|
||||
X32975000Y-63510000D03*
|
||||
X32975000Y-66050000D03*
|
||||
X32975000Y-68590000D03*
|
||||
X32975000Y-71130000D03*
|
||||
X32975000Y-73670000D03*
|
||||
X32975000Y-76210000D03*
|
||||
X32975000Y-78750000D03*
|
||||
X32975000Y-81290000D03*
|
||||
D17*
|
||||
X28256000Y-17005000D03*
|
||||
X28256000Y-19005000D03*
|
||||
X28256000Y-21005000D03*
|
||||
X28206000Y-23005000D03*
|
||||
X30006000Y-20005000D03*
|
||||
X30006000Y-22005000D03*
|
||||
X31756000Y-17005000D03*
|
||||
X31756000Y-19005000D03*
|
||||
X31756000Y-21005000D03*
|
||||
X31756000Y-23005000D03*
|
||||
D15*
|
||||
X28830000Y-5080000D03*
|
||||
D16*
|
||||
X31370000Y-5080000D03*
|
||||
D12*
|
||||
X19250000Y-4765564D03*
|
||||
X12750000Y-4765564D03*
|
||||
X19250000Y-9265564D03*
|
||||
X12750000Y-9265564D03*
|
||||
D15*
|
||||
X34370000Y-5080000D03*
|
||||
D16*
|
||||
X36910000Y-5080000D03*
|
||||
D12*
|
||||
X14750000Y-20820000D03*
|
||||
X21250000Y-20820000D03*
|
||||
X14750000Y-25320000D03*
|
||||
X21250000Y-25320000D03*
|
||||
M02*
|
||||
26
Hardware/2.9 Flasher by Jonas/gerber/jig2-Edge_Cuts.gm1
Normal file
26
Hardware/2.9 Flasher by Jonas/gerber/jig2-Edge_Cuts.gm1
Normal file
@@ -0,0 +1,26 @@
|
||||
G04 #@! TF.GenerationSoftware,KiCad,Pcbnew,(6.0.11)*
|
||||
G04 #@! TF.CreationDate,2023-03-04T17:24:57+01:00*
|
||||
G04 #@! TF.ProjectId,jig2,6a696732-2e6b-4696-9361-645f70636258,rev?*
|
||||
G04 #@! TF.SameCoordinates,Original*
|
||||
G04 #@! TF.FileFunction,Profile,NP*
|
||||
%FSLAX46Y46*%
|
||||
G04 Gerber Fmt 4.6, Leading zero omitted, Abs format (unit mm)*
|
||||
G04 Created by KiCad (PCBNEW (6.0.11)) date 2023-03-04 17:24:57*
|
||||
%MOMM*%
|
||||
%LPD*%
|
||||
G01*
|
||||
G04 APERTURE LIST*
|
||||
G04 #@! TA.AperFunction,Profile*
|
||||
%ADD10C,0.100000*%
|
||||
G04 #@! TD*
|
||||
G04 APERTURE END LIST*
|
||||
D10*
|
||||
X0Y-85000000D02*
|
||||
X0Y0D01*
|
||||
X40000000Y-85000000D02*
|
||||
X0Y-85000000D01*
|
||||
X40000000Y0D02*
|
||||
X40000000Y-85000000D01*
|
||||
X0Y0D02*
|
||||
X40000000Y0D01*
|
||||
M02*
|
||||
528
Hardware/2.9 Flasher by Jonas/gerber/jig2-F_Cu.gtl
Normal file
528
Hardware/2.9 Flasher by Jonas/gerber/jig2-F_Cu.gtl
Normal file
@@ -0,0 +1,528 @@
|
||||
G04 #@! TF.GenerationSoftware,KiCad,Pcbnew,(6.0.11)*
|
||||
G04 #@! TF.CreationDate,2023-03-04T17:24:56+01:00*
|
||||
G04 #@! TF.ProjectId,jig2,6a696732-2e6b-4696-9361-645f70636258,rev?*
|
||||
G04 #@! TF.SameCoordinates,Original*
|
||||
G04 #@! TF.FileFunction,Copper,L1,Top*
|
||||
G04 #@! TF.FilePolarity,Positive*
|
||||
%FSLAX46Y46*%
|
||||
G04 Gerber Fmt 4.6, Leading zero omitted, Abs format (unit mm)*
|
||||
G04 Created by KiCad (PCBNEW (6.0.11)) date 2023-03-04 17:24:56*
|
||||
%MOMM*%
|
||||
%LPD*%
|
||||
G01*
|
||||
G04 APERTURE LIST*
|
||||
G04 Aperture macros list*
|
||||
%AMRoundRect*
|
||||
0 Rectangle with rounded corners*
|
||||
0 $1 Rounding radius*
|
||||
0 $2 $3 $4 $5 $6 $7 $8 $9 X,Y pos of 4 corners*
|
||||
0 Add a 4 corners polygon primitive as box body*
|
||||
4,1,4,$2,$3,$4,$5,$6,$7,$8,$9,$2,$3,0*
|
||||
0 Add four circle primitives for the rounded corners*
|
||||
1,1,$1+$1,$2,$3*
|
||||
1,1,$1+$1,$4,$5*
|
||||
1,1,$1+$1,$6,$7*
|
||||
1,1,$1+$1,$8,$9*
|
||||
0 Add four rect primitives between the rounded corners*
|
||||
20,1,$1+$1,$2,$3,$4,$5,0*
|
||||
20,1,$1+$1,$4,$5,$6,$7,0*
|
||||
20,1,$1+$1,$6,$7,$8,$9,0*
|
||||
20,1,$1+$1,$8,$9,$2,$3,0*%
|
||||
%AMFreePoly0*
|
||||
4,1,22,0.500000,-0.750000,0.000000,-0.750000,0.000000,-0.745033,-0.079941,-0.743568,-0.215256,-0.701293,-0.333266,-0.622738,-0.424486,-0.514219,-0.481581,-0.384460,-0.499164,-0.250000,-0.500000,-0.250000,-0.500000,0.250000,-0.499164,0.250000,-0.499963,0.256109,-0.478152,0.396186,-0.417904,0.524511,-0.324060,0.630769,-0.204165,0.706417,-0.067858,0.745374,0.000000,0.744959,0.000000,0.750000,
|
||||
0.500000,0.750000,0.500000,-0.750000,0.500000,-0.750000,$1*%
|
||||
%AMFreePoly1*
|
||||
4,1,20,0.000000,0.744959,0.073905,0.744508,0.209726,0.703889,0.328688,0.626782,0.421226,0.519385,0.479903,0.390333,0.500000,0.250000,0.500000,-0.250000,0.499851,-0.262216,0.476331,-0.402017,0.414519,-0.529596,0.319384,-0.634700,0.198574,-0.708877,0.061801,-0.746166,0.000000,-0.745033,0.000000,-0.750000,-0.500000,-0.750000,-0.500000,0.750000,0.000000,0.750000,0.000000,0.744959,
|
||||
0.000000,0.744959,$1*%
|
||||
G04 Aperture macros list end*
|
||||
G04 #@! TA.AperFunction,SMDPad,CuDef*
|
||||
%ADD10FreePoly0,180.000000*%
|
||||
G04 #@! TD*
|
||||
G04 #@! TA.AperFunction,SMDPad,CuDef*
|
||||
%ADD11FreePoly1,180.000000*%
|
||||
G04 #@! TD*
|
||||
G04 #@! TA.AperFunction,SMDPad,CuDef*
|
||||
%ADD12RoundRect,0.250000X0.350000X0.450000X-0.350000X0.450000X-0.350000X-0.450000X0.350000X-0.450000X0*%
|
||||
G04 #@! TD*
|
||||
G04 #@! TA.AperFunction,SMDPad,CuDef*
|
||||
%ADD13RoundRect,0.250000X-0.450000X0.350000X-0.450000X-0.350000X0.450000X-0.350000X0.450000X0.350000X0*%
|
||||
G04 #@! TD*
|
||||
G04 #@! TA.AperFunction,ComponentPad*
|
||||
%ADD14C,2.000000*%
|
||||
G04 #@! TD*
|
||||
G04 #@! TA.AperFunction,ComponentPad*
|
||||
%ADD15R,1.700000X1.700000*%
|
||||
G04 #@! TD*
|
||||
G04 #@! TA.AperFunction,ComponentPad*
|
||||
%ADD16O,1.700000X1.700000*%
|
||||
G04 #@! TD*
|
||||
G04 #@! TA.AperFunction,SMDPad,CuDef*
|
||||
%ADD17R,0.800000X1.900000*%
|
||||
G04 #@! TD*
|
||||
G04 #@! TA.AperFunction,ComponentPad*
|
||||
%ADD18R,1.800000X1.800000*%
|
||||
G04 #@! TD*
|
||||
G04 #@! TA.AperFunction,ComponentPad*
|
||||
%ADD19C,1.800000*%
|
||||
G04 #@! TD*
|
||||
G04 #@! TA.AperFunction,SMDPad,CuDef*
|
||||
%ADD20RoundRect,0.250000X-0.350000X-0.450000X0.350000X-0.450000X0.350000X0.450000X-0.350000X0.450000X0*%
|
||||
G04 #@! TD*
|
||||
G04 #@! TA.AperFunction,SMDPad,CuDef*
|
||||
%ADD21R,1.900000X0.800000*%
|
||||
G04 #@! TD*
|
||||
G04 #@! TA.AperFunction,SMDPad,CuDef*
|
||||
%ADD22FreePoly0,90.000000*%
|
||||
G04 #@! TD*
|
||||
G04 #@! TA.AperFunction,SMDPad,CuDef*
|
||||
%ADD23FreePoly1,90.000000*%
|
||||
G04 #@! TD*
|
||||
G04 #@! TA.AperFunction,ComponentPad*
|
||||
%ADD24C,1.270000*%
|
||||
G04 #@! TD*
|
||||
G04 #@! TA.AperFunction,SMDPad,CuDef*
|
||||
%ADD25C,2.500000*%
|
||||
G04 #@! TD*
|
||||
G04 #@! TA.AperFunction,ViaPad*
|
||||
%ADD26C,0.600000*%
|
||||
G04 #@! TD*
|
||||
G04 #@! TA.AperFunction,Conductor*
|
||||
%ADD27C,0.500000*%
|
||||
G04 #@! TD*
|
||||
G04 #@! TA.AperFunction,Conductor*
|
||||
%ADD28C,1.000000*%
|
||||
G04 #@! TD*
|
||||
G04 APERTURE END LIST*
|
||||
G36*
|
||||
X25250000Y-81350000D02*
|
||||
G01*
|
||||
X24750000Y-81350000D01*
|
||||
X24750000Y-80950000D01*
|
||||
X25250000Y-80950000D01*
|
||||
X25250000Y-81350000D01*
|
||||
G37*
|
||||
G36*
|
||||
X25250000Y-80550000D02*
|
||||
G01*
|
||||
X24750000Y-80550000D01*
|
||||
X24750000Y-80150000D01*
|
||||
X25250000Y-80150000D01*
|
||||
X25250000Y-80550000D01*
|
||||
G37*
|
||||
D10*
|
||||
X25650000Y-80750000D03*
|
||||
D11*
|
||||
X24350000Y-80750000D03*
|
||||
D12*
|
||||
X18250000Y-81120000D03*
|
||||
X16250000Y-81120000D03*
|
||||
D13*
|
||||
X6000000Y-13000000D03*
|
||||
X6000000Y-15000000D03*
|
||||
D14*
|
||||
X9250000Y-4765564D03*
|
||||
X2750000Y-4765564D03*
|
||||
X2750000Y-9265564D03*
|
||||
X9250000Y-9265564D03*
|
||||
D15*
|
||||
X7620000Y-35560000D03*
|
||||
D16*
|
||||
X7620000Y-38100000D03*
|
||||
X7620000Y-40640000D03*
|
||||
X7620000Y-43180000D03*
|
||||
X7620000Y-45720000D03*
|
||||
X7620000Y-48260000D03*
|
||||
X7620000Y-50800000D03*
|
||||
X7620000Y-53340000D03*
|
||||
X7620000Y-55880000D03*
|
||||
X7620000Y-58420000D03*
|
||||
X7620000Y-60960000D03*
|
||||
X7620000Y-63500000D03*
|
||||
X7620000Y-66040000D03*
|
||||
X7620000Y-68580000D03*
|
||||
X7620000Y-71120000D03*
|
||||
X7620000Y-73660000D03*
|
||||
X7620000Y-76200000D03*
|
||||
X7620000Y-78740000D03*
|
||||
X7620000Y-81280000D03*
|
||||
D17*
|
||||
X23380000Y-75120000D03*
|
||||
X21480000Y-75120000D03*
|
||||
X22430000Y-78120000D03*
|
||||
D13*
|
||||
X35500000Y-10000000D03*
|
||||
X35500000Y-12000000D03*
|
||||
X30500000Y-10000000D03*
|
||||
X30500000Y-12000000D03*
|
||||
X16000000Y-13000000D03*
|
||||
X16000000Y-15000000D03*
|
||||
D18*
|
||||
X23290000Y-5080000D03*
|
||||
D19*
|
||||
X25830000Y-5080000D03*
|
||||
D20*
|
||||
X21250000Y-71120000D03*
|
||||
X23250000Y-71120000D03*
|
||||
D15*
|
||||
X32975000Y-35570000D03*
|
||||
D16*
|
||||
X32975000Y-38110000D03*
|
||||
X32975000Y-40650000D03*
|
||||
X32975000Y-43190000D03*
|
||||
X32975000Y-45730000D03*
|
||||
X32975000Y-48270000D03*
|
||||
X32975000Y-50810000D03*
|
||||
X32975000Y-53350000D03*
|
||||
X32975000Y-55890000D03*
|
||||
X32975000Y-58430000D03*
|
||||
X32975000Y-60970000D03*
|
||||
X32975000Y-63510000D03*
|
||||
X32975000Y-66050000D03*
|
||||
X32975000Y-68590000D03*
|
||||
X32975000Y-71130000D03*
|
||||
X32975000Y-73670000D03*
|
||||
X32975000Y-76210000D03*
|
||||
X32975000Y-78750000D03*
|
||||
X32975000Y-81290000D03*
|
||||
D21*
|
||||
X13400000Y-74470000D03*
|
||||
X13400000Y-76370000D03*
|
||||
X16400000Y-75420000D03*
|
||||
D13*
|
||||
X24500000Y-10000000D03*
|
||||
X24500000Y-12000000D03*
|
||||
D22*
|
||||
X26250000Y-72650000D03*
|
||||
D23*
|
||||
X26250000Y-71350000D03*
|
||||
D24*
|
||||
X28256000Y-17005000D03*
|
||||
X28256000Y-19005000D03*
|
||||
X28256000Y-21005000D03*
|
||||
X28206000Y-23005000D03*
|
||||
X30006000Y-20005000D03*
|
||||
X30006000Y-22005000D03*
|
||||
X31756000Y-17005000D03*
|
||||
X31756000Y-19005000D03*
|
||||
X31756000Y-21005000D03*
|
||||
X31756000Y-23005000D03*
|
||||
D25*
|
||||
X28500000Y-77500000D03*
|
||||
D18*
|
||||
X28830000Y-5080000D03*
|
||||
D19*
|
||||
X31370000Y-5080000D03*
|
||||
D13*
|
||||
X10750000Y-25500000D03*
|
||||
X10750000Y-27500000D03*
|
||||
D14*
|
||||
X19250000Y-4765564D03*
|
||||
X12750000Y-4765564D03*
|
||||
X19250000Y-9265564D03*
|
||||
X12750000Y-9265564D03*
|
||||
D18*
|
||||
X34370000Y-5080000D03*
|
||||
D19*
|
||||
X36910000Y-5080000D03*
|
||||
D12*
|
||||
X18750000Y-71120000D03*
|
||||
X16750000Y-71120000D03*
|
||||
D14*
|
||||
X14750000Y-20820000D03*
|
||||
X21250000Y-20820000D03*
|
||||
X14750000Y-25320000D03*
|
||||
X21250000Y-25320000D03*
|
||||
D26*
|
||||
X23000000Y-35500000D03*
|
||||
X24000000Y-39700000D03*
|
||||
X11000000Y-29750000D03*
|
||||
X11500000Y-15000000D03*
|
||||
X14440000Y-79310000D03*
|
||||
X7620000Y-21620000D03*
|
||||
X20500000Y-17000000D03*
|
||||
X20500000Y-15250000D03*
|
||||
X21000000Y-13750000D03*
|
||||
X26750000Y-38250000D03*
|
||||
D27*
|
||||
X34975000Y-20225000D02*
|
||||
X33755000Y-19005000D01*
|
||||
X33755000Y-19005000D02*
|
||||
X31756000Y-19005000D01*
|
||||
X34975000Y-58970000D02*
|
||||
X34975000Y-20225000D01*
|
||||
X32975000Y-60970000D02*
|
||||
X34975000Y-58970000D01*
|
||||
X25830000Y-8670000D02*
|
||||
X24500000Y-10000000D01*
|
||||
X25830000Y-5080000D02*
|
||||
X25830000Y-8670000D01*
|
||||
X30671000Y-24090000D02*
|
||||
X31756000Y-23005000D01*
|
||||
X29050000Y-49200000D02*
|
||||
X29050000Y-25700000D01*
|
||||
X30660000Y-24090000D02*
|
||||
X30671000Y-24090000D01*
|
||||
X29000000Y-49250000D02*
|
||||
X29050000Y-49200000D01*
|
||||
X29050000Y-25700000D02*
|
||||
X30660000Y-24090000D01*
|
||||
X32975000Y-50810000D02*
|
||||
X30560000Y-50810000D01*
|
||||
X30560000Y-50810000D02*
|
||||
X29000000Y-49250000D01*
|
||||
X31370000Y-9130000D02*
|
||||
X30500000Y-10000000D01*
|
||||
X31370000Y-5080000D02*
|
||||
X31370000Y-9130000D01*
|
||||
X28300000Y-24450000D02*
|
||||
X29300000Y-24450000D01*
|
||||
X32975000Y-53350000D02*
|
||||
X29350000Y-53350000D01*
|
||||
X30006000Y-23244000D02*
|
||||
X30006000Y-22005000D01*
|
||||
X29300000Y-24450000D02*
|
||||
X30000000Y-23750000D01*
|
||||
X30000000Y-23250000D02*
|
||||
X30006000Y-23244000D01*
|
||||
X29350000Y-53350000D02*
|
||||
X28300000Y-52300000D01*
|
||||
X28300000Y-52300000D02*
|
||||
X28300000Y-24450000D01*
|
||||
X30000000Y-23750000D02*
|
||||
X30000000Y-23250000D01*
|
||||
X36910000Y-8590000D02*
|
||||
X35500000Y-10000000D01*
|
||||
X36910000Y-5080000D02*
|
||||
X36910000Y-8590000D01*
|
||||
X17500000Y-15000000D02*
|
||||
X16000000Y-15000000D01*
|
||||
D28*
|
||||
X21480000Y-75120000D02*
|
||||
X21480000Y-71350000D01*
|
||||
X26250000Y-69750000D02*
|
||||
X26250000Y-71350000D01*
|
||||
X21250000Y-68500000D02*
|
||||
X25000000Y-68500000D01*
|
||||
D27*
|
||||
X24500000Y-12000000D02*
|
||||
X20500000Y-12000000D01*
|
||||
X10750000Y-27500000D02*
|
||||
X10750000Y-29500000D01*
|
||||
X20500000Y-12000000D02*
|
||||
X17500000Y-15000000D01*
|
||||
X11000000Y-29750000D02*
|
||||
X17250000Y-29750000D01*
|
||||
D28*
|
||||
X21480000Y-71350000D02*
|
||||
X21250000Y-71120000D01*
|
||||
D27*
|
||||
X17250000Y-29750000D02*
|
||||
X23000000Y-35500000D01*
|
||||
X35500000Y-12000000D02*
|
||||
X30500000Y-12000000D01*
|
||||
D28*
|
||||
X25000000Y-68500000D02*
|
||||
X26250000Y-69750000D01*
|
||||
X21250000Y-42450000D02*
|
||||
X21250000Y-68500000D01*
|
||||
X21250000Y-71120000D02*
|
||||
X18750000Y-71120000D01*
|
||||
D27*
|
||||
X6000000Y-15000000D02*
|
||||
X11500000Y-15000000D01*
|
||||
D28*
|
||||
X24000000Y-39700000D02*
|
||||
X21250000Y-42450000D01*
|
||||
D27*
|
||||
X10750000Y-29500000D02*
|
||||
X11000000Y-29750000D01*
|
||||
D28*
|
||||
X21250000Y-68500000D02*
|
||||
X21250000Y-71120000D01*
|
||||
D27*
|
||||
X13000000Y-15000000D02*
|
||||
X16000000Y-15000000D01*
|
||||
X30500000Y-12000000D02*
|
||||
X24500000Y-12000000D01*
|
||||
X11500000Y-15000000D02*
|
||||
X13000000Y-15000000D01*
|
||||
X16000000Y-12515564D02*
|
||||
X16000000Y-13000000D01*
|
||||
X12750000Y-9265564D02*
|
||||
X16000000Y-12515564D01*
|
||||
X19250000Y-9265564D02*
|
||||
X16000000Y-12515564D01*
|
||||
X6000000Y-12515564D02*
|
||||
X6000000Y-13000000D01*
|
||||
X2750000Y-9265564D02*
|
||||
X6000000Y-12515564D01*
|
||||
X9250000Y-9265564D02*
|
||||
X6000000Y-12515564D01*
|
||||
X14750000Y-25320000D02*
|
||||
X11320000Y-25320000D01*
|
||||
X21250000Y-25320000D02*
|
||||
X14750000Y-25320000D01*
|
||||
X28206000Y-23005000D02*
|
||||
X28206000Y-23544000D01*
|
||||
X27600000Y-55100000D02*
|
||||
X28390000Y-55890000D01*
|
||||
X28390000Y-55890000D02*
|
||||
X32975000Y-55890000D01*
|
||||
X27600000Y-24150000D02*
|
||||
X27600000Y-55100000D01*
|
||||
X28206000Y-23544000D02*
|
||||
X27600000Y-24150000D01*
|
||||
X34275000Y-57130000D02*
|
||||
X34275000Y-23524000D01*
|
||||
X32975000Y-58430000D02*
|
||||
X34275000Y-57130000D01*
|
||||
X34275000Y-23524000D02*
|
||||
X31756000Y-21005000D01*
|
||||
X13400000Y-78270000D02*
|
||||
X13400000Y-76370000D01*
|
||||
X8420000Y-20820000D02*
|
||||
X7620000Y-21620000D01*
|
||||
X9250000Y-4765564D02*
|
||||
X2750000Y-4765564D01*
|
||||
X16250000Y-81120000D02*
|
||||
X14440000Y-79310000D01*
|
||||
X14750000Y-20820000D02*
|
||||
X8420000Y-20820000D01*
|
||||
X12750000Y-4765564D02*
|
||||
X9250000Y-4765564D01*
|
||||
X14440000Y-79310000D02*
|
||||
X13400000Y-78270000D01*
|
||||
X14750000Y-20820000D02*
|
||||
X21250000Y-20820000D01*
|
||||
X19250000Y-4765564D02*
|
||||
X12750000Y-4765564D01*
|
||||
X30006000Y-20005000D02*
|
||||
X30006000Y-15744000D01*
|
||||
X35675000Y-16425000D02*
|
||||
X35675000Y-68430000D01*
|
||||
X34250000Y-15000000D02*
|
||||
X35675000Y-16425000D01*
|
||||
X30006000Y-15744000D02*
|
||||
X30750000Y-15000000D01*
|
||||
X35675000Y-68430000D02*
|
||||
X32975000Y-71130000D01*
|
||||
X30750000Y-15000000D02*
|
||||
X34250000Y-15000000D01*
|
||||
X26200000Y-34800000D02*
|
||||
X26200000Y-19550000D01*
|
||||
X26745000Y-19005000D02*
|
||||
X28256000Y-19005000D01*
|
||||
X26200000Y-19550000D02*
|
||||
X26745000Y-19005000D01*
|
||||
X22900000Y-38100000D02*
|
||||
X26200000Y-34800000D01*
|
||||
X7620000Y-38100000D02*
|
||||
X22900000Y-38100000D01*
|
||||
X9460000Y-38800000D02*
|
||||
X7620000Y-40640000D01*
|
||||
X26900000Y-35100000D02*
|
||||
X23200000Y-38800000D01*
|
||||
X27245000Y-21005000D02*
|
||||
X26900000Y-21350000D01*
|
||||
X28256000Y-21005000D02*
|
||||
X27245000Y-21005000D01*
|
||||
X26900000Y-21350000D02*
|
||||
X26900000Y-35100000D01*
|
||||
X23200000Y-38800000D02*
|
||||
X9460000Y-38800000D01*
|
||||
X3400000Y-19089950D02*
|
||||
X3400000Y-46400000D01*
|
||||
X4169975Y-18319975D02*
|
||||
X3400000Y-19089950D01*
|
||||
X3400000Y-46400000D02*
|
||||
X5260000Y-48260000D01*
|
||||
X20500000Y-17000000D02*
|
||||
X19180025Y-18319975D01*
|
||||
X5260000Y-48260000D02*
|
||||
X7620000Y-48260000D01*
|
||||
X19180025Y-18319975D02*
|
||||
X4169975Y-18319975D01*
|
||||
X2700000Y-51700000D02*
|
||||
X4340000Y-53340000D01*
|
||||
X2700000Y-18800000D02*
|
||||
X2700000Y-51700000D01*
|
||||
X18130025Y-17619975D02*
|
||||
X18010050Y-17500000D01*
|
||||
X20500000Y-15250000D02*
|
||||
X18130025Y-17619975D01*
|
||||
X4000000Y-17500000D02*
|
||||
X2700000Y-18800000D01*
|
||||
X18010050Y-17500000D02*
|
||||
X4000000Y-17500000D01*
|
||||
X4340000Y-53340000D02*
|
||||
X7620000Y-53340000D01*
|
||||
X3380000Y-55880000D02*
|
||||
X7620000Y-55880000D01*
|
||||
X17519390Y-16000000D02*
|
||||
X16769390Y-16750000D01*
|
||||
X20939339Y-13750000D02*
|
||||
X18689339Y-16000000D01*
|
||||
X16769390Y-16750000D02*
|
||||
X3250000Y-16750000D01*
|
||||
X3250000Y-16750000D02*
|
||||
X2000000Y-18000000D01*
|
||||
X21000000Y-13750000D02*
|
||||
X20939339Y-13750000D01*
|
||||
X2000000Y-18000000D02*
|
||||
X2000000Y-54500000D01*
|
||||
X2000000Y-54500000D02*
|
||||
X3380000Y-55880000D01*
|
||||
X18689339Y-16000000D02*
|
||||
X17519390Y-16000000D01*
|
||||
X15060000Y-70310000D02*
|
||||
X10790000Y-66040000D01*
|
||||
X10790000Y-66040000D02*
|
||||
X7620000Y-66040000D01*
|
||||
X16750000Y-71120000D02*
|
||||
X15060000Y-72810000D01*
|
||||
X15060000Y-72810000D02*
|
||||
X13400000Y-74470000D01*
|
||||
X15060000Y-72810000D02*
|
||||
X15060000Y-70310000D01*
|
||||
X23380000Y-75120000D02*
|
||||
X23380000Y-71250000D01*
|
||||
X23380000Y-75120000D02*
|
||||
X23380000Y-75470000D01*
|
||||
X23380000Y-71250000D02*
|
||||
X23250000Y-71120000D01*
|
||||
X22330000Y-76520000D02*
|
||||
X17500000Y-76520000D01*
|
||||
X23380000Y-75470000D02*
|
||||
X22330000Y-76520000D01*
|
||||
X17500000Y-76520000D02*
|
||||
X16400000Y-75420000D01*
|
||||
D28*
|
||||
X26000000Y-55250000D02*
|
||||
X26000000Y-38750000D01*
|
||||
X28500000Y-77500000D02*
|
||||
X28500000Y-57750000D01*
|
||||
X25650000Y-80350000D02*
|
||||
X28500000Y-77500000D01*
|
||||
X28500000Y-57750000D02*
|
||||
X26000000Y-55250000D01*
|
||||
X26000000Y-38750000D02*
|
||||
X26500000Y-38250000D01*
|
||||
X25650000Y-80750000D02*
|
||||
X25650000Y-80350000D01*
|
||||
X26500000Y-38250000D02*
|
||||
X26650000Y-38250000D01*
|
||||
X21250000Y-78120000D02*
|
||||
X18250000Y-81120000D01*
|
||||
X23380000Y-78120000D02*
|
||||
X22430000Y-78120000D01*
|
||||
X26250000Y-72650000D02*
|
||||
X26250000Y-75250000D01*
|
||||
X22430000Y-78830000D02*
|
||||
X24350000Y-80750000D01*
|
||||
X22430000Y-78120000D02*
|
||||
X21250000Y-78120000D01*
|
||||
X22430000Y-78120000D02*
|
||||
X22430000Y-78830000D01*
|
||||
X26250000Y-75250000D02*
|
||||
X23380000Y-78120000D01*
|
||||
M02*
|
||||
178
Hardware/2.9 Flasher by Jonas/gerber/jig2-F_Mask.gts
Normal file
178
Hardware/2.9 Flasher by Jonas/gerber/jig2-F_Mask.gts
Normal file
@@ -0,0 +1,178 @@
|
||||
G04 #@! TF.GenerationSoftware,KiCad,Pcbnew,(6.0.11)*
|
||||
G04 #@! TF.CreationDate,2023-03-04T17:24:57+01:00*
|
||||
G04 #@! TF.ProjectId,jig2,6a696732-2e6b-4696-9361-645f70636258,rev?*
|
||||
G04 #@! TF.SameCoordinates,Original*
|
||||
G04 #@! TF.FileFunction,Soldermask,Top*
|
||||
G04 #@! TF.FilePolarity,Negative*
|
||||
%FSLAX46Y46*%
|
||||
G04 Gerber Fmt 4.6, Leading zero omitted, Abs format (unit mm)*
|
||||
G04 Created by KiCad (PCBNEW (6.0.11)) date 2023-03-04 17:24:57*
|
||||
%MOMM*%
|
||||
%LPD*%
|
||||
G01*
|
||||
G04 APERTURE LIST*
|
||||
G04 Aperture macros list*
|
||||
%AMRoundRect*
|
||||
0 Rectangle with rounded corners*
|
||||
0 $1 Rounding radius*
|
||||
0 $2 $3 $4 $5 $6 $7 $8 $9 X,Y pos of 4 corners*
|
||||
0 Add a 4 corners polygon primitive as box body*
|
||||
4,1,4,$2,$3,$4,$5,$6,$7,$8,$9,$2,$3,0*
|
||||
0 Add four circle primitives for the rounded corners*
|
||||
1,1,$1+$1,$2,$3*
|
||||
1,1,$1+$1,$4,$5*
|
||||
1,1,$1+$1,$6,$7*
|
||||
1,1,$1+$1,$8,$9*
|
||||
0 Add four rect primitives between the rounded corners*
|
||||
20,1,$1+$1,$2,$3,$4,$5,0*
|
||||
20,1,$1+$1,$4,$5,$6,$7,0*
|
||||
20,1,$1+$1,$6,$7,$8,$9,0*
|
||||
20,1,$1+$1,$8,$9,$2,$3,0*%
|
||||
%AMFreePoly0*
|
||||
4,1,22,0.500000,-0.750000,0.000000,-0.750000,0.000000,-0.745033,-0.079941,-0.743568,-0.215256,-0.701293,-0.333266,-0.622738,-0.424486,-0.514219,-0.481581,-0.384460,-0.499164,-0.250000,-0.500000,-0.250000,-0.500000,0.250000,-0.499164,0.250000,-0.499963,0.256109,-0.478152,0.396186,-0.417904,0.524511,-0.324060,0.630769,-0.204165,0.706417,-0.067858,0.745374,0.000000,0.744959,0.000000,0.750000,
|
||||
0.500000,0.750000,0.500000,-0.750000,0.500000,-0.750000,$1*%
|
||||
%AMFreePoly1*
|
||||
4,1,20,0.000000,0.744959,0.073905,0.744508,0.209726,0.703889,0.328688,0.626782,0.421226,0.519385,0.479903,0.390333,0.500000,0.250000,0.500000,-0.250000,0.499851,-0.262216,0.476331,-0.402017,0.414519,-0.529596,0.319384,-0.634700,0.198574,-0.708877,0.061801,-0.746166,0.000000,-0.745033,0.000000,-0.750000,-0.500000,-0.750000,-0.500000,0.750000,0.000000,0.750000,0.000000,0.744959,
|
||||
0.000000,0.744959,$1*%
|
||||
G04 Aperture macros list end*
|
||||
%ADD10FreePoly0,180.000000*%
|
||||
%ADD11FreePoly1,180.000000*%
|
||||
%ADD12RoundRect,0.250000X0.350000X0.450000X-0.350000X0.450000X-0.350000X-0.450000X0.350000X-0.450000X0*%
|
||||
%ADD13RoundRect,0.250000X-0.450000X0.350000X-0.450000X-0.350000X0.450000X-0.350000X0.450000X0.350000X0*%
|
||||
%ADD14C,2.000000*%
|
||||
%ADD15R,1.700000X1.700000*%
|
||||
%ADD16O,1.700000X1.700000*%
|
||||
%ADD17R,0.800000X1.900000*%
|
||||
%ADD18R,1.800000X1.800000*%
|
||||
%ADD19C,1.800000*%
|
||||
%ADD20RoundRect,0.250000X-0.350000X-0.450000X0.350000X-0.450000X0.350000X0.450000X-0.350000X0.450000X0*%
|
||||
%ADD21R,1.900000X0.800000*%
|
||||
%ADD22FreePoly0,90.000000*%
|
||||
%ADD23FreePoly1,90.000000*%
|
||||
%ADD24C,1.371600*%
|
||||
%ADD25C,2.500000*%
|
||||
G04 APERTURE END LIST*
|
||||
D10*
|
||||
X25650000Y-80750000D03*
|
||||
D11*
|
||||
X24350000Y-80750000D03*
|
||||
D12*
|
||||
X18250000Y-81120000D03*
|
||||
X16250000Y-81120000D03*
|
||||
D13*
|
||||
X6000000Y-13000000D03*
|
||||
X6000000Y-15000000D03*
|
||||
D14*
|
||||
X9250000Y-4765564D03*
|
||||
X2750000Y-4765564D03*
|
||||
X2750000Y-9265564D03*
|
||||
X9250000Y-9265564D03*
|
||||
D15*
|
||||
X7620000Y-35560000D03*
|
||||
D16*
|
||||
X7620000Y-38100000D03*
|
||||
X7620000Y-40640000D03*
|
||||
X7620000Y-43180000D03*
|
||||
X7620000Y-45720000D03*
|
||||
X7620000Y-48260000D03*
|
||||
X7620000Y-50800000D03*
|
||||
X7620000Y-53340000D03*
|
||||
X7620000Y-55880000D03*
|
||||
X7620000Y-58420000D03*
|
||||
X7620000Y-60960000D03*
|
||||
X7620000Y-63500000D03*
|
||||
X7620000Y-66040000D03*
|
||||
X7620000Y-68580000D03*
|
||||
X7620000Y-71120000D03*
|
||||
X7620000Y-73660000D03*
|
||||
X7620000Y-76200000D03*
|
||||
X7620000Y-78740000D03*
|
||||
X7620000Y-81280000D03*
|
||||
D17*
|
||||
X23380000Y-75120000D03*
|
||||
X21480000Y-75120000D03*
|
||||
X22430000Y-78120000D03*
|
||||
D13*
|
||||
X35500000Y-10000000D03*
|
||||
X35500000Y-12000000D03*
|
||||
X30500000Y-10000000D03*
|
||||
X30500000Y-12000000D03*
|
||||
X16000000Y-13000000D03*
|
||||
X16000000Y-15000000D03*
|
||||
D18*
|
||||
X23290000Y-5080000D03*
|
||||
D19*
|
||||
X25830000Y-5080000D03*
|
||||
D20*
|
||||
X21250000Y-71120000D03*
|
||||
X23250000Y-71120000D03*
|
||||
D15*
|
||||
X32975000Y-35570000D03*
|
||||
D16*
|
||||
X32975000Y-38110000D03*
|
||||
X32975000Y-40650000D03*
|
||||
X32975000Y-43190000D03*
|
||||
X32975000Y-45730000D03*
|
||||
X32975000Y-48270000D03*
|
||||
X32975000Y-50810000D03*
|
||||
X32975000Y-53350000D03*
|
||||
X32975000Y-55890000D03*
|
||||
X32975000Y-58430000D03*
|
||||
X32975000Y-60970000D03*
|
||||
X32975000Y-63510000D03*
|
||||
X32975000Y-66050000D03*
|
||||
X32975000Y-68590000D03*
|
||||
X32975000Y-71130000D03*
|
||||
X32975000Y-73670000D03*
|
||||
X32975000Y-76210000D03*
|
||||
X32975000Y-78750000D03*
|
||||
X32975000Y-81290000D03*
|
||||
D21*
|
||||
X13400000Y-74470000D03*
|
||||
X13400000Y-76370000D03*
|
||||
X16400000Y-75420000D03*
|
||||
D13*
|
||||
X24500000Y-10000000D03*
|
||||
X24500000Y-12000000D03*
|
||||
D22*
|
||||
X26250000Y-72650000D03*
|
||||
D23*
|
||||
X26250000Y-71350000D03*
|
||||
D24*
|
||||
X28256000Y-17005000D03*
|
||||
X28256000Y-19005000D03*
|
||||
X28256000Y-21005000D03*
|
||||
X28206000Y-23005000D03*
|
||||
X30006000Y-20005000D03*
|
||||
X30006000Y-22005000D03*
|
||||
X31756000Y-17005000D03*
|
||||
X31756000Y-19005000D03*
|
||||
X31756000Y-21005000D03*
|
||||
X31756000Y-23005000D03*
|
||||
D25*
|
||||
X28500000Y-77500000D03*
|
||||
D18*
|
||||
X28830000Y-5080000D03*
|
||||
D19*
|
||||
X31370000Y-5080000D03*
|
||||
D13*
|
||||
X10750000Y-25500000D03*
|
||||
X10750000Y-27500000D03*
|
||||
D14*
|
||||
X19250000Y-4765564D03*
|
||||
X12750000Y-4765564D03*
|
||||
X19250000Y-9265564D03*
|
||||
X12750000Y-9265564D03*
|
||||
D18*
|
||||
X34370000Y-5080000D03*
|
||||
D19*
|
||||
X36910000Y-5080000D03*
|
||||
D12*
|
||||
X18750000Y-71120000D03*
|
||||
X16750000Y-71120000D03*
|
||||
D14*
|
||||
X14750000Y-20820000D03*
|
||||
X21250000Y-20820000D03*
|
||||
X14750000Y-25320000D03*
|
||||
X21250000Y-25320000D03*
|
||||
M02*
|
||||
173
Hardware/2.9 Flasher by Jonas/gerber/jig2-F_Paste.gtp
Normal file
173
Hardware/2.9 Flasher by Jonas/gerber/jig2-F_Paste.gtp
Normal file
@@ -0,0 +1,173 @@
|
||||
G04 #@! TF.GenerationSoftware,KiCad,Pcbnew,(6.0.11)*
|
||||
G04 #@! TF.CreationDate,2023-03-04T17:24:57+01:00*
|
||||
G04 #@! TF.ProjectId,jig2,6a696732-2e6b-4696-9361-645f70636258,rev?*
|
||||
G04 #@! TF.SameCoordinates,Original*
|
||||
G04 #@! TF.FileFunction,Paste,Top*
|
||||
G04 #@! TF.FilePolarity,Positive*
|
||||
%FSLAX46Y46*%
|
||||
G04 Gerber Fmt 4.6, Leading zero omitted, Abs format (unit mm)*
|
||||
G04 Created by KiCad (PCBNEW (6.0.11)) date 2023-03-04 17:24:57*
|
||||
%MOMM*%
|
||||
%LPD*%
|
||||
G01*
|
||||
G04 APERTURE LIST*
|
||||
G04 Aperture macros list*
|
||||
%AMRoundRect*
|
||||
0 Rectangle with rounded corners*
|
||||
0 $1 Rounding radius*
|
||||
0 $2 $3 $4 $5 $6 $7 $8 $9 X,Y pos of 4 corners*
|
||||
0 Add a 4 corners polygon primitive as box body*
|
||||
4,1,4,$2,$3,$4,$5,$6,$7,$8,$9,$2,$3,0*
|
||||
0 Add four circle primitives for the rounded corners*
|
||||
1,1,$1+$1,$2,$3*
|
||||
1,1,$1+$1,$4,$5*
|
||||
1,1,$1+$1,$6,$7*
|
||||
1,1,$1+$1,$8,$9*
|
||||
0 Add four rect primitives between the rounded corners*
|
||||
20,1,$1+$1,$2,$3,$4,$5,0*
|
||||
20,1,$1+$1,$4,$5,$6,$7,0*
|
||||
20,1,$1+$1,$6,$7,$8,$9,0*
|
||||
20,1,$1+$1,$8,$9,$2,$3,0*%
|
||||
G04 Aperture macros list end*
|
||||
%ADD10C,0.150000*%
|
||||
%ADD11RoundRect,0.250000X0.350000X0.450000X-0.350000X0.450000X-0.350000X-0.450000X0.350000X-0.450000X0*%
|
||||
%ADD12RoundRect,0.250000X-0.450000X0.350000X-0.450000X-0.350000X0.450000X-0.350000X0.450000X0.350000X0*%
|
||||
%ADD13R,0.800000X1.900000*%
|
||||
%ADD14RoundRect,0.250000X-0.350000X-0.450000X0.350000X-0.450000X0.350000X0.450000X-0.350000X0.450000X0*%
|
||||
%ADD15R,1.900000X0.800000*%
|
||||
G04 APERTURE END LIST*
|
||||
D10*
|
||||
X13511904Y-49702380D02*
|
||||
X13321428Y-49702380D01*
|
||||
X13226190Y-49750000D01*
|
||||
X13178571Y-49797619D01*
|
||||
X13083333Y-49940476D01*
|
||||
X13035714Y-50130952D01*
|
||||
X13035714Y-50511904D01*
|
||||
X13083333Y-50607142D01*
|
||||
X13130952Y-50654761D01*
|
||||
X13226190Y-50702380D01*
|
||||
X13416666Y-50702380D01*
|
||||
X13511904Y-50654761D01*
|
||||
X13559523Y-50607142D01*
|
||||
X13607142Y-50511904D01*
|
||||
X13607142Y-50273809D01*
|
||||
X13559523Y-50178571D01*
|
||||
X13511904Y-50130952D01*
|
||||
X13416666Y-50083333D01*
|
||||
X13226190Y-50083333D01*
|
||||
X13130952Y-50130952D01*
|
||||
X13083333Y-50178571D01*
|
||||
X13035714Y-50273809D01*
|
||||
X14464285Y-50035714D02*
|
||||
X14464285Y-50702380D01*
|
||||
X14226190Y-49654761D02*
|
||||
X13988095Y-50369047D01*
|
||||
X14607142Y-50369047D01*
|
||||
X15369047Y-50654761D02*
|
||||
X15273809Y-50702380D01*
|
||||
X15083333Y-50702380D01*
|
||||
X14988095Y-50654761D01*
|
||||
X14940476Y-50559523D01*
|
||||
X14940476Y-50178571D01*
|
||||
X14988095Y-50083333D01*
|
||||
X15083333Y-50035714D01*
|
||||
X15273809Y-50035714D01*
|
||||
X15369047Y-50083333D01*
|
||||
X15416666Y-50178571D01*
|
||||
X15416666Y-50273809D01*
|
||||
X14940476Y-50369047D01*
|
||||
X15845238Y-50035714D02*
|
||||
X15845238Y-50702380D01*
|
||||
X15845238Y-50130952D02*
|
||||
X15892857Y-50083333D01*
|
||||
X15988095Y-50035714D01*
|
||||
X16130952Y-50035714D01*
|
||||
X16226190Y-50083333D01*
|
||||
X16273809Y-50178571D01*
|
||||
X16273809Y-50702380D01*
|
||||
X17178571Y-50035714D02*
|
||||
X17178571Y-50845238D01*
|
||||
X17130952Y-50940476D01*
|
||||
X17083333Y-50988095D01*
|
||||
X16988095Y-51035714D01*
|
||||
X16845238Y-51035714D01*
|
||||
X16750000Y-50988095D01*
|
||||
X17178571Y-50654761D02*
|
||||
X17083333Y-50702380D01*
|
||||
X16892857Y-50702380D01*
|
||||
X16797619Y-50654761D01*
|
||||
X16750000Y-50607142D01*
|
||||
X16702380Y-50511904D01*
|
||||
X16702380Y-50226190D01*
|
||||
X16750000Y-50130952D01*
|
||||
X16797619Y-50083333D01*
|
||||
X16892857Y-50035714D01*
|
||||
X17083333Y-50035714D01*
|
||||
X17178571Y-50083333D01*
|
||||
X17654761Y-50607142D02*
|
||||
X17702380Y-50654761D01*
|
||||
X17654761Y-50702380D01*
|
||||
X17607142Y-50654761D01*
|
||||
X17654761Y-50607142D01*
|
||||
X17654761Y-50702380D01*
|
||||
X18559523Y-50702380D02*
|
||||
X18559523Y-49702380D01*
|
||||
X18559523Y-50654761D02*
|
||||
X18464285Y-50702380D01*
|
||||
X18273809Y-50702380D01*
|
||||
X18178571Y-50654761D01*
|
||||
X18130952Y-50607142D01*
|
||||
X18083333Y-50511904D01*
|
||||
X18083333Y-50226190D01*
|
||||
X18130952Y-50130952D01*
|
||||
X18178571Y-50083333D01*
|
||||
X18273809Y-50035714D01*
|
||||
X18464285Y-50035714D01*
|
||||
X18559523Y-50083333D01*
|
||||
X19416666Y-50654761D02*
|
||||
X19321428Y-50702380D01*
|
||||
X19130952Y-50702380D01*
|
||||
X19035714Y-50654761D01*
|
||||
X18988095Y-50559523D01*
|
||||
X18988095Y-50178571D01*
|
||||
X19035714Y-50083333D01*
|
||||
X19130952Y-50035714D01*
|
||||
X19321428Y-50035714D01*
|
||||
X19416666Y-50083333D01*
|
||||
X19464285Y-50178571D01*
|
||||
X19464285Y-50273809D01*
|
||||
X18988095Y-50369047D01*
|
||||
D11*
|
||||
X18250000Y-81120000D03*
|
||||
X16250000Y-81120000D03*
|
||||
D12*
|
||||
X6000000Y-13000000D03*
|
||||
X6000000Y-15000000D03*
|
||||
D13*
|
||||
X23380000Y-75120000D03*
|
||||
X21480000Y-75120000D03*
|
||||
X22430000Y-78120000D03*
|
||||
D12*
|
||||
X35500000Y-10000000D03*
|
||||
X35500000Y-12000000D03*
|
||||
X30500000Y-10000000D03*
|
||||
X30500000Y-12000000D03*
|
||||
X16000000Y-13000000D03*
|
||||
X16000000Y-15000000D03*
|
||||
D14*
|
||||
X21250000Y-71120000D03*
|
||||
X23250000Y-71120000D03*
|
||||
D15*
|
||||
X13400000Y-74470000D03*
|
||||
X13400000Y-76370000D03*
|
||||
X16400000Y-75420000D03*
|
||||
D12*
|
||||
X24500000Y-10000000D03*
|
||||
X24500000Y-12000000D03*
|
||||
X10750000Y-25500000D03*
|
||||
X10750000Y-27500000D03*
|
||||
D11*
|
||||
X18750000Y-71120000D03*
|
||||
X16750000Y-71120000D03*
|
||||
M02*
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user