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...
+
+
+
+ Build Configuration
+ Import/Export
+
+
+
+
+
1. Select Board
+
+ -- Select a Board --
+
+
+
+
+
+
+
+
Board Details
+
Reference Voltage: 3.3 V
+
Total GPIO Pins: 0
+
Total Analog Pins: 0
+
Default I2C Bus: SCL: 0 , SDA: 0
+
+
+
+
+
+
+
2. Select Companion Board (Optional)
+
You can skip this step if you're not using a companion board.
+
+ -- None --
+ Adafruit Adalogger FeatherWing
+ Adafruit Datalogger FeatherWing
+ Adafruit DS3231 Precision RTC FeatherWing
+ Adafruit Ethernet FeatherWing
+ Adafruit INA219 FeatherWing
+ Adafruit AirLift FeatherWing
+ Adafruit OLED FeatherWing
+ Adafruit Prop-Maker FeatherWing
+ Adafruit NeoPixel FeatherWing
+ Adafruit Joy FeatherWing
+ Adafruit DC Motor + Stepper FeatherWing
+ Adafruit RGB Matrix FeatherWing
+
+
+
+
Companion Board Details
+
RTC: None
+
SD Card CS Pin: None
+
Additional Components:
+
+
+
+
+
3. Manual Configuration
+
+
+
SD Card Configuration
+
+
No SD card detected from companion board. Would you like to add an SD card?
+
Add SD Card
+
+
+
Select SD Card CS Pin:
+
+
+
+
+
+
+
+
RTC Configuration
+
+
No RTC detected from companion board. Select RTC type:
+
+ Software RTC
+ PCF8523
+ DS3231
+ DS1307
+
+
+
+
+
+
+
Status LED Configuration
+ Status LED Brightness (0.0-1.0):
+
+ 0.5
+
+
+
+
+
4. I2C Bus Configuration
+
+
+
Default I2C Bus
+
SCL: , SDA:
+
+
+
+
Additional I2C Bus (Optional)
+
Add Additional I2C Bus
+
+
+
Select SCL Pin:
+
+
Select SDA Pin:
+
+
+
+
+
+
I2C Multiplexers (Optional)
+
Add I2C Multiplexer
+
+
+
+
+
+
+
5. Add Components
+
+
+ I2C Components
+ DS18x20 Components
+ Pin Components
+ Pixel Components
+ PWM Components
+ Servo Components
+ UART Components
+
+
+
+
I2C Components
+
+
+
+
+ Select I2C Bus:
+
+ Default I2C Bus
+
+
+
+
+
+
+
+
+
+
DS18x20 Components
+
+
+
+
+
+
+
+
+
+
Pin Components
+
+
+
+
+
+
+
+
+
+
Pixel Components
+
+
+
+
+
+
+
+
+
+
PWM Components
+
+
+
+
+
+
+
+
+
+
Servo Components
+
+
+
+
+
+
+
+
+
+
UART Components
+
+
+
+
+
+
+
+
+
+
+
6. Selected Components
+
+
No components selected yet.
+
+
+
+
+
7. Generate Configuration
+
Generate Configuration
+
+
Configuration JSON:
+
+
Download config.json
+
+
+
+
+
+
+
Import Configuration
+
Paste your existing config.json here:
+
+
Import Configuration
+
+
+
+
Export Configuration
+
Your current configuration:
+
No configuration generated yet.
+
Download config.json
+
+
+
+
+
+
+
Configure Component
+
+
+
+
+ Cancel
+ Save
+
+
+
+
+
+
+
+
+
+
+
+
+
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 = '-- Select a Board -- ';
+
+ // 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
+ `;
+}
+
+/**
+ * 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 = '';
+
+ 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 = '';
+
+ appState.selectedComponents.forEach(component => {
+ let detailsText = '';
+
+ // Show connection details based on component type
+ if (component.componentAPI === 'i2c') {
+ // Base I2C information
+ detailsText += ` Address: ${component.i2cDeviceAddress}`;
+
+ // Show bus information
+ if (component.i2cBusScl && component.i2cBusSda) {
+ detailsText += ` Bus: Custom (SCL: ${component.i2cBusScl}, SDA: ${component.i2cBusSda})`;
+ } else if (component.i2cMuxAddress) {
+ detailsText += ` Connected to: Multiplexer ${component.i2cMuxAddress} - Channel ${component.i2cMuxChannel}`;
+ } else {
+ // Default bus
+ const defaultBus = appState.i2cBuses.find(bus => bus.id === 'default');
+ if (defaultBus) {
+ detailsText += ` Bus: Default (SCL: ${defaultBus.scl}, SDA: ${defaultBus.sda})`;
+ }
+ }
+
+ // Show sensor types
+ if (component.i2cDeviceSensorTypes && component.i2cDeviceSensorTypes.length > 0) {
+ detailsText += ' Data types: ';
+ component.i2cDeviceSensorTypes.forEach((sensor, index) => {
+ const sensorType = typeof sensor.type === 'object' ?
+ sensor.type.displayName || sensor.type.sensorType : sensor.type;
+ detailsText += (index > 0 ? ', ' : '') + sensorType;
+ });
+ }
+ } else if (component.componentAPI === 'ds18x20') {
+ detailsText += ` Pin: ${component.pinName}`;
+ detailsText += ` Resolution: ${component.sensorResolution}-bit`;
+
+ // Show sensor types
+ const sensorTypes = [];
+ for (let i = 1; i <= component.sensorTypeCount; i++) {
+ if (component[`sensorType${i}`]) {
+ sensorTypes.push(component[`sensorType${i}`]);
+ }
+ }
+
+ if (sensorTypes.length > 0) {
+ detailsText += ' Data types: ' + sensorTypes.join(', ');
+ }
+ } else if (component.componentAPI === 'pin' || component.componentAPI === 'pwm' || component.componentAPI === 'servo') {
+ detailsText += ` Pin: ${component.pinName}`;
+ } else if (component.componentAPI === 'pixel') {
+ detailsText += ` Pin: ${component.pinName}`;
+ detailsText += ` Pixels: ${component.numPixels}`;
+ } else if (component.componentAPI === 'uart') {
+ detailsText += ` TX Pin: ${component.txPin}, RX Pin: ${component.rxPin}`;
+
+ // Show sensor types
+ if (component.sensorTypes && component.sensorTypes.length > 0) {
+ detailsText += ' Data types: ';
+ component.sensorTypes.forEach((sensor, index) => {
+ const sensorType = typeof sensor === 'object' ?
+ sensor.displayName || sensor.sensorType : sensor;
+ detailsText += (index > 0 ? ', ' : '') + sensorType;
+ });
+ }
+ }
+
+ // Add polling period
+ detailsText += ` Polling period: ${component.period} seconds`;
+
+ html += `
+
+
+ ${component.name} (${component.componentAPI})
+ ${detailsText}
+
+
+ Remove
+
+
+ `;
+ });
+
+ 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
}
],