KISS Protocol

I am starting to document an implementation of M17 over KISS. This entails using a KISS TNC (modem) for M17 baseband modulation and demodulation.

This is very much a work in progress at the moment, and major design decisions are still to be worked out.

Please refer to the KISS protocol documentation here:

We will discuss both audio streaming mode and packet mode.

There are a number of design trade-offs. KISS is an acronym for “Keep it simple, stupid.” And it’s hallmark is that very little protocol work is actually done in the KISS TNC. Only layer 1 (physical layer, baseband modulation) and layer 2 (HDLC framing and CSMA) is typically done in a KISS TNC. The packet, typically an AX.25 packet, is sent over a SLIP-encoded channel with a 1-byte header. The lower nibble of the header defines the KISS data frame type. A data frame is represented by frame type 0. There are 5 control frames defined.

The upper nibble of the header byte defines the modem identifier for systems with multiple modems. The default is 0. We can consider different M17 interfaces as distinct modems in a KISS TNC implementation.

M17 is a bit more complex than AX.25 over AFSK or FSK using HDLC framing. M17 defines 3 frame types (link setup, stream, packet), and the stream frame contains two data channels, the LICH and the payload (possibly 3 channels if you consider V/D mode). The data link layer requires both a source and destination identifier. The first issue we run into is that KISS modems handle the physical and data link layer, and there is no protocol defined for setting things like source, destination or, should it be used, encryption nonce.

We are going to define three (3) modem types. Two for packet and one for streaming. We are also going to define extended KISS commands for setting source, destination and nonce.


In order to provide the most backwards-compatible implementation, packet mode using TNC identifier 0 will send packets with the source identifier set to a device type identifier, similar to the APRS TOCALL identifier. The destination address is “M17PACKET”. Packet type 0, RAW, will be used for the packet payload. The TNC will be responsible for constructing the packet frame, attaching the type field and computing the CRC. It is also responsible for unwrapping received packet frames, dropping packets with invalid CRCs and passing received raw data back to the host, discarding the packet type identifier and CRC.

The goal of this mode is that all applications that currently function with KISS TNCs can use M17 as the physical and link layer. Station identification must be done inside the payload, such as with an AX.25 MYCALL identifier.


For M17 modems, modem identifier 1 is the full-featured M17 packet interface. It expect to receive a single frame of data containing a 30-byte link setup frame followed immediately by a 3-800-byte payload. The payload includes the type field and CRC. It returns the same data to the host when packet data is received.

[color=#333333][size=small][font=Tahoma, Verdana, Arial, sans-serif]
Modem identifier 2 is the M17 stream interface. It expects to receive a single 30-byte LSF frame, followed by a series of 20-byte data frames. The last frame in the sequence must have the end-of-stream bit set.[/font][/size][/color]

Received stream packets are sent to the host in the same manner. The TNC must provide a packet every 40ms. The host must be able to handle missing/dropped packets or streams that end without an EOS bit set, possibly due to loss of signal mid-stream. [This needs to be more fully fleshed out.]

The LSF is 30 bytes.
Stream data is 20 bytes (FN, payload, CRC).

For host to TNC:

In streaming mode, the TNC receives a single 30-byte LSF payload.
It verifies that the CRC is correct and that the packet/stream bit indicates “STREAM”.
It starts transmitting the preamble, then LSF.
The host sends 20-byte data frames containing Codec2 audio payload, frame number and CRC.
The TNC checks the FN for the end-of-stream indicator, and optionally checking the CRC.
The TNC will have a backlog of up to 2 frames of audio data, collected while sending the preamble and LSF.
The TNC can insert up to 2 frames with empty payload data (and proper CRC) if the stream is interrupted. (What to do with FN?)
The TNC will end the stream if 3 or more frames have been missed. It will end the stream by sending an empty payload with the EOS bit set.


For packet mode, the TNC will receive a 33-830 byte packet containing LSF and packet payload.
It will send the preamble, LSF and split the packet into frames.

For TNC to host:

For packet mode, the TNC will capture the LSF and payload.
It will verify the CRC on both the LSF and the payload.
By default, the full-featured M17 packet modems (id 2) should operate in PASSALL mode, allowing the host to drop packets with bad checksums.

In the TNC-220 days, “connection” to the TNC was via a Serial port.
Today we have USB and TCP/IP as additional connection methods.

Which connection type is proposed here?

[color=#333333][size=small][font=Tahoma, Verdana, Arial, sans-serif]There is no need to specify the KISS transport layer in the M17 spec. It would serve no purpose. The only requirement is the host and TNC meet the data rate and latency/jitter requirements. [/font][/size][/color]

KISS uses SLIP – Serial Line Internet Protocol under the hood. It is designed to tunnel data through a serial line interface.

Since there are Serial protocols for USB (CDC), Bluetooth (SPP), and a KISS protocol for Bluetooth LE, all are viable. The hardware I am developing this on (Mobilinkd TNC3) uses USB, Bluetooth and Bluetooth LE. Standard RS-232 should work. As should those 8266-wifi/serial modules.

PR for the KISS spec has been submitted. Critical review is welcome.

Streaming and packet modes over KISS seem to be working well. Packet mode is fairly easy to implement as all the data for the sequence of frames needed for packet mode are in the TNC when the transmission starts.

Streaming mode, however, presents some challenges. Streaming mode expects a real-time stream of data to arrive, each packet arriving at a 40ms cadence.

There is a very tight timing requirement for streaming. This is easy to meet with USB serial interfaces such as the CDC interface used by the TNC3. This is harder to do with wireless interfaces such as Bluetooth and BLE. In my experience the latency and jitter between Android and the TNC3 over SPP is rather high and even with large (320ms) buffers on the TNC, the jitter is such that there are occasional drops. I found that SPP/RFCOMM jitter and latency to be too high to be usable for streaming.

I have managed, with a little tuning, to get BLE working reliably. This requires setting the LE Connection interval and slave latency as described here:

With these settings, the jitter is fairly low and it is possible to transmit fairly reliably with little buffering (80-120ms, 2-3 frames).

To address the occasional empty time-slot while transmitting voice in streaming mode due to timing jitter, I have found that inserting Link Setup Frames in the missed packet’s time-slot seems to work to keep the stream active. In the TNC3, the LSF is saved to the side. This can then be re-sent when a frame does not arrive within the time slot. The TNC3 allows up to 5 consecutive missed packets before dropping the stream completely. It will cease streaming until a new LSF is received over the KISS channel.

I am considering documenting this in the spec as permitted behavior for the transmitter.

It should be noted that when doing this, it is critical that the packet counter not be changed in any way. That is, the source of the stream is sending the numbered frames: L,1,2,3,4,5,… If the KISS device needs to insert a LSF because frame 4 arrived late, the frame numbers would look like L,1,2,3,L,4,5… with exactly that frame numbering. Any attempt to renumber the frames would break AES-CTR encryption.

It is possible that by tuning Bluetooth connection parameters, SPP/RFCOMM could be made reliable.