Protocol: Raw

The Raw protocol is one of several Protocols supported by the Serious Human Interface(tm) Platform (SHIP), and enables the GUI designer to push a typed-object (Byte, Short, String, etc.) out a communications port or receive a typed-object from a communications port.

NOTE

For an example project and a video walkthrough, see AN0504 – The Raw Communications Protocol

Advantages of the Raw Protocol

  • very quick prototyping of control of your system
  • simple to use on the GUI side; a simple assignment pushes a value out the port
  • simple to handle on the other side of the wire in your embedded code
  • lightweight, so the GUI, for example, can communicate over the UART to an 8-bit MCU

Disadvantages of the Raw Protocol

  • No packetization; your comms can get mis-aligned or out of sync
  • No error checking; data corruption in transit cannot be detected
  • No automatic retries or guaranteed delivery
  • Single data type out, single data type in
  • No firmware updates over this connection or access to advanced SHIPBridge protocol features

These disadvantages are significant. While the Raw Protocol can be useful in demos and initial bring-up as well as very simple applications, it is strongly recommended the designer consider the Modbus protocol for more robust communications. Better still, the sophisticated SHIPBridge protocol should be considered for flexible and powerful data connectivity as well as over-the-wire firmware updates and more.

Typed Data Transfer FIFOs

The Raw protocol is implemented as a pair of FIFOs, one for input, one for output. The depth of these FIFOs is runtime and platform dependent based on the amount of RAM available in the specific system. Each FIFO has a specific data type of object it can carry.

For example, you may wish to send Byte data out to your embedded device. In this case, the output FIFO would have the datatype of Byte.

The data type for the input FIFO may be different from that of the output FIFO. For example, your output FIFO could have the datatype of Byte but your input FIFO could have the datatype of String. In this configuration your GUI could send Byte data to the other MCU on the other end of the UART connection and that MCU would send back String data objects — a sequence of UTF-8 characters with a null terminator.

The data type must be specific and unchanging during the operation of any given GUI, since there are no packet headers or otherwise that could indicate a message-by-message change in the type of data being transmitted. The embedded MCU or whatever is on the “other end of the wire” must be able to assume a specific format for the data coming and going, as must the GUI.

The linkvars are as follows:
iData and oData

The iData and oData linkvars are the input FIFO and output FIFO respectively. You can only access the end of the FIFOs: reading from iData fetches the top item from the input FIFO, and writing to oData pushes a new item onto the back of the output FIFO.

Note that accesses to iData and oData are fairly unique in SHIP: reading from iData removes an item from the FIFO and writing to oData adds and item to the FIFO. Be very cognizant of this behavior: reading twice from iData, for example, will remove two items from the FIFO (assuming there are two available).

When you first instantiate the link you’ll notice (as in the picture above) there is an error on iData and oData. That is because the datatype property has not yet been set for the input and output FIFOs. Your first order of business will be to set these data types. For example, if you wish have byte input and byte output, set the datatype of iData and oData to Byte.

iState, oState, and the State Constants

In the structure, you’ll also notice two other linkvar nodes named iState and oState. These are special linkvars that indicate the state of the input and output FIFOs iData and oData respectively.

When activity happens on a FIFO, the respective state linkvar generates an event that can be listened to by a listener and action taken by a script handler.

The iState and oState linkvars can, at any given time, have one of the following values:

  • ADDED – an item was added to that FIFO
  • REMOVED – an item was removed from that FIFO
  • FULL – FIFO became full after an ADDED
  • EMPTY – FIFO became empty after a REMOVED
  • OVERRUN — FIFO overran (was full) after an attempted add
  • UNDERRUN – FIFO was empty and a remove was attempted (applies to iData only)
Sending Data Out

When data is put on the output FIFO, via code such as myLink.raw.oData = 0x00; the oState variable will become the value ADDED. If the FIFO became full as a result of that action, it will then changed to FULL. If the FIFO was full when the byte was attempted to be added to the FIFO, the oState variable will not go through the ADDED state but rather go directly to the OVERRUN state.

The transmission system inside SHIPEngine will automatically begin sending the data out the communications channel. It removes items from the head of the oData FIFO and spools it out the port. When it does the removal, REMOVED state event on oState will be generated and a subsequent EMPTY state will be generated if the FIFO became empty as a result of that removal. An UNDERRUN event will never be generated on oState.

The oState variable has the very unusual property in SHIP of generating events when updated, even if unchanged. In other words, unlike almost all other properties and values in SHIP, when the oState has a repeated REMOVED activity (for example) an event will be generated for both even though the value of oState has not changed.

Therefore, using listener nodes and scripts you could actually count up on ADDED events and down on REMOVED events an know at any given time how many items were in the output oData FIFO.

For very low frequency output data, you can just send bytes (data) using simple assignments as desired, for example myLink.raw.oData = 0x01; in any context such as when a button on the screen is pressed. In these cases, the likelihood of FIFO full/overrun is minimal with a reasonable baud rate.

However, if the frequency of output data updates is higher as compared to the baud rate such that there is a danger of overrun of the output FIFO — for example, by assigning oData rapidly in succession to the point the output FIFO is over-full and generates the OVERRRUN event — you must perform these assignments in a more structured way. There are many GUI-application dependent factors in how you do this. For example, this condition may be something you want to tell the user to “please wait” while the FIFO empties and all the messages go out, or this condition may be something you can ignore and the output request could be something you can retry later.

One way to watch for and handle this condition is, for example:

  1. create a holding variable (for example, called oDataReady) as a Boolean with initial value true.
  2. ensure that variable/listener is at the beginning of the layout tree so it is always processed early when oState changes
  3. create a listener under that variable that, when oState is FULL sets that boolean false and when oState is REMOVED sets it to true.
  4. when you want to send out a value, test that variable is true. If not, the FIFO is full and you cannot send one at that time without risking an OVERRUN
Receiving Data In

If your GUI needs to wait for and process incoming objects, create a listener on your iState linkvar and set a condition looking specifically for the “ADDED” state:

This listener will wake up every time a new state happens on the iState linkvar. In this case, the condition property is testing that event to ensure it is the ADDED event. If it is, any enclosed script will be run.

For example, if this listener is inside a text node, and you want the text to change automatically to reflect the incoming Byte, you can make the structure look like this:

Note carefully that this action of “fetching” from iData in the text assignment removes the item from the FIFO. If you need multiple places in your GUI to access this new data item, you must create a holding variable in your GUI for this incoming value and assign iData to that holding variable and all the “consumers” of that data should access that holding variable. This mechanism ensures only one retrieval per incoming item.

The iState variable has the very unusual property in SHIP of generating events when updated, even if unchanged. In other words, unlike almost all other properties and values in SHIP, when the iState has a repeated REMOVED activity (for example) an event will be generated for both even though the value of iState has not changed.