Download as PDF
- Document History
- Version Information
- Protocol Overview
- Physical Layer Specifications
- Control Layer
- 29/05/2019: Initial Release
This specification document details the implementation of JACDAC version 0 (JACDAC v0). JACDAC v0 uses the CRC polynomial
JACDAC uses the built-in UART module common to most MCUs as its communication mechanism, but instead of separate wires for transmission and reception, JACDAC uses just one wire for both. Four baud rates are supported: 1Mbaud, 500Kbaud, 250Kbaud, 125Kbaud, allowing cheaper MCUs to be used.
Microcontrollers that run the JACDAC protocol are known as JACDAC devices. They communicate JACDAC Packets to each other across a shared bus in a bus topology. Devices signal a packet transmission by driving the bus lo for a period of time known as the lo pulse. The length of the lo pulse dictates the baud rate of the upcoming UART transmission of a JACDAC packet.
Conventionally in bus topologies there is the concept of a Master and Slave (widely regarded as outdated terminology; we use Host and Client instead). JACDAC devices are all Hosts on the bus and run Services that others devices can act as Clients to. Services allow other devices to configure and use resources that they would normally not have access to, examples include: an AccelerometerService, a DisplayService, or a NeopixelService. Other devices interact with a hosted service as part of a microcontrollers’ application.
A device can enumerate one or more Services on the bus using a ControlPacket, which is a JACDAC Packet that contains: a 64-bit unique device identifier, the allocated device address, and an array of ServiceInformation which details the Services available for use by the bus. Enumeration is only required if operating a Service for others to consume; unenumerated devices are free to use enumerated services without enumerating themselves. The presence or absence of device ControlPackets indicates whether a device has been connected or removed from the bus.
Each JACDAC device has a simple stack featuring: (1) a physical layer handling the transmission and reception of packets; (2) a control layer which performs the routing of packets; to (3) services running on the device.
Physical Layer Specifications
This section describes the hardware requirements, packet format, and the logic line level transmission process. Protocol timings are generally described in terms of bytes: a byte is 10 bits, as 1 UART start and stop bit are included in the total size.
To operate on the JACDAC bus, an MCU must be capable of:
- Communicating / receiving UART-style bytes using a single wire. (10 bits: 1 byte, 1 stop bit, 1 start bit).
- Reaching one of four baud rates: 1Mbaud, 500Kbaud, 250Kbaud, 125Kbaud. Devices must also be able to receive at their highest transmission baud rate as well as be capable of receiving at any of the lesser baud rates. i.e. if a device transmits at 500Kbaud, it must be capable of receiving packets at 500, 250, and 125 kbaud.
- A GPIO with PullUp capabilities and interrupts. It’s far easier if the pin used for UART tx/rx can also generate GPIO interrupts(especially in CODAL).
- The ability to keep time (whether through instruction counting or a hardware timer).
- The ability to generate random numbers (or at least seed a software random number generator).
A JACDAC device should feature one JACDAC capable microcontroller that is connected to zero or more sensors over an onboard communication protocol such as I2C or SPI. The JACDAC microcontroller should also have the capability to connect to the JACDAC bus to communicate with other JACDAC devices.
JACDAC Packet Format
The table below specifies the packet structure of JACDAC packets transmitted on the bus. Bytes are sent in little endian format.
|Field Size (bits)||Name||Description|
|12||CRC||The CRC field calculated using the polynomial
|4||service_number||A number that identifies a service on a device. Discussed further in the Control Layer section.|
|8||device_address||A number that identifies a device on the bus.|
|8||size||The size of the data field. Values range from 0-255.|
|8 * size||data||An array of bytes, whose size is dictated by the size field above.|
The packet structure is divided into two parts:
header: the crc, service_number, device_address, size, and version fields before the data field.
data: the data field onwards.
A frame refers to a packet that is sent on the bus and includes the bus arbitration process.
Transmission & Reception
When the JACDAC bus is in idle state, all MCUs on the bus must configure their TX/RX pin to be an input with a pull up to 3.3v. In this state, the bus will read high.
When an MCU wants to transmit a packet, it must drive the bus low for 10 bits (1 byte) at the desired baud rate and wait for a minimum of 6 bytes at 1Mbaud before transmitting data. This is known as the lo pulse, the duration of this pulse dictates the baud rate of the upcoming UART transmission:
|Pulse Duration (us)||UART Baud Rate (KBaud)|
When an MCU detects the beginning of a transmission (a low pulse), it has a minimum of 60 microseconds (6 bytes at 1Mbaud) to configure any hardware registers and software buffers to receive a JACDAC packet header. After receiving the header, an MCU should either receive the remainder or ignore a packet. If no data is received after 160 microseconds (2 bytes at the 125Kbaud), the receiving devices should enter an error recovery state (described later on in this document).
A JACDAC packet that is transmitted on the wire is known as a frame and includes the bus arbitration period as well as the packet itself.
Devices that communicate at baud rates faster than 125Kbaud must also be capable of communicating at all slower baud rates e.g. a device that communicates at 1Mbaud must also be able to communicate at 500, 250, and 125 Kbaud.
It should be noted that despite supporting lower baud rates, developers must achieve the maximum baud rate possible with their chosen MCU. This is for reasons of bus efficiency, as the presence of a large number of slower devices reduces the throughput of the bus.
The process described is visualised in the image below: the bus is high for a period of time, driven low for 10 microseconds (10 bits at 1Mbaud), data following 40 microseconds later.
Physical Layer Timings
This section specifies expected and maximum timings at the physical layer. If any of the following timings are violated, devices must enter an error state and resume listening for frames once the bus idle period has been detected
Bus Idle Spacing
If a device chooses to ignore a packet or an error condition is detected when receiving a packet, a device needs to determine when the bus has entered an idle state.
An idle bus is defined as no activity (line hi) for 2 bytes at 125kbaud (160 microseconds).
The minimum time before data can be sent after a lo pulse is 60 microseconds, and the maximum gap before data begins is 160 microseconds (two bytes at the lowest baud); times are relative from the end of the lo pulse. All devices must enter an error state if a transmitting device exceeds this time.
The maximum permitted time between bytes is two bytes at the minimum baud rate (125KBaud). A transmitting device must never near the maximum interbyte spacing. All devices must enter an error state if a transmitting device exceeds this time.
The minimum space between frames is two bytes at the minimum baud rate (125KBaud). JACDAC devices should capture the time after receiving the last byte of a packet and observe the minimum interframe spacing. To prevent transmission of a frame at the same time as another device, devices must implement a random backoff for transmission.
Error Detection and Recovery
If any of the protocol timings are violated a device must enter an error state and wait until the bus is idle for the bus idle period.
To detect the idle period, a device must capture the time from when the bus last transitioned from lo to hi, resetting this time if the bus transitions again.
A state diagram for error detection when receiving a packet is shown below:
Preventing Bus Collisions
Bus arbitration is performed through pulsing the bus low for 10 bits at the desired baud rate. However, a device could disable GPIO interrupts and initiate the process of transmission by driving the bus low whilst another device is doing the same:
The diagram above exemplifies the race condition described previously showing two overlapping lo pulses and communications.
To prevent the race condition, JACDAC devices must check the bus state before beginning the lo pulse:
If the bus state is lo when performing this check, devices must enter an error state (as the lo pulse may be improperly measured) and wait for the bus to return to idle.
If two devices begin the lo pulse at exactly the same time, the UART module on the transmitting MCU will detect an error (most likely a framing error), and the received crc will be incorrect.
This section discusses the control layer and specifies: the purpose and implementation Control Packets, device address assignment, and the routing of packets to Services.
Each device must have a Control Layer responsible for sending a ControlPacket every 500 ms. A ControlPacket contains information about a device including: a unique device identifier (udid), device address, and available Services for use by the bus.
Control Packet Format
A ControlPacket has multiple purposes:
- To facilitate the allocation of device addresses on the bus.
- To reduce the overhead of standard JACDAC packets by providing meta data and addressing information for the routing of packets to services.
- To allow JACDAC devices to determine if a device has been connected or removed from the bus.
ControlPackets are embedded in the content of a standard JACDAC packet which has the
service_number fields set to zero.
The address zero must never be used as the
device_address by any device, and should be thought of as a reserved broadcast address for Control operations.
A ControlPacket has the following structure:
|Field Size (bits)||Name||Description|
|64||udid||The unique device identifier (udid) for the device.|
|8||device_address||The address allocated to the device that occupies the address field of a JACDAC packet. If the address field is set to 255, then this packet was transmitted by a transmit only device. In this case, address collision handling can be skipped. See the transmit only devices section for more detail.|
|8||device_flags||A field for the ControlService indicating the state of a device.|
|data * 8||device_name (optional)||An optional device name. The presence of the device_name field is indicated by the
|N * ServiceInformation||data||The data field is filled with an array of ServiceInformation structs advertising Services operating on the device for use by the bus.|
The possible values of the
device_flags field values are defined as follows:
|Bit Mask (hex)||Name||Description|
|0x01||REJECT||This bit indicates there is an address collision between devices.|
|0x02||PROPOSING||This bit indicates that the device is in the proposal phase of address allocation.|
|0x04||HAS_NAME||This bit indicates that a name is present before the ServiceInformation array in the data field.|
|0x08||NACK||This bit can be used to indicate a negative acknowledgement of a ControlPacket|
|0x10||RESERVED||Reserved for future emerging use cases|
|0x20||RESERVED||Reserved for future emerging use cases|
|0x40||RESERVED||Reserved for future emerging use cases|
|0x80||ERROR||Indicates a device with an irrecoverable error; the error_code for such an error consumes the remaining 7 bits. In an error condition the data field should contain an error message with more information, the size of which is dictated by JDPacket->size - 10 (size of the ControlPacket header) field.|
The data field of a ControlPacket contains one or more ServiceInformation structs which have the following structure:
|Field Size (bits)||Name||Description|
|32||service_class||This field identifies a specific service implementation. The allocation of these numbers is specified in the service class allocation section.|
|8||service_flags||Flags for the service, optionally populated by a Service. If the top bit (bit 7) is set, the device has an irrecoverable hardware error; the error_code for such an error consumes the remaining 7 bits. In an error condition the advertisement_data field should contain an error message with more information, the size of which is dictated by the advertisement_size field.|
|8||advertisement_size||A field that indicates the size of the upcoming advertisement_data field. The developer is responsible for ensuring that all services on a device are able to add advertisement_data if required.|
|8 * advertisement_size||advertisement_data (optional)||Optional advertisement data indicating additional runtime properties of the service.|
Generating Unique Device Identifiers (UDID)
JACDAC uses the IETF EUI-64 specification for 64-bit unique device identifier generation.
There are two types of address that can be used:
1) Globally unique addresses 2) Locally unique addresses
Commonly microcontrollers ship with 64-bit serial numbers (or greater) flashed at the factory. In this case, the transformation to a JACDAC 64-bit UDID is simple: take or generate a 64-bit serial number and set the Universal/Local bit to 0 (bit 0x0200000000000000, 7th bit from the MSB)
There may be the allocation of globally unique device identifiers in the future, but for now every JACDAC device should be capable of producing a unique device identifier using the above algorithm given a factory flashed serial number or a random number.
The data field of a ControlPacket should be parsed as follows:
0) Is the
ERROR bit set in the
device_flags field? If so handle the error message and go to 4.
1) Inspect the size of the JACDAC Packet, and subtract the size of a ControlPacket header (10 bytes), the remainder will be the size of the data field called
data_field_size. Create a variable called
offset set to zero
data_field_size? If yes go to 4.
3) Iterate over the
- Cast the
ControlPacket->data + offsetto a
- Determine if the
ServiceInformationmatches services running on the device.
- Read the value of the
advertisement_sizefield and extract advertisement data (if required).
advertisement_size? If no go to 2.
Device Address Allocation
When a device is first connected to the bus, it must obtain an address to use. The process to obtain an address is known as enumeration and a device is said to be enumerated when it has a confirmed address. Enumeration uses ControlPackets to obtain an address, and in normal operation devices must emit ControlPackets every 500 ms. Other devices must only use services that are offered by a device once it is enumerated.
When enumerating, devices must propose an address to use by setting the
device_address and the
PROPOSAL flag in its ControlPackets. If an enumerated device on the bus is already using the proposed address, the enumerated device must return the same ControlPacket with the
REJECT flag set. If a proposing device receives the previously sent ControlPacket with the
REJECT flag set, it must pick a new address and begin the proposal phase again. Similarly, if the proposing device receives a ControlPacket from another device using the proposed address, it must pick a new address and begin the proposal phase again (this may be the case if the proposing device communicates at an incompatible baud rate for another MCU). If two devices propose to use the same address, the address collision rules must be observed.
Some devices may maintain additional state of addresses used on the bus and can return a different address in the device_address field in the returned ControlPacket.
After two ControlPackets without rejection, a proposing device is considered bound to that address. A bound address is indicated by the absence of the
Addresses 0 and 255 are reserved and must not be allocated to any devices on the bus. Address 0 is exclusively used for control data, and address 255 is used for transmit only devices.
Device Address Collisions
Address collisions are identified by a device receiving a ControlPacket containing its
device_address and a different
udid. It is likely that there will be address collisions if two large, established buses are joined together.
On the occurrence of an address collision, the device that detected the colliding ControlPacket must begin the enumeration process again to establish a new address. In other words, the first device to communicate a ControlPacket when two buses are joined remains bound to that address.
If a ControlPacket is not seen from a device for 1 second (2 ControlPackets), the device is considered removed.
On the reception of a JACDAC packet a packet must be routed to any corresponding services matching the JACDAC packet.
A JACDAC packet contains a
service_number field, which when combined identify a service running on a device. Previously received ControlPackets contain the necessary metadata to map a JACDAC packet to a corresponding device and service. If a JACDAC device requires a packet to be routed to a service it must maintain the relevant state contained in a control packet.
device_address can be directly obtained from the
device_address field of a ControlPacket. However, the
service_number must be calculated by determining the position of the relevant ServiceInformation in the array contained in the data payload of a ControlPacket:
ControlPacket: device_address: 3 unique_device_id: 1235464738 flags: 0 data: Service: service_class: Button flags: 0 status: 0 advertisement_size: 0 Service: service_class: Accelerometer flags: 0 status: 0 advertisement_size: 0 Service: service_class: Servo flags: 0 status: 0 advertisement_size: 0
A JACDAC packet addressed to the Accelerometer service using the ControlPacket above would look as follows:
JACDAC Packet: crc: XXXX service_number: 1 address: 3 size: 6 data: x: 999 y: 20 z: 500
By maintaining a small amount of state, minimal metadata is placed in a JACDAC packet allowing more space for service data.
Transmit Only Devices
Low cost, powerful, 4/8-bit microcontrollers are not going away anytime soon. The JACDAC protocol enables such microcontrollers to easily integrate into the ecosystem with only a small amount of control logic and no requirement for receiving packets.
Transmit only devices must transmit control packets every 500 ms, but they do not need to detect addressing collisions or negotiate addresses with other devices on the bus. All transmit only devices share the
Transmit only devices must be able to perform bus arbitration and error recovery.
For transmit only devices, control packets retain the same structure and addressing. To indicate that the device is transmit only, the
device_address field in the control packet must be set to 255.
The computation of the service number for Services remains the same as in conventional JACDAC operation.
As transmit only devices cannot receive, they must operate in a separate address space for the communication of normal data packets. All devices share the same address 255, and this address can be thought of as a broadcast channel.
JACDAC packets sent using address 255 must contain the UUID of the transmitting device as the first 8 bytes of the data payload so that the Control Layer can correctly route packets to corresponding services.
Packets emitted from services on a transmit only device should use the computed
service_number as per normal JACDAC operation.
When is enumeration required?
Device enumeration is only required when a device is running a Service for others on the bus. If a device seeks to only consume / control a Service of another device, device enumeration is not required.
Services are advertised by JACDAC devices on the bus and build on the Control Layer. They expose APIs for programmers to access, actuate, and network with other devices on the bus.
The ServiceInformation structure inside ControlPackets contain information about services running on the device, each containing:
- The service class – a 32-bit number that identifies a specific service implementation.
- Any service flags – Flags indicating the current state of the service. The bits 0 - 6 are service specific and are left to be defined by the service creator.
- Optional service advertisement data – Each service can place an optional application payload in ControlPackets for other services to consume.
For more detail on the ServiceInformation struct see the ControlPacket Format section.
At runtime, Services send JACDAC packets placing the
device_address allocated by the ControlLayer into each packet. The combination of these fields can be conceptually be thought of as either source or destination addresses depending on the context: If a service emits a packet, it is a source address. If another device emits a packet destined for a service it is a destination address.
The implementation of communication paradigm abstractions is left up to the stack developer, although the CODAL implementation is documented.
Packets emitted by a service must take the form of a JACDAC packet, but the data payload of the packet can be freely defined by a developer. A pull request must be made to the jacdac services respository and include a description of the functionality of the service and the specification of expected packet formats. This allows others to implement services in their preferred programming language. The developer should indicate whether their service is ready to have a class allocation in their pull request; a TypeScript implementation of a service can expedite adoption.
|Compulsory Service Number||0|
On every JACDAC device there must be a service responsible for routing control packets to corresponding services.
See the ControlPacket format for the expected packet layout.
Optional Recommended Services
More capable devices may choose to run a configuration service to provide a more usable device experience, and those with random number generators may operate a random number generator service for less capable devices to use.
|Compulsory Service Number||1|
The configuration service receives packets that modify the name of a device or triggers an identification of a device to its user. This enables users to configure deployed JACDAC networks remotely from a user interface… perhaps a Web page running a JACDAC WebUSB stack…
ConfigurationService packets have the following format:
|Field Size (bits)||Name||Description|
|8||device_address||The source address of the device performing the identification or naming request.|
|8||request_type||This field identifies the layout of the data field. A request_type of 1 is a naming request, 2 is an identification request|
|variable||data||If the request_type is 2, there is no data payload and the device should simply identify itself to a user. A request type of 1 places the size of the new device_name at data, with the remaining bytes containing the new device_name.|
Random Number Generator Service
|Compulsory Service Number||2|
The purpose of the random number generator service is to return a random number when requested for less capable microcontrollers that do not have the ability to generate random numbers.
Random number generate service packets take the following form:
|Field Size (bits)||Name||Description|
|32||request_type||This field determines if the packet is a request or response. A request_type of 1 is a request, 2 is a response to a previous request.|
|32||random_number||When request_type is set to 2, this field must contain a random 32 bit number.|
Service Class Allocation
4000 are reserved for core services and must not be allocated in this range by developers.
0x0000FFFF) can be used dynamically, although there are no guarantees of the uniqueness of classes in this range. These classes must be used for local prototyping and should not be used for deployed products or services.
The remaining addresses are reserved for allocated service classes. Developers must ask for a class number before distributing a service or deploying their product. Service classes are allocated by submitting a pull request to the jacdac services repository.