BeaconerM
From CSL Wiki
The BeaconerM module is used only by leaf nodes and it is responsible for sending periodic beacons. When nodes are just turned on, they have not yet joined the network. By "joining the network," we mean that the node still does not know its parent and, as such, it cannot send any sensed data to the sink. In that case, according to the CentRoute protocol, the node is supposed to send periodic beacons hoping that someone hears them. If some node already connected to the tree hears a beacon, it forwards the beacon to the sink so that a final decision can be made. Every node that forwards the beacon adds its own address to the packet so that the sink knows the beacon's traversed path. The sink might possibly receive the same beacon from several different paths, so it waits a while after receiving the first beacon. After a timeout, the sink makes a decision based on the received different paths and assigns a parent to the node. A reply packet is then sent back so that the node knows its parent.
The BeaconerM module might be used by many client modules and, for each client, there is an entry which stores the state of the beacon. The information currently stored is the beacon's period, the time remaining to actually send it, its length, and its state. The basic structure of the beacon entry is below.
struct _beaconer_entry {
int16_t period;
int16_t time_remaining;
int8_t length;
int8_t state;
}
Initially, a vector of these structures is allocated and each structure is assigned to a module that uses the BeaconerM module.
The state of the entry can be either B_CLEAR, PENDING, PROCESSED, and DISABLED, as defined below. Each of the entry states will be explained in the next section along with the explanation of the BeaconerI interface.
#define B_CLEAR 0 #define PENDING 1 #define PROCESSED 2 #define DISABLED 3
BeaconerI
The BeaconerI interface is the only interface provided by the BeaconerM module. It is composed of a few global variables, commands, and events. Besides the beacon entry vector described above, there is also a variable called max_length, which stores the maximum length that a beacon message can have. Beacons of the clients might be aggregated on the same message and this variable stores the size of the largest beacon possible, a message with beacons from all clients. Additionaly, there are three flags which are used by the module. The flags are stored in a single byte called flags. The TIMER_STARTED flag is responsible for keeping track of the state of the timer. If it has already been started, then this flag is set and otherwise it is reset. The BEACON_PAYLOAD_SET keeps track of the state of the payload. If the data of the payload has been loaded, this flag is active. The third variable is called BUF_BUSY. If there is data in the buffer not yet sent or being currently sent by the radio, this flag is active. Finally, there is a packet buffer to store the data to be sent by the radio. The above-mentioned variables are described below.
uint8_t max_length; int8_t flags; TOS_Msg pktbuf;
The BeaconerI interface is composed of a few commands and events, as shown below.
interface BeaconerI
{
command result_t init(int8_t length, int16_t period);
command result_t set_period(int16_t period);
command int16_t get_period();
command int8_t send(char *data, int8_t now);
command void ignore();
event void send_done(result_t result);
event void receive(uint16_t src, char *data, int8_t length);
event int8_t beacon_ready(uint16_t time_remaining);
command void disable();
command void enable();
}
The init() command performs a few checkings regarding its parameters and initializes the length and the period of the beacon. This way, different client modules might have different lengths and periods. These values are stored on the client's structure showed in the previous section. The time_remaining variable of each client is also initialized with the given period argument. Additionally, for each client we add its length to the max_length variable to make sure we are below the maximum payload size, MAX_PAYLOAD, in the worst-case scenario where all clients send the beacon in the same message. Finally, the first client that calls the init() command starts the timer.
In the BeaconerM module, there is only one timer which periodically ticks, decrements the time_remaining variable of each client, and checks if one of them have become zero. In that case, the BeaconerM module sets the state of every client to PENDING and sends the beacon_ready() signal to all clients with their respective time_remaining value. As a result, each client module can decide if it actually wants to send its beacon together with the message that is about to be sent. If the client does not want to send its beacon with the current message, it calls the ignore() command. The ignore() command just resets the client's state back to B_CLEAR and, if there is no more clients in the PENDING state, it finally sends the beacon. If the client's time_remaining expired or if it chooses to aggregate its own beacon in the message, it must call the send() command. The send() command performs a few initial checks, such as checking the BUF_BUSY flag, and then it calls the assemble_packet() function. This function just adds the beacon data provided by the client to the current message and updates the packet header contents. If the message gets bigger than MAX_PAYLOAD, the function returns an error. Otherwise, it set the client's status to PROCESSED, checks if there is no more clients in the PENDING state, and sends the beacon out. This way, the command called by the last client, either the ignore() command or the send() command, will send the beacon out. One important thing to mention is that the BeaconerM module does not use the GenericComm module directly to send messages. Instead, it uses the PktQueueM module, which is an intermediate component.
The commands set_period() and get_period() just sets and gets the beacon's period, respectively.
Whenever a beacon is transmitted, the PktQueueM module signals its sendDone() event. When receiving such event, the BeaconerM module sets the state of every client which is in the PROCESSED state to the B_CLEAR state and signals a send_done() event to that client.
The receive() event is signaled whenever a beacon is received. This event is triggered directly by the GenericComm ReceiveMsg() event. In that case, the BeaconerM module wraps off the message's headers and demultiplexes each beacon inside the message to the corresponding client for appropriate handling.
The commands enable() and disable() sets the client's state to B_CLEAR and DISABLED, respectively.
The format of the beacon message is shown below in Fig. 1. The message is composed of an initial beacon header which is composed of two fields. The first one is the source address of the node, since the address field in the TOS_Msg header is dedicated only to the destination. The second field is the number of beacon entries in the packet. Each beacon entry is in fact the data provided by the client modules and we can have more than one entry in the same packet due to aggregation. The beacon entry is composed of a type field which is in fact the ID (or index) of the client using the BeaconerM module and the data itself. The field type is used as a multiplexing/demultiplexing field and the data is provided by the client module.
Fig. 1 - The format of the beacon messages.
Questions
- Does the sink need to know all the path taken by the beacon? Why doesn't knowing only the parent that received it is good enough?
No we don't. It can be optimized.
- Why do we need to store the beacon's length?
We just need to know the maximum length to not exceed the maximum payload size.
- Shouldn't the flags be initially 0? They are not being initialized.
Apparently, tinyOS already initializes every variable to zero for us.
- Shouldn't we initialize the state of each beacon to B_CLEAR in the init() function? They are not being initialized.
No need because they are all set to B_PENDING in the first timeout, but we could do that.
- Do we really need the BEACON_PAYLOAD_SET flag? It is not checked anywhere in the code except in one place, where it is set.
Unknown... :)
- At line 239 of BeaconerM.nc, if there is an error in the assemble_packet() function and the send() function was the last one to be called, it does not send the beacon, right? Is it a problem?
Corrected. Added the check_pending() and send_beacon() function there for that case.
