pico-otp-wasm
v0.1.0
Published
WASM bindings for pico-otp library - Generate OTP data for Raspberry Pi Pico 2 and RP2350 devices
Downloads
5
Maintainers
Readme
pico-otp-wasm
WebAssembly bindings for the pico-otp library, enabling browser-based generation and parsing of OTP (One-Time Programmable) data for Raspberry Pi Pico 2 and other RP2350-based devices.
Features
- Generate OTP data from JSON whitelabel configurations
- Parse full OTP dumps (4096 rows) from real devices
- Parse targeted whitelabel data without requiring complete OTP dumps
- Structured error handling with detailed error types and context
- Zero-copy where possible with efficient
Uint8Array/Uint16Array/Uint32Arrayusage - TypeScript definitions automatically generated
Building
# Make build script executable
chmod +x build.sh
# Build the WASM module
./build.shThis will:
- Compile the Rust code to WASM using
wasm-pack - Generate TypeScript definitions
- Create documentation in
docs/ - Prepare deployment files in
deploy/
Quick Start
Basic HTML/JavaScript Usage
<!DOCTYPE html>
<html>
<head>
<script type="module">
import init, { json_to_otp } from './pkg/pico_otp_wasm.js';
async function run() {
// Initialize the WASM module
await init();
// Create JSON configuration
const json = JSON.stringify({
device: {
vid: "0x1234",
pid: "0xabcd",
manufacturer: "My Company",
product: "My Product"
}
});
try {
// Generate OTP data
const otpData = json_to_otp(json);
console.log('USB Boot Flags:', otpData.usb_boot_flags);
console.log('Row count:', otpData.row_count);
console.log('ECC rows:', otpData.ecc_rows);
console.log('ECC bytes:', otpData.ecc_bytes);
} catch (e) {
console.error('Error:', e.message);
console.error('Type:', e.errorType);
}
}
run();
</script>
</head>
<body>
<h1>pico-otp WASM Demo</h1>
</body>
</html>Testing
Open index.html in a web browser (requires a local web server due to WASM CORS restrictions):
# Using Python
python3 -m http.server 8000
# Using Node.js http-server
npx http-server
# Then open http://localhost:8000API Reference
json_to_otp(json: string): OtpData
Generates OTP data from a JSON whitelabel configuration.
Parameters:
json- JSON string following the Raspberry Pi picotool whitelabel schema
Returns: OtpData object containing:
usb_boot_flags: number- USB boot flags value (u32)ecc_rows: Uint16Array- ECC-encoded whitelabel rowsecc_bytes: Uint8Array- Same data as little-endian bytesrow_count: number- Number of OTP rows required
Throws: Error with errorType property:
"Json"- Invalid JSON format"StringTooLong"- String field exceeds maximum (includesmaxLengthproperty)"InvalidWhiteLabelData"- Invalid configuration
Example:
const json = JSON.stringify({
device: {
vid: "0x1234",
pid: "0xabcd",
manufacturer: "ACME Corp 😀",
product: "Widget Pro 号",
serial_number: "SN12345 🚀"
}
});
try {
const otp = json_to_otp(json);
// Write to device:
// 1. Write otp.usb_boot_flags to rows 0x059, 0x05a, 0x05b (non-ECC)
// 2. Write 0x100 to row 0x05c (USB_WHITE_LABEL_ADDR, non-ECC)
// 3. Write otp.ecc_rows starting at row 0x100 (with ECC)
console.log(`Generated ${otp.row_count} rows`);
} catch (e) {
if (e.errorType === "StringTooLong") {
console.error(`String too long (max: ${e.maxLength})`);
}
}full_otp_to_json(non_ecc: Uint32Array, ecc: Uint16Array, strict: boolean): string
Parses a complete OTP dump (4096 rows) and extracts whitelabel configuration as JSON.
Parameters:
non_ecc- Uint32Array[4096] containing non-ECC OTP dataecc- Uint16Array[4096] containing ECC OTP datastrict- Enable strict validation (recommended:true)
Returns: JSON string containing the whitelabel configuration
Throws: Error with errorType property:
"TooFewRows"/"TooManyRows"- Array must be exactly 4096 elements"InvalidWhiteLabelAddress"- WHITE_LABEL_ADDR_VALID bit not set"NonMatchingUsbBootFlags"- USB_BOOT_FLAGS copies don't match (strict mode)"OtpDataError"- Inconsistency in OTP data (includesdetailsproperty)
Example:
// Read from device using picoflash or similar
const nonEccData = new Uint32Array(4096);
const eccData = new Uint16Array(4096);
// ... populate from device ...
try {
const json = full_otp_to_json(nonEccData, eccData, true);
const config = JSON.parse(json);
console.log('VID:', config.device.vid);
console.log('PID:', config.device.pid);
} catch (e) {
console.error(`Parse failed: ${e.message} (${e.errorType})`);
}whitelabel_otp_to_json(boot_flags: number, rows: Uint16Array, strict: boolean): string
Parses targeted whitelabel OTP data without requiring a full dump.
Parameters:
boot_flags- USB boot flags value (from rows 0x059-0x05b)rows- Uint16Array containing whitelabel ECC rowsstrict- Enable strict validation
Returns: JSON string containing the whitelabel configuration
Throws: Error with errorType property:
"TooFewRows"- Insufficient rows (includesrequiredRowsproperty)"OtpDataError"- Data inconsistency (includesdetailsproperty)"InvalidWhiteLabelData"- Invalid structure
Example:
const bootFlags = 0x00000001;
const whitelabelRows = new Uint16Array(256);
// ... populate from device rows 0x100+ ...
try {
const json = whitelabel_otp_to_json(bootFlags, whitelabelRows, true);
const config = JSON.parse(json);
console.log('Config:', config);
} catch (e) {
if (e.errorType === "TooFewRows") {
console.error(`Need ${e.requiredRows} rows, got ${whitelabelRows.length}`);
}
}Error Handling
All functions throw JavaScript Error objects with an additional errorType property for structured error handling:
try {
const otp = json_to_otp(invalidJson);
} catch (e) {
console.error('Message:', e.message);
console.error('Type:', e.errorType);
// Handle specific error types
switch (e.errorType) {
case 'StringTooLong':
console.error('Max length:', e.maxLength);
break;
case 'TooFewRows':
console.error('Required rows:', e.requiredRows);
break;
case 'InvalidWhiteLabelData':
case 'OtpDataError':
console.error('Details:', e.details);
break;
}
}Error Types
| errorType | Description | Additional Properties |
|-----------|-------------|----------------------|
| Json | Invalid JSON format | - |
| StringTooLong | String exceeds maximum length | maxLength: number |
| TooFewRows | Insufficient OTP rows | requiredRows: number |
| TooManyRows | Too many rows provided | providedRows: number |
| InvalidWhiteLabelAddress | WHITE_LABEL_ADDR invalid | - |
| InvalidWhiteLabelAddressValue | Address points to reserved row | address: number |
| NonMatchingUsbBootFlags | Boot flags copies don't match | - |
| OtpDataError | Inconsistency in OTP data | details: string |
| InvalidWhiteLabelData | Invalid whitelabel structure | details: string |
| InternalInconsistency | Library bug detected | details: string |
Integration with pico⚡flash
This WASM module is designed to integrate with pico⚡flash for direct device interaction:
// Read OTP from device
const nonEccData = await picoflash.readOtpNonEcc();
const eccData = await picoflash.readOtpEcc();
// Parse with pico-otp-wasm
const json = full_otp_to_json(nonEccData, eccData, true);
const config = JSON.parse(json);
// Display to user...
// Generate new configuration
const newJson = JSON.stringify(modifiedConfig);
const newOtp = json_to_otp(newJson);
// Write back to device
await picoflash.writeOtpNonEcc(0x059, [newOtp.usb_boot_flags]);
await picoflash.writeOtpNonEcc(0x05a, [newOtp.usb_boot_flags]);
await picoflash.writeOtpNonEcc(0x05b, [newOtp.usb_boot_flags]);
await picoflash.writeOtpNonEcc(0x05c, [0x100]);
await picoflash.writeOtpEcc(0x100, newOtp.ecc_rows);JSON Schema
The JSON format follows the Raspberry Pi picotool whitelabel schema. See the pico-otp documentation for the complete schema.
Example:
{
"device": {
"vid": "0x1234",
"pid": "0xabcd",
"manufacturer": "Company Name",
"product": "Product Name",
"serial_number": "SN123456",
"bcd_device": "0x0200"
},
"attributes": {
"power": "0xfa"
},
"scsi": {
"vendor": "VENDOR",
"product": "PRODUCT",
"version": "1.00"
},
"storage": {
"volume_label": "VOLUME"
},
"uf2": {
"model": "Model Name",
"board_id": "Board ID"
},
"redirect": {
"url": "https://example.com",
"name": "NAME"
}
}Development
Prerequisites
- Rust (latest stable)
- wasm-pack:
cargo install wasm-pack - Node.js (for typedoc)
Building
./build.shProject Structure
pico-otp-wasm/
├── src/
│ └── lib.rs # Main WASM bindings
├── pkg/ # Generated WASM output (after build)
├── docs/ # Generated TypeScript docs (after build)
├── deploy/ # Deployment-ready files (after build)
├── Cargo.toml # Rust dependencies
├── build.sh # Build script
├── index.html # Test interface
└── README.md # This fileLicense
Same as pico-otp (MIT OR Apache-2.0)
Author
Piers Finlayson (@piers_rocks)
