Published using Google Docs
j4cDAC Protocol Draft
Updated automatically every 5 minutes

THIS IS AN OLD VERSION OF THE DOCUMENT. SEE: http://moroso.emarhavil.com/~jacob/j4cDAC/protocol

Communication with the DAC happens over TCP on port {??}. The DAC will only communicate with one host at a time; another device that connects while the DAC has an established control connection will have its connection attempt rejected. Connections time out after one second. If the host does not send anything to the device for one second, the device will close the TCP connection.

        The j4cDAC has a USB interface as well. The current plan is to have it emulate a standard USB CDC-ECM network interface, so the protocol will be exactly the same. In this mode, the DAC will choose a link-local IP address.

        In this document, protocol snippets are described as structs. Fields are packed together with no padding, {uint,int}{8,16,32}_t have their usual meanings, and multi-byte values are transmitted big-endian (“network byte order”; most-significant byte appears first).

        There are three distinct state machines within the DAC: light engine, playback, and source. The light engine states are:

        (0) Ready

        (1) Warmup - in the case where the DAC is also used for thermal control of laser apparatus, this is the state that is entered after power-up.

        (2) Cooldown - lasers are off but thermal control is still active.

        (3) Emergency stop - an emergency stop has been triggered, either by an E-stop input on the DAC, an E-stop command over the network, or a fault such as over-temperature.

        {TODO}: Define how transitions to and from Warmup / Cooldown occur.

        The

In addition to the main control protocol, each node broadcasts over UDP to its local network’s broadcast address a status/ID datagram every second. This datagram is formed as follows:

struct j4cDAC_broadcast {

        uint8_t mac_address[6];

        uint16_t hw_revision;

        uint16_t sw_revision;

        uint16_t buffer_capacity;

        uint32_t max_point_rate;

        uint16_t buffer_fullness;

        uint32_t point_rate;

        uint32_t point_count;

        uint32_t flags;

};

mac_address, hw_revision, sw_revision, buffer_capacity, and max_point_rate describe the particular DAC sending the datagram. (Its IP address is included as the source of the IP packet, and so not repeated.) buffer_fullness is the number of points currently in the DAC’s buffer, or zero if it is not actively producing output. point_rate is the points-per-second rate at which the DAC is playing back, or zero if not producing output. point_count is the total number of points that the DAC has emitted since the current stream began, and is reset by the Prepare Stream command.

“flags” is a bitfield with the following bits:

[7:0]        State: values as defined below.

[8]        Shutter status (1 open, 0 closed)

[9]        Underflow - 1 if the last stream ended with underflow, rather than a “stop” command. Reset to zero by the ‘p’ command.

[10]        Link loss - 1 if the last stream ended by a loss of Ethernet link. Reset to 0 by the ‘p’ command.

[11]        Network interface. 0 for Ethernet, 1 for USB.

[15:11]        Currently reserved

[23:16]        Current playback source.

        0 - Network

        1 - SD

        2+ - not yet assigned

{TODO}: Anything else?

The device will transmit this packet even if it is configured to use DHCP and has not yet acquired an IP address. In this case, the packets will be sent from a source IP of 0.0.0.0.

        The device is always in one of the following states:

        At power-up, the device enters the “idle” state. When the host first connects to the device, the device immediately sends it a status reply, as if the host had sent a ping packet (described later). The host sends to the device a series of commands. All commands receive a response from the DAC; responses are described after the list of commands. The commands are as follows:

Prepare stream.

Single byte: ‘p' (0x70)

        This command causes the DAC to enter the “ready” state. It is valid when the DAC is idle or ready. The DAC resets its buffer to be empty, resets “point_count” to 0, and prepares to begin producing output. The DAC replies with either an ACK or a NAK - Invalid.

Begin playback.

struct begin_command {

        uint8_t command; /* ‘b’ (0x62) */

        uint32_t point_rate;

};

        This causes the DAC to begin producing output. point_rate is the number of points per second to be read from the buffer. The DAC replies with either an ACK or a NAK - Invalid (if the DAC was not previously ready, or point_rate is not acceptable.)

        Write data.

struct data_command {

        uint8_t command; /* ‘d’ (0x64) */

        uint16_t npoints;

        struct dac_point data[];

};

struct dac_point {

uint16_t control;

int16_t x;

int16_t y;

        uint16_t i;

        uint16_t r;

        uint16_t g;

        uint16_t b;

        uint16_t u1;

        uint16_t u2;

};

        This provides data for the DAC to add to its buffer. The data values are full-scale (for instance, for color channels, 65535 is full output); the least-significant bits of each word will be ignored if the DAC’s resolution is less than 16 bits. The DAC will reply with ACK if the incoming packet can fully fit in its buffer, or NAK - Full if it cannot. It is valid for npoints to be zero; in this case, no point will be added to the buffer, but the packet will still be ACKed (as long as the DAC is Ready or Playing.)

        The “control” field is a bitfield with the following bits defined:

0-14        Reserved for future expansion - TTL outputs, etc

15        Shutter. 0 produces 0v on the ILDA shutter pin; 1 produces 5v.

        If the DAC’s buffer runs out, it will return to the idle state.

        Stop.

        Single byte: ‘s’ (0x73)

        The stop command causes the DAC to immediately stop playing and return to the idle state. It does not open the interlock loop. It is ACKed if the DAC was playing; otherwise it is replied to with NAK - Invalid.

Emergency stop.

Single byte: 0x00 or 0xFF. (The DAC will recognize either one.)

The e-stop command causes the laser to immediately stop playing and return to the idle state, open the interlock, and refuse to close it or restart playback until a Clear E-Stop command is received. It is always ACKed.

        Clear E-Stop.

        Single byte: ‘c’ (0x63)

        This causes the DAC to return to the Idle state from E-Stop. It is ACKed if the DAC was previously in E-Stop; otherwise it is replied to with a NAK - Invalid.

        Ping.

        Single byte: ‘?’ (0x3F)

        The DAC will reply to this with an ACK packet. This serves as a keep-alive for the connection when the DAC is not actively streaming.

        GPIO, i2c, UART, …

        TODO: The DAC has numerous GPIO pins and also hardware i2c and UART peripherals available on expansion connectors. Commands will exist to read/write/configure these interfaces.

        The DMX interface is handled separately. {TODO}: ArtNet?

        Responses only have one form.

struct dac_response {

        uint8_t response;

        uint8_t command;

        uint16_t buffer_fullness;

        uint32_t point_rate;

        uint32_t point_count;

        uint32_t flags;

};

        The buffer_fullness, point_rate, point_count, and flags fields have the same meaning here as in the broadcast datagram. “command” echoes back the command to which the response is sent. (Commands are always sent in order, so this field exists for sanity-checking on the host side.) The response field can be one of the following:

        ACK - ‘a’ (0x61) - The previous command was accepted.

        NAK - Full - ‘F’ (0x46) - The write command could not be performed because there was not enough buffer space when it was received.

        NAK - Invalid - ‘I’ (0x46) - The command contained an invalid command byte or parameters.

        There are several conditions that will cause the device to stop producing output. In all these cases, the R, G, B, U1, U2, and I outputs are immediately set to zero; X and Y are left at their last-written value. The shutter output is also set to zero.