Contributing Devices to PinViz Database¶
Thank you for contributing to the PinViz device database! This guide explains how to add new Raspberry Pi-compatible devices.
Quick Start¶
There are three ways to add devices:
- Automated (Recommended): Use the MCP
parse_device_from_urltool - Manual: Edit
user_devices.jsondirectly - Contribution: Submit a PR to add devices to the main database
Method 1: Automated Device Addition (Recommended)¶
Using Claude Desktop with PinViz MCP Server¶
The easiest way is to let Claude extract device specifications automatically:
Claude will: 1. Fetch the datasheet/product page 2. Extract device specifications using AI 3. Validate against the schema 4. Optionally save to your user database
Example conversation:
You: Add this BME680 sensor to my user database:
https://www.adafruit.com/product/3660
Claude: I'll parse the device specifications from that URL.
[Uses parse_device_from_url tool]
Found: BME680 Gas, Temperature, Humidity, Pressure Sensor
- Category: sensor
- Protocols: I2C, SPI
- Voltage: 3.3V
- I2C Address: 0x76
- 4 pins: VCC, GND, SCL, SDA
Would you like me to save this to your user database?
You: Yes, save it
Claude: ✓ Saved to user_devices.json
Supported Vendor URLs¶
The automated parser works best with these vendors:
- Adafruit: https://www.adafruit.com/product/*
- SparkFun: https://www.sparkfun.com/products/*
- Waveshare: https://www.waveshare.com/*
- Pimoroni: https://shop.pimoroni.com/products/*
- Seeed Studio: https://www.seeedstudio.com/*
- Pololu: https://www.pololu.com/product/*
- DFRobot: https://www.dfrobot.com/*
Note: PDF datasheets work but HTML pages are preferred (faster, better extraction).
Manual Override with URL Parser¶
If auto-detection isn't perfect, you can provide additional details:
Parse https://www.adafruit.com/product/1234
and set the device ID to "my-custom-sensor"
with I2C address 0x68
Method 2: Manual Device Entry¶
Step 1: Locate user_devices.json¶
If the file doesn't exist, create it with this structure:
Step 2: Add Your Device¶
Add a device entry following this template:
{
"id": "my-device-id",
"name": "Human Readable Device Name",
"category": "sensor",
"description": "Brief description of the device",
"pins": [
{
"name": "VCC",
"role": "3V3",
"position": 0
},
{
"name": "GND",
"role": "GND",
"position": 1
},
{
"name": "SCL",
"role": "I2C_SCL",
"position": 2
},
{
"name": "SDA",
"role": "I2C_SDA",
"position": 3
}
],
"protocols": ["I2C"],
"voltage": "3.3V",
"i2c_address": "0x76",
"datasheet_url": "https://example.com/datasheet.pdf",
"manufacturer": "Company Name",
"current_draw": "100mA",
"tags": ["sensor", "temperature", "i2c"],
"notes": "Optional usage notes"
}
Step 3: Validate Your Entry¶
Run the validation test:
Or validate in Python:
from pinviz.mcp.device_manager import DeviceManager
dm = DeviceManager()
device = dm.get_device_by_id("my-device-id")
print(f"✓ Device loaded: {device.name}")
Step 4: Test Diagram Generation¶
Method 3: Contributing to Main Database¶
Prerequisites¶
- Fork the PinViz repository
- Clone your fork
- Install development dependencies:
uv sync --dev
Contribution Workflow¶
Step 1: Add Device to database.json¶
Edit src/pinviz_mcp/devices/database.json:
{
"version": "1.0.0",
"devices": [
// ... existing devices ...
{
"id": "your-new-device",
// ... your device spec ...
}
]
}
Step 2: Validate Against Schema¶
# Run device manager tests
uv run pytest tests/test_device_manager.py -v
# Validate all pin roles
uv run python -c "
from pinviz.mcp.device_manager import DeviceManager
from pinviz.model import PinRole
dm = DeviceManager()
device = dm.get_device_by_id('your-new-device')
for pin in device.pins:
PinRole(pin.role) # Validates pin role
print('✓ Device validated successfully')
"
Step 3: Add Integration Test¶
Create a test in tests/test_integration_real_world.py:
def test_your_device_connection():
"""Test wiring diagram for YourDevice."""
parser = PromptParser(use_llm=False)
dm = DeviceManager()
# Parse prompt
parsed = parser.parse("connect your-new-device to my pi")
# Get device
device = dm.get_device_by_id("your-new-device")
assert device is not None
# Generate pin assignment
assigner = PinAssigner()
assignments = assigner.assign_pins([device], "raspberry_pi_5")
# Verify key pins
assert any(a.device_id == "your-new-device" for a in assignments)
Step 4: Run Full Test Suite¶
# Run all tests
uv run pytest tests/ -v
# Check coverage
uv run pytest tests/ --cov=src/pinviz_mcp --cov-report=term-missing
# Lint and format
uv run ruff check src/pinviz_mcp/
uv run ruff format src/pinviz_mcp/
Step 5: Submit Pull Request¶
-
Commit your changes:
-
Push to your fork:
-
Create a Pull Request on GitHub with:
- Device name and description
- Link to datasheet
- Test results (pytest output)
- Example usage (prompt + generated diagram)
Device Entry Reference¶
Required Fields¶
{
"id": "string", // Unique identifier (lowercase, hyphens)
"name": "string", // Human-readable name
"category": "string", // One of: sensor, display, hat, component, actuator, breakout
"description": "string", // Brief description
"pins": [...], // Array of pin objects (see below)
"protocols": [...], // Array of protocol strings
"voltage": "string" // "3.3V", "5V", or "3.3V-5V"
}
Pin Object Structure¶
{
"name": "string", // Pin name (VCC, GND, SDA, etc.)
"role": "string", // Pin role (see valid roles below)
"position": number // Relative position on device (0-indexed)
}
Valid Pin Roles¶
From pinviz.model.PinRole enum:
- Power:
3V3,5V - Ground:
GND - I2C:
I2C_SDA,I2C_SCL - SPI:
SPI_MOSI,SPI_MISO,SPI_SCLK,SPI_CE0,SPI_CE1 - UART:
UART_TX,UART_RX - PWM:
PWM - GPIO:
GPIO(generic digital I/O)
Important: Use these exact values. Invalid roles will fail validation.
Valid Categories¶
sensor- Environmental, motion, light sensorsdisplay- OLED, LCD, E-Paper, TFT displayshat- Raspberry Pi HATs (full-size add-on boards)component- LEDs, buttons, basic componentsactuator- Motors, relays, buzzers, servobreakout- Breakout boards, ADCs, DACs, IO expanders
Valid Protocols¶
I2C- Inter-Integrated CircuitSPI- Serial Peripheral InterfaceUART- Universal Asynchronous Receiver-TransmitterGPIO- General Purpose Input/Output1-Wire- Dallas 1-Wire protocolPWM- Pulse Width Modulation
Optional Fields¶
{
"i2c_address": "0x76", // For I2C devices
"spi_max_speed": "10MHz", // For SPI devices
"datasheet_url": "https://...", // Official datasheet
"product_url": "https://...", // Where to buy
"manufacturer": "Company Name", // Manufacturer
"current_draw": "3.6µA @ 1Hz", // Typical current
"dimensions": "2.5mm x 2.5mm x 0.93mm", // Physical size
"tags": ["sensor", "environmental"], // Searchable tags
"notes": "Additional usage information" // Special notes
}
Device Naming Guidelines¶
Device IDs¶
- Lowercase only:
bme280notBME280 - Use hyphens:
ssd1306-olednotssd1306_oled - Be specific:
dht22notdht(if multiple variants exist) - Include size for displays:
lcd-16x2not justlcd - Include model:
mcp3008not justadc
Device Names¶
- Include model number: "BME280" not "Bosch Sensor"
- Add key specs: "SSD1306 0.96\" OLED Display"
- Use manufacturer names: "Adafruit Motor HAT" if it's specific
Common Device Types¶
I2C Sensor Template¶
{
"id": "sensor-name",
"name": "Sensor Display Name",
"category": "sensor",
"description": "What it measures",
"pins": [
{"name": "VCC", "role": "3V3", "position": 0},
{"name": "GND", "role": "GND", "position": 1},
{"name": "SCL", "role": "I2C_SCL", "position": 2},
{"name": "SDA", "role": "I2C_SDA", "position": 3}
],
"protocols": ["I2C"],
"voltage": "3.3V",
"i2c_address": "0xXX",
"tags": ["sensor", "i2c"]
}
SPI Device Template¶
{
"id": "device-name",
"name": "Device Display Name",
"category": "display",
"pins": [
{"name": "VCC", "role": "3V3", "position": 0},
{"name": "GND", "role": "GND", "position": 1},
{"name": "SCK", "role": "SPI_SCLK", "position": 2},
{"name": "MOSI", "role": "SPI_MOSI", "position": 3},
{"name": "MISO", "role": "SPI_MISO", "position": 4},
{"name": "CS", "role": "SPI_CE0", "position": 5}
],
"protocols": ["SPI"],
"voltage": "3.3V",
"spi_max_speed": "10MHz",
"tags": ["display", "spi"]
}
GPIO Component Template¶
{
"id": "component-name",
"name": "Component Display Name",
"category": "component",
"pins": [
{"name": "VCC", "role": "3V3", "position": 0},
{"name": "GND", "role": "GND", "position": 1},
{"name": "SIG", "role": "GPIO", "position": 2}
],
"protocols": ["GPIO"],
"voltage": "3.3V",
"tags": ["component", "gpio"]
}
HAT Template¶
{
"id": "hat-name",
"name": "HAT Display Name",
"category": "hat",
"description": "What the HAT provides",
"pins": [
// HATs typically use multiple pins
{"name": "3V3", "role": "3V3", "position": 0},
{"name": "5V", "role": "5V", "position": 1},
{"name": "GND", "role": "GND", "position": 2},
{"name": "SDA", "role": "I2C_SDA", "position": 3},
{"name": "SCL", "role": "I2C_SCL", "position": 4}
// ... more pins as needed
],
"protocols": ["I2C", "GPIO"], // Multiple protocols common
"voltage": "3.3V-5V",
"tags": ["hat"]
}
Pin Role Guidelines¶
When to use GPIO vs specific roles:¶
- Use I2C_SDA/I2C_SCL: When device explicitly uses I2C protocol
- Use SPI_*: When device explicitly uses SPI protocol
- Use GPIO: For generic digital pins (buttons, LEDs, 1-Wire, DHT sensors)
- Use PWM: Only when device requires PWM-capable pins
- Use UART_TX/UART_RX: When device uses serial communication
Pin Position¶
Number pins from 0 in the order they appear on the device:
"pins": [
{"name": "VCC", "role": "3V3", "position": 0}, // First pin
{"name": "GND", "role": "GND", "position": 1}, // Second pin
{"name": "SDA", "role": "I2C_SDA", "position": 2}, // Third pin
{"name": "SCL", "role": "I2C_SCL", "position": 3} // Fourth pin
]
Testing Your Device¶
Test 1: Device Loads Successfully¶
from pinviz.mcp.device_manager import DeviceManager
dm = DeviceManager()
device = dm.get_device_by_id("your-device-id")
assert device is not None
print(f"✓ Device: {device.name}")
print(f" Category: {device.category}")
print(f" Protocols: {', '.join(device.protocols)}")
print(f" Pins: {len(device.pins)}")
Test 2: Fuzzy Matching Works¶
# Try different variations
variations = [
"your device id",
"yourdeviceid",
"Your Device",
"YOURDEVICE"
]
for query in variations:
device = dm.get_device_by_id(query)
if device:
print(f"✓ '{query}' matched '{device.id}'")
Test 3: Diagram Generation Works¶
# Via Claude Desktop:
"Connect your-device to raspberry pi"
# Should generate valid YAML with no errors
Test 4: Pin Roles Are Valid¶
from pinviz.model import PinRole
# This will raise ValueError if any role is invalid
for pin in device.pins:
PinRole(pin.role)
print("✓ All pin roles are valid")
Common Mistakes¶
❌ Invalid Pin Role¶
✅ Fix: Use valid PinRole enum value:
❌ Missing Required Fields¶
{
"id": "my-sensor",
"name": "My Sensor"
// Missing: category, description, pins, protocols, voltage
}
✅ Fix: Include all required fields
❌ Invalid I2C Address Format¶
✅ Fix: Use hex format with 0x prefix:
❌ Wrong Category¶
✅ Fix: Use correct category:
❌ Inconsistent Protocols and Pin Roles¶
✅ Fix: Make protocols match pin roles:
Tips for Quality Contributions¶
- Reference official datasheets: Link to manufacturer documentation
- Test with real hardware: Verify pinouts if possible
- Add comprehensive tags: Makes devices easier to discover
- Include usage notes: Special configuration, common issues
- Specify I2C addresses: Critical for bus sharing
- Indicate voltage ranges: Prevents damage to devices
- Note current draw: Helps users plan power requirements
Device Database Structure¶
devices/
├── database.json # Main database (25 devices)
├── user_devices.json # User-added devices
└── schema.json # JSON schema for validation
Priority order: 1. user_devices.json (highest priority, overrides main database) 2. database.json (default database)
Support¶
- Questions: https://github.com/nordstad/PinViz/discussions
- Bugs: https://github.com/nordstad/PinViz/issues
- Device requests: https://github.com/nordstad/PinViz/issues (use "device-request" label)
License¶
All contributed devices must have publicly available datasheets. Contributions are licensed under MIT (same as PinViz).