New Hexabus Packet Format

myeisha edited this page Jul 3, 2013 · 16 revisions
Clone this wiki locally

New Hexabus Packet Format

The Hexabus default port is 61616. Broadcasts and direct messages should be sent to that port.

Byte                 | 0-3    | 4           | 5     | 6++ (variable length) | n-1, n-2
Field Name / Content | "HX0C" | Packet Type | Flags | Payload               | CRC

Hexabus Packets are wrapped in UDP Packets. The UDP Data of a Hexabus Packet starts with the Bytes 0x48 0x58 0x30 0x43 (HX0C) to identify it as a Hexabus Packet. Then follow one Byte of Packet Type, one Byte of Flags, a variable number of Bytes for the actual Data, and two Bytes for the checksum.

The String "HX0C" is defined in shared/hexabus_definitions.h as HXB_HEADER.

Hexabus Devices

A Hexabus Device has several endpoints, which are identified by an Endpoint ID. The Endpoint ID (EID) identifies the function on the device. Each endpoint can have a value which can be queried, written or broadcast.

The EID 0 has to exist on every Hexabus Device, and it contains the Device Descriptor.

Currently used EIDs

At the moment, a Hexabus-Socket broadcasts its Endpoint 2 every 60 to 90 seconds.

Device Descriptor

Endpoints that have EIDs divisable by 32 are special endpoints. They contain device descriptors. EID 0 has to be present on every Hexabus device, EIDs 32, 64, ... are optional.

When EID 0 is queried on a Hexabus device, it has to reply with an info packet containing its device descriptor. The device descriptor is a 32 Bit field. The 32 bits in the field correspond to the endpoints with the EIDs 0 to 31 (the LSB to 0, the MSB to 31). If a bit is set, the corresponding endpoint exists on the device. Querying EID 32 will reveal thirty-two more endpoints. The next device descriptor will then be at EID 64, and so on.

Hexabus Packet types

0x00: Error

This Packet indicates something went wrong.

Byte       | 0-3    | 4           | 5       | 6            | n-1, n-2
Field Name | Header | Packet Type | Flags   | Error Code   | CRC
Content    | "HX0C" | 0x00        |         |              |

In hexabus_packet.h, there is a struct named hxb_packet_error that can be used for this, as well as the enumerator HXB_PTYPE_ERROR in hexabus_types.h that contains the 0x00 for the packet type.

There are also several error codes. At the moment, these are:

  • 0x01 (HXB_ERR_UNKNOWNEID) - A Query or Write-Packet was received, but the EID matches none of the endpoints of the device
  • 0x02 (HXB_ERR_WRITEREADONLY) - A Set Value-Packet was received, but the EID corresponds to a read-only endpoint
  • 0x03 (HXB_ERR_CRCFAILED) - The CRC check of a packet failed.
  • 0x04 (HXB_ERR_DATATYPE) - A Write packet was received, but the data type does not match the EID.

0x01: Info

An Info packet contains information from an endpoint. This may either be a reply to a Query packet, or an autonomous broadcast of a value. If it is a reply to a query, it is sent to the source address of the query packet, if it is a broadcast, it is sent to the site-local multicast address.

Byte       | 0-3    | 4           | 5     | 6-9 | 10        | 11++ (size depending on data type of the value) | n-1, n-2
Field Name | Header | Packet Type | Flags | EID | Data Type | Value                                           | CRC
Content    | "HX0C" | 0x01        |       |     |           |                                                 |

The structs hxb_packet_<T> in connection with HXB_PTYPE_INFO can be used to construct Info packets. Possible values for <T> are bool, u8, u32, float, 16bytes, 66bytes, and 128string.

For the contents of the "data type" field, see below.

If the value is boolean, the defines HXB_TRUE and HXB_FALSE can be used to map boolean values to integers. For now, they are defined as 1 and 0.

0x04: Write

Directly set an endpoint on the device to a value.

Byte     | 0-3    | 4                  | 5     | 6-9 | 10        | 10++ (size depending on data type of the value) | n-1, n-2
Content  | "HX0C" | Packet Type = 0x04 | Flags | EID | Data Type | Value                                           | CRC

The enumerator HXB_PTYPE_WRITE contains 0x04, which can be used for the packet type field.

If a Setvalue-packet is recieved for an endpoint which can not be set on a particular device (e.g. power consumption on a Hexabus-Socket), the device will reply with an error packet with HXB_ERR_WRITEREADONLY in the error code field.

If a Setvalue-packet is recieved for a EID which is not used on a device, the device will reply with an error packet with HXB_ERR_UNKNOWNEID in the error code field.

When receiving a Setvalue-packet specifying a data type which does not match the EID (for examply, trying to set a boolean value with a UINT32 packet), the device replies with HXB_ERR_DATATYPE.

0x02: Query

Requests a sensor value or state (on/off) of a device.

Byte     | 0-3    | 4                  | 5     | 6-9 | 10,11
Content  | "HX0C" | Packet Type = 0x02 | Flags | EID | CRC

This packet can be constructed using the hxb_packet_query struct. The packet type value is defined as HXB_PTYPE_QUERY.

The device will reply with a Packet of the type HXB_PTYPE_INFO with the appropriate value if the EID is present, or with an error packet with HXB_ERR_UNKNOWNEID if it is not.

Flags

At the moment, the flags 0x01 (HXB_FLAG_CONFIRM) is planned. The idea is that packets that are somehow important and have to be confirmed upon reception can be flagged as such, they need to be acknowledged by the receiver (via a packet of type INFO), and if no acknowledgement is received within a certain span of time, the packet is retransmitted.

This is not impemented yet.

Data Types

For easier parsing of the packets, a data type field was included. It is one byte wide, and at the moment, the following data types are defined:

  • 0x00 - Reserved. Used to denote "no data at all" in the internal code of the hexabus device.
  • 0x01 (HXB_DTYPE_BOOL) - Boolean. The value filed is 1 byte wide, but can only contain HXB_TRUE and HXB_FALSE. Packet type hxb_packet_bool.
  • 0x02 (HXB_DTYPE_UINT8) - 8 Bit unsigned integer (one byte). Packet type hxb_packet_u8.
  • 0x03 (HXB_DTYPE_UINT32) - 32 bit unsigned integer (four bytes). At the moment it is also used for the device descriptor. Packet type hxb_packet_u32.
  • 0x04 (HXB_DTYPE_DATETIME) - Date/time data structure (sizeof(struct datetime) bytes, whatever that is at the moment). Packet type hxb_packet_datetime.
  • 0x05 (HXB_DTYPE_FLOAT) - 32 bit float (four bytes). Packet type hxb_packet_float.
  • 0x06 (HXB_DTYPE_128STRING) - Character string, 128 bytes of it. Must be 0-terminated. Packet type hxb_packet_128string.
  • 0x07 (HXB_DTYPE_TIMESTAMP) - Timestamp in secondes since device was booted up, 32 bit unsigned integer (4 bytes). Packet type hxb_packet_timestamp.
  • 0x08 (HXB_DTYPE_66BYTES) - 66 bytes of raw binary data. Packet type hxb_packet_66bytes.
  • 0x09 (HXB_DTYPE_16BYTES) - 16 bytes of raw binary data. Packet type hxb_packet_16bytes.

In the future, more datatypes will be implemented as needed.

Calculating the Checksum

The checksum is calculated via the CRC16-Kermit method. There are several names and definitions floating around on the internet, some call it CRC16-CCITT, some CRC-CCITT-True, and sometimes the definitions of CRC16-CCITT contradict each other, so don't get confused.

The one used for Hexabus is: Generator Polynomial 0x1021, Initial value 0x0000, Reflect Input true, Reflect Output true, XorOut 0x0000.

It can be found on http://regregex.bbcmicro.net/crc-catalogue.htm#crc.cat.kermit

Contiki brings a function named crc16_data that calculates this checksum. Be careful, the function has to be called with three parameters: A pointer to the struct you wish to checksum, the number of bytes you wish to checksum, and an initial value. If the third parameter is not present, the checksum is accumulated over several calls of crc16_data. So if you just want to calculate the checksum of one packet, set the third parameter to 0.

The typical call looks like this (if packet is a struct hxb_packet_something):

packet.crc = crc16_data((char*)&packet, sizeof(packet)-2, 0);

or this (if packet is a pointer -- don't forget the * in the sizeof()-call):

crc16_data((char*)packet, sizeof(*packet)-2, 0)

If you use boost, you can calculate the checksum like this:

uint16_t crc16(const char* input, unsigned int length)
{
  boost::crc_optimal<16, 0x1021, 0x0000, 0, true, true> crc;
  crc.process_bytes(input, length);
  return crc.checksum();
}

On Endpoint IDs

An endpoint is globally identified by its Endpoint ID and the IP address of the device.

When a device sends out an INFO packet, either as a reply to a QUERY or as an autonomous broadcast, the packet's endpoint ID refers to the endpoint on the device that sent the packet.

When a device sends out a QUERY packet or a WRITE packet, the packet's Endpoint ID refers to the endpoint on the device that receives the packet.

To Do

Plans and ideas for the future, but not yet implemented or wikified:

Hexabus Concepts and other ideas

  • It is desired to group endpoints that are related close to each other so that a group of 32 endpoints can be queried easily.
  • Endpoints 00 to 63 (Groups starting at 00 and 32) are reserved
  • Do we want to refer to a block of 32 endpoints as a "Group"?