Setting up the connection

BLE capabilities of the hub

The LEGO® SPIKE™ Prime Hub exposes a BLE GATT service containing two characteristics: one for receiving data (RX), and one for transmitting data (TX).

The table below shows the UUIDs for the service and characteristics.

Item

UUID

Service

0000FD02-0000-1000-8000-00805F9B34FB

RX

0000FD02-0001-1000-8000-00805F9B34FB

TX

0000FD02-0002-1000-8000-00805F9B34FB

Note

“Receiving” and “transmitting” are from the perspective of the hub.

The hub includes the service UUID in the advertisement data, so that it can be used to filter scan results.

To send data to the hub, perform a write-without-response operation on the hub’s RX characteristic.

Any data from the hub will be delivered as a notification on the TX characteristic.

Hint

Make sure to enable notifications on the TX characteristic.

Handshake and negotiation

Upon connecting, the client should always initiate communication by sending an InfoRequest to the hub. The hub will respond with an InfoResponse, detailing the capabilities of the hub.

Of particular interest are the maximum sizes for packets and chunks:

Max. packet size:

The largest amount of data that can be written to the RX characteristic in a single operation.

Max. chunk size:

The maximum number of bytes allowed in the payload of a TransferChunkRequest.

The examples below show how these limits may be applied in Python:

Honoring the maximum packet size
async def send_message(message: BaseMessage) -> None:
    print(f"Sending: {message}")
    payload = message.serialize()
    frame = cobs.pack(payload)

    # use the max_packet_size from the info response if available
    # otherwise, assume the frame is small enough to send in one packet
    packet_size = info_response.max_packet_size if info_response else len(frame)

    # send the frame in packets of packet_size
    for i in range(0, len(frame), packet_size):
        packet = frame[i : i + packet_size]
        await client.write_gatt_char(rx_char, packet, response=False)
Using the maximum chunk size
running_crc = 0
for i in range(0, len(EXAMPLE_PROGRAM), info_response.max_chunk_size):
    chunk = EXAMPLE_PROGRAM[i : i + info_response.max_chunk_size]
    running_crc = crc(chunk, running_crc)
    chunk_response = await send_request(
        TransferChunkRequest(running_crc, chunk), TransferChunkResponse
    )