Moved to github

This has been moved to github.

Still as a backup:

Motivations

  • Transmit and receive in real-time
  • Transmit as few data as possible
  • Send and receive any kind of data

Basics

So the basic ideas are to :

  • Keep a TCP connection open all the time by sending regular keep-alive frames.
  • Define named channels that are only transmitted once (to reduce data consumption)
  • Send byte array, and two-dimensionnal byte arrays on these channels
  • We rely on the TCP protocol for packet checksuming, packet ordering and every little things it does very well

We send these kind of frames :

  • Identification frames
  • Channel name to id definition
  • Data transmission of single byte arrays (byte[]).
  • Data transmission of two-dimentionnal byte arrays (byte[][]).

The protocol

Identification

The client asks for authentication

[ 1 ] { 0x01 } // Identification header
[ 1 ] { 0x05 } // Size of the identification
[ 5 ] { 'h', 'e', 'l', 'l', 'o' } // Identifier

The server replies “ok” or “not ok”

[ 1 ] { 0x01 } // Identification request
[ 1 ] { 0x01 } // OK, 0x00 for not ok

Ping / Keep alives

  • From client to server

Client sends a ping request :

[ 1 ] { 0x02 } // client ping request header
[ 1 ] { 0x15 } // the number (here 0x15) can be incremented or random

Server answers the ping request :

[ 1 ] { 0x02 } // client ping server response header
[ 1 ] { 0x15 } // where 0x15 is the number of the ping request
  • From server to client

Server sends a ping request :

[ 1 ] { 0x03 } // server ping request header
[ 1 ] { 0x16 } // the number (here 0x16) can be incremented or random

Client answers the ping request :

[ 1 ] { 0x03 } // server ping client response header
[ 1 ] { 0x16 } // where 0x16 is the 0x16 number of the ping request

Defining a named channel

This applies to both client to server and server to client communcation.

[ 1 ] { 0x20 } // channel definition frame header
[ 1 ] { 0x0D } // where 13 is the size of the following message
[ 1 ] { 0x03 } // where 3 is the id of the channel
[ 12 ] { 'c', 'h', 'a', 'n', 'n', 'e', 'l', ' ', 'n', 'a', 'm', 'e' } // the name of the channel : "channel name"

Sending some data

This applies to both client to server and server to client communcation.

For 0 to 254 sized messages :

[ 1 ] { 0x21 } // one byte sized data transmission frame header
[ 1 ] { 0x06 } // size of the data
[ 1 ] { 0x03 } // where 3 is the id of the channel
[ 5 ] { 0x01, 0x02, 0x03, 0x04, 0x05 } // data transmitted on the channel

For 255 to 65534 sized data messages :

[ 1 ] { 0x41 } // two bytes sized data transmission frame header
[ 2 ] { 0x00, 0x06 } // size of the data
[ 1 ] { 0x03 } // where 3 is the id of the channel
[ 5 ] { 0x01, 0x02, 0x03, 0x04, 0x05 } // data transmitted on the channel

For 65 535 octets to 4 294 967 294 sized data messages :

[ 1 ] { 0x61 } // two bytes sized data transmission frame header
[ 4 ] { 0x00, 0x00, 0x00, 0x06 } // size of the data
[ 1 ] { 0x03 } // where 3 is the id of the channel
[ 5 ] { 0x01, 0x02, 0x03, 0x04, 0x05 } // data transmitted on the channel

For data arrray transmission, it’s the same except frame header are 0×22, 0×42, 0×62 instead of 0×21, 0×41, 0×61. If you transmit data array like this one byte[][] data = { { 0×01, 0×02 }, { 0×03, 0×04, 0×05 }, { 0×06, 0×07, 0×08, 0×09 } } :

[ 1 ] { 0x22 } // one byte sized data transmission frame header
[ 1 ] { 0x0D } // size of the data
[ 1 ] { 0x03 } // where 3 is the id of the channel
[ 1 ] { 0x02 } // size of the first array
[ 2 ] { 0x01, 0x02 } 
[ 1 ] { 0x03 } // size of the second array
[ 3 ] { 0x03, 0x04, 0x05 } 
[ 1 ] { 0x04 } // size of the third array
[ 4 ] { 0x06, 0x07, 0x08, 0x09 }

For bigger arrays, you need to define :

[ 1 ] { 0x42 } // one byte sized data transmission frame header
[ 1 ] { 0x10 } // size of the data (16)
[ 1 ] { 0x03 } // where 3 is the id of the channel
[ 2 ] { 0x00, 0x02 } // size of the first array
[ 2 ] { 0x01, 0x02 } 
[ 2 ] { 0x00, 0x03 } // size of the second array
[ 3 ] { 0x03, 0x04, 0x05 } 
[ 2 ] { 0x00, 0x04 } // size of the third array
[ 4 ] { 0x06, 0x07, 0x08, 0x09 }

and so on :

[ 1 ] { 0x62 } // one byte sized data transmission frame header
[ 1 ] { 0x16 } // size of the data (22)
[ 1 ] { 0x03 } // where 3 is the id of the channel
[ 4 ] { 0x00, 0x00, 0x00, 0x02 } // size of the first array
[ 2 ] { 0x01, 0x02 } 
[ 4 ] { 0x00, 0x00, 0x00, 0x03 } // size of the second array
[ 3 ] { 0x03, 0x04, 0x05 } 
[ 4 ] { 0x00, 0x00, 0x00, 0x04 } // size of the third array
[ 4 ] { 0x06, 0x07, 0x08, 0x09 }

Sample data types transmitted

  • Strings Easy… Just the bytes value of the string

  • Position

When moving :

[ 4 ] Timestamp : UInt32 (since 1970 or 2009 if you wish)
[ 4 ] Longitude : Float
[ 4 ] Latitude : Float
[ 2 ] Speed : UInt16
[ 2 ] Altitude : UInt16
= 16 bytes

When stopped :

[ 4 ] Timestamp : UInt32 (since 1970 or 2009 if you wish)
[ 4 ] Longitude : Float
[ 4 ] Latitude : Float
= 12 bytes

When no location could be found yet:

[ 4 ] Timestamp : UInt32 (since 1970 or 2009 if you wish)
[ 1 ] Nomber of satellites : Uint8
= 5 bytes

Battery Percentage :

[ 1 ] Percentage (Byte)

Voltage :

[ 2 ] Voltage in mV (UInt16)

Sample communication

This is just to show how is working a sample communication

--> [] { 0x01, 0x04, 0x01, 0x02, 0x03, 0x04 } // identification frame
<-- [] { 0x01, 0x01 } // identification accepted
--> [] { 0x20, 0x08, 0x00, 'b', 'a', 't', 't', 'e', 'r', 'y' } // We define the channel "battery" with id 0x00
--> [] { 0x21, 0x02, 0x00, 70 } // We send 70 (meaning 70%) on the "battery" channel.

On top of that

Higher levels

On top of that, we have status and settings management specifications

Settings management

The server can get and set some settings on the _set channel.

  • If it’s a two-dimentionnal array and the first element is “s” (like “set”) it means that it sets some settings, the next elements will be “setting1=value1″, “setting2=value2″, etc.
  • If it’s a two-dimentionnal array and the first element is “g” (like “get”) it means that it needs the client to report the value of some settings, like “setting1″, “setting2″
  • If it’s a single dimentionnal array that contains “ga” (like “get all”) it means that the server wants the client to report the value of every settings.
  • If the client doesn’t know some settings that were sent to it, it can report it using “u”. The client can also send “c” (in the same format as the get) asynchronously if a setting changed on the client side.

Sample transmission:

Setting values:

--("_set")--> { "s", "setting1=value1", "setting2=value2" }

It’s the responsibility of the server to send an acknowledge request to know that the client has actually successfully received this settings.

Getting values:

--("_set")--> { "g", "setting1", "setting2" }
<--("_set")--  { "g", "setting1=value1", "setting2=value2" }

Setting and gettings value :

--("_set")--> { "sg", "setting1=value1", "setting2=value2" }
<--("_set")--  { "g", "setting1=value1", "setting2=value2" }

Gettings all the values :

--("_set")--> "ga"
<--("_set")--  { "g", "setting1=value1", "setting2=value2" }

Sending a value that is unknown to the client :

--("_set")--> { "sg", "setting1=value1", "setting98=value98", "setting99=value99" }
<--("_set")--  { "g", "setting1=value1", "setting98", "setting99" }

Same since here except we don’t specify the get. The client might decide to tell us the setting is unknown.

--("_set")--> { "s", "setting1=value1", "setting98=value98", "setting99=value99" }
<--("_set")--  { "u", "setting98", "setting99" }

Status parameters management

The server can get some status parameters on the “_sta” channel. Status are pretty much like settings except that they describe parameters that cannot be changed from the server.

Getting a parameter:

--("_sta")--> { "g", "version" }
<--("_sta")--  { "g", "version=0.0.1" }

Getting all parameters:

--("_sta")--> { "ga" }
<--("_sta")--  { "g", "hw=tc65i/1.1", "sw=sampleprogram/0.0.1", "cap=sms,applicationA/1.0" }

Value changed on the client side

<--("_set")-- { "c", "setting1=value1" }

### Capacities

Previously, capacities where requested by server with the “?” message on the “_cap” channel but they are now handled through the standard status message and the “cap” parameter.

--("_sta")--> { "g", "cap" }
<--("_sta")--  { "g", "cap=sms_relay_send_1,gpio_1" }

Command execution

To send a command:

--("_cmd")--> { "e", "<commandId>", "command", "arg1", "arg2" }

To acknowledge a command:

<--("_cmd")--  { "a", "<commandId>" }

In the future commands could algo be acknowledge as a group by sending an acknowledge request.

# Gateway behavior and sub equipements

Any equipment can act as a gateway by encapsulating data of an sub-equipment in a channel name like this “_eq/[sub equipment identifier]” but this has never been used for the moment.

Every packet coming from the sub-equipment is relayed to the server as a data packet. If the equipment, identified locally by “mac:112233445566″ sends an identification to the server, this will work like this :

Subequipment to Equipment:

[ 0x01 0x04 't' 'o' 't' 'o' ]

This will be relayed to the server as data message like this :

--("_eq/mac:112233445566")--> [ 0x01, 0x04, 't', 'o', 't', 'o' ]

Meaning in raw data :

[0x21 0x07 0x03 0x01 0x04 't' 'o' 't' 'o' ]

(where 0×03 is the generated channel id)