Compare commits
313 commits
offline-me
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
aacfeb9911 | ||
|
|
c9dd1a2902 | ||
|
|
df2cb16499 | ||
|
|
f98a5429e3 | ||
|
|
a0eb1a6833 | ||
|
|
d38264a533 | ||
|
|
c320cf79b1 | ||
|
|
38dab380be | ||
|
|
6f9633db41 | ||
|
|
8fa835a11b | ||
|
|
aafc9aeb59 | ||
|
|
37a7a0a668 | ||
|
|
b70abd72ea | ||
|
|
94847e7792 | ||
|
|
053e675503 | ||
|
|
ff9790297e | ||
|
|
6dcfecc8cb | ||
|
|
129875768f | ||
|
|
04918f1d11 | ||
|
|
9fa0d9c025 | ||
|
|
0b0b767d6c | ||
|
|
b2d32ea9a7 | ||
|
|
8b8aa8efd0 | ||
|
|
0158a39fc6 | ||
|
|
5923445413 | ||
|
|
a5f81a5053 | ||
|
|
8b0068859b | ||
|
|
f44d1ab582 | ||
|
|
3a4fd3e8df | ||
|
|
6ef92f1809 | ||
|
|
4ace2bc551 | ||
|
|
f2f36f039d | ||
|
|
74074b1973 | ||
|
|
8f3a6df7be | ||
|
|
9b02dc97b8 | ||
|
|
3dd3c637a7 | ||
|
|
8467d92062 | ||
|
|
036492889e | ||
|
|
38a1794a32 | ||
|
|
1c8273c1ba | ||
|
|
684427ae77 | ||
|
|
08a4e9b2b2 | ||
|
|
189b306683 | ||
|
|
f67593fc9f | ||
|
|
c42f98f149 | ||
|
|
1f78bc096c | ||
|
|
44e42807f5 | ||
|
|
07039fdd66 | ||
|
|
869ba82b9a | ||
|
|
bcb8b4e4ca | ||
|
|
3ef787e79e | ||
|
|
3e41dc1bd4 | ||
|
|
835a05c769 | ||
|
|
eb673ca465 | ||
|
|
d33fb423bd | ||
|
|
935de10302 | ||
|
|
1e65b05eb3 | ||
|
|
ca2412d5a7 | ||
|
|
07daf2ba6b | ||
|
|
d8bea463ab | ||
|
|
fc160d0ec5 | ||
|
|
12c5d5f686 | ||
|
|
e97ff1003f | ||
|
|
b40ab1405a | ||
|
|
9dd14ab71c | ||
|
|
7bde129844 | ||
|
|
8996ab0661 | ||
|
|
02cc369eb0 | ||
|
|
daa4ec8503 | ||
|
|
b34994845b | ||
|
|
e3dd1baf5c | ||
|
|
fcf757c9b9 | ||
|
|
24f117a110 | ||
|
|
b874489933 | ||
|
|
3032861ab1 | ||
|
|
33a94b2075 | ||
|
|
e6a8a16db4 | ||
|
|
b442fc7bb8 | ||
|
|
c6028d6788 | ||
|
|
e268e11d35 | ||
|
|
9bc8652835 | ||
|
|
9207e9ea53 | ||
|
|
042a5c8eb9 | ||
|
|
84bc331930 | ||
|
|
6f24c55f21 | ||
|
|
c73cbe4244 | ||
|
|
cb547052cf | ||
|
|
ffd58c9354 | ||
|
|
0d2d66868b | ||
|
|
2113f0be13 | ||
|
|
a86aff5e4a | ||
|
|
a6e0396065 | ||
|
|
1484cdc272 | ||
|
|
452ceb2cc2 | ||
|
|
0d8ada2c8f | ||
|
|
d6db3be31f | ||
|
|
156a8874a6 | ||
|
|
efddc88fba | ||
|
|
12d885ef88 | ||
|
|
e94960708b | ||
|
|
f44f1399f2 | ||
|
|
fb96006700 | ||
|
|
7237adef36 | ||
|
|
ec40ec204c | ||
|
|
50b92e2fa9 | ||
|
|
14de6bb28a | ||
|
|
e81647dc92 | ||
|
|
5622d26234 | ||
|
|
4ad941abc0 | ||
|
|
98882dfc12 | ||
|
|
e5d2c5b741 | ||
|
|
8a143dc843 | ||
|
|
7dd87f9375 | ||
|
|
e4369c12ab | ||
|
|
b219e9ce52 | ||
|
|
3098b7bbea | ||
|
|
1a7afb7123 | ||
|
|
5fcb3d0632 | ||
|
|
02b0047c3d | ||
|
|
433e5c0c56 | ||
|
|
9b61f68fba | ||
|
|
7bd3d8569a | ||
|
|
de0ad47791 | ||
|
|
f3746bd9ef | ||
|
|
f1b2b307ca | ||
|
|
0fa0f41816 | ||
|
|
b759bd61ac | ||
|
|
ecd234c700 | ||
|
|
5e64255ffa | ||
|
|
1b63e31296 | ||
|
|
e045fffe61 | ||
|
|
af6267bc11 | ||
|
|
0183ddfbbe | ||
|
|
4c227a64e7 | ||
|
|
bdbbf8183a | ||
|
|
2e4e52213d | ||
|
|
228f53d64f | ||
|
|
518be8e77a | ||
|
|
3837ae8f6d | ||
|
|
d5de3ec831 | ||
|
|
53c3718827 | ||
|
|
6cebbcbfe3 | ||
|
|
4f7d684ad0 | ||
|
|
58ce35381b | ||
|
|
51cb68e07b | ||
|
|
6902e98d7b | ||
|
|
4ea19f134e | ||
|
|
d9b1d05e4d | ||
|
|
749c5ebce4 | ||
|
|
2d0122bebb | ||
|
|
18fb29eca3 | ||
|
|
6bc2fb421f | ||
|
|
72ea8bd966 | ||
|
|
7158b56fbb | ||
|
|
ac1b004d12 | ||
|
|
22664089df | ||
|
|
683fe9941f | ||
|
|
7ea402d237 | ||
|
|
d6a7eb424d | ||
|
|
c3eb493628 | ||
|
|
1804a75a88 | ||
|
|
cd4fb4a704 | ||
|
|
4fa03081a7 | ||
|
|
c89d912086 | ||
|
|
d551c26413 | ||
|
|
9cbb0a84af | ||
|
|
31967fd2c7 | ||
|
|
aa742d035a | ||
|
|
e01e54a6cd | ||
|
|
4f55fa436d | ||
|
|
338d810cc2 | ||
|
|
f55eaeffb8 | ||
|
|
e88aeb8e5d | ||
|
|
73780bf899 | ||
|
|
a69caa87e0 | ||
|
|
c84d8a0f6d | ||
|
|
6daee3cad1 | ||
|
|
ace03cfa4b | ||
|
|
24b238c411 | ||
|
|
0bbde5e157 | ||
|
|
8c1c5ce333 | ||
|
|
77b1a351a3 | ||
|
|
28f9086d25 | ||
|
|
96a4235b0d | ||
|
|
df6b65bdf7 | ||
|
|
20b7b73a33 | ||
|
|
3766e7bf16 | ||
|
|
45455cfaf9 | ||
|
|
0ada820901 | ||
|
|
2631d07976 | ||
|
|
58f91746aa | ||
|
|
905543bfff | ||
|
|
fa550ae756 | ||
|
|
b4ecb3d3a5 | ||
|
|
878dd1e4db | ||
|
|
a733ad0317 | ||
|
|
5a21ce6cce | ||
|
|
74df0c938e | ||
|
|
3de8fea1d5 | ||
|
|
c4277b95ee | ||
|
|
6270941e2a | ||
|
|
064bd2e040 | ||
|
|
e392458e4f | ||
|
|
7f01135915 | ||
|
|
3b70ba4bbf | ||
|
|
fd7126da46 | ||
|
|
29298b49b8 | ||
|
|
88596b23ba | ||
|
|
94139d0c4d | ||
|
|
838943d525 | ||
|
|
aaac3cbac8 | ||
|
|
13246f747a | ||
|
|
c10d8b5407 | ||
|
|
029c9bbf0e | ||
|
|
97b0030995 | ||
|
|
9812af7654 | ||
|
|
495fedb4ac | ||
|
|
a83314aa10 | ||
|
|
8674eb81ea | ||
|
|
cea33ed69c | ||
|
|
4e8de958c2 | ||
|
|
3423400fbe | ||
|
|
ea79c82438 | ||
|
|
1db950963d | ||
|
|
0c4f9fd366 | ||
|
|
698cf890a8 | ||
|
|
5dd5e88eef | ||
|
|
f762fd71d9 | ||
|
|
67f6b4c00b | ||
|
|
bc4eccec20 | ||
|
|
9cca894e20 | ||
|
|
ba64f9bf5f | ||
|
|
bbf48f8e41 | ||
|
|
bb42bd4e5e | ||
|
|
b7acec2c40 | ||
|
|
485cefb020 | ||
|
|
ceb2140e02 | ||
|
|
172df1110d | ||
|
|
83b54257a0 | ||
|
|
b0313d0d9b | ||
|
|
dc2df8f6bf | ||
|
|
70c4f18f23 | ||
|
|
0575f0f0a9 | ||
|
|
6148f8a7f5 | ||
|
|
ec034bcdc9 | ||
|
|
25bb96aec7 | ||
|
|
cf4b3bdee8 | ||
|
|
e360a24b86 | ||
|
|
9a1c11aaff | ||
|
|
1f0621f6a0 | ||
|
|
e09ad2d04b | ||
|
|
9c2b562403 | ||
|
|
1afea7a60e | ||
|
|
e4eaf4e981 | ||
|
|
18d3d2ef7f | ||
|
|
9b349bdd5e | ||
|
|
e6cae7daf4 | ||
|
|
6c37524656 | ||
|
|
2b55d72f1f | ||
|
|
7e56d2be60 | ||
|
|
f0dc37476d | ||
|
|
63eed1706b | ||
|
|
062ceeb175 | ||
|
|
6057d5fc02 | ||
|
|
1c60b34570 | ||
|
|
d8d60090f5 | ||
|
|
8278ac5ca6 | ||
|
|
9edd928f9e | ||
|
|
08d4b1ecba | ||
|
|
8318e89de1 | ||
|
|
f7bbde5253 | ||
|
|
927c23e0d9 | ||
|
|
35778c44ca | ||
|
|
e8f05b589d | ||
|
|
0e8ef5b752 | ||
|
|
c23f93581b | ||
|
|
07ba330ffc | ||
|
|
8c7570e874 | ||
|
|
082579adf3 | ||
|
|
eb5b338752 | ||
|
|
42cf174531 | ||
|
|
becfb3b253 | ||
|
|
ddbaa1da77 | ||
|
|
5f7613a86c | ||
|
|
a128c60bc6 | ||
|
|
7c5c43eb3e | ||
|
|
a846c2b817 | ||
|
|
2cb48d7e63 | ||
|
|
9c25415191 | ||
|
|
8b1115e8f4 | ||
|
|
df364ecbe7 | ||
|
|
01597a353e | ||
|
|
3c95a984b9 | ||
|
|
cffb9d5673 | ||
|
|
c79f205daf | ||
|
|
2c9ca9b67b | ||
|
|
bccb1224c1 | ||
|
|
8a5bedcdc5 | ||
|
|
9315ad1ae5 | ||
|
|
fc0cf89192 | ||
|
|
0ad3ab1a39 | ||
|
|
c1e1c1e683 | ||
|
|
e915c8d68f | ||
|
|
4cdffd1369 | ||
|
|
d8ca4c5336 | ||
|
|
e5cea990ca | ||
|
|
b7c9b5e748 | ||
|
|
140e039775 | ||
|
|
f69d82803b | ||
|
|
fc696d5ad0 | ||
|
|
4b8844a0e1 | ||
|
|
d6243f25ba | ||
|
|
83034b8e03 |
21 changed files with 15412 additions and 12094 deletions
16
.github/workflows/daily-update.yml
vendored
16
.github/workflows/daily-update.yml
vendored
|
|
@ -39,18 +39,26 @@ jobs:
|
|||
run: |
|
||||
git config --global user.name "GitHub Actions Bot"
|
||||
git config --global user.email "actions@github.com"
|
||||
git submodule update --remote --recursive
|
||||
git submodule init
|
||||
git submodule update --recursive
|
||||
cd Wippersnapper_Boards
|
||||
git reset --hard origin/offline-mode
|
||||
cd ../Wippersnapper_Components
|
||||
git reset --hard origin/offline-mode
|
||||
cd ..
|
||||
git add Wippersnapper_Boards Wippersnapper_Components
|
||||
git diff --staged --quiet || git commit -m "Update submodules to latest versions [skip ci]"
|
||||
|
||||
- name: Run conversion script
|
||||
run: |
|
||||
pip install requests beautifulsoup4
|
||||
pip install -r requirements.txt
|
||||
python convert_all_wippersnapper_definitions.py
|
||||
|
||||
- name: Commit changes if any
|
||||
run: |
|
||||
git add wippersnapper_boards.json wippersnapper_components.json
|
||||
git add wippersnapper_boards.json wippersnapper_components.json wippersnapper_boards.js wippersnapper_components.js firmware-data.js
|
||||
git diff --staged --quiet || git commit -m "Auto-update Wippersnapper definitions [skip ci]"
|
||||
git push origin main
|
||||
git push origin ${{ github.ref_name }}
|
||||
|
||||
- name: Setup Pages
|
||||
uses: actions/configure-pages@v4
|
||||
|
|
|
|||
3
.gitignore
vendored
3
.gitignore
vendored
|
|
@ -3,3 +3,6 @@
|
|||
__pycache__/
|
||||
*.pyc
|
||||
|
||||
bin/
|
||||
lib/
|
||||
pyvenv.cfg
|
||||
1
.gitmodules
vendored
1
.gitmodules
vendored
|
|
@ -4,3 +4,4 @@
|
|||
[submodule "Wippersnapper_Components"]
|
||||
path = Wippersnapper_Components
|
||||
url = https://github.com/adafruit/Wippersnapper_Components
|
||||
branch = offline-mode
|
||||
|
|
|
|||
2
LICENSE
2
LICENSE
|
|
@ -1,6 +1,6 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2025 Tyeth Gundry
|
||||
Copyright (c) 2025 Tyeth Gundry for Adafruit Industries
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
|
|
|||
69
README.md
69
README.md
|
|
@ -1,5 +1,68 @@
|
|||
# Adafruit_Wippersnapper_Offline_Configurator
|
||||
A single web page for the creation / amending of config.json files to support Wippersnapper Offline Data Logger firmware
|
||||
# Adafruit WipperSnapper Offline Configurator
|
||||
This web page is for the creation / amending of `config.json` files to support the free and open-source Adafruit ["WipperSnapper"](https://github.com/adafruit/Adafruit_Wippersnapper_Arduino/tree/offline-mode) Offline Data Logger firmware.
|
||||
|
||||
It allows users to select their microcontroller board, automatically (or manually) setup the Real Time Clock (RTC) and an SD card Chip Select pin (uses default SPI bus), or any companion boards with SD cards and/or RTCs, and then the attached components (sensors, analog pins, etc) for data logging.
|
||||
|
||||
The page can also be used offline by including the javascript (.js) files, ideally minified (180k > 60k), and index.html (i.e. copy to your device or SD card)
|
||||
|
||||
|
||||
Visit the site here:
|
||||
### [https://tyeth.github.io/Adafruit_Wippersnapper_Offline_Configurator/](https://tyeth.github.io/Adafruit_Wippersnapper_Offline_Configurator/)
|
||||
[https://adafruit.github.io/Adafruit_Wippersnapper_Offline_Configurator/](https://adafruit.github.io/Adafruit_Wippersnapper_Offline_Configurator/)
|
||||
|
||||
See this Learn Guide for more info on using Adafruit Wippersnapper Firmware (offline mode) as a Data Logger, which also has the **Supported Hardware** page:
|
||||
[No-Code Offline Data Logger with WipperSnapper](https://learn.adafruit.com/no-code-offline-data-logging-with-wippersnapper/)
|
||||
|
||||
## Development
|
||||
We gratefully accept pull-requests and issues (open-source ❤️) although the main [Wippersnapper repository](https://github.com/adafruit/Adafruit_Wippersnapper_Arduino/issues) is better suited for issues (or the [boards](https://github.com/adafruit/Wippersnapper_Boards) or [components](https://github.com/adafruit/Wippersnapper_Components) repos), as this is a stop-gap solution until the main Adafruit IO website performs the desired functionality (Wippersnapper v2), but it has proven useful so maybe will continue to do so.
|
||||
|
||||
If you wish to play with the website design / functionality then the main files to edit are:
|
||||
* index.html
|
||||
* load-wippersnapper-data.js
|
||||
* wippersnapper-config-builder.js
|
||||
|
||||
The remaining files are involved in updating automatically generated board and component definitions.
|
||||
|
||||
#### Adding new components / boards / companion boards / RTCs
|
||||
|
||||
If you wish to add companion boards then those are manually defined (search featherwing), but boards and components should be added to WipperSnapper to be picked up automatically.
|
||||
If you wish to add new RTCs, they must first be added to the offline firmware, and then we/you can add the RTC to the web interface. The repositories are linked above.
|
||||
|
||||
#### Running the board+component+firmware fetching/conversion process
|
||||
|
||||
To recreate the build process, which processes the boards+component definitions and fetches images + firmware versions, you'll need python installed (and pip) and then to install the requirements:
|
||||
```shell
|
||||
pip install -r requirements.txt
|
||||
```
|
||||
Then before running you'll need to initialise the submodules with git (or download the submodules and unzip manually)
|
||||
```shell
|
||||
git submodule update --init
|
||||
```
|
||||
|
||||
Finally run the convert script:
|
||||
```shell
|
||||
python ./convert_all_wippersnapper_definitions.py
|
||||
```
|
||||
|
||||
And you should see output like this:
|
||||
```
|
||||
=== Conversion Complete ===
|
||||
Converted 23 boards and 98 components
|
||||
Time taken: 24.39 seconds
|
||||
Output files:
|
||||
- C:\dev\js\Adafruit_Wippersnapper_Offline_Configurator\wippersnapper_boards.json
|
||||
- C:\dev\js\Adafruit_Wippersnapper_Offline_Configurator\wippersnapper_components.json
|
||||
- C:\dev\js\Adafruit_Wippersnapper_Offline_Configurator\firmware-data.js
|
||||
```
|
||||
|
||||
That will have replaced the following files:
|
||||
* wippersnapper_boards.js + .json
|
||||
* wippersnapper_components.js + .json
|
||||
* firmware-data.js
|
||||
|
||||
|
||||
## Attribution
|
||||
Written by Tyeth Gundry (with some "assistance"🤦 from Claude3.7/Copilot/GPT4), for Adafruit Industries.
|
||||
|
||||
Adafruit invests time and resources providing this open source code,
|
||||
please support Adafruit and open-source hardware by purchasing
|
||||
products from Adafruit!
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
Subproject commit 86fd50d32b66abe1fbf8c9a1926f310308dc5bea
|
||||
Subproject commit 941e94a52bbe1f33aed3328ddd0ec7a5f3adfbd0
|
||||
|
|
@ -1 +1 @@
|
|||
Subproject commit 848a7bc7fef4eb4701236ba2a0af4fb6f268e19d
|
||||
Subproject commit dc7f57cb1697e4efc4e9490cc51b7ec569f32c15
|
||||
|
|
@ -2,6 +2,7 @@ import os
|
|||
import time
|
||||
from convert_boards_to_json import convert_boards_to_json
|
||||
from convert_components_to_json import convert_components_to_json
|
||||
from fetch_latest_release_info_and_assets import main as fetch_latest_release_info_and_assets
|
||||
|
||||
def main():
|
||||
"""
|
||||
|
|
@ -20,6 +21,9 @@ def main():
|
|||
print("\n--- Converting Components ---")
|
||||
components = convert_components_to_json()
|
||||
|
||||
# fetch latest release info and assets
|
||||
release_info = fetch_latest_release_info_and_assets()
|
||||
|
||||
# Print summary
|
||||
elapsed_time = time.time() - start_time
|
||||
print("\n=== Conversion Complete ===")
|
||||
|
|
@ -28,6 +32,7 @@ def main():
|
|||
print(f"Output files:")
|
||||
print(f" - {os.path.abspath(r'wippersnapper_boards.json')}")
|
||||
print(f" - {os.path.abspath(r'wippersnapper_components.json')}")
|
||||
print(f" - {os.path.abspath(r'firmware-data.json')}")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import os
|
|||
import json
|
||||
import glob
|
||||
from pathlib import Path
|
||||
from convert_components_to_json import get_image_from_adafruit_product_url
|
||||
|
||||
# Base directory for the boards
|
||||
BOARDS_DIR = r"./Wippersnapper_Boards/boards"
|
||||
|
|
@ -11,57 +12,7 @@ def add_custom_board_definitions(boards):
|
|||
"""
|
||||
Add custom board definitions for boards that don't have definition.json files
|
||||
"""
|
||||
# Adafruit Feather RP2040 Adalogger
|
||||
boards["feather-rp2040-adalogger"] = {
|
||||
"boardName": "Adafruit Feather RP2040 Adalogger",
|
||||
"mcuName": "RP2040",
|
||||
"referenceVoltage": 3.3,
|
||||
"displayName": "Adafruit Feather RP2040 Adalogger",
|
||||
"vendor": "Adafruit",
|
||||
"productURL": "https://www.adafruit.com/product/5980",
|
||||
"documentationURL": "https://learn.adafruit.com/adafruit-feather-rp2040-adalogger",
|
||||
"installMethod": "uf2",
|
||||
"totalGPIOPins": 22,
|
||||
"totalAnalogPins": 4,
|
||||
"pins": [
|
||||
{"number": 0, "name": "D0", "displayName": "GPIO0", "hasPWM": True, "hasServo": True},
|
||||
{"number": 1, "name": "D1", "displayName": "GPIO1", "hasPWM": True, "hasServo": True},
|
||||
{"number": 2, "name": "D2", "displayName": "GPIO2", "hasPWM": True, "hasServo": True},
|
||||
{"number": 3, "name": "D3", "displayName": "GPIO3", "hasPWM": True, "hasServo": True},
|
||||
{"number": 4, "name": "D4", "displayName": "GPIO4", "hasPWM": True, "hasServo": True},
|
||||
{"number": 5, "name": "D5", "displayName": "GPIO5", "hasPWM": True, "hasServo": True},
|
||||
{"number": 6, "name": "D6", "displayName": "GPIO6", "hasPWM": True, "hasServo": True},
|
||||
{"number": 7, "name": "D7", "displayName": "GPIO7", "hasPWM": True, "hasServo": True},
|
||||
{"number": 8, "name": "D8", "displayName": "GPIO8", "hasPWM": True, "hasServo": True},
|
||||
{"number": 9, "name": "D9", "displayName": "GPIO9", "hasPWM": True, "hasServo": True},
|
||||
{"number": 10, "name": "D10", "displayName": "GPIO10", "hasPWM": True, "hasServo": True},
|
||||
{"number": 11, "name": "D11", "displayName": "GPIO11", "hasPWM": True, "hasServo": True},
|
||||
{"number": 12, "name": "D12", "displayName": "GPIO12", "hasPWM": True, "hasServo": True},
|
||||
{"number": 13, "name": "D13", "displayName": "GPIO13 (LED)", "hasPWM": True, "hasServo": True},
|
||||
{"number": 16, "name": "D16", "displayName": "GPIO16 (MOSI)", "hasPWM": True, "hasServo": True},
|
||||
{"number": 17, "name": "D17", "displayName": "GPIO17 (A1/SCK/SD CS)", "hasPWM": True, "hasServo": True},
|
||||
{"number": 18, "name": "D18", "displayName": "GPIO18 (MISO)", "hasPWM": True, "hasServo": True},
|
||||
{"number": 19, "name": "D19", "displayName": "GPIO19", "hasPWM": True, "hasServo": True},
|
||||
{"number": 20, "name": "D20", "displayName": "GPIO20", "hasPWM": True, "hasServo": True},
|
||||
{"number": 24, "name": "D24", "displayName": "GPIO24 (A0)", "hasPWM": True, "hasServo": True},
|
||||
{"number": 25, "name": "D25", "displayName": "GPIO25 (A3)", "hasPWM": True, "hasServo": True},
|
||||
{"number": 29, "name": "D29", "displayName": "GPIO29 (A2)", "hasPWM": True, "hasServo": True}
|
||||
],
|
||||
"analogPins": [
|
||||
{"name": "A0", "displayName": "GPIO24 (A0)", "direction": "INPUT"},
|
||||
{"name": "A1", "displayName": "GPIO17 (A1/SD CS)", "direction": "INPUT"},
|
||||
{"name": "A2", "displayName": "GPIO29 (A2)", "direction": "INPUT"},
|
||||
{"name": "A3", "displayName": "GPIO25 (A3)", "direction": "INPUT"}
|
||||
],
|
||||
"defaultI2C": {
|
||||
"i2cPortId": 0,
|
||||
"SCL": "D3",
|
||||
"SDA": "D2"
|
||||
},
|
||||
"sdCardCS": 17, # GPIO17/A1 is the SD card CS pin
|
||||
"rtcType": "PCF8523", # Built-in RTC chip
|
||||
"image": None
|
||||
}
|
||||
|
||||
# Generic ESP32-S2 based board
|
||||
boards["generic-esp32-s2"] = {
|
||||
"boardName": "Generic ESP32-S2",
|
||||
|
|
@ -72,6 +23,9 @@ def add_custom_board_definitions(boards):
|
|||
"productURL": "",
|
||||
"documentationURL": "",
|
||||
"installMethod": "uf2",
|
||||
"installBoardName": "",
|
||||
"rtc": None,
|
||||
"sdCardCS": None,
|
||||
"totalGPIOPins": 43,
|
||||
"totalAnalogPins": 20,
|
||||
"pins": [
|
||||
|
|
@ -123,8 +77,8 @@ def add_custom_board_definitions(boards):
|
|||
],
|
||||
"defaultI2C": {
|
||||
"i2cPortId": 0,
|
||||
"SCL": "D9",
|
||||
"SDA": "D8"
|
||||
"SCL": 9,
|
||||
"SDA": 8
|
||||
},
|
||||
"image": None
|
||||
}
|
||||
|
|
@ -139,6 +93,9 @@ def add_custom_board_definitions(boards):
|
|||
"productURL": "",
|
||||
"documentationURL": "",
|
||||
"installMethod": "uf2",
|
||||
"installBoardName": "",
|
||||
"rtc": None,
|
||||
"sdCardCS": None,
|
||||
"totalGPIOPins": 48,
|
||||
"totalAnalogPins": 20,
|
||||
"pins": [
|
||||
|
|
@ -192,8 +149,8 @@ def add_custom_board_definitions(boards):
|
|||
],
|
||||
"defaultI2C": {
|
||||
"i2cPortId": 0,
|
||||
"SCL": "D9",
|
||||
"SDA": "D8"
|
||||
"SCL": 9,
|
||||
"SDA": 8
|
||||
},
|
||||
"image": None
|
||||
}
|
||||
|
|
@ -208,6 +165,9 @@ def add_custom_board_definitions(boards):
|
|||
"productURL": "",
|
||||
"documentationURL": "",
|
||||
"installMethod": "uf2",
|
||||
"installBoardName": "",
|
||||
"rtc": None,
|
||||
"sdCardCS": None,
|
||||
"totalGPIOPins": 30,
|
||||
"totalAnalogPins": 4,
|
||||
"pins": [
|
||||
|
|
@ -250,8 +210,8 @@ def add_custom_board_definitions(boards):
|
|||
],
|
||||
"defaultI2C": {
|
||||
"i2cPortId": 0,
|
||||
"SCL": "D3",
|
||||
"SDA": "D2"
|
||||
"SCL": 3,
|
||||
"SDA": 2
|
||||
},
|
||||
"image": None
|
||||
}
|
||||
|
|
@ -261,13 +221,16 @@ def add_custom_board_definitions(boards):
|
|||
"boardName": "Generic RP23xx",
|
||||
"mcuName": "RP23xx",
|
||||
"referenceVoltage": 3.3,
|
||||
"displayName": "Generic RP23xx Board",
|
||||
"displayName": "Generic RP2350/RP2354 board",
|
||||
"vendor": "Generic",
|
||||
"productURL": "",
|
||||
"documentationURL": "",
|
||||
"installMethod": "uf2",
|
||||
"totalGPIOPins": 30,
|
||||
"totalAnalogPins": 4,
|
||||
"installBoardName": "",
|
||||
"rtc": None,
|
||||
"sdCardCS": None,
|
||||
"totalGPIOPins": 48,
|
||||
"totalAnalogPins": 8,
|
||||
"pins": [
|
||||
{"number": 0, "name": "D0", "displayName": "GPIO0", "hasPWM": True, "hasServo": True},
|
||||
{"number": 1, "name": "D1", "displayName": "GPIO1", "hasPWM": True, "hasServo": True},
|
||||
|
|
@ -295,83 +258,48 @@ def add_custom_board_definitions(boards):
|
|||
{"number": 23, "name": "D23", "displayName": "GPIO23", "hasPWM": True, "hasServo": True},
|
||||
{"number": 24, "name": "D24", "displayName": "GPIO24", "hasPWM": True, "hasServo": True},
|
||||
{"number": 25, "name": "D25", "displayName": "GPIO25", "hasPWM": True, "hasServo": True},
|
||||
{"number": 26, "name": "D26", "displayName": "GPIO26", "hasPWM": True, "hasServo": True},
|
||||
{"number": 27, "name": "D27", "displayName": "GPIO27", "hasPWM": True, "hasServo": True},
|
||||
{"number": 28, "name": "D28", "displayName": "GPIO28", "hasPWM": True, "hasServo": True},
|
||||
{"number": 29, "name": "D29", "displayName": "GPIO29", "hasPWM": True, "hasServo": True}
|
||||
{"number": 26, "name": "A26", "displayName": "GPIO26 (ADC0)", "hasPWM": True, "hasServo": True},
|
||||
{"number": 27, "name": "A27", "displayName": "GPIO27 (ADC1)", "hasPWM": True, "hasServo": True},
|
||||
{"number": 28, "name": "A28", "displayName": "GPIO28 (ADC2)", "hasPWM": True, "hasServo": True},
|
||||
{"number": 29, "name": "A29", "displayName": "GPIO29 (ADC3)", "hasPWM": True, "hasServo": True},
|
||||
{"number": 30, "name": "D30", "displayName": "GPIO30", "hasPWM": True, "hasServo": True},
|
||||
{"number": 31, "name": "D31", "displayName": "GPIO31", "hasPWM": True, "hasServo": True},
|
||||
{"number": 32, "name": "D32", "displayName": "GPIO32", "hasPWM": True, "hasServo": True},
|
||||
{"number": 33, "name": "D33", "displayName": "GPIO33", "hasPWM": True, "hasServo": True},
|
||||
{"number": 34, "name": "D34", "displayName": "GPIO34", "hasPWM": True, "hasServo": True},
|
||||
{"number": 35, "name": "D35", "displayName": "GPIO35", "hasPWM": True, "hasServo": True},
|
||||
{"number": 36, "name": "D36", "displayName": "GPIO36", "hasPWM": True, "hasServo": True},
|
||||
{"number": 37, "name": "D37", "displayName": "GPIO37", "hasPWM": True, "hasServo": True},
|
||||
{"number": 38, "name": "D38", "displayName": "GPIO38", "hasPWM": True, "hasServo": True},
|
||||
{"number": 39, "name": "D39", "displayName": "GPIO39", "hasPWM": True, "hasServo": True},
|
||||
{"number": 40, "name": "A40", "displayName": "GPIO40 (ADC0)", "hasPWM": True, "hasServo": True},
|
||||
{"number": 41, "name": "A41", "displayName": "GPIO41 (ADC1)", "hasPWM": True, "hasServo": True},
|
||||
{"number": 42, "name": "A42", "displayName": "GPIO42 (ADC2)", "hasPWM": True, "hasServo": True},
|
||||
{"number": 43, "name": "A43", "displayName": "GPIO43 (ADC3)", "hasPWM": True, "hasServo": True},
|
||||
{"number": 44, "name": "D44", "displayName": "GPIO44", "hasPWM": True, "hasServo": True},
|
||||
{"number": 45, "name": "D45", "displayName": "GPIO45", "hasPWM": True, "hasServo": True},
|
||||
{"number": 46, "name": "D46", "displayName": "GPIO46", "hasPWM": True, "hasServo": True},
|
||||
{"number": 47, "name": "D47", "displayName": "GPIO47", "hasPWM": True, "hasServo": True},
|
||||
{"number": 48, "name": "D48", "displayName": "GPIO48", "hasPWM": True, "hasServo": True}
|
||||
],
|
||||
"analogPins": [
|
||||
{"name": "A0", "displayName": "ADC0", "direction": "INPUT"},
|
||||
{"name": "A1", "displayName": "ADC1", "direction": "INPUT"},
|
||||
{"name": "A2", "displayName": "ADC2", "direction": "INPUT"},
|
||||
{"name": "A3", "displayName": "ADC3", "direction": "INPUT"}
|
||||
{"name": "A26", "displayName": "GPIO26 (ADC0)", "direction": "INPUT"},
|
||||
{"name": "A27", "displayName": "GPIO27 (ADC1)", "direction": "INPUT"},
|
||||
{"name": "A28", "displayName": "GPIO28 (ADC2)", "direction": "INPUT"},
|
||||
{"name": "A29", "displayName": "GPIO29 (ADC3)", "direction": "INPUT"},
|
||||
{"name": "A40", "displayName": "GPIO40 (ADC0)", "direction": "INPUT"},
|
||||
{"name": "A41", "displayName": "GPIO41 (ADC1)", "direction": "INPUT"},
|
||||
{"name": "A42", "displayName": "GPIO42 (ADC2)", "direction": "INPUT"},
|
||||
{"name": "A43", "displayName": "GPIO43 (ADC3)", "direction": "INPUT"}
|
||||
],
|
||||
"defaultI2C": {
|
||||
"i2cPortId": 0,
|
||||
"SCL": "D3",
|
||||
"SDA": "D2"
|
||||
"SCL": 3,
|
||||
"SDA": 2
|
||||
},
|
||||
"image": None
|
||||
}
|
||||
|
||||
# Adafruit Metro RP2350 with SD card
|
||||
boards["metro-rp2350-sd"] = {
|
||||
"boardName": "Adafruit Metro RP2350",
|
||||
"mcuName": "RP2350",
|
||||
"referenceVoltage": 3.3,
|
||||
"displayName": "Adafruit Metro RP2350 (with SD card)",
|
||||
"vendor": "Adafruit",
|
||||
"productURL": "https://www.adafruit.com/product/5786",
|
||||
"documentationURL": "https://learn.adafruit.com/adafruit-metro-rp2350",
|
||||
"installMethod": "uf2",
|
||||
"totalGPIOPins": 30,
|
||||
"totalAnalogPins": 4,
|
||||
"pins": [
|
||||
{"number": 0, "name": "D0", "displayName": "GPIO0", "hasPWM": True, "hasServo": True},
|
||||
{"number": 1, "name": "D1", "displayName": "GPIO1", "hasPWM": True, "hasServo": True},
|
||||
{"number": 2, "name": "D2", "displayName": "GPIO2", "hasPWM": True, "hasServo": True},
|
||||
{"number": 3, "name": "D3", "displayName": "GPIO3", "hasPWM": True, "hasServo": True},
|
||||
{"number": 4, "name": "D4", "displayName": "GPIO4", "hasPWM": True, "hasServo": True},
|
||||
{"number": 5, "name": "D5", "displayName": "GPIO5", "hasPWM": True, "hasServo": True},
|
||||
{"number": 6, "name": "D6", "displayName": "GPIO6", "hasPWM": True, "hasServo": True},
|
||||
{"number": 7, "name": "D7", "displayName": "GPIO7", "hasPWM": True, "hasServo": True},
|
||||
{"number": 8, "name": "D8", "displayName": "GPIO8", "hasPWM": True, "hasServo": True},
|
||||
{"number": 9, "name": "D9", "displayName": "GPIO9", "hasPWM": True, "hasServo": True},
|
||||
{"number": 10, "name": "D10", "displayName": "GPIO10 (SD CS)", "hasPWM": True, "hasServo": True},
|
||||
{"number": 11, "name": "D11", "displayName": "GPIO11", "hasPWM": True, "hasServo": True},
|
||||
{"number": 12, "name": "D12", "displayName": "GPIO12", "hasPWM": True, "hasServo": True},
|
||||
{"number": 13, "name": "D13", "displayName": "GPIO13", "hasPWM": True, "hasServo": True},
|
||||
{"number": 14, "name": "D14", "displayName": "GPIO14", "hasPWM": True, "hasServo": True},
|
||||
{"number": 15, "name": "D15", "displayName": "GPIO15", "hasPWM": True, "hasServo": True},
|
||||
{"number": 16, "name": "D16", "displayName": "GPIO16", "hasPWM": True, "hasServo": True},
|
||||
{"number": 17, "name": "D17", "displayName": "GPIO17", "hasPWM": True, "hasServo": True},
|
||||
{"number": 18, "name": "D18", "displayName": "GPIO18", "hasPWM": True, "hasServo": True},
|
||||
{"number": 19, "name": "D19", "displayName": "GPIO19", "hasPWM": True, "hasServo": True},
|
||||
{"number": 20, "name": "D20", "displayName": "GPIO20", "hasPWM": True, "hasServo": True},
|
||||
{"number": 21, "name": "D21", "displayName": "GPIO21", "hasPWM": True, "hasServo": True},
|
||||
{"number": 22, "name": "D22", "displayName": "GPIO22", "hasPWM": True, "hasServo": True},
|
||||
{"number": 23, "name": "D23", "displayName": "GPIO23", "hasPWM": True, "hasServo": True},
|
||||
{"number": 24, "name": "D24", "displayName": "GPIO24", "hasPWM": True, "hasServo": True},
|
||||
{"number": 25, "name": "D25", "displayName": "GPIO25", "hasPWM": True, "hasServo": True},
|
||||
{"number": 26, "name": "D26", "displayName": "GPIO26", "hasPWM": True, "hasServo": True},
|
||||
{"number": 27, "name": "D27", "displayName": "GPIO27", "hasPWM": True, "hasServo": True},
|
||||
{"number": 28, "name": "D28", "displayName": "GPIO28", "hasPWM": True, "hasServo": True},
|
||||
{"number": 29, "name": "D29", "displayName": "GPIO29", "hasPWM": True, "hasServo": True}
|
||||
],
|
||||
"analogPins": [
|
||||
{"name": "A0", "displayName": "ADC0", "direction": "INPUT"},
|
||||
{"name": "A1", "displayName": "ADC1", "direction": "INPUT"},
|
||||
{"name": "A2", "displayName": "ADC2", "direction": "INPUT"},
|
||||
{"name": "A3", "displayName": "ADC3", "direction": "INPUT"}
|
||||
],
|
||||
"defaultI2C": {
|
||||
"i2cPortId": 0,
|
||||
"SCL": "D3",
|
||||
"SDA": "D2"
|
||||
},
|
||||
"sdCardCS": 10,
|
||||
"image": None
|
||||
}
|
||||
|
||||
return boards
|
||||
|
||||
|
|
@ -409,18 +337,28 @@ def convert_boards_to_json():
|
|||
"productURL": board_data.get("productURL", ""),
|
||||
"documentationURL": board_data.get("documentationURL", ""),
|
||||
"installMethod": board_data.get("installMethod", ""),
|
||||
"installBoardName": board_data.get("installBoardName", board_data.get("boardName", "")),
|
||||
"rtc": board_data.get("rtc", None),
|
||||
"sdCardCS": board_data.get("sdCardCS", None),
|
||||
"pins": [],
|
||||
"analogPins": [],
|
||||
"defaultI2C": {},
|
||||
"image": None
|
||||
}
|
||||
|
||||
SUPPORTED_ARCHITECTURES = ["rp2350", "rp2040", "esp32s2", "esp32s3"]
|
||||
# Filter out boards that are not supported
|
||||
if board_info["mcuName"] not in SUPPORTED_ARCHITECTURES:
|
||||
print(f"Skipping {board_dir} - Unsupported architecture {board_info['mcuName']}")
|
||||
continue
|
||||
|
||||
# Get pins
|
||||
if "components" in board_data and "digitalPins" in board_data["components"]:
|
||||
for pin in board_data["components"]["digitalPins"]:
|
||||
# Extract the numeric part of the pin name (e.g., "D13" -> 13)
|
||||
pin_name = pin.get("name", "")
|
||||
if pin_name.startswith("D"):
|
||||
# if board_info["pins"].filter(lambda x: x["name"] == pin_name) == 0:
|
||||
try:
|
||||
pin_number = int(pin_name[1:])
|
||||
board_info["pins"].append({
|
||||
|
|
@ -428,7 +366,8 @@ def convert_boards_to_json():
|
|||
"name": pin_name,
|
||||
"displayName": pin.get("displayName", pin_name),
|
||||
"hasPWM": pin.get("hasPWM", False),
|
||||
"hasServo": pin.get("hasServo", False)
|
||||
"hasServo": pin.get("hasServo", False),
|
||||
"direction": pin.get("direction", "")
|
||||
})
|
||||
except ValueError:
|
||||
print(f"Skipping pin {pin_name} - Cannot parse pin number")
|
||||
|
|
@ -438,12 +377,27 @@ def convert_boards_to_json():
|
|||
analog_count = 0
|
||||
for pin in board_data["components"]["analogPins"]:
|
||||
pin_name = pin.get("name", "")
|
||||
try:
|
||||
pin_number = int(pin_name[1:])
|
||||
board_info["analogPins"].append({
|
||||
"number": pin_number,
|
||||
"name": pin_name,
|
||||
"displayName": pin.get("displayName", pin_name),
|
||||
"direction": pin.get("direction", "INPUT")
|
||||
"direction": pin.get("direction", "")
|
||||
})
|
||||
analog_count += 1
|
||||
# Add to pins list as well if number not already present
|
||||
if not any(p["number"] == pin_number for p in board_info["pins"]):
|
||||
board_info["pins"].append({
|
||||
"number": pin_number,
|
||||
"name": pin_name,
|
||||
"displayName": pin.get("displayName", pin_name),
|
||||
"direction": "", # Blank for both input and output
|
||||
"hasPWM": True,
|
||||
"hasServo": True
|
||||
})
|
||||
except ValueError:
|
||||
print(f"Skipping pin {pin_name} - Cannot parse pin number")
|
||||
board_info["totalAnalogPins"] = analog_count
|
||||
|
||||
# Get I2C ports
|
||||
|
|
@ -472,6 +426,10 @@ def convert_boards_to_json():
|
|||
board_info["image"] = f"boards/{board_dir}/image{ext}"
|
||||
break
|
||||
|
||||
# Fetch missing images
|
||||
if board_info["image"] is None and board_info["productURL"] != "":
|
||||
board_info["image"] = get_image_from_adafruit_product_url(board_info["productURL"])
|
||||
|
||||
# Add to boards dictionary
|
||||
boards[board_dir] = board_info
|
||||
print(f"Processed {board_dir}")
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import json
|
|||
import glob
|
||||
import re
|
||||
import requests
|
||||
from bs4 import BeautifulSoup
|
||||
# from bs4 import BeautifulSoup
|
||||
from pathlib import Path
|
||||
|
||||
# Base directory for the components
|
||||
|
|
@ -12,8 +12,8 @@ OUTPUT_FILE = r"./wippersnapper_components.json"
|
|||
|
||||
def get_image_from_adafruit_product_url(product_url):
|
||||
"""
|
||||
Fetch the product image URL from an Adafruit product page by extracting
|
||||
the og:image meta tag from the HTML.
|
||||
Fetch the product image URL from an Adafruit product API
|
||||
(was from prod page by extracting the og:image meta tag from the HTML).
|
||||
|
||||
Args:
|
||||
product_url (str): URL to an Adafruit product page
|
||||
|
|
@ -21,29 +21,44 @@ def get_image_from_adafruit_product_url(product_url):
|
|||
Returns:
|
||||
str or None: URL to the product image, or None if not found
|
||||
"""
|
||||
if not product_url or not re.match(r'https?://(?:www\.)?adafruit\.com/product/\d+', product_url):
|
||||
if not product_url or not re.match(r'https?://(?:www\.)?adafruit\.com/(?:product|category)/\d+', product_url):
|
||||
print(f"Invalid Adafruit product URL ({product_url}) provided. Skipping image fetch.")
|
||||
return None
|
||||
|
||||
# Grab product JSON from https://www.adafruit.com/api/products, cache, save as ISO date string filename so can be easily used as cache key
|
||||
try:
|
||||
response = requests.get(product_url, timeout=10)
|
||||
product_id = re.search(r'/product/(\d+)', product_url)
|
||||
category = re.search(r'/category/(\d+)', product_url)
|
||||
url_to_fetch = f"https://www.adafruit.com/api/{("category" if category else "product")}/{(product_id.groups(1)[0] if product_id else category.groups(1)[0])}"
|
||||
print(f"Fetching image from Adafruit API for {product_url}...\nGET {url_to_fetch}")
|
||||
response = requests.get(url_to_fetch, timeout=10)
|
||||
if response.status_code != 200:
|
||||
print(f"Failed to fetch product page: {product_url}, status code: {response.status_code}")
|
||||
print(f"Failed to fetch product data: {product_url}, status code: {response.status_code}")
|
||||
return None
|
||||
response_json = response.json()
|
||||
if (response_json is None or response_json == [] or response_json == {} or 'error' in response_json):
|
||||
print(f"Invalid response from API for {product_url}: {response_json}")
|
||||
return None
|
||||
|
||||
soup = BeautifulSoup(response.text, 'html.parser')
|
||||
og_image = soup.find('meta', property='og:image')
|
||||
|
||||
if og_image and 'content' in og_image.attrs:
|
||||
image_url = og_image['content']
|
||||
print(f"Found image URL from product page: {image_url}")
|
||||
if 'product_image' in response_json:
|
||||
image_url = response_json['product_image']
|
||||
print(f"Found image URL from API: {image_url}")
|
||||
return image_url
|
||||
else:
|
||||
print(f"No og:image meta tag found for: {product_url}")
|
||||
elif 'subcategories' in response_json:
|
||||
subcategories = response_json['subcategories']
|
||||
for subcategory in subcategories:
|
||||
if 'product_image' in subcategory:
|
||||
image_url = subcategory['product_image']
|
||||
print(f"Found image URL from API: {image_url}")
|
||||
return image_url
|
||||
print(f"No image found in subcategory for: {product_url}")
|
||||
return None
|
||||
except Exception as e:
|
||||
print(f"Error fetching image from product URL {product_url}: {str(e)}")
|
||||
print(f"Error fetching image from API for {product_url}: {str(e)}")
|
||||
return None
|
||||
|
||||
## Consider removing beautifulsoup...
|
||||
|
||||
def map_datatypes_to_offline_types(datatype):
|
||||
"""
|
||||
Map datatypes to offline types.
|
||||
|
|
@ -84,9 +99,15 @@ def convert_components_to_json():
|
|||
with open(definition_file, 'r') as f:
|
||||
component_data = json.load(f)
|
||||
|
||||
# Skip non-input components for now
|
||||
if category == "pin":
|
||||
if component_data.get("direction", "").lower() != "input":
|
||||
continue
|
||||
|
||||
# Extract relevant information
|
||||
component_info = {
|
||||
"id": component_dir,
|
||||
"displayName": component_data.get("displayName", component_dir),
|
||||
"name": component_data.get("name", component_dir),
|
||||
"description": component_data.get("description", ""),
|
||||
"category": category,
|
||||
|
|
@ -98,6 +119,10 @@ def convert_components_to_json():
|
|||
if "productURL" in component_data:
|
||||
component_info["productUrl"] = component_data["productURL"]
|
||||
|
||||
# store documentation URL if available
|
||||
if "documentationURL" in component_data:
|
||||
component_info["documentationUrl"] = component_data["documentationURL"]
|
||||
|
||||
# Extract data types if available
|
||||
if "subcomponents" in component_data:
|
||||
for meas_type in component_data["subcomponents"]:
|
||||
|
|
@ -109,6 +134,10 @@ def convert_components_to_json():
|
|||
else:
|
||||
component_info["dataTypes"].append(map_datatypes_to_offline_types(meas_type))
|
||||
|
||||
# Handle pin category (MODE becomes componentAPI value)
|
||||
if category == "pin":
|
||||
component_info["componentAPI"] = component_data.get("mode", "digital").lower() + "io"
|
||||
|
||||
# Handle I2C-specific properties
|
||||
if category == "i2c":
|
||||
# Extract I2C address from parameters
|
||||
|
|
@ -128,6 +157,69 @@ def convert_components_to_json():
|
|||
else:
|
||||
component_info["channels"] = 4
|
||||
|
||||
# Special handling for GPS over I2C
|
||||
if component_data.get("isGps", False):
|
||||
component_info["isGps"] = True
|
||||
component_info["gps"] = {}
|
||||
gps_data = component_data.get("gps", {})
|
||||
component_info["gps"]["period"] = gps_data.get("period", 30000)
|
||||
if "commands_ubxes" in gps_data:
|
||||
component_info["gps"]["commands_ubxes"] = gps_data["commands_ubxes"]
|
||||
if "commands_pmtks" in gps_data:
|
||||
component_info["gps"]["commands_pmtks"] = gps_data["commands_pmtks"]
|
||||
|
||||
# Handle UART-specific properties
|
||||
if category == "uart":
|
||||
# Required properties
|
||||
component_info["deviceType"] = component_data.get("deviceType")
|
||||
component_info["deviceId"] = component_data.get("deviceId")
|
||||
# Specific deviceType properties
|
||||
if component_info["deviceType"] == "generic_input":
|
||||
component_info["generic_input"] = {}
|
||||
generic_input_data = component_data.get("generic_input", {})
|
||||
component_info["generic_input"]["period"] = generic_input_data.get("period", 30000)
|
||||
# Extract data types
|
||||
if "generic_input" in component_data and "sensor_types" in component_data["generic_input"]:
|
||||
for meas_type in component_data["generic_input"]["sensor_types"]:
|
||||
if isinstance(meas_type, dict) and "sensorType" in meas_type:
|
||||
component_info["dataTypes"].append({
|
||||
"displayName": meas_type["displayName"] if "displayName" in meas_type else meas_type["sensorType"],
|
||||
"sensorType": map_datatypes_to_offline_types(meas_type["sensorType"]) if "sensorType" in meas_type else None
|
||||
})
|
||||
else:
|
||||
component_info["dataTypes"].append(map_datatypes_to_offline_types(meas_type))
|
||||
elif component_info["deviceType"] == "gps":
|
||||
component_info["gps"] = {}
|
||||
gps_data = component_data.get("gps", {})
|
||||
component_info["gps"]["period"] = gps_data.get("period", 30000)
|
||||
if "commands_ubxes" in gps_data:
|
||||
component_info["gps"]["commands_ubxes"] = gps_data["commands_ubxes"]
|
||||
if "commands_pmtks" in gps_data:
|
||||
component_info["gps"]["commands_pmtks"] = gps_data["commands_pmtks"]
|
||||
elif component_info["deviceType"] == "pm25aqi":
|
||||
component_info["pm25aqi"] = {}
|
||||
# Parse PM2.5 AQI properties
|
||||
if "pm25aqi" in component_data:
|
||||
component_info["pm25aqi"]["period"] = component_data["pm25aqi"].get("period", 30000) # Default to 30s if not specified
|
||||
if component_data["pm25aqi"].get("is_pm1006", False):
|
||||
component_info["pm25aqi"]["is_pm1006"] = True
|
||||
# Extract data types
|
||||
pm25aqi_data = component_data
|
||||
if "pm25aqi" in pm25aqi_data and "sensor_types" in pm25aqi_data["pm25aqi"]:
|
||||
for meas_type in pm25aqi_data["pm25aqi"]["sensor_types"]:
|
||||
if isinstance(meas_type, dict) and "sensorType" in meas_type:
|
||||
component_info["dataTypes"].append({
|
||||
"displayName": meas_type["displayName"] if "displayName" in meas_type else meas_type["sensorType"],
|
||||
"sensorType": map_datatypes_to_offline_types(meas_type["sensorType"]) if "sensorType" in meas_type else None
|
||||
})
|
||||
else:
|
||||
component_info["dataTypes"].append(map_datatypes_to_offline_types(meas_type))
|
||||
elif component_info["deviceType"] == "tmc22xx":
|
||||
# TODO
|
||||
pass
|
||||
else:
|
||||
raise ValueError(f"Unknown deviceType {component_info['deviceType']} for {category}/{component_dir}")
|
||||
|
||||
# Look for an image file
|
||||
image_extensions = ['.jpg', '.jpeg', '.png', '.gif', '.svg']
|
||||
image_found = False
|
||||
|
|
@ -178,12 +270,12 @@ def convert_components_to_json():
|
|||
|
||||
# Write the consolidated JSON file
|
||||
with open(OUTPUT_FILE, 'w') as f:
|
||||
json.dump({"components": components}, f, indent=2)
|
||||
json.dump({"components": components}, f, ensure_ascii=False, indent=2)
|
||||
|
||||
# Write the consolidated JS file
|
||||
with open(OUTPUT_FILE.replace('.json', '.js'), 'w') as f:
|
||||
f.write("window.jsonComponentsObject = ")
|
||||
json.dump({"components": components}, f, indent=2)
|
||||
json.dump({"components": components}, f, ensure_ascii=False, indent=2)
|
||||
f.write(";\n")
|
||||
|
||||
print(f"Successfully created {OUTPUT_FILE}")
|
||||
|
|
|
|||
BIN
favicon.ico
Normal file
BIN
favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 137 KiB |
212
fetch_latest_release_info_and_assets.py
Normal file
212
fetch_latest_release_info_and_assets.py
Normal file
|
|
@ -0,0 +1,212 @@
|
|||
# Take releases for wippersnapper, filtered to semver including -offline, get next page until finished if none.
|
||||
|
||||
# Later match board name to installBoardName from boards (which falls back to boardName)
|
||||
|
||||
import requests
|
||||
import json
|
||||
import os
|
||||
from datetime import datetime
|
||||
|
||||
# Configuration
|
||||
REPO = "adafruit/Adafruit_Wippersnapper_Arduino"
|
||||
OUTPUT_DIR = "latest_firmware"
|
||||
RELEASES_API_URL = f"https://api.github.com/repos/{REPO}/releases"
|
||||
PER_PAGE = 30 # GitHub API default per page limit
|
||||
JS_OUTPUT_FILE = "firmware-data.js"
|
||||
|
||||
def fetch_latest_offline_release():
|
||||
"""Fetch the latest offline release from GitHub API."""
|
||||
print(f"Fetching releases information for {REPO}...")
|
||||
|
||||
headers = {
|
||||
"Accept": "application/vnd.github.v3+json"
|
||||
}
|
||||
|
||||
all_releases = []
|
||||
page = 1
|
||||
found_offline_release = False
|
||||
latest_offline_release = None
|
||||
|
||||
# Loop through pages until we find an offline release or run out of releases
|
||||
while not found_offline_release:
|
||||
page_url = f"{RELEASES_API_URL}?page={page}&per_page={PER_PAGE}"
|
||||
print(f"Fetching page {page} of releases...")
|
||||
|
||||
response = requests.get(page_url, headers=headers)
|
||||
response.raise_for_status()
|
||||
|
||||
page_releases = response.json()
|
||||
all_releases.extend(page_releases)
|
||||
|
||||
# Check if this page contains any offline releases
|
||||
offline_releases = [r for r in page_releases if "offline" in r.get("tag_name", "")]
|
||||
|
||||
offline_releases_count = len(offline_releases)
|
||||
print(f"Found {offline_releases_count} offline releases on page {page}")
|
||||
|
||||
if offline_releases_count > 0:
|
||||
found_offline_release = True
|
||||
# Get the latest offline release by published date
|
||||
latest_offline_release = sorted(
|
||||
offline_releases,
|
||||
key=lambda x: x.get("published_at", ""),
|
||||
reverse=True
|
||||
)[0]
|
||||
break
|
||||
|
||||
# Move to next page if we didn't find any offline releases and there are more pages
|
||||
page += 1
|
||||
|
||||
# Stop if we received fewer items than the per_page limit (meaning we're on the last page)
|
||||
if len(page_releases) < PER_PAGE:
|
||||
print(f"Reached the last page with {len(page_releases)} items (less than {PER_PAGE} per page)")
|
||||
break
|
||||
|
||||
# Check if we found an offline release
|
||||
if latest_offline_release is None:
|
||||
raise Exception(f"No offline release found after checking {page} pages of releases.")
|
||||
|
||||
tag_name = latest_offline_release.get("tag_name")
|
||||
release_name = latest_offline_release.get("name")
|
||||
release_id = latest_offline_release.get("id")
|
||||
release_html_url = latest_offline_release.get("html_url")
|
||||
release_published_date = latest_offline_release.get("published_at")
|
||||
|
||||
print(f"Found latest offline release: {release_name} (Tag: {tag_name}, ID: {release_id})")
|
||||
|
||||
return latest_offline_release, release_id, tag_name, release_name, release_html_url, release_published_date
|
||||
|
||||
def fetch_release_assets(release_id):
|
||||
"""Fetch all assets for a specific release."""
|
||||
assets_url = f"https://api.github.com/repos/{REPO}/releases/{release_id}/assets"
|
||||
print(f"Fetching complete assets list from: {assets_url}")
|
||||
|
||||
headers = {
|
||||
"Accept": "application/vnd.github.v3+json"
|
||||
}
|
||||
|
||||
all_assets = []
|
||||
assets_page = 1
|
||||
|
||||
# Loop through pages to get all assets
|
||||
while True:
|
||||
assets_page_url = f"{assets_url}?page={assets_page}&per_page={PER_PAGE}"
|
||||
print(f"Fetching page {assets_page} of assets...")
|
||||
|
||||
response = requests.get(assets_page_url, headers=headers)
|
||||
response.raise_for_status()
|
||||
|
||||
page_assets = response.json()
|
||||
|
||||
# Add to all assets - ensure it's a list
|
||||
if isinstance(page_assets, list):
|
||||
all_assets.extend(page_assets)
|
||||
else:
|
||||
all_assets.append(page_assets)
|
||||
|
||||
# Move to next page if there are more assets
|
||||
assets_page += 1
|
||||
|
||||
# Stop if we received fewer items than the per_page limit (meaning we're on the last page)
|
||||
if len(page_assets) < PER_PAGE:
|
||||
print(f"Reached the last page of assets with {len(page_assets)} items")
|
||||
break
|
||||
|
||||
# Filter for UF2 files
|
||||
print("Filtering for firmware files...")
|
||||
firmware_assets = [
|
||||
asset for asset in all_assets
|
||||
if asset.get("name", "").endswith(".uf2")# or asset.get("name", "").endswith(".bin")
|
||||
]
|
||||
|
||||
# Check if we found any assets
|
||||
if len(firmware_assets) == 0:
|
||||
print(f"No firmware files (.uf2) found in this release.")
|
||||
|
||||
return firmware_assets
|
||||
|
||||
def generate_js_data(release_info, assets):
|
||||
"""Generate JavaScript data structure with firmware information."""
|
||||
tag_name, release_name, release_html_url, release_published_date = release_info
|
||||
|
||||
# Format the published date
|
||||
try:
|
||||
dt = datetime.fromisoformat(release_published_date.replace("Z", "+00:00"))
|
||||
formatted_date = dt.strftime("%Y-%m-%d")
|
||||
except:
|
||||
formatted_date = release_published_date
|
||||
|
||||
# Create firmware data object
|
||||
firmware_data = {
|
||||
"releaseInfo": {
|
||||
"version": tag_name,
|
||||
"name": release_name,
|
||||
"url": release_html_url,
|
||||
"publishedDate": formatted_date
|
||||
},
|
||||
"firmwareFiles": []
|
||||
}
|
||||
|
||||
# Add firmware files info
|
||||
for asset in assets:
|
||||
if asset.get("name", "").endswith(".uf2") or asset.get("name", "").endswith(".bin"):
|
||||
firmware_data["firmwareFiles"].append({
|
||||
"name": asset.get("name"),
|
||||
"url": asset.get("browser_download_url"),
|
||||
"size": asset.get("size"),
|
||||
"downloadCount": asset.get("download_count", 0),
|
||||
"contentType": asset.get("content_type"),
|
||||
"createdAt": asset.get("created_at")
|
||||
})
|
||||
|
||||
return firmware_data
|
||||
|
||||
def save_js_data(firmware_data):
|
||||
"""Save firmware data as a JavaScript file."""
|
||||
js_content = f"""// Auto-generated on {datetime.now().strftime("%Y-%m-%d %H:%M:%S")}
|
||||
const FIRMWARE_DATA = {json.dumps(firmware_data, indent=2)};
|
||||
|
||||
// Export for use in other modules
|
||||
if (typeof module !== 'undefined' && module.exports) {{
|
||||
module.exports = {{ FIRMWARE_DATA }};
|
||||
}} else {{
|
||||
window.FIRMWARE_DATA = FIRMWARE_DATA;
|
||||
}}
|
||||
"""
|
||||
|
||||
with open(JS_OUTPUT_FILE, "w") as f:
|
||||
f.write(js_content)
|
||||
|
||||
print(f"JavaScript data saved to {JS_OUTPUT_FILE}")
|
||||
|
||||
def main():
|
||||
try:
|
||||
# Create output directory if it doesn't exist
|
||||
if not os.path.exists(OUTPUT_DIR):
|
||||
os.makedirs(OUTPUT_DIR)
|
||||
print(f"Created directory: {OUTPUT_DIR}")
|
||||
|
||||
# Fetch latest offline release
|
||||
release_data, release_id, tag_name, release_name, release_html_url, release_published_date = fetch_latest_offline_release()
|
||||
|
||||
# Fetch assets for this release
|
||||
firmware_assets = fetch_release_assets(release_id)
|
||||
|
||||
print(f"Found {len(firmware_assets)} firmware files.")
|
||||
|
||||
# Generate JavaScript data
|
||||
firmware_data = generate_js_data(
|
||||
(tag_name, release_name, release_html_url, release_published_date),
|
||||
firmware_assets
|
||||
)
|
||||
|
||||
# Save as JavaScript file
|
||||
save_js_data(firmware_data)
|
||||
|
||||
print("Process completed successfully.")
|
||||
|
||||
except Exception as e:
|
||||
print(f"Error: {str(e)}")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
162
firmware-data.js
Normal file
162
firmware-data.js
Normal file
|
|
@ -0,0 +1,162 @@
|
|||
// Auto-generated on 2025-08-29 00:19:42
|
||||
const FIRMWARE_DATA = {
|
||||
"releaseInfo": {
|
||||
"version": "1.0.0-offline-beta.4",
|
||||
"name": "WipperSnapper Offline 1.0.0-offline-beta.4",
|
||||
"url": "https://github.com/adafruit/Adafruit_Wippersnapper_Arduino/releases/tag/1.0.0-offline-beta.4",
|
||||
"publishedDate": "2025-08-18"
|
||||
},
|
||||
"firmwareFiles": [
|
||||
{
|
||||
"name": "wippersnapper.esp32s3_devkitc_1_n8.1.0.0-offline-beta.4.uf2",
|
||||
"url": "https://github.com/adafruit/Adafruit_Wippersnapper_Arduino/releases/download/1.0.0-offline-beta.4/wippersnapper.esp32s3_devkitc_1_n8.1.0.0-offline-beta.4.uf2",
|
||||
"size": 3044864,
|
||||
"downloadCount": 0,
|
||||
"contentType": "application/octet-stream",
|
||||
"createdAt": "2025-08-18T17:38:47Z"
|
||||
},
|
||||
{
|
||||
"name": "wippersnapper.feather_esp32s2.1.0.0-offline-beta.4.uf2",
|
||||
"url": "https://github.com/adafruit/Adafruit_Wippersnapper_Arduino/releases/download/1.0.0-offline-beta.4/wippersnapper.feather_esp32s2.1.0.0-offline-beta.4.uf2",
|
||||
"size": 2990080,
|
||||
"downloadCount": 0,
|
||||
"contentType": "application/octet-stream",
|
||||
"createdAt": "2025-08-18T17:38:47Z"
|
||||
},
|
||||
{
|
||||
"name": "wippersnapper.feather_esp32s2_reverse_tft.1.0.0-offline-beta.4.uf2",
|
||||
"url": "https://github.com/adafruit/Adafruit_Wippersnapper_Arduino/releases/download/1.0.0-offline-beta.4/wippersnapper.feather_esp32s2_reverse_tft.1.0.0-offline-beta.4.uf2",
|
||||
"size": 2990080,
|
||||
"downloadCount": 0,
|
||||
"contentType": "application/octet-stream",
|
||||
"createdAt": "2025-08-18T17:38:47Z"
|
||||
},
|
||||
{
|
||||
"name": "wippersnapper.feather_esp32s2_tft.1.0.0-offline-beta.4.uf2",
|
||||
"url": "https://github.com/adafruit/Adafruit_Wippersnapper_Arduino/releases/download/1.0.0-offline-beta.4/wippersnapper.feather_esp32s2_tft.1.0.0-offline-beta.4.uf2",
|
||||
"size": 2990080,
|
||||
"downloadCount": 0,
|
||||
"contentType": "application/octet-stream",
|
||||
"createdAt": "2025-08-18T17:38:47Z"
|
||||
},
|
||||
{
|
||||
"name": "wippersnapper.feather_esp32s3.1.0.0-offline-beta.4.uf2",
|
||||
"url": "https://github.com/adafruit/Adafruit_Wippersnapper_Arduino/releases/download/1.0.0-offline-beta.4/wippersnapper.feather_esp32s3.1.0.0-offline-beta.4.uf2",
|
||||
"size": 3044864,
|
||||
"downloadCount": 0,
|
||||
"contentType": "application/octet-stream",
|
||||
"createdAt": "2025-08-18T17:38:47Z"
|
||||
},
|
||||
{
|
||||
"name": "wippersnapper.feather_esp32s3_4mbflash_2mbpsram.1.0.0-offline-beta.4.uf2",
|
||||
"url": "https://github.com/adafruit/Adafruit_Wippersnapper_Arduino/releases/download/1.0.0-offline-beta.4/wippersnapper.feather_esp32s3_4mbflash_2mbpsram.1.0.0-offline-beta.4.uf2",
|
||||
"size": 3050496,
|
||||
"downloadCount": 0,
|
||||
"contentType": "application/octet-stream",
|
||||
"createdAt": "2025-08-18T17:38:47Z"
|
||||
},
|
||||
{
|
||||
"name": "wippersnapper.feather_esp32s3_reverse_tft.1.0.0-offline-beta.4.uf2",
|
||||
"url": "https://github.com/adafruit/Adafruit_Wippersnapper_Arduino/releases/download/1.0.0-offline-beta.4/wippersnapper.feather_esp32s3_reverse_tft.1.0.0-offline-beta.4.uf2",
|
||||
"size": 3050496,
|
||||
"downloadCount": 0,
|
||||
"contentType": "application/octet-stream",
|
||||
"createdAt": "2025-08-18T17:38:47Z"
|
||||
},
|
||||
{
|
||||
"name": "wippersnapper.feather_esp32s3_tft.1.0.0-offline-beta.4.uf2",
|
||||
"url": "https://github.com/adafruit/Adafruit_Wippersnapper_Arduino/releases/download/1.0.0-offline-beta.4/wippersnapper.feather_esp32s3_tft.1.0.0-offline-beta.4.uf2",
|
||||
"size": 3050496,
|
||||
"downloadCount": 0,
|
||||
"contentType": "application/octet-stream",
|
||||
"createdAt": "2025-08-18T17:38:47Z"
|
||||
},
|
||||
{
|
||||
"name": "wippersnapper.feather_rp2040_adalogger_tinyusb.1.0.0-offline-beta.4.uf2",
|
||||
"url": "https://github.com/adafruit/Adafruit_Wippersnapper_Arduino/releases/download/1.0.0-offline-beta.4/wippersnapper.feather_rp2040_adalogger_tinyusb.1.0.0-offline-beta.4.uf2",
|
||||
"size": 863232,
|
||||
"downloadCount": 0,
|
||||
"contentType": "application/octet-stream",
|
||||
"createdAt": "2025-08-18T17:38:47Z"
|
||||
},
|
||||
{
|
||||
"name": "wippersnapper.metroesp32s2.1.0.0-offline-beta.4.uf2",
|
||||
"url": "https://github.com/adafruit/Adafruit_Wippersnapper_Arduino/releases/download/1.0.0-offline-beta.4/wippersnapper.metroesp32s2.1.0.0-offline-beta.4.uf2",
|
||||
"size": 2989568,
|
||||
"downloadCount": 0,
|
||||
"contentType": "application/octet-stream",
|
||||
"createdAt": "2025-08-18T17:38:47Z"
|
||||
},
|
||||
{
|
||||
"name": "wippersnapper.metro_esp32s3.1.0.0-offline-beta.4.uf2",
|
||||
"url": "https://github.com/adafruit/Adafruit_Wippersnapper_Arduino/releases/download/1.0.0-offline-beta.4/wippersnapper.metro_esp32s3.1.0.0-offline-beta.4.uf2",
|
||||
"size": 3054592,
|
||||
"downloadCount": 0,
|
||||
"contentType": "application/octet-stream",
|
||||
"createdAt": "2025-08-18T17:38:47Z"
|
||||
},
|
||||
{
|
||||
"name": "wippersnapper.metro_rp2350_tinyusb.1.0.0-offline-beta.4.uf2",
|
||||
"url": "https://github.com/adafruit/Adafruit_Wippersnapper_Arduino/releases/download/1.0.0-offline-beta.4/wippersnapper.metro_rp2350_tinyusb.1.0.0-offline-beta.4.uf2",
|
||||
"size": 872960,
|
||||
"downloadCount": 0,
|
||||
"contentType": "application/octet-stream",
|
||||
"createdAt": "2025-08-18T17:38:47Z"
|
||||
},
|
||||
{
|
||||
"name": "wippersnapper.pico_rp2040_tinyusb.1.0.0-offline-beta.4.uf2",
|
||||
"url": "https://github.com/adafruit/Adafruit_Wippersnapper_Arduino/releases/download/1.0.0-offline-beta.4/wippersnapper.pico_rp2040_tinyusb.1.0.0-offline-beta.4.uf2",
|
||||
"size": 862208,
|
||||
"downloadCount": 0,
|
||||
"contentType": "application/octet-stream",
|
||||
"createdAt": "2025-08-18T17:38:47Z"
|
||||
},
|
||||
{
|
||||
"name": "wippersnapper.pico_rp2350_tinyusb.1.0.0-offline-beta.4.uf2",
|
||||
"url": "https://github.com/adafruit/Adafruit_Wippersnapper_Arduino/releases/download/1.0.0-offline-beta.4/wippersnapper.pico_rp2350_tinyusb.1.0.0-offline-beta.4.uf2",
|
||||
"size": 858112,
|
||||
"downloadCount": 0,
|
||||
"contentType": "application/octet-stream",
|
||||
"createdAt": "2025-08-18T17:38:47Z"
|
||||
},
|
||||
{
|
||||
"name": "wippersnapper.qtpy_esp32s2.1.0.0-offline-beta.4.uf2",
|
||||
"url": "https://github.com/adafruit/Adafruit_Wippersnapper_Arduino/releases/download/1.0.0-offline-beta.4/wippersnapper.qtpy_esp32s2.1.0.0-offline-beta.4.uf2",
|
||||
"size": 2989568,
|
||||
"downloadCount": 0,
|
||||
"contentType": "application/octet-stream",
|
||||
"createdAt": "2025-08-18T17:38:47Z"
|
||||
},
|
||||
{
|
||||
"name": "wippersnapper.qtpy_esp32s3.1.0.0-offline-beta.4.uf2",
|
||||
"url": "https://github.com/adafruit/Adafruit_Wippersnapper_Arduino/releases/download/1.0.0-offline-beta.4/wippersnapper.qtpy_esp32s3.1.0.0-offline-beta.4.uf2",
|
||||
"size": 3044352,
|
||||
"downloadCount": 0,
|
||||
"contentType": "application/octet-stream",
|
||||
"createdAt": "2025-08-18T17:38:47Z"
|
||||
},
|
||||
{
|
||||
"name": "wippersnapper.qtpy_esp32s3_n4r2.1.0.0-offline-beta.4.uf2",
|
||||
"url": "https://github.com/adafruit/Adafruit_Wippersnapper_Arduino/releases/download/1.0.0-offline-beta.4/wippersnapper.qtpy_esp32s3_n4r2.1.0.0-offline-beta.4.uf2",
|
||||
"size": 3050496,
|
||||
"downloadCount": 0,
|
||||
"contentType": "application/octet-stream",
|
||||
"createdAt": "2025-08-18T17:38:47Z"
|
||||
},
|
||||
{
|
||||
"name": "wippersnapper.xiao_esp32s3.1.0.0-offline-beta.4.uf2",
|
||||
"url": "https://github.com/adafruit/Adafruit_Wippersnapper_Arduino/releases/download/1.0.0-offline-beta.4/wippersnapper.xiao_esp32s3.1.0.0-offline-beta.4.uf2",
|
||||
"size": 3145728,
|
||||
"downloadCount": 0,
|
||||
"contentType": "application/octet-stream",
|
||||
"createdAt": "2025-08-18T17:38:47Z"
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
// Export for use in other modules
|
||||
if (typeof module !== 'undefined' && module.exports) {
|
||||
module.exports = { FIRMWARE_DATA };
|
||||
} else {
|
||||
window.FIRMWARE_DATA = FIRMWARE_DATA;
|
||||
}
|
||||
149
index.html
149
index.html
|
|
@ -3,8 +3,26 @@
|
|||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link rel="icon" href="favicon.ico" type="image/x-icon">
|
||||
<title>Wippersnapper Configuration Builder</title>
|
||||
<style>
|
||||
a.gh-link {
|
||||
text-decoration: none;
|
||||
}
|
||||
a:hover.gh-link, a:focus.gh-link{
|
||||
border-bottom: double 1px black;
|
||||
}
|
||||
.not-available-yet {
|
||||
text-decoration: line-through;
|
||||
font-size: x-small;
|
||||
}
|
||||
.component-tabcontent > h3 {
|
||||
display: inline-block;
|
||||
padding-right: 20px;
|
||||
}
|
||||
.component-tabcontent > .search-filter {
|
||||
display: inline-block;
|
||||
}
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
line-height: 1.6;
|
||||
|
|
@ -45,6 +63,9 @@
|
|||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 10px;
|
||||
scrollbar-width: auto;
|
||||
max-height: 80vh;
|
||||
overflow: scroll;
|
||||
}
|
||||
.component-card {
|
||||
border: 1px solid #ddd;
|
||||
|
|
@ -53,6 +74,12 @@
|
|||
width: 230px;
|
||||
background-color: white;
|
||||
}
|
||||
.component-card h4 {
|
||||
margin: 0 0 10px 0;
|
||||
}
|
||||
.component-card p {
|
||||
margin: 0 0 10px 0;
|
||||
}
|
||||
.component-card img {
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
|
|
@ -94,11 +121,26 @@
|
|||
.hidden {
|
||||
display: none;
|
||||
}
|
||||
.i2c-bus-config {
|
||||
.inline-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
align-content:space-around;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.dashed-section {
|
||||
margin: 10px 0;
|
||||
padding: 10px;
|
||||
border: 1px dashed #ccc;
|
||||
border-radius: 4px;
|
||||
}
|
||||
.dashed-section h3{
|
||||
padding-top: 0px;
|
||||
margin-top:0px;
|
||||
}
|
||||
|
||||
.dashed-section > :last-child {
|
||||
padding-bottom: 0px;
|
||||
margin-bottom: 0px;
|
||||
}
|
||||
.tab {
|
||||
overflow: hidden;
|
||||
|
|
@ -184,6 +226,9 @@
|
|||
.board-details {
|
||||
flex: 1;
|
||||
}
|
||||
.board-details > p {
|
||||
margin: 5px 0 0 0;
|
||||
}
|
||||
.search-filter {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
|
@ -195,13 +240,29 @@
|
|||
border: 1px solid #ddd;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div style="text-align: center; position: absolute; top: 5px; left: 5px;">
|
||||
<a href="https://github.com/adafruit/Adafruit_Wippersnapper_Offline_Configurator" target="_blank" class="gh-link">
|
||||
<img src="https://github.githubassets.com/images/modules/logos_page/GitHub-Mark.png" alt="GitHub Logo" style="width: 30px; height: 30px; vertical-align: middle;">
|
||||
<span style="font-size: 16px; vertical-align: middle;">Issues?</span>
|
||||
</a>
|
||||
</div>
|
||||
<div style="display: flex; justify-content: space-between; align-items: center;">
|
||||
<h1>Wippersnapper Configuration Builder</h1>
|
||||
<div style="flex: 1;">
|
||||
<h1>Wippersnapper Offline <a href="https://learn.adafruit.com/no-code-offline-data-logging-with-wippersnapper">[?]</a><br/>Data Logger Configurator</h1>
|
||||
</div>
|
||||
<div style="flex: 1; text-align: right;">
|
||||
|
||||
<div style="display:inline-block; font-size: smaller;"><b>Latest Release:</b> <a id="release_name" href="https://github.com/adafruit/Adafruit_Wippersnapper_Arduino/releases">Release Page</a></div>
|
||||
<div style="display:inline-block; margin: 5px; font-size: smaller;">Download: <a style="display: inline-flex;" id="firmware_file" href="https://github.com/adafruit/Adafruit_Wippersnapper_Arduino/releases">UF2 firmware</a> </div>
|
||||
</div>
|
||||
<div style="flex: 1; text-align: right;">
|
||||
<button id="reset-config-btn-top" class="reset-btn" style="background-color: #dc3545; margin-left: 20px;">Reset Configurator</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Loading indicator -->
|
||||
<div id="loading-indicator" class="section">
|
||||
|
|
@ -212,12 +273,13 @@
|
|||
|
||||
<div class="tab">
|
||||
<button class="tablinks active" onclick="openTab(event, 'BuildConfig')">Build Configuration</button>
|
||||
<button class="tablinks" onclick="openTab(event, 'ImportExport')">Import/Export</button>
|
||||
<button class="tablinks" onclick="openTab(event, 'ImportExport')">Import Config / Custom Board</button>
|
||||
</div>
|
||||
|
||||
<div id="BuildConfig" class="tabcontent" style="display: block;">
|
||||
<div class="section">
|
||||
<h2>1. Select Board</h2>
|
||||
<p><small>You may wish to use the IMPORT option above <i>first</i>, or the Reset button to clear the configuration completely.</small></p>
|
||||
<select id="board-select">
|
||||
<option value="">-- Select a Board --</option>
|
||||
<!-- Board options will be dynamically populated -->
|
||||
|
|
@ -227,11 +289,11 @@
|
|||
<div class="board-preview">
|
||||
<img id="board-image" class="board-image hidden" src="" alt="Board image">
|
||||
<div class="board-details">
|
||||
<h3>Board Details</h3>
|
||||
<h3>Board Details <span id="board-purchase"></span> <span id="board-help"></span></h3>
|
||||
<p><strong>Reference Voltage:</strong> <span id="ref-voltage">3.3</span>V</p>
|
||||
<p><strong>Total GPIO Pins:</strong> <span id="total-gpio">0</span></p>
|
||||
<p><strong>Total Analog Pins:</strong> <span id="total-analog">0</span></p>
|
||||
<p><strong>Default I2C Bus:</strong> SCL: <span id="default-scl">0</span>, SDA: <span id="default-sda">0</span></p>
|
||||
<p><strong>Default I2C Bus:</strong> SCL: <span id="default-SCL">0</span>, SDA: <span id="default-SDA">0</span></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -239,7 +301,8 @@
|
|||
|
||||
<div id="companion-board-section" class="section hidden">
|
||||
<h2>2. Select Companion Board (Optional)</h2>
|
||||
<p>You can skip this step if you're not using a companion board.</p>
|
||||
<p>You can skip this step if you're not using a companion board.
|
||||
<br/><i><small>It's just a convenient way to prepopulate the Real-time clock model and SD Card's Chip Select pin option.</small></i></p>
|
||||
<select id="companion-board-select">
|
||||
<option value="">-- None --</option>
|
||||
<option value="adalogger">Adafruit Adalogger FeatherWing</option>
|
||||
|
|
@ -251,28 +314,34 @@
|
|||
<option value="microsd-bff">Adafruit microSD Card BFF Add-On for QT Py and Xiao</option>
|
||||
<option value="winc1500-shield">Adafruit WINC1500 WiFi Shield</option>
|
||||
<option value="airlift-shield">Adafruit AirLift Shield - ESP32 WiFi Co-Processor</option>
|
||||
|
||||
<option value="seeed-xiao-s3sense-camera-addon">Seeed Studio XIAO ESP32S3 Sense Camera/SD Add-on board</option>
|
||||
<option value="seeed-xiao-ssd1306-expansion-base">Seeed Studio XIAO Expansion base board</option>
|
||||
</select>
|
||||
|
||||
<div id="companion-details" class="hidden">
|
||||
<h3>Companion Board Details</h3>
|
||||
<p><strong>RTC:</strong> <span id="companion-rtc">None</span></p>
|
||||
<div class="board-preview">
|
||||
<img id="companion-image" class="board-image hidden" src="#" alt="Companion board image">
|
||||
<div class="board-details">
|
||||
<h3>Companion Board Details <span id="companion-purchase"></span> <span id="companion-help"></span></h3>
|
||||
<p><strong>Real-time clock (RTC):</strong> <span id="companion-rtc">None</span></p>
|
||||
<p><strong>SD Card CS Pin:</strong> <span id="companion-sd-cs">None</span></p>
|
||||
<p><strong>Additional Components:</strong> <span id="companion-extras">None</span></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="manual-config-section" class="section hidden">
|
||||
<h2>3. Manual Configuration</h2>
|
||||
|
||||
<div id="sd-card-config">
|
||||
<div id="sd-card-config" class="dashed-section">
|
||||
<h3>SD Card Configuration</h3>
|
||||
<div id="sd-missing">
|
||||
<p>No SD card detected from companion board. Would you like to add an SD card? <b>(REQUIRED)</b></p>
|
||||
<label><input type="checkbox" id="add-sd-card"> Add SD Card</label>
|
||||
|
||||
<div id="sd-card-pin-select" class="hidden">
|
||||
<p>Select SD Card CS Pin:</p>
|
||||
<p>Selected SD Card CS Pin: <span id="manual-sd-cs-pin"></span></p>
|
||||
<div id="sd-pins-list" class="pins-container"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -281,13 +350,16 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div id="rtc-config">
|
||||
<h3>RTC Configuration</h3>
|
||||
<div id="rtc-config" class="dashed-section">
|
||||
<h3>Real-time clock (RTC) Configuration</h3>
|
||||
<p style="color: red; font-weight: bold;">Note: The RTC must be on the first (default) I2C bus.</p>
|
||||
<div id="rtc-missing">
|
||||
<p>No RTC detected from companion board. Select RTC type:</p>
|
||||
<p>No RTC detected from companion board.</p>
|
||||
<label for="rtc-select">Select RTC type: </label>
|
||||
<select id="rtc-select">
|
||||
<option value="soft">Software RTC</option>
|
||||
<option value="PCF8523">PCF8523</option>
|
||||
<option value="PCF8563">PCF8563</option>
|
||||
<option value="DS3231">DS3231</option>
|
||||
<option value="DS1307">DS1307</option>
|
||||
</select>
|
||||
|
|
@ -297,35 +369,37 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div id="status-led-config">
|
||||
<div id="status-led-config" class="dashed-section">
|
||||
<h3>Status LED Configuration</h3>
|
||||
<div class="inline-row">
|
||||
<label for="led-brightness">Status LED Brightness (0.0-1.0): </label>
|
||||
<input type="range" id="led-brightness" min="0" max="1" step="0.1" value="0.5">
|
||||
<input type="range" id="led-brightness" min="0" max="1" step="0.01" value="0.5">
|
||||
<span id="brightness-value">0.5</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="i2c-bus-section" class="section hidden">
|
||||
<h2>4. I2C Bus Configuration</h2>
|
||||
|
||||
<div id="default-i2c-bus" class="i2c-bus-config">
|
||||
<div id="default-i2c-bus" class="dashed-section">
|
||||
<h3>Default I2C Bus</h3>
|
||||
<p>SCL: <span id="default-i2c-scl"></span>, SDA: <span id="default-i2c-sda"></span></p>
|
||||
<p>SCL: <span id="default-i2c-SCL"></span>, SDA: <span id="default-i2c-SDA"></span></p>
|
||||
</div>
|
||||
|
||||
<div id="additional-i2c-bus-container">
|
||||
<div id="additional-i2c-bus-container" class="dashed-section">
|
||||
<h3>Additional I2C Bus (Optional)</h3>
|
||||
<label><input type="checkbox" id="add-i2c-bus"> Add Additional I2C Bus</label>
|
||||
|
||||
<div id="additional-i2c-config" class="hidden i2c-bus-config">
|
||||
<p>Select SCL Pin:</p>
|
||||
<div id="scl-pins-list" class="pins-container"></div>
|
||||
<p>Select SDA Pin:</p>
|
||||
<div id="sda-pins-list" class="pins-container"></div>
|
||||
<div id="additional-i2c-config" class="hidden dashed-section">
|
||||
<p>Select SCL Pin: <span id="alt-SCL-pin"></span></p>
|
||||
<div id="SCL-pins-list" class="pins-container"></div>
|
||||
<p>Select SDA Pin: <span id="alt-SDA-pin"></span></p>
|
||||
<div id="SDA-pins-list" class="pins-container"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="i2c-mux-container">
|
||||
<div id="i2c-mux-container" class="dashed-section">
|
||||
<h3>I2C Multiplexers (Optional)</h3>
|
||||
<button id="add-mux-btn">Add I2C Multiplexer</button>
|
||||
|
||||
|
|
@ -341,9 +415,6 @@
|
|||
<button class="comp-tab" onclick="openComponentTab(event, 'i2c-components')">I2C Components</button>
|
||||
<button class="comp-tab" onclick="openComponentTab(event, 'ds18x20-components')">DS18x20 Components</button>
|
||||
<button class="comp-tab" onclick="openComponentTab(event, 'pin-components')">Pin Components</button>
|
||||
<button class="comp-tab" onclick="openComponentTab(event, 'pixel-components')">Pixel Components</button>
|
||||
<button class="comp-tab" onclick="openComponentTab(event, 'pwm-components')">PWM Components</button>
|
||||
<button class="comp-tab" onclick="openComponentTab(event, 'servo-components')">Servo Components</button>
|
||||
<button class="comp-tab" onclick="openComponentTab(event, 'uart-components')">UART Components</button>
|
||||
</div>
|
||||
|
||||
|
|
@ -362,12 +433,6 @@
|
|||
<div class="search-filter">
|
||||
<input type="text" id="i2c-search" class="component-search" placeholder="Search I2C components...">
|
||||
</div>
|
||||
<div>
|
||||
<label for="i2c-bus-select">Select I2C Bus: </label>
|
||||
<select id="i2c-bus-select">
|
||||
<option value="default">Default I2C Bus</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="component-list" id="i2c-component-list">
|
||||
<!-- I2C components will be populated here -->
|
||||
|
|
@ -445,6 +510,7 @@
|
|||
<div id="generate-section" class="section hidden">
|
||||
<h2>7. Generate Configuration</h2>
|
||||
<button id="generate-config-btn">Generate Configuration</button>
|
||||
<input type="checkbox" id="use-auto-init" onchange="javascript:appState.enableautoConfig=this.checked;"> <label for="use-auto-init" title="Auto config fallback for I2C sensors that fail to initialise (selects alternative sensors at same address)">Use Auto Init fallback for failed or unspecified components (default)</label>
|
||||
<div id="config-output-container" class="hidden">
|
||||
<h3>Configuration JSON:</h3>
|
||||
<pre id="config-output" class="config-output"></pre>
|
||||
|
|
@ -467,9 +533,10 @@
|
|||
<textarea id="import-json" class="json-input" placeholder="Paste your configuration JSON here..."></textarea>
|
||||
<button id="import-json-btn">Import from Text</button>
|
||||
|
||||
<div class="section">
|
||||
<div class="section" style="visibility: hidden; height: 0px;">
|
||||
<h2>Export Configuration</h2>
|
||||
<p>Export the current configuration to a file:</p>
|
||||
<textarea id="export-config" class="json-input hidden" placeholder="Exported configuration JSON will appear here and on the other page (when generate is clicked)..." ></textarea>
|
||||
<button id="export-btn">Export Configuration</button>
|
||||
</div>
|
||||
|
||||
|
|
@ -494,12 +561,12 @@
|
|||
<input type="number" id="custom-board-analog" value="0" min="0" max="100">
|
||||
</div>
|
||||
<div>
|
||||
<label for="custom-board-scl">Default I2C SCL Pin:</label>
|
||||
<input type="text" id="custom-board-scl" placeholder="e.g. SCL, D3">
|
||||
<label for="custom-board-SCL">Default I2C SCL Pin:</label>
|
||||
<input type="text" id="custom-board-SCL" placeholder="e.g. SCL, D3">
|
||||
</div>
|
||||
<div>
|
||||
<label for="custom-board-sda">Default I2C SDA Pin:</label>
|
||||
<input type="text" id="custom-board-sda" placeholder="e.g. SDA, D2">
|
||||
<label for="custom-board-SDA">Default I2C SDA Pin:</label>
|
||||
<input type="text" id="custom-board-SDA" placeholder="e.g. SDA, D2">
|
||||
</div>
|
||||
<button id="add-custom-board-btn">Add Custom Board</button>
|
||||
</div>
|
||||
|
|
@ -527,6 +594,8 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Load release info -->
|
||||
<script src="firmware-data.js"></script>
|
||||
|
||||
<!-- Load the data loader script -->
|
||||
<script src="load-wippersnapper-data.js"></script>
|
||||
|
|
@ -561,7 +630,7 @@
|
|||
} else {
|
||||
// If called programmatically, find and activate the correct tab
|
||||
for (i = 0; i < tablinks.length; i++) {
|
||||
if (tablinks[i].textContent.includes(tabName.replace('Export', 'Import/Export'))) {
|
||||
if (tablinks[i].textContent.includes(tabName.replace(/(Export|Config)/, ''))) {
|
||||
tablinks[i].className += " active";
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
// Load Wippersnapper boards and components data
|
||||
|
||||
// Configuration
|
||||
const BOARDS_JSON_URL = 'https://raw.githubusercontent.com/tyeth/Adafruit_Wippersnapper_Offline_Configurator/refs/heads/main/wippersnapper_boards.json'; //'wippersnapper_boards.json';
|
||||
const COMPONENTS_JSON_URL = 'https://raw.githubusercontent.com/tyeth/Adafruit_Wippersnapper_Offline_Configurator/refs/heads/main/wippersnapper_components.json'; //'wippersnapper_components.json';
|
||||
// Configuration - technically unused (instead ./ relative links) but useful for reference
|
||||
const BOARDS_JSON_URL = 'https://raw.githubusercontent.com/adafruit/Adafruit_Wippersnapper_Offline_Configurator/refs/heads/offline-mode/wippersnapper_boards.json'; //'wippersnapper_boards.json';
|
||||
const COMPONENTS_JSON_URL = 'https://raw.githubusercontent.com/adafruit/Adafruit_Wippersnapper_Offline_Configurator/refs/heads/offline-mode/wippersnapper_components.json'; //'wippersnapper_components.json';
|
||||
|
||||
// Global app state
|
||||
const appState = {
|
||||
|
|
@ -10,6 +10,7 @@ const appState = {
|
|||
componentsData: null,
|
||||
isLoading: false,
|
||||
loadError: null,
|
||||
enableautoConfig: false,
|
||||
|
||||
// Application state properties (from original code)
|
||||
selectedBoard: null,
|
||||
|
|
@ -47,6 +48,12 @@ async function loadWippersnapperData() {
|
|||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
const boardsData = window['jsonBoardObject'];
|
||||
const componentsData = window['jsonComponentsObject'];
|
||||
const firmwareData = window['FIRMWARE_DATA'];
|
||||
if (firmwareData && firmwareData.releaseInfo) {
|
||||
document.getElementById('release_name').innerHTML = "(" + firmwareData.releaseInfo.publishedDate + ")<br/>" + firmwareData.releaseInfo.name;
|
||||
document.getElementById('release_name').href = firmwareData.releaseInfo.url;
|
||||
}
|
||||
|
||||
appState.boardsData = boardsData.boards;
|
||||
appState.componentsData = componentsData.components;
|
||||
document.body.removeChild(componentsObject);
|
||||
|
|
@ -58,6 +65,10 @@ async function loadWippersnapperData() {
|
|||
appState.componentsData.i2c.push({
|
||||
id: 'pca9546',
|
||||
name: 'PCA9546 I2C Multiplexer',
|
||||
displayName: 'PCA9546 I2C Multiplexer',
|
||||
productURL: 'https://www.adafruit.com/product/5664',
|
||||
documentationURL: 'https://learn.adafruit.com/adafruit-pca9546-4-channel-stemma-qt-multiplexer',
|
||||
image: 'https://cdn-shop.adafruit.com/640x480/5664-00.jpg',
|
||||
address: '0x70',
|
||||
addresses: ['0x70', '0x71', '0x72', '0x73', '0x74', '0x75', '0x76', '0x77'],
|
||||
dataTypes: [],
|
||||
|
|
@ -68,6 +79,10 @@ async function loadWippersnapperData() {
|
|||
appState.componentsData.i2c.push({
|
||||
id: 'pca9548',
|
||||
name: 'PCA9548 I2C Multiplexer',
|
||||
displayName: 'PCA9548 I2C Multiplexer',
|
||||
productURL: 'https://www.adafruit.com/product/5626',
|
||||
documentationURL: 'https://learn.adafruit.com/adafruit-pca9548-8-channel-stemma-qt-qwiic-i2c-multiplexer',
|
||||
image: 'https://cdn-shop.adafruit.com/640x480/5626-06.jpg',
|
||||
address: '0x70',
|
||||
addresses: ['0x70', '0x71', '0x72', '0x73', '0x74', '0x75', '0x76', '0x77'],
|
||||
dataTypes: [],
|
||||
|
|
@ -78,6 +93,10 @@ async function loadWippersnapperData() {
|
|||
appState.componentsData.i2c.push({
|
||||
id: 'tca9546',
|
||||
name: 'TCA9546 I2C Multiplexer',
|
||||
displayName: 'TCA9546 4Ch I2C Multiplexer',
|
||||
productURL: 'https://www.adafruit.com/product/5663',
|
||||
documentationURL: 'https://learn.adafruit.com/adafruit-pca9546-4-channel-i2c-multiplexer',
|
||||
image: 'https://cdn-shop.adafruit.com/640x480/5663-04.jpg',
|
||||
address: '0x70',
|
||||
addresses: ['0x70', '0x71', '0x72', '0x73', '0x74', '0x75', '0x76', '0x77'],
|
||||
dataTypes: [],
|
||||
|
|
@ -88,6 +107,10 @@ async function loadWippersnapperData() {
|
|||
appState.componentsData.i2c.push({
|
||||
id: 'tca9548',
|
||||
name: 'TCA9548 I2C Multiplexer',
|
||||
displayName: 'TCA9548 8Ch I2C Multiplexer',
|
||||
productURL: 'https://www.adafruit.com/product/2717',
|
||||
documentationURL: 'https://learn.adafruit.com/adafruit-tca9548a-1-to-8-i2c-multiplexer-breakout',
|
||||
image: 'https://cdn-shop.adafruit.com/640x480/2717-00.jpg',
|
||||
address: '0x70',
|
||||
addresses: ['0x70', '0x71', '0x72', '0x73', '0x74', '0x75', '0x76', '0x77'],
|
||||
dataTypes: [],
|
||||
|
|
@ -136,16 +159,19 @@ function populateBoardSelect() {
|
|||
|
||||
// Filter boards to only include those with UF2 install method
|
||||
const filteredBoards = Object.entries(appState.boardsData)
|
||||
.filter(([boardId, board]) => ['uf2', 'web-native-usb'].includes(board.installMethod));
|
||||
.filter(([boardId, board]) => ['uf2', 'web-native-usb'].includes(board.installMethod)); //funhouse
|
||||
|
||||
// Sort boards by vendor and name
|
||||
const sortedBoards = filteredBoards
|
||||
.sort((a, b) => {
|
||||
const vendorA = a[1].vendor || '';
|
||||
const vendorB = b[1].vendor || '';
|
||||
|
||||
if (vendorA === 'Generic') {
|
||||
return 1; // Sort Generic to the end
|
||||
} else if (vendorB === 'Generic') {
|
||||
return -1; // Sort Generic to the end
|
||||
} else if (vendorA !== vendorB) {
|
||||
// Sort by vendor first
|
||||
if (vendorA !== vendorB) {
|
||||
return vendorA.localeCompare(vendorB);
|
||||
}
|
||||
|
||||
|
|
@ -191,19 +217,14 @@ function convertBoardDataToConfig(boardId) {
|
|||
// Extract pin numbers from board data
|
||||
const pins = boardData.pins.map(pin => pin.number).filter(num => !isNaN(num));
|
||||
|
||||
boardConfig = boardData;
|
||||
// Create board config
|
||||
const boardConfig = {
|
||||
referenceVoltage: boardData.referenceVoltage,
|
||||
totalGPIOPins: boardData.totalGPIOPins,
|
||||
totalAnalogPins: boardData.totalAnalogPins || 0,
|
||||
defaultI2C: {
|
||||
scl: boardData.defaultI2C.SCL,
|
||||
sda: boardData.defaultI2C.SDA
|
||||
},
|
||||
pins: pins,
|
||||
displayName: boardData.displayName,
|
||||
image: boardData.image
|
||||
boardConfig.totalAnalogPins= boardData.totalAnalogPins || 0;
|
||||
boardConfig.defaultI2C= {
|
||||
SCL: boardData.defaultI2C.SCL,
|
||||
SDA: boardData.defaultI2C.SDA
|
||||
};
|
||||
boardConfig.pins= pins;
|
||||
|
||||
return boardConfig;
|
||||
}
|
||||
|
|
@ -229,6 +250,7 @@ function convertComponentsDataToConfig() {
|
|||
componentsConfig.i2c.push({
|
||||
id: component.id,
|
||||
name: component.name,
|
||||
displayName: component.displayName || component.name,
|
||||
address: component.address || '0x00',
|
||||
addresses: component.addresses || [component.address || '0x00'],
|
||||
dataTypes: component.dataTypes || [],
|
||||
|
|
@ -242,6 +264,7 @@ function convertComponentsDataToConfig() {
|
|||
appState.componentsData.ds18x20.forEach(component => {
|
||||
componentsConfig.ds18x20.push({
|
||||
id: component.id,
|
||||
displayName: component.displayName || component.name,
|
||||
name: component.name,
|
||||
dataTypes: component.dataTypes || []
|
||||
});
|
||||
|
|
@ -253,6 +276,7 @@ function convertComponentsDataToConfig() {
|
|||
appState.componentsData.pin.forEach(component => {
|
||||
componentsConfig.pin.push({
|
||||
id: component.id,
|
||||
displayName: component.displayName || component.name,
|
||||
name: component.name,
|
||||
dataTypes: component.dataTypes || []
|
||||
});
|
||||
|
|
@ -264,6 +288,7 @@ function convertComponentsDataToConfig() {
|
|||
appState.componentsData.pixel.forEach(component => {
|
||||
componentsConfig.pixel.push({
|
||||
id: component.id,
|
||||
displayName: component.displayName || component.name,
|
||||
name: component.name,
|
||||
dataTypes: component.dataTypes || []
|
||||
});
|
||||
|
|
@ -275,6 +300,7 @@ function convertComponentsDataToConfig() {
|
|||
appState.componentsData.pwm.forEach(component => {
|
||||
componentsConfig.pwm.push({
|
||||
id: component.id,
|
||||
displayName: component.displayName || component.name,
|
||||
name: component.name,
|
||||
dataTypes: component.dataTypes || []
|
||||
});
|
||||
|
|
@ -286,6 +312,7 @@ function convertComponentsDataToConfig() {
|
|||
appState.componentsData.servo.forEach(component => {
|
||||
componentsConfig.servo.push({
|
||||
id: component.id,
|
||||
displayName: component.displayName || component.name,
|
||||
name: component.name,
|
||||
dataTypes: component.dataTypes || []
|
||||
});
|
||||
|
|
@ -297,6 +324,7 @@ function convertComponentsDataToConfig() {
|
|||
appState.componentsData.uart.forEach(component => {
|
||||
componentsConfig.uart.push({
|
||||
id: component.id,
|
||||
displayName: component.displayName || component.name,
|
||||
name: component.name,
|
||||
dataTypes: component.dataTypes || []
|
||||
});
|
||||
|
|
@ -310,81 +338,21 @@ function convertComponentsDataToConfig() {
|
|||
* Attach event listeners to the UI elements
|
||||
*/
|
||||
function attachEventListeners() {
|
||||
// Board selection handler
|
||||
document.getElementById('board-select').addEventListener('change', function() {
|
||||
const boardId = this.value;
|
||||
if (!boardId) {
|
||||
document.getElementById('board-details').classList.add('hidden');
|
||||
hideSubsequentSections();
|
||||
return;
|
||||
}
|
||||
// BOARD SELECTION HANDLER HAS BEEN REMOVED
|
||||
// The duplicate event handler from load-wippersnapper-data.js has been removed
|
||||
// to prevent conflicts with the handler in wippersnapper-config-builder.js
|
||||
|
||||
// Convert board data to config format
|
||||
const boardConfig = convertBoardDataToConfig(boardId);
|
||||
appState.selectedBoard = {
|
||||
id: boardId,
|
||||
...boardConfig
|
||||
};
|
||||
|
||||
// Update board details display
|
||||
document.getElementById('ref-voltage').textContent = boardConfig.referenceVoltage;
|
||||
document.getElementById('total-gpio').textContent = boardConfig.totalGPIOPins;
|
||||
document.getElementById('total-analog').textContent = boardConfig.totalAnalogPins;
|
||||
document.getElementById('default-scl').textContent = boardConfig.defaultI2C.scl;
|
||||
document.getElementById('default-sda').textContent = boardConfig.defaultI2C.sda;
|
||||
document.getElementById('board-details').classList.remove('hidden');
|
||||
|
||||
// If there's a board image, show it
|
||||
const boardImageElem = document.getElementById('board-image');
|
||||
if (boardImageElem) {
|
||||
if (boardConfig.image) {
|
||||
if (!boardConfig.image.startsWith('http')) {
|
||||
boardImageElem.src = "https://raw.githubusercontent.com/adafruit/Wippersnapper_Boards/refs/heads/main/" + boardConfig.image;
|
||||
} else {
|
||||
boardImageElem.src = boardConfig.image;
|
||||
}
|
||||
boardImageElem.classList.remove('hidden');
|
||||
} else {
|
||||
boardImageElem.classList.add('hidden');
|
||||
}
|
||||
}
|
||||
|
||||
// Set up default I2C bus
|
||||
appState.i2cBuses = [{
|
||||
id: 'default',
|
||||
scl: boardConfig.defaultI2C.scl,
|
||||
sda: boardConfig.defaultI2C.sda
|
||||
}];
|
||||
|
||||
// Update default I2C bus display
|
||||
document.getElementById('default-i2c-scl').textContent = boardConfig.defaultI2C.scl;
|
||||
document.getElementById('default-i2c-sda').textContent = boardConfig.defaultI2C.sda;
|
||||
|
||||
// Mark default I2C pins as used
|
||||
appState.usedPins.add(boardConfig.defaultI2C.scl);
|
||||
appState.usedPins.add(boardConfig.defaultI2C.sda);
|
||||
|
||||
// Show companion board section
|
||||
document.getElementById('companion-board-section').classList.remove('hidden');
|
||||
|
||||
// Reset subsequent sections
|
||||
resetSubsequentSelections();
|
||||
|
||||
// Initialize SD and RTC sections based on board
|
||||
initializeManualConfig();
|
||||
|
||||
// Initialize pins lists for SD and I2C configuration
|
||||
populatePinsLists();
|
||||
// Instead, we'll prepare the data in the format expected by wippersnapper-config-builder.js
|
||||
console.log('Data loading complete, board selection handler is in wippersnapper-config-builder.js');
|
||||
|
||||
// Convert component data to config format
|
||||
const componentsConfig = convertComponentsDataToConfig();
|
||||
console.log('not using Components data converted to config format:', componentsConfig);
|
||||
// Update the components data in appState with the converted format
|
||||
// so it's ready for use in the other script
|
||||
// appState.componentsData = componentsConfig;
|
||||
|
||||
// Initialize components sections with the loaded data
|
||||
populateComponentLists(componentsConfig);
|
||||
});
|
||||
|
||||
// Remaining event listeners should be added here or in the original script
|
||||
// ...
|
||||
// No other event listeners needed here as they are handled in wippersnapper-config-builder.js
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
1
requirements.txt
Normal file
1
requirements.txt
Normal file
|
|
@ -0,0 +1 @@
|
|||
requests
|
||||
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue