The TOS system is constructed such that each re-Mote device can be assigned a unique 16 bit ID number. This ID can be used to identify the specific device in radio and serial messages. Messages can be sent to a specific Mote, by ID, or Broadcast to all devices in range. The same messages may be sent directly over a serial connection or, by using a BaseStation intermediary, over the radio channel.
Using this, ToastComm implements a stateless call-and-response system where each message received by a re-Mote is answered by an appropriate reply message. A host machine initiates an interaction by sending a request and the re-Mote replies with the requested information. If the re-Mote does not reply, or either message is dropped, the interaction can be retried until it is successful or the host decides to abandon it.
The actual messages are arbitrary. ToastComm implements three specific classes which may be generally useful: Config, Status, and Data.
Under the assumption that there are many re-Motes that move in and out of range of one or more central hosts while collecting and storing data, this is the pseudo-code for simple data collection system:
If all data is collected:
// note that the naming convention for MIG looks for, example:
// "TestSerialMsg" to find the message struct and
// "AM_TESTSERIALMSG" to find it's associated AM type number
// where the command line might be:
// mig java -target=null
// -java-classname=com.etantdonnes.tinyos.toast.TestSerialMsg
// TestSerial.h TestSerialMsg -o TestSerialMsg.java
/** example message type ...sent and received for system validation... **/
typedef nx_struct TestSerialMsg
{
nx_uint16_t counter;
} test_serial_msg_t;
/** ToastComm system message types **/
/** Status Request **/
typedef nx_struct StatusReqMsg
{
nx_uint8_t backoff; // backoff reply by this amount per mote
} StatusReqMsg_t;
/** Returned status values **/
typedef nx_struct StatusRetMsg
{
// implement as needed
nx_uint8_t data[TOSH_DATA_LENGTH]; // max buffer length, default=28 bytes
} StatusRetMsg_t;
/** set configuration, returned with actual values **/
typedef nx_struct ConfigMsg
{
// implement as needed
nx_uint8_t data[TOSH_DATA_LENGTH]; // max buffer length, default=28 bytes
} ConfigMsg_t;
/** Clear and reset data/log storage -- sends status message as reply **/
typedef nx_struct DataRstMsg
{
nx_uint8_t flag; // dummy value....
} DataRstMsg_t;
/** Request data dump **/
typedef nx_struct DataReqMsg
{
nx_uint16_t block; // data block number being requested,
} DataReqMsg_t;
/** Returned data block **/
typedef nx_struct DataRetMsg
{
nx_uint16_t block; // data block number being returned
// chunk'o'data -- should be set to maximum we can get
nx_uint8_t data[TOSH_DATA_LENGTH - sizeof(nx_uint16_t)];
send...
} DataRetMsg_t;
/** Our AM message type IDs -- see note above about naming conventions.
* By schip's convention the StatusRequest is always number 0... **/
enum
{
AM_STATUSREQMSG = 0, // 0
AM_STATUSRETMSG, // 1
AM_CONFIGMSG, // 2
AM_DATARSTMSG, // 3
AM_DATAREQMSG, // 4
AM_DATARETMSG, // 5
AM_TESTSERIALMSG = 0x89, // 137d
};
#define TOSH_DATA_LENGTH 28
The platform specific message container structure is in
platforms/z1/platform_message.h.
The cc2420 header is in chips/cc2420/cc2420.h and contains
elements specific to radio transmission. The serial header
is in lib/serial/Serial.h.
I am going to assume that the Base-Station code translates
the headers to/from radio and serial formats as it passes messages
back and forth (this was not always the case in T1...).
I am also going to assume that the serial dest and src fields
are set to the correct MoteID values during the translation
(this is not the case with direct serial messages however).
typedef union message_header
{
cc2420_header_t cc2420;
serial_header_t serial;
} message_header_t;
typedef nx_struct serial_header
{
nx_am_addr_t dest;
nx_am_addr_t src;
nx_uint8_t length;
nx_am_group_t group;
nx_am_id_t type;
} serial_header_t;
On Java side the raw receive buffer is in net/tinyos/packet/Packetizer.java
where the MTU==256 bytes max:
private byte[] receiveBuffer = new byte[MTU];
It is filled from the io channel and then copied into the message buffer
sent to the user in:
private byte[] readFramedPacket()
It is most convenient to set a MOTECOM environment variable to point
to the I/O device on which your re-Mote is connected:
Then start the program using Java:
export MOTECOM="serial@/dev/ttyUSB0:telosb
java -cp "[path-to]toastcomm.jar;[path-to]tinyos.jar" ToastComm
?>> ?
Use SPACE delimiters!!
? -- print this help
Quit
-- send empty status request message
Dest dest -- set send message destination address
Message type -- set send message type
Send ... -- set and send command with given values
Entering an empty line will send the default status command
which should elicit a status reply:
?>>
Sending packet to: 0 Message [backoff=0xff]
Received packet: class com.etantdonnes.tinyos.toast.StatusRetMsg
AMtype: 1; to: 65535; src: 0; dest: 65535
offset: 0 length: 28
0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
If the PULSETIME define is set in the re-Mote ToastComm program
a periodic TestSerialMsg will be sent to the host, so you should
see un-solicited messages being received:
Received packet: class com.etantdonnes.tinyos.toast.TestSerialMsg
AMtype: 137; to: 65535; src: 0; dest: 65535
offset: 0 length: 2
0 2
The program defaults to sending BROADCAST messages using destination
address -1, but you may set a specific address with, e.g.:
d 4
The program also defaults to sending a Status Request message when
you use the "send" command, however you can select other types of messages,
for instance a Data Request (AM type ID 5), with:
m 5
and then send the message with:
s [param1] [...] [paramN]
Where the params are the ordered data elements in the specific message
structure.
And finally you can quit the program with:
q