275 lines
12 KiB
HTML
275 lines
12 KiB
HTML
|
<!DOCTYPE html>
|
|||
|
<html>
|
|||
|
<head>
|
|||
|
<title>Ether Dream - Protocol</title>
|
|||
|
<link rel="stylesheet" type="text/css" href="main.css" />
|
|||
|
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
|
|||
|
</head>
|
|||
|
<body>
|
|||
|
<img src="etherdream.png" width="298" height="75" alt="ether dream">
|
|||
|
<div id="header"><a href="protocol.html">Ether Dream</a></div>
|
|||
|
<div id="content">
|
|||
|
<h1>Ether Dream - Protocol</h1>
|
|||
|
<h2>Contents</h2>
|
|||
|
|
|||
|
<ul>
|
|||
|
<li><a href="#intro">Introduction</a></li>
|
|||
|
<li><a href="#stm">State Machines</a></li>
|
|||
|
<li><a href="#status">Status Responses</a></li>
|
|||
|
<li><a href="#broadcast">Broadcast</a></li>
|
|||
|
<li><a href="#commands">Commands</a></li>
|
|||
|
<li><a href="#responses">Responses</a></li>
|
|||
|
</ul>
|
|||
|
|
|||
|
<h2 id="intro">Introduction</h2>
|
|||
|
|
|||
|
<p>
|
|||
|
Communication with the DAC happens over TCP on port 7765. 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.
|
|||
|
|
|||
|
If the host does not send anything to the device for one second, the device will close
|
|||
|
the TCP connection. <em>TODO: The 1-second timeout is not implemented, but will be
|
|||
|
in the future.</em>
|
|||
|
</p>
|
|||
|
<p>
|
|||
|
The DAC has a USB interface as well. This is used for firmware updates. <em>TODO:
|
|||
|
In the future, the USB interface may also emulate a standard USB CDC-ECM network
|
|||
|
device. This would allow the same communication protocol to be spoken over USB. In
|
|||
|
this mode, the DAC would choose a link-local IP address.</em>
|
|||
|
</p>
|
|||
|
<p>
|
|||
|
In this document, protocol messages are described as structs. Fields are packed
|
|||
|
together with no padding (<code>__attribute__((packed))</code> or <code>#pragma pack(1)</code>);
|
|||
|
standard C typedefs such as uint8_t, uint32_t, int32_t, etc. have
|
|||
|
their usual meanings, and multi-byte values are transmitted little-endian
|
|||
|
("host" byte order on x86 and ARM): the most significant byte appears first.
|
|||
|
</p>
|
|||
|
|
|||
|
<h2 id="stm">State Machines</h2>
|
|||
|
|
|||
|
<p>
|
|||
|
There are three distinct state machines within the DAC: light engine, playback, and source. The light
|
|||
|
engine states are:
|
|||
|
</p>
|
|||
|
<ul>
|
|||
|
<li>0: Ready.</li>
|
|||
|
<li>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.</li>
|
|||
|
<li>2: Cooldown. Lasers are off but thermal control is still active.</li>
|
|||
|
<li>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.</li>
|
|||
|
</ul>
|
|||
|
<p>
|
|||
|
<em class="todo">TODO: Since thermal control is not implemented yet, it is not defined how
|
|||
|
transitions to and from the "Warmup" and "Cooldown" states occur.</em>
|
|||
|
</p>
|
|||
|
<p>
|
|||
|
The DAC has one playback system, which buffers data and sends it to the analog output
|
|||
|
hardware at its current point rate. At any given time, the playback system is connected
|
|||
|
to a source. Usually, the source is the network streamer, which uses the protocol described
|
|||
|
in this document; however, other sources exist, such as a built-in abstract generator and
|
|||
|
file playback from SD card. The playback system is in one of the following states:
|
|||
|
</p>
|
|||
|
<ul>
|
|||
|
<li>0: Idle. This is the default state. No points may be added to the buffer. No output is
|
|||
|
generated; all analog outputs are at 0v, and the shutter is controlled by the data source.</li>
|
|||
|
<li>1: Prepared. The buffer will accept points. The output is the same as in the Idle state.</li>
|
|||
|
<li>2: Playing. Points are being sent to the output.</li>
|
|||
|
</ul>
|
|||
|
|
|||
|
<h2 id="status">Status Responses</h2>
|
|||
|
|
|||
|
<p>Periodically, and as part of ACK packets, the DAC sends to the host information on its
|
|||
|
current playback status. The status struct is:</p>
|
|||
|
|
|||
|
<blockquote><pre>struct dac_status {
|
|||
|
uint8_t protocol;
|
|||
|
uint8_t light_engine_state;
|
|||
|
uint8_t playback_state;
|
|||
|
uint8_t source;
|
|||
|
uint16_t light_engine_flags;
|
|||
|
uint16_t playback_flags;
|
|||
|
uint16_t source_flags;
|
|||
|
uint16_t buffer_fullness;
|
|||
|
uint32_t point_rate;
|
|||
|
uint32_t point_count;
|
|||
|
};</pre></blockquote>
|
|||
|
<p>
|
|||
|
The light_engine_state field gives the current state of the light engine. If the light engine
|
|||
|
is Ready, light_engine_flags will be 0. Otherwise, bits in light_engine_flags will be set as follows:
|
|||
|
</p>
|
|||
|
<ul>
|
|||
|
<li>[0]: Emergency stop occurred due to E-Stop packet or invalid command.
|
|||
|
<li>[1]: Emergency stop occurred due to E-Stop input to projector.
|
|||
|
<li>[2]: Emergency stop input to projector is currently active.
|
|||
|
<li>[3]: Emergency stop occurred due to overtemperature condition.
|
|||
|
<li>[4]: Overtemperature condition is currently active.
|
|||
|
<li>[5]: Emergency stop occurred due to loss of Ethernet link.
|
|||
|
<li>[15:5]: Future use.
|
|||
|
</ul>
|
|||
|
<p>
|
|||
|
<p>Similarly, playback_state gives the state of the playback system. The playback_flags field may
|
|||
|
be nonzero during normal operation. Its bits are defined as follows:</p>
|
|||
|
<ul>
|
|||
|
<li>[0]: Shutter state: 0 = closed, 1 = open.
|
|||
|
<li>[1]: Underflow. 1 if the last stream ended with underflow, rather than a
|
|||
|
Stop command. Reset to zero by the Prepare command.
|
|||
|
<li>[2]: E-Stop. 1 if the last stream ended because the E-Stop state was entered.
|
|||
|
Reset to zero by the Prepare command.
|
|||
|
</ul>
|
|||
|
<p>
|
|||
|
The buffer_fullness field contains the number of points currently buffered.
|
|||
|
point_rate is the number of points per second for which the DAC is configured (if Prepared or
|
|||
|
Playing), or zero if the DAC is idle. point_count is the number of points that the DAC has
|
|||
|
actually emitted since it started playing (if Playing), or zero (if Prepared or Idle).
|
|||
|
</p>
|
|||
|
<p>
|
|||
|
The currently-selected data source is specified in the source field:
|
|||
|
</p>
|
|||
|
<ul>
|
|||
|
<li>0: Network streaming (the protocol defined in the rest of this document).
|
|||
|
<li>1: ILDA playback from SD card.
|
|||
|
<li>2: Internal abstract generator.
|
|||
|
</ul>
|
|||
|
|
|||
|
<h2 id="broadcast">Broadcast</h2>
|
|||
|
<p>
|
|||
|
Regardless of the data source being used, each DAC broadcasts a status/ID datagram over UDP to
|
|||
|
its local network's broadcast address once per second. This datagram is formed as follows:
|
|||
|
</p>
|
|||
|
<blockquote><pre>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;
|
|||
|
struct dac_status status;
|
|||
|
};</pre></blockquote>
|
|||
|
|
|||
|
<h2 id="commands">Commands</h2>
|
|||
|
|
|||
|
<p>
|
|||
|
When a 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:
|
|||
|
</p>
|
|||
|
|
|||
|
<h3>Prepare Stream</h3>
|
|||
|
<p>Single byte: 'p' (0x70)</p>
|
|||
|
<p>This command causes the playback system to enter the Prepared state. The DAC resets its buffer to
|
|||
|
be empty and sets "point_count" to 0. This command may only be sent if the light engine is Ready
|
|||
|
and the playback system is Idle. If so, the DAC replies with ACK; otherwise, it replies with NAK - Invalid.</p>
|
|||
|
|
|||
|
<h3>Begin Playback</h3>
|
|||
|
<blockquote><pre>struct begin_command {
|
|||
|
uint8_t command; /* 'b' (0x62) */
|
|||
|
uint16_t low_water_mark;
|
|||
|
uint32_t point_rate;
|
|||
|
};</pre></blockquote>
|
|||
|
<p>This causes the DAC to begin producing output. point_rate is the number of points per second to be
|
|||
|
read from the buffer. If the playback system was Prepared and there was data in the buffer, then the
|
|||
|
DAC will reply with ACK; otherwise, it replies with NAK - Invalid.</p>
|
|||
|
<p><span class="todo">TODO: The low_water_mark parameter is currently unused.</span></p>
|
|||
|
|
|||
|
<h3>Queue Rate Change</h3>
|
|||
|
<blockquote><pre>struct queue_change_command {
|
|||
|
uint8_t command; /* 'q' (0x74) */
|
|||
|
uint32_t point_rate;
|
|||
|
};</pre></blockquote>
|
|||
|
<p>This adds a new point rate to the point rate buffer. Point rate changes are read out of the buffer when
|
|||
|
a point with an appropriate flag is played; see the Write Data command. If the DAC is not Prepared or
|
|||
|
Playing, it replies with NAK - Invalid. If the point rate buffer is full, it replies with NAK - Full.
|
|||
|
Otherwise, it replies with ACK.</p>
|
|||
|
|
|||
|
<h3>Write Data</h3>
|
|||
|
<blockquote><pre>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 r;
|
|||
|
uint16_t g;
|
|||
|
uint16_t b;
|
|||
|
uint16_t i;
|
|||
|
uint16_t u1;
|
|||
|
uint16_t u2;
|
|||
|
};</pre></blockquote>
|
|||
|
<p>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 Prepared or Playing.)</p>
|
|||
|
<p>The "control" field has the following fields defined:</p>
|
|||
|
<ul>
|
|||
|
<li>[15]: Change point rate. If this bit is set, and there are any values in the point rate change
|
|||
|
buffer, then a new rate is read out of the buffer and set as the current playback rate. If the
|
|||
|
buffer is empty, the point rate is not changed.
|
|||
|
<li>Other bits: reserved for future expansion to support extra TTL outputs, etc.
|
|||
|
</ul>
|
|||
|
|
|||
|
<h3>Stop</h3>
|
|||
|
<p>Single byte: 's' (0x73)</p>
|
|||
|
<p>The stop command causes the DAC to immediately stop playing and return to the Idle state. It is
|
|||
|
ACKed if the DAC was Playing or Prepared; otherwise it is replied to with NAK - Invalid.</p>
|
|||
|
|
|||
|
<h3>Emergency Stop</h3>
|
|||
|
<p>Single byte: 0x00 or 0xFF. (The DAC will recognize either one.)</p>
|
|||
|
<p>The e-stop command causes the light engine to enter the E-Stop state, regardless
|
|||
|
of its previous state. It is always ACKed.</p>
|
|||
|
<p>Any unrecognized command will also be trested as E-stop; however, software should not send
|
|||
|
undefined commands deliberately, since they may be defined in the future.</p>
|
|||
|
|
|||
|
<h3>Clear E-Stop</h3>
|
|||
|
<p>Single byte: 'c' (0x63)</p>
|
|||
|
<p>If the light engine was in E-Stop state due to an emergency stop command (either from a local
|
|||
|
stop condition or over the network), then this command resets it to be Ready. It is ACKed if the
|
|||
|
DAC was previously in E-Stop; otherwise it is replied to with a NAK - Invalid. If the condition
|
|||
|
that caused the emergency stop is still active (E-Stop input still asserted, temperature still
|
|||
|
out of bounds, etc.), then a NAK - Stop Condition is sent.</p>
|
|||
|
|
|||
|
<h3>Ping</h3>
|
|||
|
<p>Single byte: '?' (0x3F)</p>
|
|||
|
<p>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.</p>
|
|||
|
|
|||
|
<h2 id="responses">Responses</h2>
|
|||
|
<p>Responses have one form:</p>
|
|||
|
<blockquote><pre>struct dac_response {
|
|||
|
uint8_t response;
|
|||
|
uint8_t command;
|
|||
|
struct status dac_status;
|
|||
|
};</pre></blockquote>
|
|||
|
<p>In the case of ACK/NAK responses, "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:</p>
|
|||
|
<ul>
|
|||
|
<li>ACK - 'a' (0x61) - The previous command was accepted.
|
|||
|
<li>NAK - Full - 'F' (0x46) - The write command could not be performed because there was not
|
|||
|
enough buffer space when it was received.
|
|||
|
<li>NAK - Invalid - 'I' (0x49) - The command contained an invalid command byte or parameters.
|
|||
|
<li>NAK - Stop Condition - '!' (0x21) - An emergency-stop condition still exists.
|
|||
|
</ul>
|
|||
|
|
|||
|
</div>
|
|||
|
<div id="footer">
|
|||
|
© 2010-2021 Jacob Potter.
|
|||
|
</div>
|
|||
|
<div id="menu">
|
|||
|
<ul>
|
|||
|
<li><a href="../index.html">LJ doc</a></li>
|
|||
|
<li><a href="protocol.html">Protocol</a></li>
|
|||
|
<li><a href="userguide.html">V1 User Guide</a></li>
|
|||
|
<li><a href="manual.html">V1 Developer Manual</a></li>
|
|||
|
<li><a href="dmx.html">V1 DMX Board</a></li>
|
|||
|
</ul>
|
|||
|
</div>
|
|||
|
</body>
|
|||
|
</html>
|