Installation Guide

Step 1

Enter Bootloader

Hold BOOTSEL while plugging in USB on your Pico/Pico2.

Step 2

Mount Drive

Wait for RPI-RP2 drive to appear.

Step 3

Drag & Drop

Copy the .uf2 file to the drive.

Download Images

DeviceDetailsDownload
Pico (RP2040)Standard dual-core ARM Cortex-M0+
Pico 2 (RP2350)Dual-core RISC-V / ARM Cortex-M33
RP2350BHigher Pin Count RP2350
LogicWeave Core2 Channel PSU and Multimeter

Initialization

Installing libraries and connecting.

LogicWeave provides a unified communication layer to control GPIOs, peripherals, and board-level features through Python.

1. Install Library

Install the Python package via pip:

pip install logic-weave

2. Windows Driver Setup

Windows Users: If Python cannot find the device, you must install the WinUSB driver.

  1. Download Zadig.
  2. Plugin your device.
  3. Open Zadig and go to Options > List All Devices
  4. Select your device from the dropdown.
  5. Ensure the target driver (right side) is set to WinUSB
  6. Click Replace Driver or Install Driver

3. Connection

The library uses a context manager to handle the USB connection automatically.

from LogicWeave import LogicWeave

# Connects to the board (Auto-detects USB)
with LogicWeave() as lw:
    print("Connected to LogicWeave firmware!")
    
    # Read version info
    info = lw.read_firmware_info()
    print(f"Firmware Version: {info.version}")

Commands

Command Description
LogicWeave() Finds the USB device and starts the connection context.
read_firmware_info() Checks connection and gets the device version.

Plugin Development

Package your scripts into .whl files with custom UI elements.

LogicWeave allows you to compile your Python scripts into Wheels (.whl). This allows you to define custom inputs (sliders, text fields) and outputs for the Web IDE.

1. Project Structure

Your project folder should look like this. The package name (e.g., logicweave_test) must match your configuration.

my-project/
├── pyproject.toml       # Build configuration
└── logicweave_test/     # Source folder
    ├── __init__.py      # (Empty file)
    ├── main.py          # Logic code
    └── config.json      # UI definition

2. Configuration (pyproject.toml)

This file tells the builder how to package your code. Crucial: You must define the entry-point so the Web IDE knows which function to run.

[build-system]
requires = ["setuptools>=61.0"]
build-backend = "setuptools.build_meta"

[project]
name = "logicweave-test"
version = "0.1.0"
description = "A test wheel for the LogicWeave IDE"
requires-python = ">=3.7"
dependencies = []

[tool.setuptools]
packages = ["logicweave_test"]
include-package-data = true

# This ensures config.json is bundled with the code
[tool.setuptools.package-data]
logicweave_test = ["config.json"]

# ENTRY POINT: Maps 'app' to the 'run' function in main.py
[project.entry-points."logicweave.plugins"]
app = "logicweave_test.main:run"

3. UI Definition (config.json)

Define inputs (sent to your script) and outputs (displayed in the IDE).

  • usage "input": Creates UI controls (sliders, inputs) passed to your run() function.
  • usage "output": Creates display widgets updated via lw.log().
[
  {
    "key": "blink_speed",
    "label": "Blink Speed (s)",
    "type": "float",
    "default": 0.5,
    "step": 0.1,
    "max": 10,
    "min": 0.1,
    "usage": "input" 
  },
  {
    "key": "led_state",
    "label": "Live LED Status",
    "type": "text",
    "default": "OFF",
    "usage": "output"
  }
]

4. Logic (main.py)

Your script must accept a config dictionary. The function lw.log(key, value) is automatically injected by the runner to update the Web UI.

import time
from LogicWeave import LogicWeave

def run(config=None):
    # 1. Get Inputs from Web UI
    blink_speed = float(config.get("blink_speed", 0.5))
    loop_count  = int(config.get("loop_count", 5))

    print(f"▶️ Starting Run: {blink_speed}s interval")

    with LogicWeave() as lw:
        led = lw.gpio(25)
        led.set_function(7) 
        state = False

        for i in range(loop_count * 2):
            state = not state
            led.write(state)
            
            # 2. Send Data back to Web UI
            stateStr = 'ON' if state else 'OFF'
            lw.log("led_state", stateStr)
            
            time.sleep(blink_speed)

    print("✅ Script Finished.")

5. Build & Upload

Open your terminal in the project folder and run:

pip install build
python -m build

This will generate a .whl file in the dist/ folder. Upload this file to the LogicWeave Web IDE to run your plugin.

LogicWeave Core

Programmable PSU, Multimeter, and Power Delivery control.

The LogicWeave Core extends the base firmware capabilities with dedicated hardware for power generation and measurement. The LogicWeaveCore Python class inherits all standard GPIO/Protocol features while adding control for the onboard peripherals.

Initialization

To use the Core features, import the specialized class. It functions exactly like the standard LogicWeave context manager but with extra methods.

from LogicWeaveCore import LogicWeaveCore

# Connects to LogicWeave Core
with LogicWeaveCore() as core:
    print("Connected to Core!")

Programmable Power Supply (PSU)

The Core features two programmable channels capable of 1V to 17V output.

with LogicWeaveCore() as core:
    # 1. Configure Channel 1: 5.0V with 1.5A current limit
    # Channels: 1 or 2
    core.configure_psu(channel=1, voltage=5.0, current_limit=1.5)

    # 2. Enable the output
    core.set_psu_output(channel=1, state=True)
    
    # 3. Read back real-time metrics
    status = core.read_power_monitor()
    print(status)

Multimeter & Probes

Built-in voltage and resistance measurement capabilities.

with LogicWeaveCore() as lw:
    # Zero/Calibrate probes before low-resistance measurements
    lw.cal_probes()

    # Read Voltage
    volts = lw.read_voltage()
    print(f"Probe Voltage: {volts} V")

    # Read Resistance
    ohms = lw.read_resistance()
    print(f"Resistance: {ohms} Ohms")

Power Delivery (PD)

Monitor the status of the 100W PD Source.

with LogicWeaveCore() as lw:
    pd_status = lw.read_pd()
    print(f"PD Source Status: {pd_status}")

Reference (Core)

Method Arguments Description
read_voltage None Returns the voltage measured at the probes.
read_resistance None Returns the resistance measured at the probes.
configure_psu channel, voltage, current_limit Sets the target V and I for the specified channel (1 or 2).
set_psu_output channel, state Enables (True) or disables (False) the output for the channel.
read_power_monitor None Returns telemetry (Voltage/Current/Power) from the PSU rails.
read_pd None Returns status information from the PD controller.
cal_probes None Performs zero-offset calibration for the probes.
read_calibration_data None Dumps raw calibration data from the device.

Digital Input/Output (GPIO)

Control logic levels (HIGH/LOW) for LEDs, buttons, and switches.

Digital Output

from LogicWeave import LogicWeave, GpioFunction
import time

with LogicWeave() as lw:
    led = lw.gpio(25) 
    
    # 1. Set pin function to SIO_OUT (Simple I/O Output)
    led.set_function(GpioFunction.sio_out)
    
    # Blink loop
    for _ in range(3):
        led.write(True)   # HIGH (3.3V)
        time.sleep(0.5)
        led.write(False)  # LOW (0V)
        time.sleep(0.5)

Digital Input

from LogicWeave import LogicWeave, GpioFunction

with LogicWeave() as lw:
    button = lw.gpio(14)
    
    # 1. Set pin function to SIO_IN (Simple I/O Input)
    button.set_function(GpioFunction.sio_in)
    
    # 2. Set internal Pull Down resistor
    # state: 0=None, 1=Pull Down, 2=Pull Up
    button.set_pull(1) 
    
    state = button.read() # Returns True (HIGH) or False (LOW)
    print(f"Button state: {state}")

Reference (Digital I/O)

Method Usage Description
set_function(mode) pin.set_function(GpioFunction.sio_out) Sets the pin's dedicated peripheral function.
write(bool) led.write(True) Sets the pin High or Low. Requires function to be SIO_OUT first.
read() btn.read() Returns True if High. Requires function to be SIO_IN first.
set_pull(int) btn.set_pull(1) Sets internal resistor: 0=None, 1=Pull Down, 2=Pull Up.

Analog-to-Digital Conversion (ADC)

Measure analog voltage signals (0V to 3.3V) on ADC-capable pins.

from LogicWeave import LogicWeave

with LogicWeave() as lw:
    # Pin 26 typically maps to ADC0 on RP2040-based devices
    sensor = lw.gpio(26) 
    
    volts = sensor.read_adc() 
    print(f"Analog voltage: {volts:.2f}V")

Reference (ADC)

Method Usage Description
read_adc() v = pin.read_adc() Measures pin voltage (0V-~3.3V) and returns float.

Pulse Width Modulation (PWM)

Generate analog-like output signals for dimming, speed control, and tones.

from LogicWeave import LogicWeave
import time

with LogicWeave() as lw:
    pwm_pin = lw.gpio(15)
    
    # Configure PWM with a wrap (period) of 1000. 
    # This sets the resolution of the duty cycle (0 to 1000).
    pwm_pin.setup_pwm(wrap=1000)
    
    # Cycle the brightness from 0% to 100%
    for level in range(0, 1001, 100):
        # Set level to 500 for a 50% duty cycle (500/1000)
        pwm_pin.set_pwm_level(level) 
        time.sleep(0.1)

Reference (PWM)

Method Usage Description
setup_pwm(...) pin.setup_pwm(wrap=1000) Configures the PWM period (wrap) and frequency.
set_pwm_level(int) pin.set_pwm_level(500) Sets the PWM duty cycle level (0 to wrap).

I2C Protocol

Communicate with sensors, EEPROMs, and displays.

with LogicWeave() as lw:
    # Setup I2C controller 0
    # Pins: SDA=4, SCL=5
    i2c = lw.i2c(instance_num=0, sda_pin=4, scl_pin=5)
    
    # 1. Write: Send 2 bytes to device at address 0x27
    i2c.write(0x27, b'\x01\x02')
    
    # 2. Read: Request 2 bytes from device at address 0x27
    data = i2c.read(0x27, 2)
    print(f"Received: {data.hex()}")

    # 3. Write-Then-Read (Atomic transaction)
    # Useful for reading registers (Write register addr -> Read value)
    # Write 1 byte (0xA0), then immediately read 4 bytes
    reg_val = i2c.write_then_read(0x27, b'\xA0', 4)

Reference (I2C)

Method Usage Description
setup lw.i2c(instance, sda, scl) Returns an I2C object.
write i2c.write(addr, bytes) Writes byte data to the specified 7-bit addr.
read i2c.read(addr, length) Reads length bytes from the specified addr.
write_then_read i2c.write_then_read(addr, bytes, len) Writes bytes, sends a repeated start, and reads len bytes atomically.

SPI Protocol

High-speed synchronous communication.

with LogicWeave() as lw:
    # Setup SPI0 at 1MHz with a default Chip Select (CS) on pin 9
    spi = lw.spi(instance_num=0, sclk_pin=6, mosi_pin=7, miso_pin=8, 
                 baud_rate=1_000_000, default_cs_pin=9)
    
    # 1. Write bytes (uses default_cs_pin=9)
    spi.write(b'\xAA\x55')
    
    # 2. Read 2 bytes (sends 0x00 on MOSI while reading)
    rx = spi.read(2)
    
    # 3. Full Duplex / Custom CS
    # Send specific data while reading, using a different CS pin (10)
    rx_custom = spi.read(byte_count=4, cs_pin=10, data_to_send=0xFF)

Reference (SPI)

Method Usage Description
setup lw.spi(instance, sclk, mosi, miso, baud, default_cs) Returns an SPI object.
write spi.write(bytes, cs_pin=None) Writes data. If cs_pin is omitted, uses the default_cs.
read spi.read(len, cs_pin=None, data_to_send=0) Reads len bytes. Optionally sends data_to_send (int) on MOSI during the read.

UART Serial

Asynchronous serial communication.

with LogicWeave() as lw:
    # Setup UART0 at 9600 baud on TX=0, RX=1
    uart = lw.uart(instance_num=0, tx_pin=0, rx_pin=1, baud_rate=9600)

    # 1. Send text (must be encoded to bytes)
    uart.write(b'Hello World\n')

    # 2. Read specific number of bytes
    # Blocks until 64 bytes are received or timeout expires
    response = uart.read(64, timeout_ms=2000)
    print(response.decode('utf-8'))

Reference (UART)

Method Usage Description
setup lw.uart(instance, tx, rx, baud) Returns a UART object.
write uart.write(bytes, timeout_ms=1000) Sends bytes. Blocks if buffer is full until timeout.
read uart.read(len, timeout_ms=1000) Waits to receive len bytes. Returns partial data if timeout occurs.

Protobuf Interface

Low-level cross-language control.

🔌 Protobuf Interface

For cross-language or low-level applications, LogicWeave uses Protocol Buffers (Protobuf) to exchange commands and responses over USB. The entire client interface is built upon this protocol, allowing users to create custom controllers.


📦 Message Structure (Request/Response)

Communication is always a synchronous request-response over the USB endpoints.

Message Direction Purpose
RequestMessage Host $\to$ Device Sends a command (e.g., GPIOWriteRequest).
ResponseMessage Device $\to$ Host Sends a result or ErrorResponse.

The logicweave.proto file defines all possible payloads using a oneof field in the root RequestMessage and ResponseMessage.


⚡ USB Framing and Endpoints

The Protobuf binary data is transmitted over a dedicated USB interface using bulk transfers. A strict 64-byte framing protocol is enforced on both endpoints.

Framing Protocol

Each USB packet sent and received must be exactly 64 bytes.

  1. Byte 0 (Length): The first byte of the 64-byte buffer specifies the actual length (L) of the serialized Protobuf message (excluding the length byte itself).
  2. Bytes 1 to L: These bytes contain the serialized Protobuf message (RequestMessage or ResponseMessage).
  3. Bytes L+1 to 63: These bytes are padding (zero-filled) to ensure the total packet length is 64 bytes.

Note: The actual Protobuf payload must be less than 63 bytes to fit within a single 64-byte frame, as one byte is reserved for the length field.

Endpoint Details

Parameter Value Description
Vendor ID 0x2E8A LogicWeave's VENDOR ID.
Product ID 0x000A LogicWeave's PRODUCT ID.
OUT Endpoint 0x05 Host $\to$ Device (Sends framed RequestMessage).
IN Endpoint 0x84 Device $\to$ Host (Receives framed ResponseMessage).

🛠️ Custom Interface Development

To build a custom client, you must implement a USB transport layer that handles:

  1. Serialization/Framing: Prepending the message length byte and adding zero padding to ensure the final buffer is 64 bytes before writing to the OUT endpoint.
  2. De-framing/Deserialization: Reading 64 bytes from the IN endpoint, extracting the true Protobuf message length from Byte 0, and then deserializing the message from the subsequent bytes.