Skip to content

Lowcar Protocol

Vincent Le edited this page Oct 2, 2021 · 6 revisions

Table of Contents

1. Motivation & Overview

Runtime communicates with the lowcar devices over a serial connection; that is, through a stream of bytes. This means if we want to say, "update me with the state of the limit switch every millisecond," or, "set the motor to 50% speed backward," it has to be expressed through a series of bytes.

In a sense, dev handler and lowcar agree upon a language (the lowcar protocol) so they can understand each other when each entity says to the other a series of sounds (bytes). The goal of the lowcar protocol is to be able to say as much as possible with the fewest number of bytes to reduce the computing time spent on encoding and decoding messages.

In the rest of this page, we will refer to the entity sending a message as the speaker, and the entity receiving the message as the listener.

2. Message Types

Below is a chart of the types of messages and a brief description of its purpose. Each of these will be covered in more detail in later sections.

Type Speaker Listener Description
PING DEV_HANDLER LOWCAR Periodic status update
ACK LOWCAR DEV_HANDLER Lowcar verification; Device identification
DEVICE_WRITE DEV_HANDLER LOWCAR Write to specific device parameters
DEVICE_DATA LOWCAR DEV_HANDLER Data on readable paramters
LOG LOWCAR DEV_HANDLER Message string for human-readable logs

3. General Message Flow

  1. Dev handler notices a new device connected and sends a PING. If the device is a lowcar device, it will send an ACKNOWLEDGEMENT as a response.
  2. If dev handler does not receive an ACK, it does not proceed with communicating with the device. Otherwise, proceed with step 3.
  3. From now on, dev handler will send PING messages to the Lowcar approximately once per second. If the Lowcar device doesn't get a PING for a specified amount of time (about a few seconds), then we clean up. If dev handler doesn't receive any messages from Lowcar for a specified amount of time, we clean up.
  4. From now on, the lowcar devices will send DEVICE_DATA messages with all readable parameters at a hardcoded interval.
  5. From now on, dev handler will parse incoming DEVICE_DATA messages and update shared memory accordingly.
  6. From now on when shared memory is updated with a device command, dev handler will send DEVICE_WRITE messages to write to the appropriate parameters.
  7. From now on whenever dev handler receives LOG messages, it sends them to the runtime logger to be outputted to stdout and/or Dawn.

4. Anatomy of a Packet

Delimiter Cobs Length Cobs-encoded Message
Size One Byte One byte Cobs Length Bytes
  • Delimiter: A 0x00 byte indicating the start of a message The listener will be constantly reading the byte stream, and if it sees the delimiter, it knows that a message is coming in.
  • Cobs Length: The number of bytes in Cobs-encoded Message.
    The listener reads this byte after reading the delimiter, knowing to read exactly Cobs Length bytes to get the Cobs-encoded Message
  • Cobs-encoded Message: A message (see below) that has been cobs-encoded, which is an encoding scheme that eliminates all 0x00 bytes in the original message.
    The listener can cobs-decode this message to get the original message

4.1. Anatomy of a (Cobs-decoded) Message in a Packet

After reading the cobs-encoded message in a packet, the listener will cobs-decode it to get a message of this form:

Message Type Payload Length Payload Checksum
Size One Byte One Byte Payload Length Bytes One Byte
  • Message Type: A number indicating the type of message this is (see below for message types).
    The listener uses this to know how to interpret the payload.
  • Payload Length: Indicates how many bytes to read to get the payload.
  • Payload: The core of the message--it is the data being sent over. The payload's anatomy differs based on the receive Message Type (see below).
  • Checksum: A byte used to tell whether or not the message was corrupted while sent over serial.
    The speaker computes the checksum by bitwise XOR-ing all the bytes in Message Type, Payload Length, and Payload and appends it to the end of the message.
    The listener reads the speaker's recomputed checksum and computes the checksum himself. If the listener's checksum and the speaker's checksum differ, then the listener knows that the message is corrupted and should be dropped.

5. Message Types (Anatomy of a Payload)

There are 6 types of messages that are sent between the Raspberry Pi and the Arduino devices. The message type determines how to build/interpret the payload. For each message type below, we describe the purpose of each message and the anatomy of the corresponding payload.

5.1. Ping

A ping is a message with no payload. It is sent by the speaker to the listener to say "I'm still on"
Direction: Dev handler --> Lowcar
Payload Length: 0
Payload:

None
Size 0

5.2. Acknowledgement (ACK)

A one-time message sent by a lowcar device to dev handler, containing information uniquely identifying the device. It is sent by the lowcar device when it receives its very first PING. The device is "disabled" until it sends an ACK.
Direction: Lowcar --> Dev handler
Payload Length: 10 Bytes
Payload:

Device Type Device Year UID
Size One Byte One Byte 8 Bytes
  • Device Type: A number indicating what type of device the speaker is (ex: 0x00 for LimitSwitch)
  • Device Year: A number indicating what PiE year the lowcar device is from
    It is hardcoded into the lowcar device when it is flashed, and is used by the student to verify they have the most recent devices.
  • UID: Also hardcoded into the device when flashed. All devices should have a unique UID. The students use this directly in their code to refer to a specific device.

5.3. Device Data

The lowcar device continuously sends DEVICE_DATA messages. It contains the current values of all of its readable parameters.

Direction: Lowcar --> Dev handler
Payload Length: Variable
Payload:

Parameter Bitmap Values
Size 32 Bits Variable
  • Parameter Bitmap: It tells dev handler which (readable) parameters we are sending.
  • Values: A variable-length "chunk" of the payload with the actual values being read. Only the parameters turned "on" in parameter bitmap will be included in values, and vice-versa.

5.3.1. Device Data Example

Let's say a lowcar device has only two readable parameters with indices 0 and 2 of types INT and BOOL, respectively.

  • The parameter bitmap will be 0b0101(bits 4 through 31 are zeroes)
  • Let's say the respective values are 7 and True The values will be sizeof(INT) + sizeof(BOOL) bytes long, containing, 7 and 1 with no separator between each individual value.

5.4. Device Write

A message sent to the device to write to specified parameters (ex: writing 0.5 to duty_cycle to move motors forward at 50% speed). A DEVICE_WRITE message is capable of writing to all writeable parameters at once, and the payload length depends on how many parameters are being written to.

The structure of this payload is exactly like a DEVICE_DATA message, except this is used by dev handler to write to a lowcar device, while a DEVICE_DATA message is a lowcar device reading data to dev handler.

Direction: Dev handler --> lowcar
Payload Length: Variable
Payload:

Parameter Bitmap Values
Size 32 Bits Variable
  • Parameter Bitmap: It tells the lowcar device which (writable) parameters we will be writing to.
  • Values: A variable-length "chunk" of the payload with the actual values to write. Only the parameters turned "on" in parameter bitmap will be included in values, and vice-versa.

5.4.1. Device Write Example

Let's write to 3 parameters with indices 2, 5, and 7 which are types INT, FLOAT, and BOOL, respectively.

  • The parameter bitmap will be 0b1010 0100(bits 8 through 31 are zeroes)
  • Let's write 7, 3.14, and True to the respective parameters.
    The values will be sizeof(INT) + sizeof(FLOAT) + sizeof(BOOL) bytes long, containing, 7, 3.14, and 1 with no separator between each individual value.

5.5. Log

A message sent to dev handler (which will be sent to the runtime logger) from the lowcar device. It carries a string invoked by lowcar_printf() in the lowcar code.

This message is used as a reliable way to print messages from the lowcar code to debug.

Direction: Lowcar --> Dev handler
Payload Length: Variable
Payload:

Message String
Size Variable
Clone this wiki locally