SerialPilot

/02 — Reference

Parsers

Every parser is a Node Transform stream. You pipe a port into one, listen for data, and you get framed messages instead of a chaotic byte stream. Pick the parser that matches your protocol's framing strategy.

Pick a parser

If your protocol…Use
ends each message with a newlineReadlineParser
uses any other byte sequence as a separatorDelimiterParser
has fixed-size messagesByteLengthParser
splits on a regex (mixed line endings, e.g. \r?\n)RegexParser
length-prefixes each packetPacketLengthParser
relies on inter-byte silence (Modbus RTU)InterByteTimeoutParser
prints a banner before it's readyReadyParser
frames binary with SLIP escapesSlipEncoder/Decoder
brackets data with start/end markersStartEndParser
is ccTalk (coin/bill validators)CCTalkParser
is CCSDS Space PacketSpacePacketParser

ReadlineParser

Emits one string per line. The line-oriented sweet spot for most Arduino-class boards.

import { SerialPilot, ReadlineParser } from 'serialpilot'

const port = new SerialPilot({ path: ‘/dev/ttyUSB0’, baudRate: 9600 }) const parser = port.pipe(new ReadlineParser({ delimiter: ‘\r\n’ })) parser.on(‘data’, line => console.log(line))

OptionTypeDefaultNotes
delimiterstring | Buffer'\n'Line terminator.
encodingstring'utf8'Text encoding for emitted strings.
includeDelimiterbooleanfalseWhether emitted lines keep the delimiter.

DelimiterParser

Generic version of ReadlineParser — split on any byte sequence and emit Buffers (or strings if you set encoding downstream).

const parser = port.pipe(new DelimiterParser({
delimiter: Buffer.from([0xAA, 0x55]),
}))
OptionTypeNotes
delimiterstring | Buffer | number[]Required. Length ≥ 1.
includeDelimiterbooleanDefault false.

ByteLengthParser

Emits a Buffer every time a fixed number of bytes have been received.

const parser = port.pipe(new ByteLengthParser({ length: 8 }))
parser.on('data', buf => console.log(buf)) // 8 bytes each time

Throws if length is missing, zero, or negative.


RegexParser

Like ReadlineParser, but the splitter is a RegExp. Useful when devices send mixed line endings:

const parser = port.pipe(new RegexParser({ regex: /\r?\n/ }))
OptionTypeDefault
regexRegExprequired
encodingstring'utf8'

PacketLengthParser

For protocols that prefix each packet with its length. Reads the length field at a configured offset and waits for the full packet to land before emitting.

const parser = port.pipe(new PacketLengthParser({
delimiter: 0xa5,        // header byte
packetOverhead: 5,      // non-payload bytes
lengthBytes: 1,         // size of the length field
lengthOffset: 2,        // offset of the length field
maxLen: 0xff,
}))
OptionDefaultNotes
delimiter0xaaHeader byte that starts a packet.
packetOverhead2Bytes outside the payload.
lengthBytes1Size of the length field.
lengthOffset1Position of the length field.
maxLen0xffLargest valid packet — guards against runaway frames.

InterByteTimeoutParser

Emits buffered data when the line stays quiet for interval milliseconds. Modbus RTU's classic "3.5-character silence" framing fits here.

const parser = port.pipe(new InterByteTimeoutParser({ interval: 30 }))
OptionDefaultNotes
intervalrequiredMilliseconds of silence (≥ 1).
maxBufferSize65536Bytes buffered before forced emit.

ReadyParser

Buffers everything until a configured ready sequence arrives, then emits ready and forwards bytes from that point on. Most Arduino-class boards print a banner on reset; this parser tells you when they're done.

const parser = port.pipe(new ReadyParser({ delimiter: 'READY' }))
parser.on('ready', () => port.write('PING\n'))
parser.on('data', console.log) // data after the banner

SlipEncoder / SlipDecoder

Two complementary streams that implement RFC 1055 SLIP framing. Common in ESP-IDF tools and embedded MCUs.

const decoder = port.pipe(new SlipDecoder())
decoder.on('data', frame => console.log('frame:', frame))

const encoder = new SlipEncoder() encoder.pipe(port) encoder.write(Buffer.from([0x01, 0x02, 0xc0])) // 0xc0 escaped automatically

Both honour the standard SLIP byte values: END=0xC0, ESC=0xDB, ESC_END=0xDC, ESC_ESC=0xDD.


StartEndParser

Emits the bytes between a configured start delimiter and a configured end delimiter. Suits NMEA, custom binary frames, or anything that uses opening/closing markers.

const parser = port.pipe(new StartEndParser({
startDelimiter: Buffer.from([0xa5]),
endDelimiter: Buffer.from([0x5a]),
}))

CCTalkParser

Parses the ccTalk protocol used by coin acceptors and bill validators in vending and gaming hardware. Emits each fully-received frame as a Buffer.

const parser = port.pipe(new CCTalkParser())
parser.on('data', frame => console.log(frame))

SpacePacketParser

Parses CCSDS Space Packet framing — the international standard used by spacecraft telemetry/telecommand links. Emits objects with parsed primary header fields and the payload buffer.

const parser = port.pipe(new SpacePacketParser())
parser.on('data', packet => {
console.log(packet.header.apid, packet.data)
})

Header fields exposed: version, type, secondaryHeaderFlag, apid, sequenceFlags, sequenceCount, length.


Writing your own

A parser is just a Node Transform stream. Roll your own by extending stream.Transform and implementing _transform(chunk, _, cb) — most of the parsers above are under 100 lines of source.

Edit this page on GitHub