Skip to content

Replace flusbserial with PSLab-owned desktop serial transport #3368

Description

@mariobehling

The PSLab app currently depends on flusbserial for desktop USB serial communication. We should remove this dependency and replace it with a small FOSSASIA-maintained serial transport layer that is focused on PSLab hardware.

The goal is not to create a general-purpose USB serial library. PSLab only needs reliable communication with PSLab devices. Therefore the replacement should be scoped to the serial communication requirements of PSLab and should avoid unnecessary support for unrelated USB serial chipsets.

flusbserial states that it is based on/inspired by code from UsbSerial and quick_usb. To avoid dependency and provenance concerns, the new implementation should be developed independently and documented clearly.

Goal

Implement a PSLab-owned serial transport layer that allows the app to communicate with PSLab hardware on desktop platforms without depending on flusbserial.

This issue focuses on Option A:

  • Remove flusbserial from the PSLab desktop path.
  • Keep the existing Android USB serial implementation temporarily.
  • Introduce a clean PSLab serial abstraction.
  • Implement desktop communication through standard OS serial ports where possible.
  • Replace Android separately in a later issue.

Non-goals

This issue does not aim to:

  • Build a generic Flutter USB serial library.
  • Support every USB serial chipset.
  • Reimplement flusbserial.
  • Reuse/copy code from flusbserial, quick_usb, or UsbSerial.
  • Replace the existing Android serial implementation immediately.

Technical architecture

1. Add a PSLab serial abstraction

Create a small transport interface used by the PSLab app instead of calling flusbserial directly.

Suggested structure:

lib/
  communication/
    serial/
      pslab_serial_transport.dart
      pslab_serial_device.dart
      pslab_serial_port.dart
      implementations/
        desktop_serial_transport.dart
        android_serial_transport.dart

Suggested interface:

abstract class PslabSerialTransport {
  Future<List<PslabSerialDevice>> listDevices();

  Future<PslabSerialPort> openDevice(
    PslabSerialDevice device, {
    required int baudRate,
  });
}

abstract class PslabSerialPort {
  Stream<Uint8List> get input;

  Future<void> open();
  Future<int> write(Uint8List data);
  Future<void> close();

  bool get isOpen;
}

Optional methods can be added only if PSLab actually needs them:

Future<void> setDtr(bool value);
Future<void> setRts(bool value);
Future<void> flush();

The app should depend on this abstraction, not directly on a third-party USB serial package.

2. Keep Android path temporarily

For this issue, Android should continue using the existing Android USB serial implementation behind the new abstraction.

Implementation idea:

android_serial_transport.dart

This class should wrap the current Android serial behavior and expose it through PslabSerialTransport.

This allows us to remove flusbserial from the desktop path without breaking Android.

3. Implement desktop serial transport

For Linux, macOS and Windows, implement a PSLab desktop transport using standard serial ports instead of direct USB/libusb access.

Expected platform behavior:

  • Linux: detect /dev/ttyUSB*, /dev/ttyACM*, preferably /dev/serial/by-id/*
  • macOS: detect /dev/cu.* or /dev/tty.*
  • Windows: detect COMx ports

The PSLab device should be detected using available serial-port metadata where possible, for example:

  • USB vendor ID
  • USB product ID
  • serial number
  • device path/name containing CP210x / Silicon Labs / PSLab identifiers where available

If metadata is incomplete on one platform, fallback behavior may allow the user or app to select a candidate serial port.

4. Avoid direct libusb for the first implementation

The first implementation should not use direct libusb unless there is a strong reason.

Reason:

  • PSLab communication is serial communication.
  • CP210x devices can appear as standard virtual COM/TTY devices through normal OS drivers.
  • Standard serial ports reduce complexity.
  • This avoids Windows driver replacement issues that can happen with direct USB/libusb approaches.
  • It keeps the implementation small and PSLab-focused.

5. App integration

Update the app code so that PSLab communication goes through the new abstraction.

Required changes:

  • Identify all imports/usages of package:flusbserial.
  • Replace them with PslabSerialTransport.
  • Keep platform selection inside the transport layer, not in feature/instrument code.
  • Keep the instrument logic independent from the underlying serial implementation.

Example platform selection:

PslabSerialTransport createPslabSerialTransport() {
  if (Platform.isAndroid) {
    return AndroidSerialTransport();
  }

  if (Platform.isLinux || Platform.isMacOS || Platform.isWindows) {
    return DesktopSerialTransport();
  }

  throw UnsupportedError('Unsupported platform');
}

Implementation steps

Step 1: Audit current usage

  • Find all usages of flusbserial.

  • Identify which PSLab features depend on it.

  • Document the required operations:

    • list devices
    • open device
    • set baud rate
    • read bytes
    • write bytes
    • close connection
    • handle disconnect/reconnect

Step 2: Add transport interfaces

  • Add PslabSerialTransport.
  • Add PslabSerialPort.
  • Add PslabSerialDevice.
  • Do not change behavior yet.

Step 3: Add temporary adapters

  • Add an Android adapter around the current Android serial implementation.
  • Add a temporary desktop adapter if needed to keep the app working during migration.
  • Ensure instrument code uses only the new abstraction.

Step 4: Implement desktop transport

Implement desktop serial communication using standard serial ports.

Minimum required functionality:

  • List likely PSLab serial devices.
  • Open selected serial port.
  • Configure baud rate.
  • Write binary data.
  • Read binary data.
  • Close port.
  • Report connection errors clearly.

Step 5: Replace desktop flusbserial usage

  • Remove direct desktop usage of flusbserial.
  • Ensure no app code imports package:flusbserial.
  • Keep Android behavior unchanged for this issue.

Step 6: Validate with PSLab hardware

Test at least:

  • Device detection
  • Connect/disconnect
  • Firmware/version query
  • Basic command-response communication
  • Multimeter workflow
  • Oscilloscope/data acquisition workflow
  • Waveform generator workflow, if currently supported
  • Long-running read/write test
  • Reconnect after unplug/replug
  • App restart with device connected

Step 7: Remove dependency

After desktop communication works:

  • Remove flusbserial from pubspec.yaml.
  • Run dependency cleanup.
  • Confirm build works on supported platforms.
  • Add documentation for serial-port permissions and troubleshooting.

Acceptance criteria

  • pslab-app no longer depends on flusbserial.
  • No source file imports package:flusbserial.
  • PSLab desktop communication works on Linux, macOS and Windows through the new PSLab-owned serial transport.
  • Android continues to work through the existing Android implementation wrapped behind the new abstraction.
  • Core PSLab communication works with real PSLab hardware.
  • The implementation is PSLab-focused and does not attempt to become a general-purpose USB serial library.
  • Basic documentation is added for desktop serial-port setup and troubleshooting.

Suggested follow-up issue

After this issue is completed, create a separate issue:

Replace Android usb_serial dependency with PSLab-owned Android serial transport

That follow-up can implement a native Android CP210x/PSLab serial path using Android UsbManager, but it should not block removal of flusbserial from the desktop path.

Estimated effort

For an experienced Flutter/native developer familiar with serial communication:

  • Minimal PSLab-focused desktop replacement: 1–2 weeks
  • With review, hardware testing and cleanup: 2–4 weeks

This estimate assumes we keep the scope limited to PSLab communication and do not build a generic USB serial library.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions