diff --git a/.gitignore b/.gitignore index 35e82d4..9ba9880 100644 Binary files a/.gitignore and b/.gitignore differ diff --git a/convert_components_to_json.py b/convert_components_to_json.py index b14c47f..c08d50f 100644 --- a/convert_components_to_json.py +++ b/convert_components_to_json.py @@ -48,35 +48,29 @@ def convert_components_to_json(): "image": None } - # Extract parameters and data types - if "parameters" in component_data: - component_info["parameters"] = component_data["parameters"] - # Extract data types if available - if "measurements" in component_data: - for measurement in component_data["measurements"]: - if "type" in measurement: - meas_type = measurement["type"] - if isinstance(meas_type, dict) and "displayName" in meas_type and "sensorType" in meas_type: - component_info["dataTypes"].append({ - "displayName": meas_type["displayName"], - "sensorType": meas_type["sensorType"] - }) - else: - component_info["dataTypes"].append(meas_type) + if "subcomponents" in component_data: + for meas_type in component_data["subcomponents"]: + 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": meas_type["sensorType"] + }) + else: + component_info["dataTypes"].append(meas_type) # Handle I2C-specific properties if category == "i2c": # Extract I2C address from parameters - for param in component_data.get("parameters", []): - if param.get("name") == "i2cAddress": - default_address = param.get("options", [{}])[0].get("value", "") - component_info["address"] = default_address - - # Get all possible addresses - component_info["addresses"] = [opt.get("value") for opt in param.get("options", [])] - break - + if "i2cAddresses" in component_data: + default_address = component_data["i2cAddresses"][0] + component_info["address"] = default_address + + # Get all possible addresses + component_info["addresses"] = component_data["i2cAddresses"] + else: + raise ValueError(f"No i2cAddresses found for {category}/{component_dir}") + # Special handling for multiplexers if "multiplexer" in component_dir.lower() or "mux" in component_dir.lower(): if "pca9548" in component_dir.lower() or "tca9548" in component_dir.lower(): diff --git a/improved-wippersnapper-config-builder.html b/improved-wippersnapper-config-builder.html new file mode 100644 index 0000000..ff770e4 --- /dev/null +++ b/improved-wippersnapper-config-builder.html @@ -0,0 +1,559 @@ + + + + + + Wippersnapper Configuration Builder + + + +

Wippersnapper Configuration Builder

+ + +
+

Loading Wippersnapper Data

+
+

Loading board and component definitions...

+
+ +
+ + +
+ +
+
+

1. Select Board

+ + + +
+ + + + + + + + + + + + +
+ +
+
+

Import Configuration

+

Paste your existing config.json here:

+ + +
+ +
+

Export Configuration

+

Your current configuration:

+
No configuration generated yet.
+ +
+
+ + + + + + + + + + + + + diff --git a/load-wippersnapper-data.js b/load-wippersnapper-data.js new file mode 100644 index 0000000..56e891a --- /dev/null +++ b/load-wippersnapper-data.js @@ -0,0 +1,382 @@ +// 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'; + +// Global app state +const appState = { + boardsData: null, + componentsData: null, + isLoading: false, + loadError: null, + + // Application state properties (from original code) + selectedBoard: null, + companionBoard: null, + sdCardCS: null, + rtcType: 'soft', + statusLEDBrightness: 0.5, + i2cBuses: [], + i2cMultiplexers: [], + selectedComponents: [], + usedPins: new Set(), + nextComponentId: 1 +}; + +/** + * Load the Wippersnapper boards and components data from JSON files + */ +async function loadWippersnapperData() { + try { + appState.isLoading = true; + + // Load boards data + const boardsResponse = await fetch(BOARDS_JSON_URL); + if (!boardsResponse.ok) { + throw new Error(`Failed to load boards data: ${boardsResponse.status} ${boardsResponse.statusText}`); + } + const boardsData = await boardsResponse.json(); + + // Load components data + const componentsResponse = await fetch(COMPONENTS_JSON_URL); + if (!componentsResponse.ok) { + throw new Error(`Failed to load components data: ${componentsResponse.status} ${componentsResponse.statusText}`); + } + const componentsData = await componentsResponse.json(); + + // Store data in app state + appState.boardsData = boardsData.boards; + appState.componentsData = componentsData.components; + + // Initialize the UI with the data + initializeUI(); + + console.log('Successfully loaded Wippersnapper data', { + boards: Object.keys(boardsData.boards).length, + components: Object.keys(componentsData.components) + .filter(key => !key.endsWith('_metadata')) + .reduce((acc, key) => acc + componentsData.components[key].length, 0) + }); + + appState.isLoading = false; + return true; + } catch (error) { + console.error('Error loading Wippersnapper data:', error); + appState.loadError = error.message; + appState.isLoading = false; + showLoadError(error.message); + return false; + } +} + +/** + * Initialize the UI with the loaded data + */ +function initializeUI() { + // Populate board select dropdown + populateBoardSelect(); + + // Set up event listeners + attachEventListeners(); +} + +/** + * Populate the board select dropdown with available boards + */ +function populateBoardSelect() { + const boardSelect = document.getElementById('board-select'); + boardSelect.innerHTML = ''; + + // Sort boards by vendor and name + const sortedBoards = Object.entries(appState.boardsData) + .sort((a, b) => { + const vendorA = a[1].vendor || ''; + const vendorB = b[1].vendor || ''; + + // Sort by vendor first + if (vendorA !== vendorB) { + 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]) => { + const vendor = board.vendor || 'Other'; + if (!boardsByVendor[vendor]) { + boardsByVendor[vendor] = []; + } + 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); + }); +} + +/** + * Convert the loaded board data to the format expected by the application + * @param {string} boardId The ID of the selected board + * @returns {Object} The board configuration object + */ +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)); + + // 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 + }; + + return boardConfig; +} + +/** + * Convert the loaded component data to the format expected by the application + * @returns {Object} The components configuration object + */ +function convertComponentsDataToConfig() { + const componentsConfig = { + i2c: [], + ds18x20: [], + pin: [], + pixel: [], + pwm: [], + servo: [], + uart: [] + }; + + // Process I2C components + if (appState.componentsData.i2c) { + appState.componentsData.i2c.forEach(component => { + componentsConfig.i2c.push({ + id: component.id, + name: component.name, + address: component.address || '0x00', + addresses: component.addresses || [component.address || '0x00'], + dataTypes: component.dataTypes || [], + channels: component.channels || 0 + }); + }); + } + + // Process DS18x20 components + if (appState.componentsData.ds18x20) { + appState.componentsData.ds18x20.forEach(component => { + componentsConfig.ds18x20.push({ + id: component.id, + name: component.name, + dataTypes: component.dataTypes || [] + }); + }); + } + + // Process Pin components + if (appState.componentsData.pin) { + appState.componentsData.pin.forEach(component => { + componentsConfig.pin.push({ + id: component.id, + name: component.name, + dataTypes: component.dataTypes || [] + }); + }); + } + + // Process Pixel components + if (appState.componentsData.pixel) { + appState.componentsData.pixel.forEach(component => { + componentsConfig.pixel.push({ + id: component.id, + name: component.name, + dataTypes: component.dataTypes || [] + }); + }); + } + + // Process PWM components + if (appState.componentsData.pwm) { + appState.componentsData.pwm.forEach(component => { + componentsConfig.pwm.push({ + id: component.id, + name: component.name, + dataTypes: component.dataTypes || [] + }); + }); + } + + // Process Servo components + if (appState.componentsData.servo) { + appState.componentsData.servo.forEach(component => { + componentsConfig.servo.push({ + id: component.id, + name: component.name, + dataTypes: component.dataTypes || [] + }); + }); + } + + // Process UART components + if (appState.componentsData.uart) { + appState.componentsData.uart.forEach(component => { + componentsConfig.uart.push({ + id: component.id, + name: component.name, + dataTypes: component.dataTypes || [] + }); + }); + } + + return componentsConfig; +} + +/** + * 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; + } + + // 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) { + 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(); + + // Convert component data to config format + const componentsConfig = convertComponentsDataToConfig(); + + // Initialize components sections with the loaded data + populateComponentLists(componentsConfig); + }); + + // Remaining event listeners should be added here or in the original script + // ... +} + +/** + * Display an error message to the user when data loading fails + * @param {string} message The error message to display + */ +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'; + errorElem.style.backgroundColor = '#ffdddd'; + errorElem.style.color = '#cc0000'; + 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 = ` +

Error Loading Data

+

${message}

+

Please check that the JSON files are available and properly formatted.

+ + `; +} + +/** + * Retry loading the Wippersnapper data + */ +function retryLoading() { + // Remove the error message + const errorElem = document.getElementById('load-error'); + if (errorElem) { + errorElem.remove(); + } + + // Try loading again + loadWippersnapperData(); +} + +// Initialize the application by loading the data when the document is ready +document.addEventListener('DOMContentLoaded', function() { + loadWippersnapperData(); +}); diff --git a/wippersnapper-config-builder.js b/wippersnapper-config-builder.js new file mode 100644 index 0000000..2a73be6 --- /dev/null +++ b/wippersnapper-config-builder.js @@ -0,0 +1,1520 @@ + +// Companion board configurations +const companionBoardConfigs = { + 'adalogger': { + rtc: 'PCF8523', + sdCardCS: 10, + extras: 'SD Card' + }, + 'datalogger-m0': { + rtc: 'PCF8523', + sdCardCS: 10, + extras: 'SD Card' + }, + 'ds3231-precision': { + rtc: 'DS3231', + sdCardCS: null, + extras: 'None' + }, + 'ethernet': { + rtc: null, + sdCardCS: null, + extras: 'Ethernet Controller' + }, + 'ina219': { + rtc: null, + sdCardCS: null, + extras: 'INA219 Current Sensor' + }, + 'airlift': { + rtc: null, + sdCardCS: null, + extras: 'WiFi Module' + }, + 'oled': { + rtc: null, + sdCardCS: null, + extras: 'OLED Display' + }, + 'prop-maker': { + rtc: null, + sdCardCS: null, + extras: 'NeoPixel, Amp, Accelerometer' + }, + 'neopixel': { + rtc: null, + sdCardCS: null, + extras: 'NeoPixel Matrix' + }, + 'joy-featherwing': { + rtc: null, + sdCardCS: null, + extras: 'Joystick, NeoPixel, Display' + }, + 'motorm4': { + rtc: null, + sdCardCS: null, + extras: 'DC/Stepper Motor Controller' + }, + 'rgb-matrix': { + rtc: null, + sdCardCS: null, + extras: 'RGB Matrix Driver' + } +}; + +// // Sample components data - will be replaced by data from the JSON files +// let componentsData = { +// i2c: [], +// ds18x20: [], +// pin: [], +// pixel: [], +// pwm: [], +// servo: [], +// uart: [] +// }; +let componentsData = appState.componentsData; + +// // Global state +// const appState = { +// selectedBoard: null, +// companionBoard: null, +// sdCardCS: null, +// rtcType: 'soft', +// statusLEDBrightness: 0.5, +// i2cBuses: [], +// i2cMultiplexers: [], +// selectedComponents: [], +// usedPins: new Set(), +// nextComponentId: 1 +// }; + +// Initialize the page +document.addEventListener('DOMContentLoaded', function() { + // Hide loading indicator once data is loaded + document.getElementById('loading-indicator').classList.add('hidden'); + + // 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; + } + + const board = appState.boardsData[boardId]; + appState.selectedBoard = { + id: boardId, + ...board + }; + + // Update board details display + document.getElementById('ref-voltage').textContent = board.referenceVoltage; + document.getElementById('total-gpio').textContent = board.totalGPIOPins; + document.getElementById('total-analog').textContent = board.totalAnalogPins; + document.getElementById('default-scl').textContent = board.defaultI2C.scl; + document.getElementById('default-sda').textContent = board.defaultI2C.sda; + document.getElementById('board-details').classList.remove('hidden'); + + // Set up default I2C bus + appState.i2cBuses = [{ + id: 'default', + scl: board.defaultI2C.scl, + sda: board.defaultI2C.sda + }]; + + // Update default I2C bus display + document.getElementById('default-i2c-scl').textContent = board.defaultI2C.scl; + document.getElementById('default-i2c-sda').textContent = board.defaultI2C.sda; + + // Mark default I2C pins as used + appState.usedPins.add(board.defaultI2C.scl); + appState.usedPins.add(board.defaultI2C.sda); + + // Show companion board section and all subsequent sections + document.getElementById('companion-board-section').classList.remove('hidden'); + document.getElementById('manual-config-section').classList.remove('hidden'); + document.getElementById('i2c-bus-section').classList.remove('hidden'); + document.getElementById('components-section').classList.remove('hidden'); + document.getElementById('selected-components-section').classList.remove('hidden'); + document.getElementById('generate-section').classList.remove('hidden'); + + // Reset companion board selection but keep sections visible + document.getElementById('companion-board-select').value = ''; + document.getElementById('companion-details').classList.add('hidden'); + + // Initialize SD and RTC sections based on board + initializeManualConfig(); + + // Initialize pins lists for SD and I2C configuration + populatePinsLists(); + + // Initialize components sections + populateComponentLists(); + }); + + // Companion board selection handler + document.getElementById('companion-board-select').addEventListener('change', function() { + const companionId = this.value; + appState.companionBoard = companionId ? { id: companionId, ...companionBoardConfigs[companionId] } : null; + + if (companionId) { + const companion = companionBoardConfigs[companionId]; + + // Update companion details display + document.getElementById('companion-rtc').textContent = companion.rtc || 'None'; + document.getElementById('companion-sd-cs').textContent = companion.sdCardCS !== null ? companion.sdCardCS : 'None'; + document.getElementById('companion-extras').textContent = companion.extras; + document.getElementById('companion-details').classList.remove('hidden'); + + // Update SD card section + if (companion.sdCardCS !== null) { + appState.sdCardCS = companion.sdCardCS; + document.getElementById('sd-missing').classList.add('hidden'); + document.getElementById('sd-present').classList.remove('hidden'); + document.getElementById('sd-cs-pin').textContent = companion.sdCardCS; + + // Mark SD CS pin as used + appState.usedPins.add(companion.sdCardCS); + } else { + // Companion board doesn't provide SD card, show manual config + document.getElementById('sd-missing').classList.remove('hidden'); + document.getElementById('sd-present').classList.add('hidden'); + appState.sdCardCS = null; + } + + // Update RTC section + if (companion.rtc) { + appState.rtcType = companion.rtc; + document.getElementById('rtc-missing').classList.add('hidden'); + document.getElementById('rtc-present').classList.remove('hidden'); + document.getElementById('rtc-type').textContent = companion.rtc; + } else { + // Companion board doesn't provide RTC, show manual config + document.getElementById('rtc-missing').classList.remove('hidden'); + document.getElementById('rtc-present').classList.add('hidden'); + appState.rtcType = 'soft'; + document.getElementById('rtc-select').value = 'soft'; + } + } else { + document.getElementById('companion-details').classList.add('hidden'); + + // Reset SD card and RTC sections to manual configuration + document.getElementById('sd-missing').classList.remove('hidden'); + document.getElementById('sd-present').classList.add('hidden'); + document.getElementById('rtc-missing').classList.remove('hidden'); + document.getElementById('rtc-present').classList.add('hidden'); + + appState.sdCardCS = null; + appState.rtcType = 'soft'; + document.getElementById('rtc-select').value = 'soft'; + } + + // Refresh pin lists to reflect used pins + populatePinsLists(); + }); + + // Add SD card checkbox handler + document.getElementById('add-sd-card').addEventListener('change', function() { + if (this.checked) { + document.getElementById('sd-card-pin-select').classList.remove('hidden'); + } else { + document.getElementById('sd-card-pin-select').classList.add('hidden'); + appState.sdCardCS = null; + } + }); + + // RTC type selection handler + document.getElementById('rtc-select').addEventListener('change', function() { + appState.rtcType = this.value; + }); + + // LED brightness slider handler + document.getElementById('led-brightness').addEventListener('input', function() { + const value = parseFloat(this.value); + appState.statusLEDBrightness = value; + document.getElementById('brightness-value').textContent = value.toFixed(1); + }); + + // Add additional I2C bus checkbox handler + document.getElementById('add-i2c-bus').addEventListener('change', function() { + if (this.checked) { + document.getElementById('additional-i2c-config').classList.remove('hidden'); + } else { + document.getElementById('additional-i2c-config').classList.add('hidden'); + + // Remove additional I2C bus if unchecked + const additionalBusIndex = appState.i2cBuses.findIndex(bus => bus.id !== 'default'); + if (additionalBusIndex !== -1) { + // Free up the pins + const bus = appState.i2cBuses[additionalBusIndex]; + appState.usedPins.delete(bus.scl); + appState.usedPins.delete(bus.sda); + + // Remove the bus + appState.i2cBuses.splice(additionalBusIndex, 1); + + // Update I2C bus select options + updateI2CBusOptions(); + } + } + }); + + // Add I2C Multiplexer button handler + document.getElementById('add-mux-btn').addEventListener('click', function() { + showAddMultiplexerModal(); + }); + + // Generate Configuration button handler + document.getElementById('generate-config-btn').addEventListener('click', function() { + generateConfiguration(); + }); + + // Download Configuration button handler + document.getElementById('download-config-btn').addEventListener('click', function() { + downloadConfiguration(); + }); + + // Import Configuration button handler + document.getElementById('import-btn').addEventListener('click', function() { + importConfiguration(); + }); + + // Export Configuration button handler + document.getElementById('export-btn').addEventListener('click', function() { + downloadConfiguration(true); + }); + + // Modal cancel button handler + document.getElementById('modal-cancel').addEventListener('click', function() { + closeModal(); + }); + + // Modal save button handler + document.getElementById('modal-save').addEventListener('click', function() { + saveModalData(); + }); + + // Component search functionality + document.querySelectorAll('.component-search').forEach(searchInput => { + searchInput.addEventListener('input', function() { + const searchTerm = this.value.toLowerCase(); + const componentType = this.id.split('-')[0]; // Extract type from ID (e.g., "i2c" from "i2c-search") + const componentList = document.getElementById(`${componentType}-component-list`); + + // Filter components + const components = componentList.querySelectorAll('.component-card'); + components.forEach(component => { + const componentName = component.querySelector('h4').textContent.toLowerCase(); + const shouldShow = componentName.includes(searchTerm); + component.style.display = shouldShow ? 'block' : 'none'; + }); + }); + }); +}); + +// Helper functions +function hideSubsequentSections() { + document.getElementById('companion-board-section').classList.add('hidden'); + document.getElementById('manual-config-section').classList.add('hidden'); + document.getElementById('i2c-bus-section').classList.add('hidden'); + document.getElementById('components-section').classList.add('hidden'); + document.getElementById('selected-components-section').classList.add('hidden'); + document.getElementById('generate-section').classList.add('hidden'); +} + +function resetSubsequentSelections() { + // Reset companion board selection + document.getElementById('companion-board-select').value = ''; + document.getElementById('companion-details').classList.add('hidden'); + + // Reset manual config + document.getElementById('add-sd-card').checked = false; + document.getElementById('sd-card-pin-select').classList.add('hidden'); + document.getElementById('rtc-select').value = 'soft'; + document.getElementById('led-brightness').value = 0.5; + document.getElementById('brightness-value').textContent = '0.5'; + + // Reset I2C bus config + document.getElementById('add-i2c-bus').checked = false; + document.getElementById('additional-i2c-config').classList.add('hidden'); + + // Reset component selections + appState.selectedComponents = []; + appState.i2cMultiplexers = []; + updateSelectedComponentsList(); + + // Reset used pins to just the default I2C pins + appState.usedPins = new Set(); + if (appState.selectedBoard) { + appState.usedPins.add(appState.selectedBoard.defaultI2C.scl); + appState.usedPins.add(appState.selectedBoard.defaultI2C.sda); + } +} + +function initializeManualConfig() { + // Initialize SD card section + document.getElementById('sd-missing').classList.remove('hidden'); + document.getElementById('sd-present').classList.add('hidden'); + + // Initialize RTC section + document.getElementById('rtc-missing').classList.remove('hidden'); + document.getElementById('rtc-present').classList.add('hidden'); + + // Initialize LED brightness + document.getElementById('led-brightness').value = 0.5; + document.getElementById('brightness-value').textContent = '0.5'; + appState.statusLEDBrightness = 0.5; +} + +function populatePinsLists() { + if (!appState.selectedBoard) return; + + const pins = appState.selectedBoard.pins; + + // Populate SD card pins list + const sdPinsList = document.getElementById('sd-pins-list'); + sdPinsList.innerHTML = ''; + pins.forEach(pin => { + const pinElem = document.createElement('div'); + pinElem.className = 'pin' + (appState.usedPins.has(pin) ? ' used' : ''); + pinElem.textContent = pin; + + if (!appState.usedPins.has(pin)) { + pinElem.addEventListener('click', function() { + // Deselect any previously selected SD CS pin + if (appState.sdCardCS !== null) { + appState.usedPins.delete(appState.sdCardCS); + } + + // Set new SD CS pin + appState.sdCardCS = pin; + appState.usedPins.add(pin); + + // Update pin selection UI + const allPins = sdPinsList.querySelectorAll('.pin'); + allPins.forEach(p => p.classList.remove('selected')); + pinElem.classList.add('selected'); + + // Refresh other pin lists + populatePinsLists(); + }); + } + + sdPinsList.appendChild(pinElem); + }); + + // Populate SCL pins list for additional I2C bus + const sclPinsList = document.getElementById('scl-pins-list'); + sclPinsList.innerHTML = ''; + pins.forEach(pin => { + const pinElem = document.createElement('div'); + pinElem.className = 'pin' + (appState.usedPins.has(pin) ? ' used' : ''); + pinElem.textContent = pin; + + if (!appState.usedPins.has(pin)) { + pinElem.addEventListener('click', function() { + // Find additional I2C bus or create it + let additionalBus = appState.i2cBuses.find(bus => bus.id !== 'default'); + if (additionalBus) { + // Free up old SCL pin if it exists + if (additionalBus.scl !== undefined) { + appState.usedPins.delete(additionalBus.scl); + } + + // Set new SCL pin + additionalBus.scl = pin; + } else { + // Create new additional bus + additionalBus = { + id: 'additional', + scl: pin, + sda: undefined + }; + appState.i2cBuses.push(additionalBus); + } + + // Mark pin as used + appState.usedPins.add(pin); + + // Update pin selection UI + const allPins = sclPinsList.querySelectorAll('.pin'); + allPins.forEach(p => p.classList.remove('selected')); + pinElem.classList.add('selected'); + + // Refresh other pin lists + populatePinsLists(); + + // Update I2C bus dropdown in components section + if (additionalBus.sda !== undefined) { + updateI2CBusOptions(); + } + }); + } + + sclPinsList.appendChild(pinElem); + }); + + // Populate SDA pins list for additional I2C bus + const sdaPinsList = document.getElementById('sda-pins-list'); + sdaPinsList.innerHTML = ''; + pins.forEach(pin => { + const pinElem = document.createElement('div'); + pinElem.className = 'pin' + (appState.usedPins.has(pin) ? ' used' : ''); + pinElem.textContent = pin; + + if (!appState.usedPins.has(pin)) { + pinElem.addEventListener('click', function() { + // Find additional I2C bus or create it + let additionalBus = appState.i2cBuses.find(bus => bus.id !== 'default'); + if (additionalBus) { + // Free up old SDA pin if it exists + if (additionalBus.sda !== undefined) { + appState.usedPins.delete(additionalBus.sda); + } + + // Set new SDA pin + additionalBus.sda = pin; + } else { + // Create new additional bus + additionalBus = { + id: 'additional', + scl: undefined, + sda: pin + }; + appState.i2cBuses.push(additionalBus); + } + + // Mark pin as used + appState.usedPins.add(pin); + + // Update pin selection UI + const allPins = sdaPinsList.querySelectorAll('.pin'); + allPins.forEach(p => p.classList.remove('selected')); + pinElem.classList.add('selected'); + + // Refresh other pin lists + populatePinsLists(); + + // Update I2C bus dropdown in components section + if (additionalBus.scl !== undefined) { + updateI2CBusOptions(); + } + }); + } + + sdaPinsList.appendChild(pinElem); + }); +} + +function updateI2CBusOptions() { + const i2cBusSelect = document.getElementById('i2c-bus-select'); + i2cBusSelect.innerHTML = ''; + + appState.i2cBuses.forEach(bus => { + if (bus.scl !== undefined && bus.sda !== undefined) { + const option = document.createElement('option'); + option.value = bus.id; + option.textContent = bus.id === 'default' ? + 'Default I2C Bus (SCL: ' + bus.scl + ', SDA: ' + bus.sda + ')' : + 'Additional I2C Bus (SCL: ' + bus.scl + ', SDA: ' + bus.sda + ')'; + i2cBusSelect.appendChild(option); + } + }); + + // Add options for each multiplexer channel + appState.i2cMultiplexers.forEach(mux => { + for (let i = 0; i < mux.channels; i++) { + const option = document.createElement('option'); + const busId = `mux-${mux.id}-ch-${i}`; + option.value = busId; + option.textContent = `Multiplexer ${mux.address} - Channel ${i}`; + i2cBusSelect.appendChild(option); + } + }); +} + +function populateComponentLists() { + // Populate I2C components + const i2cList = document.getElementById('i2c-component-list'); + i2cList.innerHTML = ''; + appState.componentsData.i2c.forEach(component => { + const card = createComponentCard(component, 'i2c'); + i2cList.appendChild(card); + }); + + // Populate DS18x20 components + const ds18x20List = document.getElementById('ds18x20-component-list'); + ds18x20List.innerHTML = ''; + appState.componentsData.ds18x20.forEach(component => { + const card = createComponentCard(component, 'ds18x20'); + ds18x20List.appendChild(card); + }); + + // Populate Pin components + const pinList = document.getElementById('pin-component-list'); + pinList.innerHTML = ''; + appState.componentsData.pin.forEach(component => { + const card = createComponentCard(component, 'pin'); + pinList.appendChild(card); + }); + + // Populate Pixel components + const pixelList = document.getElementById('pixel-component-list'); + pixelList.innerHTML = ''; + appState.componentsData.pixel.forEach(component => { + const card = createComponentCard(component, 'pixel'); + pixelList.appendChild(card); + }); + + // Populate PWM components + const pwmList = document.getElementById('pwm-component-list'); + pwmList.innerHTML = ''; + appState.componentsData.pwm.forEach(component => { + const card = createComponentCard(component, 'pwm'); + pwmList.appendChild(card); + }); + + // Populate Servo components + const servoList = document.getElementById('servo-component-list'); + servoList.innerHTML = ''; + appState.componentsData.servo.forEach(component => { + const card = createComponentCard(component, 'servo'); + servoList.appendChild(card); + }); + + // Populate UART components + const uartList = document.getElementById('uart-component-list'); + uartList.innerHTML = ''; + appState.componentsData.uart.forEach(component => { + const card = createComponentCard(component, 'uart'); + uartList.appendChild(card); + }); + + // Update I2C bus options + updateI2CBusOptions(); +} + +function createComponentCard(component, type) { + const card = document.createElement('div'); + card.className = 'component-card'; + card.dataset.id = component.id; + card.dataset.type = type; + + // Add image if available + if (component.image) { + const img = document.createElement('img'); + img.src = "https://raw.githubusercontent.com/adafruit/Wippersnapper_Components/refs/heads/main/" + component.image; + img.alt = component.name; + card.appendChild(img); + } + + const title = document.createElement('h4'); + title.textContent = component.name; + card.appendChild(title); + + if (type === 'i2c' && component.address) { + const address = document.createElement('p'); + address.textContent = `Address: ${component.address}`; + card.appendChild(address); + } + + if (component.dataTypes && component.dataTypes.length > 0) { + const dataTypes = document.createElement('p'); + dataTypes.textContent = `Data Types: ${component.dataTypes.length}`; + card.appendChild(dataTypes); + } + + const addBtn = document.createElement('button'); + addBtn.textContent = 'Add Component'; + addBtn.addEventListener('click', function() { + showComponentConfigModal(component, type); + }); + card.appendChild(addBtn); + + return card; +} + +function showComponentConfigModal(component, type) { + const modal = document.getElementById('component-modal'); + const modalTitle = document.getElementById('modal-title'); + const modalContent = document.getElementById('modal-content'); + + // Set modal title + modalTitle.textContent = `Configure ${component.name}`; + + // Clear previous content + modalContent.innerHTML = ''; + + // Build configuration form based on component type + let html = '
'; + + // Common fields + html += ` +
+ + +
+
+ + +
+ `; + + // Component-specific fields + if (type === 'i2c') { + // I2C bus selection + html += ` +
+ + +
+ `; + + // I2C Address + html += ` +
+ + +
+ `; + + // Special case for multiplexers + if (component.id === 'pca9546' || component.id === 'pca9548' || + component.id === 'tca9546' || component.id === 'tca9548') { + + const defaultChannels = component.id.includes('9548') ? 8 : 4; + + html += ` +
+ + +

This multiplexer has ${defaultChannels} channels

+
+ `; + } + } else if (type === 'ds18x20' || type === 'pin' || type === 'pixel' || type === 'pwm' || type === 'servo') { + // Pin selection for other component types + html += ` +
+ + +
+ `; + + // Additional fields for pixel components + if (type === 'pixel') { + html += ` +
+ + +
+ `; + } + } else if (type === 'uart') { + // UART pin selection + html += ` +
+ + +
+
+ + +
+ `; + } + + // Data type selection + if (component.dataTypes && component.dataTypes.length > 0) { + html += ` +
+

Select Data Types:

+
+ `; + + component.dataTypes.forEach(dataType => { + const displayName = typeof dataType === 'string' ? + dataType : (dataType.displayName || dataType.sensorType); + const value = typeof dataType === 'string' ? + dataType : JSON.stringify(dataType); + + html += ` +
+ +
+ `; + }); + + html += ` +
+
+ `; + } + + html += '
'; + + modalContent.innerHTML = html; + + // Store component info for use when saving + modalContent.dataset.componentId = component.id; + modalContent.dataset.componentType = type; + + // Show the modal + modal.style.display = 'block'; +} + +function closeModal() { + const modal = document.getElementById('component-modal'); + modal.style.display = 'none'; +} + +function saveModalData() { + const modalContent = document.getElementById('modal-content'); + const componentId = modalContent.dataset.componentId; + const componentType = modalContent.dataset.componentType; + + // Get component template + const componentTemplate = componentsData[componentType].find(c => c.id === componentId); + + // Get form values + const name = document.getElementById('component-name').value; + const period = parseInt(document.getElementById('component-period').value); + + // Initialize component config + const componentConfig = { + instanceId: appState.nextComponentId++, + name: name, + componentAPI: componentType, + period: period + }; + + // Special handling for I2C + if (componentType === 'i2c') { + const i2cBus = document.getElementById('modal-i2c-bus').value; + const i2cAddress = document.getElementById('modal-i2c-address').value; + + componentConfig.i2cDeviceName = componentId; + componentConfig.i2cDeviceAddress = i2cAddress; + + // Handle multiplexer channel if selected + if (i2cBus.startsWith('mux-')) { + const [_, muxId, __, channelNum] = i2cBus.split('-'); + const mux = appState.i2cMultiplexers.find(m => m.id === parseInt(muxId)); + + if (mux) { + componentConfig.i2cMuxAddress = mux.address; + componentConfig.i2cMuxChannel = channelNum; + } + } else if (i2cBus === 'additional') { + // Add SCL/SDA for additional bus + const additionalBus = appState.i2cBuses.find(b => b.id === 'additional'); + if (additionalBus) { + componentConfig.i2cBusScl = additionalBus.scl.toString(); + componentConfig.i2cBusSda = additionalBus.sda.toString(); + } + } + + // Special case for multiplexers + if (componentId === 'pca9546' || componentId === 'pca9548' || + componentId === 'tca9546' || componentId === 'tca9548') { + + const channels = parseInt(document.getElementById('modal-mux-channels').value); + + // Add to multiplexers list + const muxConfig = { + id: appState.nextComponentId - 1, // Use the same ID assigned to componentConfig + address: i2cAddress, + channels: channels + }; + + appState.i2cMultiplexers.push(muxConfig); + + // Update I2C bus options + updateI2CBusOptions(); + + // Remove any components using this multiplexer + appState.selectedComponents = appState.selectedComponents.filter(c => + !(c.i2cMuxAddress && c.i2cMuxAddress === componentConfig.i2cDeviceAddress)); + } else { + // Add data types for non-multiplexer components + const dataTypeCheckboxes = document.querySelectorAll('input[name="data-type"]:checked'); + if (dataTypeCheckboxes.length > 0) { + componentConfig.i2cDeviceSensorTypes = Array.from(dataTypeCheckboxes).map(checkbox => { + try { + return { type: JSON.parse(checkbox.value) }; + } catch (e) { + return { type: checkbox.value }; + } + }); + } + } + } else if (componentType === 'ds18x20') { + const pin = document.getElementById('modal-pin-select').value; + + componentConfig.pinName = `D${pin}`; + componentConfig.sensorResolution = 12; + + // Mark pin as used + appState.usedPins.add(parseInt(pin)); + + // Add data types + const dataTypeCheckboxes = document.querySelectorAll('input[name="data-type"]:checked'); + if (dataTypeCheckboxes.length > 0) { + componentConfig.sensorTypeCount = dataTypeCheckboxes.length; + + // Add each sensor type + Array.from(dataTypeCheckboxes).forEach((checkbox, index) => { + const typeValue = checkbox.value.replace(/"/g, ''); + componentConfig[`sensorType${index + 1}`] = typeValue; + }); + } + } else if (componentType === 'pin' || componentType === 'pwm' || componentType === 'servo') { + const pin = document.getElementById('modal-pin-select').value; + + componentConfig.pinName = `D${pin}`; + + // Mark pin as used + appState.usedPins.add(parseInt(pin)); + } else if (componentType === 'pixel') { + const pin = document.getElementById('modal-pin-select').value; + const pixelCount = document.getElementById('modal-pixel-count').value; + + componentConfig.pinName = `D${pin}`; + componentConfig.numPixels = parseInt(pixelCount); + + // Mark pin as used + appState.usedPins.add(parseInt(pin)); + } else if (componentType === 'uart') { + const txPin = document.getElementById('modal-uart-tx').value; + const rxPin = document.getElementById('modal-uart-rx').value; + + componentConfig.txPin = `D${txPin}`; + componentConfig.rxPin = `D${rxPin}`; + + // Mark pins as used + appState.usedPins.add(parseInt(txPin)); + appState.usedPins.add(parseInt(rxPin)); + + // Add data types + const dataTypeCheckboxes = document.querySelectorAll('input[name="data-type"]:checked'); + if (dataTypeCheckboxes.length > 0) { + componentConfig.sensorTypes = Array.from(dataTypeCheckboxes).map(checkbox => { + try { + return JSON.parse(checkbox.value); + } catch (e) { + return checkbox.value; + } + }); + } + } + + // Add component to the selected components list + appState.selectedComponents.push(componentConfig); + + // Update the selected components list + updateSelectedComponentsList(); + + // Refresh pin lists + populatePinsLists(); + + // Close the modal + closeModal(); +} + +function showAddMultiplexerModal() { + // Check if there are I2C buses available + if (appState.i2cBuses.length === 0) { + alert('No I2C buses available. Please configure an I2C bus first.'); + return; + } + + // Find the multiplexer components + const multiplexers = appState.componentsData.i2c.filter(c => + c.id === 'pca9546' || c.id === 'pca9548' || + c.id === 'tca9546' || c.id === 'tca9548' + ); + + if (multiplexers.length === 0) { + alert('No multiplexer components found in the component data.'); + return; + } + + // Show component config modal for the first multiplexer + showComponentConfigModal(multiplexers[0], 'i2c'); +} + +function updateSelectedComponentsList() { + const selectedList = document.getElementById('selected-components-list'); + + if (appState.selectedComponents.length === 0) { + selectedList.innerHTML = '

No components selected yet.

'; + return; + } + + let html = ''; + selectedList.innerHTML = html; +} + +function removeComponent(instanceId) { + // Find the component + const componentIndex = appState.selectedComponents.findIndex(c => c.instanceId === instanceId); + if (componentIndex === -1) return; + + const component = appState.selectedComponents[componentIndex]; + + // Free up pins used by this component + if (component.pinName) { + const pinNumber = parseInt(component.pinName.replace('D', '')); + appState.usedPins.delete(pinNumber); + } + + if (component.txPin) { + const txPinNumber = parseInt(component.txPin.replace('D', '')); + appState.usedPins.delete(txPinNumber); + } + + if (component.rxPin) { + const rxPinNumber = parseInt(component.rxPin.replace('D', '')); + appState.usedPins.delete(rxPinNumber); + } + + // Check if this is a multiplexer and remove it from the multiplexers list + if (component.componentAPI === 'i2c' && + (component.i2cDeviceName === 'pca9546' || component.i2cDeviceName === 'pca9548' || + component.i2cDeviceName === 'tca9546' || component.i2cDeviceName === 'tca9548')) { + + const muxIndex = appState.i2cMultiplexers.findIndex(m => m.id === component.instanceId); + if (muxIndex !== -1) { + appState.i2cMultiplexers.splice(muxIndex, 1); + + // Update I2C bus options + updateI2CBusOptions(); + + // Remove any components using this multiplexer + appState.selectedComponents = appState.selectedComponents.filter(c => + !(c.i2cMuxAddress && c.i2cMuxAddress === component.i2cDeviceAddress)); + } + } + + // Remove the component + appState.selectedComponents.splice(componentIndex, 1); + + // Update the selected components list + updateSelectedComponentsList(); + + // Refresh pin lists + populatePinsLists(); +} + +function generateConfiguration() { + // Check if a board is selected + if (!appState.selectedBoard) { + alert('Please select a board before generating configuration.'); + return; + } + + // Check if there are any components + if (appState.selectedComponents.length === 0) { + alert('Please add at least one component before generating configuration.'); + return; + } + + // Build the configuration object + const config = { + exportedFromDevice: { + referenceVoltage: appState.selectedBoard.referenceVoltage, + totalGPIOPins: appState.selectedBoard.totalGPIOPins, + totalAnalogPins: appState.selectedBoard.totalAnalogPins, + statusLEDBrightness: appState.statusLEDBrightness + }, + components: [] + }; + + // Add SD card CS pin if present + if (appState.sdCardCS !== null) { + config.exportedFromDevice.sd_cs_pin = appState.sdCardCS; + } + + // Add RTC type if not 'soft' + if (appState.rtcType !== 'soft') { + config.exportedFromDevice.rtc = appState.rtcType; + } + + // Add components + appState.selectedComponents.forEach(component => { + // Create a clean component object without the instanceId + const cleanComponent = {...component}; + delete cleanComponent.instanceId; + + config.components.push(cleanComponent); + }); + + // Convert to formatted JSON and display + const jsonOutput = JSON.stringify(config, null, 4); + document.getElementById('config-output').textContent = jsonOutput; + document.getElementById('config-output-container').classList.remove('hidden'); + + // Also update the export tab + document.getElementById('export-config').textContent = jsonOutput; +} + +function downloadConfiguration(fromExportTab = false) { + // Get the configuration JSON + const configText = fromExportTab ? + document.getElementById('export-config').textContent : + document.getElementById('config-output').textContent; + + if (configText === 'No configuration generated yet.') { + alert('Please generate a configuration first.'); + return; + } + + // Create a Blob with the configuration + const blob = new Blob([configText], { type: 'application/json' }); + const url = URL.createObjectURL(blob); + + // Create a download link and trigger it + const a = document.createElement('a'); + a.href = url; + a.download = 'config.json'; + document.body.appendChild(a); + a.click(); + + // Clean up + document.body.removeChild(a); + URL.revokeObjectURL(url); +} + +function importConfiguration() { + const jsonText = document.getElementById('import-json').value.trim(); + if (!jsonText) { + alert('Please paste a valid JSON configuration.'); + return; + } + + try { + // Parse the JSON + const config = JSON.parse(jsonText); + + // Reset the application state + resetAppState(); + + // Import the configuration + importConfigObject(config); + + // Update the UI + document.getElementById('import-json').value = ''; + alert('Configuration imported successfully. Please check the Build tab to see your configuration.'); + + // Switch to the Build tab + openTab(null, 'BuildConfig'); + } catch (error) { + alert('Error importing configuration: ' + error.message); + } +} + +function resetAppState() { + appState.selectedBoard = null; + appState.companionBoard = null; + appState.sdCardCS = null; + appState.rtcType = 'soft'; + appState.statusLEDBrightness = 0.5; + appState.i2cBuses = []; + appState.i2cMultiplexers = []; + appState.selectedComponents = []; + appState.usedPins = new Set(); + appState.nextComponentId = 1; + + // Reset UI elements + document.getElementById('board-select').value = ''; + document.getElementById('companion-board-select').value = ''; + document.getElementById('led-brightness').value = 0.5; + document.getElementById('brightness-value').textContent = '0.5'; + document.getElementById('add-sd-card').checked = false; + document.getElementById('add-i2c-bus').checked = false; + + // Hide sections + hideSubsequentSections(); +} + +function importConfigObject(config) { + // Import device details + const deviceConfig = config.exportedFromDevice; + + // Try to find the board that matches the configuration + let matchedBoard = null; + for (const [boardId, boardConfig] of Object.entries(appState.boardsData)) { + if (boardConfig.referenceVoltage === deviceConfig.referenceVoltage && + boardConfig.totalGPIOPins === deviceConfig.totalGPIOPins && + boardConfig.totalAnalogPins === deviceConfig.totalAnalogPins) { + matchedBoard = boardId; + break; + } + } + + if (matchedBoard) { + // Select the matched board + document.getElementById('board-select').value = matchedBoard; + const event = new Event('change'); + document.getElementById('board-select').dispatchEvent(event); + + // Import SD card CS pin + if (deviceConfig.sd_cs_pin !== undefined) { + appState.sdCardCS = deviceConfig.sd_cs_pin; + document.getElementById('add-sd-card').checked = true; + document.getElementById('sd-card-pin-select').classList.remove('hidden'); + appState.usedPins.add(deviceConfig.sd_cs_pin); + } + + // Import RTC type + if (deviceConfig.rtc) { + appState.rtcType = deviceConfig.rtc; + document.getElementById('rtc-select').value = deviceConfig.rtc; + const rtcEvent = new Event('change'); + document.getElementById('rtc-select').dispatchEvent(rtcEvent); + } + + // Import LED brightness + if (deviceConfig.statusLEDBrightness !== undefined) { + appState.statusLEDBrightness = deviceConfig.statusLEDBrightness; + document.getElementById('led-brightness').value = deviceConfig.statusLEDBrightness; + document.getElementById('brightness-value').textContent = deviceConfig.statusLEDBrightness; + } + + // Import components + if (config.components && Array.isArray(config.components)) { + // First pass: find and set up multiplexers + config.components.forEach(component => { + if (component.componentAPI === 'i2c' && + (component.i2cDeviceName === 'pca9546' || component.i2cDeviceName === 'pca9548' || + component.i2cDeviceName === 'tca9546' || component.i2cDeviceName === 'tca9548')) { + const channels = component.i2cDeviceName.includes('9548') ? 8 : 4; + const muxConfig = { + id: appState.nextComponentId++, + address: component.i2cDeviceAddress, + channels: channels + }; + + appState.i2cMultiplexers.push(muxConfig); + + // Add to selected components + const componentConfig = { + ...component, + instanceId: muxConfig.id + }; + + appState.selectedComponents.push(componentConfig); + } + }); + + // Second pass: import other components + config.components.forEach(component => { + if (component.componentAPI === 'i2c' && + (component.i2cDeviceName === 'pca9546' || component.i2cDeviceName === 'pca9548' || + component.i2cDeviceName === 'tca9546' || component.i2cDeviceName === 'tca9548')) { + // Skip multiplexers (already handled) + return; + } + + // Add component to the selected components + const componentConfig = { + ...component, + instanceId: appState.nextComponentId++ + }; + + appState.selectedComponents.push(componentConfig); + + // Mark used pins + if (component.pinName) { + const pinNumber = parseInt(component.pinName.replace('D', '')); + appState.usedPins.add(pinNumber); + } + + if (component.txPin) { + const txPinNumber = parseInt(component.txPin.replace('D', '')); + appState.usedPins.add(txPinNumber); + } + + if (component.rxPin) { + const rxPinNumber = parseInt(component.rxPin.replace('D', '')); + appState.usedPins.add(rxPinNumber); + } + }); + + // Update selected components list + updateSelectedComponentsList(); + } + + // Show all sections + document.getElementById('companion-board-section').classList.remove('hidden'); + document.getElementById('manual-config-section').classList.remove('hidden'); + document.getElementById('i2c-bus-section').classList.remove('hidden'); + document.getElementById('components-section').classList.remove('hidden'); + document.getElementById('selected-components-section').classList.remove('hidden'); + document.getElementById('generate-section').classList.remove('hidden'); + + // Update I2C bus options + updateI2CBusOptions(); + + // Refresh pin lists + populatePinsLists(); + } else { + alert('Could not identify the board from the configuration. Please select a board manually.'); + } +} + +// Initialize sample data components if there is no external data +function initializeSampleComponents() { + // Sample I2C components + appState.componentsData.i2c = [ + { id: 'bme280', name: 'BME280', address: '0x77', dataTypes: ['ambient-temp', 'ambient-temp-fahrenheit', 'relative-humidity', 'pressure', 'altitude'] }, + { id: 'sht30', name: 'SHT30', address: '0x44', dataTypes: ['ambient-temp', 'ambient-temp-fahrenheit', 'relative-humidity'] }, + { id: 'mcp9808', name: 'MCP9808', address: '0x18', dataTypes: ['ambient-temp', 'ambient-temp-fahrenheit'] }, + { id: 'bh1750', name: 'BH1750', address: '0x23', dataTypes: ['light'] }, + { id: 'sgp30', name: 'SGP30', address: '0x58', dataTypes: ['eco2', 'tvoc'] }, + { id: 'pca9546', name: 'PCA9546 4-Channel Multiplexer', address: '0x70', channels: 4 }, + { id: 'pca9548', name: 'PCA9548 8-Channel Multiplexer', address: '0x70', channels: 8 }, + { id: 'tca9546', name: 'TCA9546 4-Channel Multiplexer', address: '0x70', channels: 4 }, + { id: 'tca9548', name: 'TCA9548 8-Channel Multiplexer', address: '0x70', channels: 8 } + ]; + + // Sample DS18x20 components + appState.componentsData.ds18x20 = [ + { id: 'ds18b20', name: 'DS18B20', dataTypes: ['object-temp', 'object-temp-fahrenheit'] }, + { id: 'ds18b20_waterproof', name: 'DS18B20 Waterproof', dataTypes: ['object-temp', 'object-temp-fahrenheit'] } + ]; + + // Sample pin components + appState.componentsData.pin = [ + { id: 'led', name: 'LED', dataTypes: [] }, + { id: 'push_button', name: 'Push Button', dataTypes: ['digital-input'] }, + { id: 'toggle_switch', name: 'Toggle Switch', dataTypes: ['digital-input'] }, + { id: 'potentiometer', name: 'Potentiometer', dataTypes: ['analog-input'] } + ]; + + // Sample pixel components + appState.componentsData.pixel = [ + { id: 'neopixel', name: 'NeoPixel', dataTypes: [] }, + { id: 'dotstar', name: 'DotStar', dataTypes: [] } + ]; + + // Sample PWM components + appState.componentsData.pwm = [ + { id: 'dimmable_led', name: 'Dimmable LED', dataTypes: [] }, + { id: 'piezo_buzzer', name: 'Piezo Buzzer', dataTypes: [] } + ]; + + // Sample servo components + appState.componentsData.servo = [ + { id: 'servo', name: 'Servo Motor', dataTypes: [] } + ]; + + // Sample UART components + appState.componentsData.uart = [ + { id: 'pms5003', name: 'PMS5003 Air Quality Sensor', dataTypes: ['pm10-std', 'pm25-std', 'pm100-std'] } + ]; +} + +// Initialize sample board configs if there is no external data +function initializeSampleBoards() { + appState.boardsData = { + 'feather-esp32': { + referenceVoltage: 3.3, + totalGPIOPins: 21, + totalAnalogPins: 14, + defaultI2C: { scl: 22, sda: 23 }, + pins: [0, 2, 4, 5, 12, 13, 14, 15, 16, 17, 18, 19, 21, 22, 23, 25, 26, 27, 32, 33, 34, 35, 36, 39] + }, + 'feather-esp32s2': { + referenceVoltage: 3.3, + totalGPIOPins: 22, + totalAnalogPins: 6, + defaultI2C: { scl: 42, sda: 41 }, + pins: [0, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 21, 33, 34, 35, 36, 37, 38, 39, 41, 42] + }, + 'feather-esp32s3-tft': { + referenceVoltage: 3.3, + totalGPIOPins: 18, + totalAnalogPins: 6, + defaultI2C: { scl: 9, sda: 8 }, + pins: [1, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 17, 18, 21, 38, 39, 40, 41, 42] + }, + 'feather-esp32c3': { + referenceVoltage: 3.3, + totalGPIOPins: 13, + totalAnalogPins: 4, + defaultI2C: { scl: 5, sda: 4 }, + pins: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 18, 19, 20, 21] + }, + 'qtpy-esp32c3': { + referenceVoltage: 3.3, + totalGPIOPins: 11, + totalAnalogPins: 4, + defaultI2C: { scl: 5, sda: 4 }, + pins: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10] + } + }; +} + +// Run initialization if no data loader is detected +if (typeof loadWippersnapperData === 'undefined') { + console.log('No data loader detected, initializing with sample data'); + document.addEventListener('DOMContentLoaded', function() { + // Hide loading indicator + document.getElementById('loading-indicator').classList.add('hidden'); + + // Initialize with sample data + initializeSampleBoards(); + initializeSampleComponents(); + }); +} diff --git a/wippersnapper_components.json b/wippersnapper_components.json index 306937d..a09af17 100644 --- a/wippersnapper_components.json +++ b/wippersnapper_components.json @@ -6,7 +6,10 @@ "name": "ds18b20", "description": "", "category": "ds18x20", - "dataTypes": [], + "dataTypes": [ + "ambient-temp", + "ambient-temp-fahrenheit" + ], "image": null }, { @@ -14,7 +17,10 @@ "name": "ds18b20_hi_temp_waterproof", "description": "", "category": "ds18x20", - "dataTypes": [], + "dataTypes": [ + "ambient-temp", + "ambient-temp-fahrenheit" + ], "image": null }, { @@ -22,7 +28,10 @@ "name": "ds18b20_waterproof", "description": "", "category": "ds18x20", - "dataTypes": [], + "dataTypes": [ + "ambient-temp", + "ambient-temp-fahrenheit" + ], "image": null } ], @@ -32,576 +41,1303 @@ "name": "adt7410", "description": "", "category": "i2c", - "dataTypes": [], - "image": null + "dataTypes": [ + "ambient-temp", + "ambient-temp-fahrenheit" + ], + "image": null, + "address": "0x48", + "addresses": [ + "0x48", + "0x49", + "0x4A", + "0x4B" + ] }, { "id": "aht20", "name": "aht20", "description": "Inexpensive temperature and humidity sensor for I2C-capable boards.", "category": "i2c", - "dataTypes": [], - "image": null + "dataTypes": [ + "ambient-temp", + "ambient-temp-fahrenheit", + "humidity" + ], + "image": null, + "address": "0x38", + "addresses": [ + "0x38" + ] }, { "id": "aht21", "name": "aht21", "description": "", "category": "i2c", - "dataTypes": [], - "image": "components/i2c/aht21/image.jpg" + "dataTypes": [ + "ambient-temp", + "ambient-temp-fahrenheit", + "humidity" + ], + "image": "components/i2c/aht21/image.jpg", + "address": "0x38", + "addresses": [ + "0x38" + ] }, { "id": "am2301b", "name": "am2301b", "description": "", "category": "i2c", - "dataTypes": [], - "image": null + "dataTypes": [ + "ambient-temp", + "ambient-temp-fahrenheit", + "humidity" + ], + "image": null, + "address": "0x38", + "addresses": [ + "0x38" + ] }, { "id": "am2315c", "name": "am2315c", "description": "", "category": "i2c", - "dataTypes": [], - "image": null + "dataTypes": [ + "ambient-temp", + "ambient-temp-fahrenheit", + "humidity" + ], + "image": null, + "address": "0x38", + "addresses": [ + "0x38" + ] }, { "id": "bh1750", "name": "bh1750", "description": "", "category": "i2c", - "dataTypes": [], - "image": null + "dataTypes": [ + "light" + ], + "image": null, + "address": "0x23", + "addresses": [ + "0x23", + "0x5C" + ] }, { "id": "bme280", "name": "bme280", "description": "", "category": "i2c", - "dataTypes": [], - "image": null + "dataTypes": [ + "ambient-temp", + "ambient-temp-fahrenheit", + "humidity", + "pressure", + "altitude" + ], + "image": null, + "address": "0x76", + "addresses": [ + "0x76", + "0x77" + ] }, { "id": "bme680", "name": "bme680", "description": "", "category": "i2c", - "dataTypes": [], - "image": null + "dataTypes": [ + "ambient-temp", + "ambient-temp-fahrenheit", + "humidity", + "pressure", + "altitude", + "gas-resistance" + ], + "image": null, + "address": "0x76", + "addresses": [ + "0x76", + "0x77" + ] }, { "id": "bme688", "name": "bme688", "description": "", "category": "i2c", - "dataTypes": [], - "image": null + "dataTypes": [ + "ambient-temp", + "ambient-temp-fahrenheit", + "humidity", + "pressure", + "altitude", + "gas-resistance" + ], + "image": null, + "address": "0x76", + "addresses": [ + "0x76", + "0x77" + ] }, { "id": "bmp280", "name": "bmp280", "description": "", "category": "i2c", - "dataTypes": [], - "image": null + "dataTypes": [ + "ambient-temp", + "ambient-temp-fahrenheit", + "pressure", + "altitude" + ], + "image": null, + "address": "0x76", + "addresses": [ + "0x76", + "0x77" + ] }, { "id": "bmp388", "name": "bmp388", "description": "", "category": "i2c", - "dataTypes": [], - "image": null + "dataTypes": [ + "ambient-temp", + "ambient-temp-fahrenheit", + "pressure", + "altitude" + ], + "image": null, + "address": "0x76", + "addresses": [ + "0x76", + "0x77" + ] }, { "id": "bmp390", "name": "bmp390", "description": "", "category": "i2c", - "dataTypes": [], - "image": null + "dataTypes": [ + "ambient-temp", + "ambient-temp-fahrenheit", + "pressure", + "altitude" + ], + "image": null, + "address": "0x76", + "addresses": [ + "0x76", + "0x77" + ] }, { "id": "dht20", "name": "dht20", "description": "", "category": "i2c", - "dataTypes": [], - "image": null + "dataTypes": [ + "ambient-temp", + "ambient-temp-fahrenheit", + "humidity" + ], + "image": null, + "address": "0x38", + "addresses": [ + "0x38" + ] }, { "id": "dps310", "name": "dps310", "description": "", "category": "i2c", - "dataTypes": [], - "image": null + "dataTypes": [ + "ambient-temp", + "ambient-temp-fahrenheit", + "pressure" + ], + "image": null, + "address": "0x76", + "addresses": [ + "0x76", + "0x77" + ] }, { "id": "ds2484", "name": "ds2484", "description": "Adafruit DS2484 I2C to 1-Wire Bus Adapter Breakout - Converts a single DS18b20 temperature sensor to I2C", "category": "i2c", - "dataTypes": [], - "image": null + "dataTypes": [ + "ambient-temp", + "ambient-temp-fahrenheit" + ], + "image": null, + "address": "0x18", + "addresses": [ + "0x18" + ] }, { "id": "ens160", "name": "ens160", "description": "", "category": "i2c", - "dataTypes": [], - "image": "components/i2c/ens160/image.jpg" + "dataTypes": [ + "tvoc", + "eco2", + { + "displayName": "AQI", + "sensorType": "raw" + } + ], + "image": "components/i2c/ens160/image.jpg", + "address": "0x52", + "addresses": [ + "0x52", + "0x53" + ] }, { "id": "hdc302x", "name": "hdc302x", "description": "Precision temperature (\u00c2\u00b10.1\u00c2\u00b0C typical) and humidity sensors (\u00c2\u00b10.5% typ). HDC3020 / HDC3021 / HDC3022", "category": "i2c", - "dataTypes": [], - "image": null + "dataTypes": [ + "ambient-temp", + "ambient-temp-fahrenheit", + "humidity" + ], + "image": null, + "address": "0x44", + "addresses": [ + "0x44", + "0x45", + "0x46", + "0x47" + ] }, { "id": "hts221", "name": "hts221", "description": "", "category": "i2c", - "dataTypes": [], - "image": null + "dataTypes": [ + "ambient-temp", + "ambient-temp-fahrenheit", + "humidity" + ], + "image": null, + "address": "0x5F", + "addresses": [ + "0x5F" + ] }, { "id": "htu21d", "name": "htu21d", "description": "", "category": "i2c", - "dataTypes": [], - "image": null + "dataTypes": [ + "ambient-temp", + "ambient-temp-fahrenheit", + "humidity" + ], + "image": null, + "address": "0x40", + "addresses": [ + "0x40" + ] }, { "id": "htu31d", "name": "htu31d", "description": "", "category": "i2c", - "dataTypes": [], - "image": null + "dataTypes": [ + "ambient-temp", + "ambient-temp-fahrenheit", + "humidity" + ], + "image": null, + "address": "0x40", + "addresses": [ + "0x40", + "0x41" + ] }, { "id": "ina219", "name": "ina219", "description": "", "category": "i2c", - "dataTypes": [], - "image": null + "dataTypes": [ + "voltage", + "current" + ], + "image": null, + "address": "0x40", + "addresses": [ + "0x40", + "0x41", + "0x44", + "0x45" + ] }, { "id": "lc709203f", "name": "lc709203f", "description": "", "category": "i2c", - "dataTypes": [], - "image": null + "dataTypes": [ + { + "displayName": "Battery Cell Voltage", + "sensorType": "voltage" + }, + { + "displayName": "Battery Cell Percent", + "sensorType": "unitless-percent" + } + ], + "image": null, + "address": "0x0B", + "addresses": [ + "0x0B" + ] }, { "id": "lps22hb", "name": "lps22hb", "description": "", "category": "i2c", - "dataTypes": [], - "image": null + "dataTypes": [ + "ambient-temp", + "ambient-temp-fahrenheit", + "pressure" + ], + "image": null, + "address": "0x5C", + "addresses": [ + "0x5C", + "0x5D" + ] }, { "id": "lps25hb", "name": "lps25hb", "description": "", "category": "i2c", - "dataTypes": [], - "image": "components/i2c/lps25hb/image.jpg" + "dataTypes": [ + "ambient-temp", + "ambient-temp-fahrenheit", + "pressure" + ], + "image": "components/i2c/lps25hb/image.jpg", + "address": "0x5C", + "addresses": [ + "0x5C", + "0x5D" + ] }, { "id": "lps28dfw", "name": "lps28dfw", "description": "From 260 to 4060 hPa, this is our largest range pressure sensor (24bit).", "category": "i2c", - "dataTypes": [], - "image": "components/i2c/lps28dfw/image.jpg" + "dataTypes": [ + "ambient-temp", + "ambient-temp-fahrenheit", + "pressure" + ], + "image": "components/i2c/lps28dfw/image.jpg", + "address": "0x5C", + "addresses": [ + "0x5C", + "0x5D" + ] }, { "id": "lps33hw", "name": "lps33hw", "description": "", "category": "i2c", - "dataTypes": [], - "image": "components/i2c/lps33hw/image.jpg" + "dataTypes": [ + "ambient-temp", + "ambient-temp-fahrenheit", + "pressure" + ], + "image": "components/i2c/lps33hw/image.jpg", + "address": "0x5C", + "addresses": [ + "0x5C", + "0x5D" + ] }, { "id": "lps35hw", "name": "lps35hw", "description": "", "category": "i2c", - "dataTypes": [], - "image": null + "dataTypes": [ + "ambient-temp", + "ambient-temp-fahrenheit", + "pressure" + ], + "image": null, + "address": "0x5C", + "addresses": [ + "0x5C", + "0x5D" + ] }, { "id": "ltr303", "name": "ltr303", "description": "", "category": "i2c", - "dataTypes": [], - "image": "components/i2c/ltr303/image.jpg" + "dataTypes": [ + { + "displayName": "Ambient Light", + "sensorType": "light" + }, + { + "displayName": "Infrared", + "sensorType": "raw" + } + ], + "image": "components/i2c/ltr303/image.jpg", + "address": "0x29", + "addresses": [ + "0x29" + ] }, { "id": "ltr329", "name": "ltr329", "description": "", "category": "i2c", - "dataTypes": [], - "image": "components/i2c/ltr329/image.jpg" + "dataTypes": [ + { + "displayName": "Ambient Light", + "sensorType": "light" + }, + { + "displayName": "Infrared", + "sensorType": "raw" + } + ], + "image": "components/i2c/ltr329/image.jpg", + "address": "0x29", + "addresses": [ + "0x29" + ] }, { "id": "ltr390", "name": "ltr390", "description": "", "category": "i2c", - "dataTypes": [], - "image": "components/i2c/ltr390/image.jpg" + "dataTypes": [ + { + "displayName": "Ambient Light", + "sensorType": "light" + }, + { + "displayName": "UV Count", + "sensorType": "raw" + } + ], + "image": "components/i2c/ltr390/image.jpg", + "address": "0x53", + "addresses": [ + "0x53" + ] }, { "id": "max17048", "name": "max17048", "description": "", "category": "i2c", - "dataTypes": [], - "image": null + "dataTypes": [ + { + "displayName": "Battery Cell Voltage", + "sensorType": "voltage" + }, + { + "displayName": "Battery Cell Percent", + "sensorType": "unitless-percent" + } + ], + "image": null, + "address": "0x36", + "addresses": [ + "0x36" + ] }, { "id": "mcp3421", "name": "mcp3421", "description": "18-bit ADC. Great for Strain Gauges, Thermocouples and Pressure sensors, between 0 and 2.048 volts", "category": "i2c", - "dataTypes": [], - "image": "components/i2c/mcp3421/image.jpg" + "dataTypes": [ + { + "displayName": "ADC Reading", + "sensorType": "raw" + } + ], + "image": "components/i2c/mcp3421/image.jpg", + "address": "0x68", + "addresses": [ + "0x68" + ] }, { "id": "mcp9601", "name": "mcp9601", "description": "Thermocouple / ambient temperature sensor. *Note* Needs hotplugging after i2c scans + selecting component!", "category": "i2c", - "dataTypes": [], - "image": null + "dataTypes": [ + { + "displayName": "Ambient Temperature (\u00c2\u00b0C)", + "sensorType": "ambient-temp" + }, + { + "displayName": "Ambient Temperature (\u00c2\u00b0F)", + "sensorType": "ambient-temp-fahrenheit" + }, + { + "displayName": "Type K Thermocouple (\u00c2\u00b0C)", + "sensorType": "raw" + } + ], + "image": null, + "address": "0x60", + "addresses": [ + "0x60", + "0x61", + "0x62", + "0x63", + "0x64", + "0x65", + "0x66", + "0x67" + ] }, { "id": "mcp9808", "name": "mcp9808", "description": "", "category": "i2c", - "dataTypes": [], - "image": null + "dataTypes": [ + "ambient-temp", + "ambient-temp-fahrenheit" + ], + "image": null, + "address": "0x18", + "addresses": [ + "0x18", + "0x19", + "0x1A", + "0x1B", + "0x1C", + "0x1D", + "0x1E", + "0x1F" + ] }, { "id": "mpl115a2", "name": "mpl115a2", "description": "", "category": "i2c", - "dataTypes": [], - "image": null + "dataTypes": [ + "ambient-temp", + "ambient-temp-fahrenheit", + "pressure" + ], + "image": null, + "address": "0x60", + "addresses": [ + "0x60" + ] }, { "id": "mprls", "name": "mprls", "description": "", "category": "i2c", - "dataTypes": [], - "image": null + "dataTypes": [ + "pressure" + ], + "image": null, + "address": "0x18", + "addresses": [ + "0x18" + ] }, { "id": "ms8607", "name": "ms8607", "description": "", "category": "i2c", - "dataTypes": [], - "image": null + "dataTypes": [ + "ambient-temp", + "ambient-temp-fahrenheit", + "humidity", + "pressure" + ], + "image": null, + "address": "0x40", + "addresses": [ + "0x40", + "0x76" + ] }, { "id": "nau7802", "name": "nau7802", "description": "24-bit ADC with 128x gain, used with a load cell for weight/force sensing", "category": "i2c", - "dataTypes": [], - "image": "components/i2c/nau7802/image.jpg" + "dataTypes": [ + { + "displayName": "Weight Sensor", + "sensorType": "raw" + } + ], + "image": "components/i2c/nau7802/image.jpg", + "address": "0x2A", + "addresses": [ + "0x2A" + ] }, { "id": "pct2075", "name": "pct2075", "description": "", "category": "i2c", - "dataTypes": [], - "image": "components/i2c/pct2075/image.jpg" + "dataTypes": [ + "ambient-temp", + "ambient-temp-fahrenheit" + ], + "image": "components/i2c/pct2075/image.jpg", + "address": "0x48", + "addresses": [ + "0x48", + "0x49", + "0x4A", + "0x4B", + "0x4C", + "0x4D", + "0x4E", + "0x4F", + "0x70", + "0x71", + "0x72", + "0x73", + "0x74", + "0x75", + "0x76", + "0x77", + "0x28", + "0x29", + "0x2A", + "0x2B", + "0x2C", + "0x2D", + "0x2E", + "0x2F", + "0x35", + "0x36", + "0x37" + ] }, { "id": "pmsa003i", "name": "pmsa003i", "description": "", "category": "i2c", - "dataTypes": [], - "image": null + "dataTypes": [ + "pm10-std", + "pm25-std", + "pm100-std" + ], + "image": null, + "address": "0x12", + "addresses": [ + "0x12" + ] }, { "id": "rotary_encoder", "name": "rotary_encoder", "description": "", "category": "i2c", - "dataTypes": [], - "image": "components/i2c/rotary_encoder/image.jpeg" + "dataTypes": [ + { + "displayName": "Rotary Encoder Value", + "sensorType": "raw" + } + ], + "image": "components/i2c/rotary_encoder/image.jpeg", + "address": "0x36", + "addresses": [ + "0x36", + "0x37", + "0x38", + "0x39", + "0x3A", + "Ox3B", + "0x3C", + "0x3D" + ] }, { "id": "scd30", "name": "scd30", "description": "", "category": "i2c", - "dataTypes": [], - "image": null + "dataTypes": [ + "ambient-temp", + "ambient-temp-fahrenheit", + "humidity", + "co2" + ], + "image": null, + "address": "0x61", + "addresses": [ + "0x61" + ] }, { "id": "scd40", "name": "scd40", "description": "", "category": "i2c", - "dataTypes": [], - "image": null + "dataTypes": [ + "ambient-temp", + "ambient-temp-fahrenheit", + "humidity", + "co2" + ], + "image": null, + "address": "0x62", + "addresses": [ + "0x62" + ] }, { "id": "sen50", "name": "sen50", "description": "", "category": "i2c", - "dataTypes": [], - "image": "components/i2c/sen50/image.png" + "dataTypes": [ + "pm10-std", + "pm25-std", + "pm100-std" + ], + "image": "components/i2c/sen50/image.png", + "address": "0x69", + "addresses": [ + "0x69" + ] }, { "id": "sen54", "name": "sen54", "description": "", "category": "i2c", - "dataTypes": [], - "image": "components/i2c/sen54/image.png" + "dataTypes": [ + "ambient-temp", + "ambient-temp-fahrenheit", + "humidity", + "pm10-std", + "pm25-std", + "pm100-std", + "voc-index" + ], + "image": "components/i2c/sen54/image.png", + "address": "0x69", + "addresses": [ + "0x69" + ] }, { "id": "sen55", "name": "sen55", "description": "", "category": "i2c", - "dataTypes": [], - "image": "components/i2c/sen55/image.png" + "dataTypes": [ + "ambient-temp", + "ambient-temp-fahrenheit", + "humidity", + "pm10-std", + "pm25-std", + "pm100-std", + "voc-index", + "nox-index" + ], + "image": "components/i2c/sen55/image.png", + "address": "0x69", + "addresses": [ + "0x69" + ] }, { "id": "sen5x", "name": "sen5x", "description": "", "category": "i2c", - "dataTypes": [], - "image": "components/i2c/sen5x/image.png" + "dataTypes": [ + "ambient-temp", + "ambient-temp-fahrenheit", + "humidity", + "pm10-std", + "pm25-std", + "pm100-std", + "voc-index", + "nox-index" + ], + "image": "components/i2c/sen5x/image.png", + "address": "0x69", + "addresses": [ + "0x69" + ] }, { "id": "sen66", "name": "sen66", "description": "", "category": "i2c", - "dataTypes": [], - "image": "components/i2c/sen66/image.png" + "dataTypes": [ + "ambient-temp", + "ambient-temp-fahrenheit", + "humidity", + "pm10-std", + "pm25-std", + "pm100-std", + "voc-index", + "nox-index", + "co2" + ], + "image": "components/i2c/sen66/image.png", + "address": "0x6B", + "addresses": [ + "0x6B" + ] }, { "id": "sgp30", "name": "sgp30", "description": "", "category": "i2c", - "dataTypes": [], - "image": null + "dataTypes": [ + "eco2", + "tvoc" + ], + "image": null, + "address": "0x58", + "addresses": [ + "0x58" + ] }, { "id": "sgp40", "name": "sgp40", "description": "", "category": "i2c", - "dataTypes": [], - "image": null + "dataTypes": [ + "voc-index", + { + "displayName": "Raw (For Reference Only)", + "sensorType": "raw" + } + ], + "image": null, + "address": "0x59", + "addresses": [ + "0x59" + ] }, { "id": "sht20", "name": "sht20", "description": "", "category": "i2c", - "dataTypes": [], - "image": "components/i2c/sht20/image.jpg" + "dataTypes": [ + "ambient-temp", + "ambient-temp-fahrenheit", + "humidity" + ], + "image": "components/i2c/sht20/image.jpg", + "address": "0x40", + "addresses": [ + "0x40" + ] }, { "id": "sht30_mesh", "name": "sht30_mesh", "description": "", "category": "i2c", - "dataTypes": [], - "image": null + "dataTypes": [ + "ambient-temp", + "ambient-temp-fahrenheit", + "humidity" + ], + "image": null, + "address": "0x44", + "addresses": [ + "0x44" + ] }, { "id": "sht30_shell", "name": "sht30_shell", "description": "", "category": "i2c", - "dataTypes": [], - "image": null + "dataTypes": [ + "ambient-temp", + "ambient-temp-fahrenheit", + "humidity" + ], + "image": null, + "address": "0x44", + "addresses": [ + "0x44" + ] }, { "id": "sht3x", "name": "sht3x", "description": "", "category": "i2c", - "dataTypes": [], - "image": null + "dataTypes": [ + "ambient-temp", + "ambient-temp-fahrenheit", + "humidity" + ], + "image": null, + "address": "0x44", + "addresses": [ + "0x44", + "0x45" + ] }, { "id": "sht40", "name": "sht40", "description": "", "category": "i2c", - "dataTypes": [], - "image": null + "dataTypes": [ + "ambient-temp", + "ambient-temp-fahrenheit", + "humidity" + ], + "image": null, + "address": "0x44", + "addresses": [ + "0x44" + ] }, { "id": "sht41", "name": "sht41", "description": "", "category": "i2c", - "dataTypes": [], - "image": null + "dataTypes": [ + "ambient-temp", + "ambient-temp-fahrenheit", + "humidity" + ], + "image": null, + "address": "0x44", + "addresses": [ + "0x44" + ] }, { "id": "sht45", "name": "sht45", "description": "", "category": "i2c", - "dataTypes": [], - "image": null + "dataTypes": [ + "ambient-temp", + "ambient-temp-fahrenheit", + "humidity" + ], + "image": null, + "address": "0x44", + "addresses": [ + "0x44" + ] }, { "id": "shtc3", "name": "shtc3", "description": "", "category": "i2c", - "dataTypes": [], - "image": null + "dataTypes": [ + "ambient-temp", + "ambient-temp-fahrenheit", + "humidity" + ], + "image": null, + "address": "0x70", + "addresses": [ + "0x70" + ] }, { "id": "si7021", "name": "si7021", "description": "", "category": "i2c", - "dataTypes": [], - "image": null + "dataTypes": [ + "ambient-temp", + "ambient-temp-fahrenheit", + "humidity" + ], + "image": null, + "address": "0x40", + "addresses": [ + "0x40" + ] }, { "id": "stemma_soil", "name": "stemma_soil", "description": "", "category": "i2c", - "dataTypes": [], - "image": null + "dataTypes": [ + "ambient-temp", + "ambient-temp-fahrenheit", + { + "displayName": "Capacitive Sensor", + "sensorType": "raw" + } + ], + "image": null, + "address": "0x36", + "addresses": [ + "0x36", + "0x37", + "0x38", + "0x39" + ] }, { "id": "tc74a0", "name": "tc74a0", "description": "", "category": "i2c", - "dataTypes": [], - "image": null + "dataTypes": [ + "ambient-temp", + "ambient-temp-fahrenheit" + ], + "image": null, + "address": "0x48", + "addresses": [ + "0x48" + ] }, { "id": "tmp117", "name": "tmp117", "description": "", "category": "i2c", - "dataTypes": [], - "image": "components/i2c/tmp117/image.jpg" + "dataTypes": [ + "ambient-temp", + "ambient-temp-fahrenheit" + ], + "image": "components/i2c/tmp117/image.jpg", + "address": "0x48", + "addresses": [ + "0x48", + "0x49", + "0x4A", + "0x4B" + ] }, { "id": "tsl2591", "name": "tsl2591", "description": "", "category": "i2c", - "dataTypes": [], - "image": null + "dataTypes": [ + "light" + ], + "image": null, + "address": "0x29", + "addresses": [ + "0x29", + "0x39", + "0x49" + ] }, { "id": "vcnl4020", "name": "vcnl4020", "description": "Proximity sensor works from 0 to 200mm (about 7.5 inches) & light sensor with range of 0.26 to 16,000 lux.", "category": "i2c", - "dataTypes": [], - "image": "components/i2c/vcnl4020/image.jpg" + "dataTypes": [ + "light", + "proximity" + ], + "image": "components/i2c/vcnl4020/image.jpg", + "address": "0x13", + "addresses": [ + "0x13" + ] }, { "id": "vcnl4040", "name": "vcnl4040", "description": "Proximity sensor works from 0 to 200mm (about 7.5 inches) & light sensor with range of 0.0125 to 6,553.5 lux", "category": "i2c", - "dataTypes": [], - "image": null + "dataTypes": [ + "light", + "proximity" + ], + "image": null, + "address": "0x60", + "addresses": [ + "0x60" + ] }, { "id": "vcnl4200", "name": "vcnl4200", "description": "Proximity sensor works from 0 to 1.5m (about 59 inches) & light sensor with range of 0.003 to 1570 lux", "category": "i2c", - "dataTypes": [], - "image": null + "dataTypes": [ + "light", + "proximity" + ], + "image": null, + "address": "0x51", + "addresses": [ + "0x51" + ] }, { "id": "veml7700", "name": "veml7700", "description": "", "category": "i2c", - "dataTypes": [], - "image": null + "dataTypes": [ + "light" + ], + "image": null, + "address": "0x10", + "addresses": [ + "0x10" + ] }, { "id": "vl53l0x", "name": "vl53l0x", "description": "Time of Flight (ToF) distance sensor with about ~50 to 1200mm range", "category": "i2c", - "dataTypes": [], - "image": "components/i2c/vl53l0x/image.jpg" + "dataTypes": [ + { + "displayName": "ToF Sensor", + "sensorType": "proximity" + } + ], + "image": "components/i2c/vl53l0x/image.jpg", + "address": "0x29", + "addresses": [ + "0x29" + ] }, { "id": "vl53l1x", "name": "vl53l1x", "description": "Time of Flight (ToF) distance sensor with about ~30 to 4000mm range", "category": "i2c", - "dataTypes": [], - "image": "components/i2c/vl53l1x/image.jpg" + "dataTypes": [ + { + "displayName": "ToF Sensor", + "sensorType": "proximity" + } + ], + "image": "components/i2c/vl53l1x/image.jpg", + "address": "0x29", + "addresses": [ + "0x29" + ] }, { "id": "vl53l4cd", "name": "vl53l4cd", "description": "Time of Flight (ToF) distance sensor with about ~1 to 1300mm range", "category": "i2c", - "dataTypes": [], - "image": "components/i2c/vl53l4cd/image.jpg" + "dataTypes": [ + { + "displayName": "ToF Sensor", + "sensorType": "proximity" + } + ], + "image": "components/i2c/vl53l4cd/image.jpg", + "address": "0x29", + "addresses": [ + "0x29" + ] }, { "id": "vl53l4cx", "name": "vl53l4cx", "description": "Time of Flight (ToF) distance sensor with about ~1 to 6000mm range + 'multi object detection'", "category": "i2c", - "dataTypes": [], - "image": null + "dataTypes": [ + { + "displayName": "ToF Sensor - Object 1", + "sensorType": "proximity" + }, + { + "displayName": "ToF Sensor - Object 2", + "sensorType": "raw" + } + ], + "image": null, + "address": "0x29", + "addresses": [ + "0x29" + ] }, { "id": "vl6180x", "name": "vl6180x", "description": "Time of Flight (ToF) distance sensor with about ~5 to 200mm range", "category": "i2c", - "dataTypes": [], - "image": "components/i2c/vl6180x/image.jpg" + "dataTypes": [ + { + "displayName": "ToF Sensor", + "sensorType": "proximity" + }, + "light" + ], + "image": "components/i2c/vl6180x/image.jpg", + "address": "0x29", + "addresses": [ + "0x29" + ] } ], "pin": [ @@ -818,7 +1554,9 @@ "name": "pm1006", "description": "", "category": "uart", - "dataTypes": [], + "dataTypes": [ + "pm25-env" + ], "image": "components/uart/pm1006/image.png" }, { @@ -826,7 +1564,14 @@ "name": "pms5003", "description": "", "category": "uart", - "dataTypes": [], + "dataTypes": [ + "pm10-std", + "pm25-std", + "pm100-std", + "pm10-env", + "pm25-env", + "pm100-env" + ], "image": null } ],