Update definitions and readme

This commit is contained in:
Tyeth Gundry 2025-04-25 23:02:28 +01:00
parent 029c9bbf0e
commit c10d8b5407
8 changed files with 7581 additions and 7755 deletions

View file

@ -1,5 +1,55 @@
# 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" Offline Data Logger firmware.
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.
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) (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.
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.
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 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.
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
```
That will have replaced the following files:
wippersnapper_boards.js + .json
wippersnapper_components.js + .json
firmware-data.js

View file

@ -10,20 +10,20 @@ def main():
"""
print("=== Wippersnapper Definitions Converter ===")
print("Converting all Wippersnapper definitions to JSON...")
start_time = time.time()
# Convert boards
print("\n--- Converting Boards ---")
boards = convert_boards_to_json()
# Convert components
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 ===")
@ -32,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()

View file

@ -1,4 +1,4 @@
// Auto-generated on 2025-04-25 17:55:57
// Auto-generated on 2025-04-25 21:55:05
const FIRMWARE_DATA = {
"releaseInfo": {
"version": "1.0.0-offline-beta.2",

View file

@ -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/use_boards_sd_card/wippersnapper_boards.json'; //'wippersnapper_boards.json';
const COMPONENTS_JSON_URL = 'https://raw.githubusercontent.com/tyeth/Adafruit_Wippersnapper_Offline_Configurator/refs/heads/use_boards_sd_card/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/use_boards_sd_card/wippersnapper_boards.json'; //'wippersnapper_boards.json';
const COMPONENTS_JSON_URL = 'https://raw.githubusercontent.com/adafruit/Adafruit_Wippersnapper_Offline_Configurator/refs/heads/use_boards_sd_card/wippersnapper_components.json'; //'wippersnapper_components.json';
// Global app state
const appState = {
@ -11,7 +11,7 @@ const appState = {
isLoading: false,
loadError: null,
enableautoConfig: false,
// Application state properties (from original code)
selectedBoard: null,
companionBoard: null,
@ -60,7 +60,7 @@ async function loadWippersnapperData() {
appState.componentsData = componentsData.components;
document.body.removeChild(componentsObject);
document.body.removeChild(jsonObject);
// Add I2C multiplexer components manually since they're not in the JSON data
if (appState.componentsData.i2c) {
// Add PCA9546 - 4-channel I2C multiplexer
@ -76,7 +76,7 @@ async function loadWippersnapperData() {
dataTypes: [],
channels: 4
});
// Add PCA9548 - 8-channel I2C multiplexer
appState.componentsData.i2c.push({
id: 'pca9548',
@ -90,7 +90,7 @@ async function loadWippersnapperData() {
dataTypes: [],
channels: 8
});
// Add TCA9546 - 4-channel I2C multiplexer
appState.componentsData.i2c.push({
id: 'tca9546',
@ -104,7 +104,7 @@ async function loadWippersnapperData() {
dataTypes: [],
channels: 4
});
// Add TCA9548 - 8-channel I2C multiplexer
appState.componentsData.i2c.push({
id: 'tca9548',
@ -119,17 +119,17 @@ async function loadWippersnapperData() {
channels: 8
});
}
// Initialize the UI with the data
initializeUI();
console.log('Successfully loaded Wippersnapper data', {
boards: Object.keys(appState.boardsData).length,
components: Object.keys(appState.componentsData)
.filter(key => !key.endsWith('_metadata'))
.reduce((acc, key) => acc + appState.componentsData[key].length, 0)
});
appState.isLoading = false;
return true;
} catch (error) {
@ -147,7 +147,7 @@ async function loadWippersnapperData() {
function initializeUI() {
// Populate board select dropdown
populateBoardSelect();
// Set up event listeners
attachEventListeners();
}
@ -158,11 +158,11 @@ function initializeUI() {
function populateBoardSelect() {
const boardSelect = document.getElementById('board-select');
boardSelect.innerHTML = '<option value="">-- Select a Board --</option>';
// Filter boards to only include those with UF2 install method
const filteredBoards = Object.entries(appState.boardsData)
.filter(([boardId, board]) => board.installMethod === 'uf2'); //['uf2', 'web-native-usb'].includes(board.installMethod)); //funhouse
// Sort boards by vendor and name
const sortedBoards = filteredBoards
.sort((a, b) => {
@ -176,11 +176,11 @@ function populateBoardSelect() {
// Sort by vendor first
return vendorA.localeCompare(vendorB);
}
// Then by display name
return a[1].displayName.localeCompare(b[1].displayName);
});
// Group boards by vendor
const boardsByVendor = {};
sortedBoards.forEach(([boardId, board]) => {
@ -190,19 +190,19 @@ function populateBoardSelect() {
}
boardsByVendor[vendor].push([boardId, board]);
});
// Add boards to select, grouped by vendor
Object.entries(boardsByVendor).forEach(([vendor, boards]) => {
const optgroup = document.createElement('optgroup');
optgroup.label = vendor;
boards.forEach(([boardId, board]) => {
const option = document.createElement('option');
option.value = boardId;
option.textContent = board.displayName;
optgroup.appendChild(option);
});
boardSelect.appendChild(optgroup);
});
}
@ -215,10 +215,10 @@ function populateBoardSelect() {
function convertBoardDataToConfig(boardId) {
const boardData = appState.boardsData[boardId];
if (!boardData) return null;
// Extract pin numbers from board data
const pins = boardData.pins.map(pin => pin.number).filter(num => !isNaN(num));
boardConfig = boardData;
// Create board config
boardConfig.totalAnalogPins= boardData.totalAnalogPins || 0;
@ -227,7 +227,7 @@ function convertBoardDataToConfig(boardId) {
SDA: boardData.defaultI2C.SDA
};
boardConfig.pins= pins;
return boardConfig;
}
@ -245,7 +245,7 @@ function convertComponentsDataToConfig() {
servo: [],
uart: []
};
// Process I2C components
if (appState.componentsData.i2c) {
appState.componentsData.i2c.forEach(component => {
@ -260,7 +260,7 @@ function convertComponentsDataToConfig() {
});
});
}
// Process DS18x20 components
if (appState.componentsData.ds18x20) {
appState.componentsData.ds18x20.forEach(component => {
@ -272,7 +272,7 @@ function convertComponentsDataToConfig() {
});
});
}
// Process Pin components
if (appState.componentsData.pin) {
appState.componentsData.pin.forEach(component => {
@ -284,7 +284,7 @@ function convertComponentsDataToConfig() {
});
});
}
// Process Pixel components
if (appState.componentsData.pixel) {
appState.componentsData.pixel.forEach(component => {
@ -296,7 +296,7 @@ function convertComponentsDataToConfig() {
});
});
}
// Process PWM components
if (appState.componentsData.pwm) {
appState.componentsData.pwm.forEach(component => {
@ -308,7 +308,7 @@ function convertComponentsDataToConfig() {
});
});
}
// Process Servo components
if (appState.componentsData.servo) {
appState.componentsData.servo.forEach(component => {
@ -320,7 +320,7 @@ function convertComponentsDataToConfig() {
});
});
}
// Process UART components
if (appState.componentsData.uart) {
appState.componentsData.uart.forEach(component => {
@ -332,7 +332,7 @@ function convertComponentsDataToConfig() {
});
});
}
return componentsConfig;
}
@ -343,17 +343,17 @@ function attachEventListeners() {
// 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
// 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;
// No other event listeners needed here as they are handled in wippersnapper-config-builder.js
}
@ -364,7 +364,7 @@ function attachEventListeners() {
function showLoadError(message) {
// Create or update an error message element
let errorElem = document.getElementById('load-error');
if (!errorElem) {
errorElem = document.createElement('div');
errorElem.id = 'load-error';
@ -373,11 +373,11 @@ function showLoadError(message) {
errorElem.style.padding = '15px';
errorElem.style.margin = '15px 0';
errorElem.style.borderRadius = '5px';
// Insert at the top of the body
document.body.insertBefore(errorElem, document.body.firstChild);
}
errorElem.innerHTML = `
<h3>Error Loading Data</h3>
<p>${message}</p>
@ -395,7 +395,7 @@ function retryLoading() {
if (errorElem) {
errorElem.remove();
}
// Try loading again
loadWippersnapperData();
}

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