npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@am32/serial-msp

v0.1.0

Published

Standalone MSP codec and browser serial transport utilities for AM32.

Readme

@am32/serial-msp

MSP codec, parser, client, and browser serial transport utilities extracted from the AM32 configurator.

@am32/serial-msp is split into focused entrypoints so you can use MSP helpers on their own or combine them with the Web Serial transport layer.

Install

Install the package itself:

npm install @am32/serial-msp

Serial transport usage is built on top of webserial-wrapper, which is included as a package dependency.

Entrypoints

  • @am32/serial-msp - convenience root export for both MSP and serial APIs
  • @am32/serial-msp/msp - MSP commands, encoders, parser, and MspClient
  • @am32/serial-msp/serial - packet boundary probes and browser/Web Serial transport

Use the subpath imports when you want the clearest intent in application code:

import { MSP_COMMANDS, MspClient, encodeMspCommand, parseMspResponse } from '@am32/serial-msp/msp';
import { SerialTransport, inferPacketProbe } from '@am32/serial-msp/serial';

The root entrypoint is available as a convenience:

import { MSP_COMMANDS, MspClient, SerialTransport, inferPacketProbe } from '@am32/serial-msp';

What This Package Covers

  • MSP utilities are transport-agnostic and can be used independently of Web Serial
  • parseMspResponse() parses MSP v1 $M... packets only; it does not parse MSP v2 responses
  • SerialTransport is browser-focused and built around the Web Serial API
  • serial transport usage requires a webserial-wrapper WebSerial instance and a SerialPort

MSP Examples

Encode an MSP request

Use encodeMspCommand() when you want the package to encode MSP v1 for command IDs <= 254 and MSP v2 otherwise.

import { MSP_COMMANDS, encodeMspCommand } from '@am32/serial-msp/msp';

const request = encodeMspCommand(MSP_COMMANDS.MSP_API_VERSION);
const bytes = new Uint8Array(request);

// bytes: "$M<" + payload length + command + checksum

To force a specific protocol version, use encodeMspV1() or encodeMspV2() directly:

import { MSP_COMMANDS, encodeMspV1, encodeMspV2 } from '@am32/serial-msp/msp';

const v1Request = encodeMspV1(MSP_COMMANDS.MSP_MOTOR_CONFIG, new Uint8Array());

const dshotPayload = new Uint8Array([0x01, 0x00, 0x00, 0x00]);
const v2Request = encodeMspV2(MSP_COMMANDS.MSP2_SEND_DSHOT_COMMAND, dshotPayload);

Parse an MSP response buffer

parseMspResponse() consumes a Uint8Array. On successful parse it returns an object with the numeric command ID in the commandName field and a DataView over the payload; otherwise it returns undefined for incomplete or checksum-invalid input. It parses MSP v1 $M... packets only, not MSP v2 responses.

import { MSP_COMMANDS, parseMspResponse } from '@am32/serial-msp/msp';

const response = new Uint8Array([
    36, 77, 62,
    3,
    MSP_COMMANDS.MSP_API_VERSION,
    1, 44, 0,
    47
]);

const parsed = parseMspResponse(response);

if (parsed?.commandName === MSP_COMMANDS.MSP_API_VERSION) {
    const major = parsed.data.getUint8(0);
    const minor = parsed.data.getUint8(1);
    const patch = parsed.data.getUint8(2);

    console.log({ major, minor, patch });
}

The current implementation accepts $M<, $M>, and $M! marker bytes after the $M header.

Create an MspClient with an adapter

MspClient wraps request encoding plus response parsing. The adapter shape matches the AM32 configurator pattern and includes write(), read(), and canRead() methods. Only write() is needed for send() and sendWithPromise(), while read() and canRead() are used by MspClient.read().

import { MSP_COMMANDS, MspClient } from '@am32/serial-msp/msp';

const adapter = {
    async write(buffer: ArrayBuffer, timeout = 250): Promise<Uint8Array | null> {
        void timeout;

        // Replace with your own transport implementation.
        return new Uint8Array([
            36, 77, 62,
            3,
            MSP_COMMANDS.MSP_API_VERSION,
            1, 44, 0,
            47
        ]);
    },
    async read<T = Uint8Array>(): Promise<ReadableStreamReadResult<T>> {
        return {
            done: false,
            value: undefined
        };
    },
    canRead() {
        return false;
    }
};

const client = new MspClient(adapter, {
    log: console.log,
    logError: console.error
});

const parsed = await client.sendWithPromise(MSP_COMMANDS.MSP_API_VERSION);
const major = parsed.data.getUint8(0);

Web Serial Transport Examples

@am32/serial-msp/serial is intended for browser environments that expose Web Serial. It uses webserial-wrapper stream helpers internally.

Construct SerialTransport

SerialTransport needs a WebSerial instance, the selected SerialPort, and optional stream getters/setters if you want to reuse the same stream across exchanges.

import type { WebSerial } from 'webserial-wrapper';
import type { StreamInfo } from 'webserial-wrapper';
import { SerialTransport } from '@am32/serial-msp/serial';

declare const serial: WebSerial;
declare const port: SerialPort;

let stream: StreamInfo | null = null;

const transport = new SerialTransport({
    serial,
    port,
    getStream: () => stream,
    setStream: (nextStream) => {
        stream = nextStream;
    },
    logError: console.error
});

Call exchange() with inferPacketProbe

This matches the configurator's request/response flow: encode an MSP packet, infer the correct completion probe from the outgoing bytes, then await the combined response buffer.

import { MSP_COMMANDS, encodeMspCommand, parseMspResponse } from '@am32/serial-msp/msp';
import { SerialTransport, inferPacketProbe } from '@am32/serial-msp/serial';

declare const transport: SerialTransport;

const request = encodeMspCommand(MSP_COMMANDS.MSP_API_VERSION);
const requestBytes = new Uint8Array(request);

const response = await transport.exchange(request, {
    timeout: 250,
    probe: inferPacketProbe(requestBytes)
});

if (response) {
    const parsed = parseMspResponse(response);
    console.log(parsed?.commandName);
}

Use SerialTransport behind an MspClient

This is the direct composition used by the AM32 configurator: adapt SerialTransport.exchange() and SerialTransport.read() to the MspClient adapter interface.

import { MspClient } from '@am32/serial-msp/msp';
import { inferPacketProbe, SerialTransport } from '@am32/serial-msp/serial';

declare const transport: SerialTransport;

const client = new MspClient({
    write: (buffer, timeout) => {
        return transport.exchange(buffer, {
            timeout,
            probe: inferPacketProbe(new Uint8Array(buffer))
        });
    },
    read: <T = Uint8Array>() => transport.read<T>(),
    canRead: () => true
}, {
    log: console.log,
    logError: console.error
});

API Notes

  • encodeMspCommand() encodes MSP v1 for command IDs <= 254 and MSP v2 otherwise
  • parseMspResponse() returns undefined when the buffer is incomplete or checksum validation fails
  • inferPacketProbe() returns the MSP packet probe for $M< / $X< requests and falls back to the FourWay probe otherwise
  • SerialTransport.exchange() resolves with accumulated bytes on timeout, can resolve null when no data was accumulated, and may reject on transport, write, or cleanup errors

Browser Caveats

  • SerialTransport is for browser/Web Serial usage, not generic Node.js serial I/O
  • Web Serial requires a compatible browser and user-granted device access
  • SerialTransport is built on webserial-wrapper and requires a WebSerial instance plus a SerialPort
  • timeout handling relies on globalThis.setTimeout

Build

Build the package from the repository root:

yarn tsc -p packages/serial-msp/tsconfig.json

The build emits JavaScript, declarations, source maps, and declaration maps in packages/serial-msp/dist.

Development

For local package builds during development, run:

yarn tsc -p packages/serial-msp/tsconfig.json