44 #include "dev/watchdog.h"
46 #include "lib/random.h"
54 #include "contiki-conf.h"
57 #ifdef EXPERIMENT_SETUP
58 #include "experiment-setup.h"
63 #ifndef WITH_ACK_OPTIMIZATION
64 #define WITH_ACK_OPTIMIZATION 1
66 #ifndef WITH_ENCOUNTER_OPTIMIZATION
67 #define WITH_ENCOUNTER_OPTIMIZATION 1
69 #ifndef WITH_STREAMING
70 #define WITH_STREAMING 1
72 #ifndef WITH_STROBE_BROADCAST
73 #define WITH_STROBE_BROADCAST 0
76 struct announcement_data {
83 #define ANNOUNCEMENT_MAX 10
86 struct announcement_msg {
88 struct announcement_data data[ANNOUNCEMENT_MAX];
93 #define ANNOUNCEMENT_MSG_HEADERLEN (sizeof (uint16_t))
96 #define TYPE_STROBE 0x10
98 #define TYPE_ANNOUNCEMENT 0x12
99 #define TYPE_STROBE_ACK 0x13
106 #define MAX_STROBE_SIZE 50
108 #ifdef CXMAC_CONF_ON_TIME
109 #define DEFAULT_ON_TIME (CXMAC_CONF_ON_TIME)
111 #define DEFAULT_ON_TIME (RTIMER_ARCH_SECOND / 160)
114 #ifdef CXMAC_CONF_OFF_TIME
115 #define DEFAULT_OFF_TIME (CXMAC_CONF_OFF_TIME)
117 #define DEFAULT_OFF_TIME (RTIMER_ARCH_SECOND / NETSTACK_RDC_CHANNEL_CHECK_RATE - DEFAULT_ON_TIME)
120 #define DEFAULT_PERIOD (DEFAULT_OFF_TIME + DEFAULT_ON_TIME)
122 #define WAIT_TIME_BEFORE_STROBE_ACK RTIMER_ARCH_SECOND / 1000
128 #if DEFAULT_PERIOD == 0
129 #undef DEFAULT_PERIOD
130 #define DEFAULT_PERIOD 1
134 #define ANNOUNCEMENT_PERIOD 4 * CLOCK_SECOND
138 #define ANNOUNCEMENT_TIME (random_rand() % (ANNOUNCEMENT_PERIOD))
140 #define DEFAULT_STROBE_WAIT_TIME (7 * DEFAULT_ON_TIME / 8)
142 struct cxmac_config cxmac_config = {
145 4 * DEFAULT_ON_TIME + DEFAULT_OFF_TIME,
146 DEFAULT_STROBE_WAIT_TIME
153 static volatile uint8_t cxmac_is_on = 0;
155 static volatile unsigned char waiting_for_packet = 0;
156 static volatile unsigned char someone_is_sending = 0;
157 static volatile unsigned char we_are_sending = 0;
158 static volatile unsigned char radio_is_on = 0;
164 #define LEDS_ON(x) leds_on(x)
165 #define LEDS_OFF(x) leds_off(x)
166 #define LEDS_TOGGLE(x) leds_toggle(x)
170 #define PRINTF(...) printf(__VA_ARGS__)
171 #define PRINTDEBUG(...) printf(__VA_ARGS__)
178 #define LEDS_TOGGLE(x)
180 #define PRINTDEBUG(...)
183 #if CXMAC_CONF_ANNOUNCEMENTS
185 static struct ctimer announcement_cycle_ctimer, announcement_ctimer;
187 static int announcement_radio_txpower;
192 static uint8_t is_listening;
194 #if CXMAC_CONF_COMPOWER
198 #if WITH_ENCOUNTER_OPTIMIZATION
204 struct encounter *next;
209 #define MAX_ENCOUNTERS 4
210 LIST(encounter_list);
211 MEMB(encounter_memb,
struct encounter, MAX_ENCOUNTERS);
214 static uint8_t is_streaming;
215 static linkaddr_t is_streaming_to, is_streaming_to_too;
216 static rtimer_clock_t stream_until;
217 #define DEFAULT_STREAM_TIME (RTIMER_ARCH_SECOND)
223 if(cxmac_is_on && radio_is_on == 0) {
233 if(cxmac_is_on && radio_is_on != 0 && is_listening == 0 &&
236 NETSTACK_RADIO.off();
242 powercycle_turn_radio_off(
void)
244 if(we_are_sending == 0 &&
245 waiting_for_packet == 0) {
248 #if CXMAC_CONF_COMPOWER
253 powercycle_turn_radio_on(
void)
255 if(we_are_sending == 0 &&
256 waiting_for_packet == 0) {
261 static struct ctimer cpowercycle_ctimer;
262 #define CSCHEDULE_POWERCYCLE(rtime) cschedule_powercycle((1ul * CLOCK_SECOND * (rtime)) / RTIMER_ARCH_SECOND)
263 static char cpowercycle(
void *ptr);
265 cschedule_powercycle(clock_time_t time)
273 (
void (*)(
void *))cpowercycle,
NULL);
278 cpowercycle(
void *ptr)
281 if(!RTIMER_CLOCK_LT(
RTIMER_NOW(), stream_until)) {
292 if(someone_is_sending > 0) {
293 someone_is_sending--;
297 powercycle_turn_radio_on();
298 CSCHEDULE_POWERCYCLE(DEFAULT_ON_TIME);
301 if(cxmac_config.off_time > 0) {
302 powercycle_turn_radio_off();
303 if(waiting_for_packet != 0) {
304 waiting_for_packet++;
305 if(waiting_for_packet > 2) {
309 waiting_for_packet = 0;
310 powercycle_turn_radio_off();
313 CSCHEDULE_POWERCYCLE(DEFAULT_OFF_TIME);
321 #if CXMAC_CONF_ANNOUNCEMENTS
323 parse_announcements(
const linkaddr_t *from)
326 struct announcement_msg adata;
339 for(i = 0; i < adata.num; ++i) {
347 adata.data[i].value);
353 format_announcement(
char *hdr)
355 struct announcement_msg adata;
363 a !=
NULL && adata.num < ANNOUNCEMENT_MAX;
365 adata.data[adata.num].id = a->id;
366 adata.data[adata.num].value = a->value;
370 memcpy(hdr, &adata,
sizeof(
struct announcement_msg));
373 return ANNOUNCEMENT_MSG_HEADERLEN +
374 sizeof(
struct announcement_data) * adata.num;
381 #if WITH_ENCOUNTER_OPTIMIZATION
383 register_encounter(
const linkaddr_t *neighbor, rtimer_clock_t time)
413 rtimer_clock_t encounter_time = 0;
415 struct cxmac_hdr *hdr;
416 int got_strobe_ack = 0;
417 uint8_t strobe[MAX_STROBE_SIZE];
419 int is_broadcast = 0;
420 int is_dispatch, is_strobe_ack;
423 struct queuebuf *packet;
424 int is_already_streaming = 0;
429 #if !NETSTACK_CONF_BRIDGE_MODE
435 PRINTDEBUG(
"cxmac: send broadcast\n");
437 #if NETSTACK_CONF_WITH_IPV6
438 PRINTDEBUG(
"cxmac: send unicast to %02x%02x:%02x%02x:%02x%02x:%02x%02x\n",
439 packetbuf_addr(PACKETBUF_ADDR_RECEIVER)->u8[0],
440 packetbuf_addr(PACKETBUF_ADDR_RECEIVER)->u8[1],
441 packetbuf_addr(PACKETBUF_ADDR_RECEIVER)->u8[2],
442 packetbuf_addr(PACKETBUF_ADDR_RECEIVER)->u8[3],
443 packetbuf_addr(PACKETBUF_ADDR_RECEIVER)->u8[4],
444 packetbuf_addr(PACKETBUF_ADDR_RECEIVER)->u8[5],
445 packetbuf_addr(PACKETBUF_ADDR_RECEIVER)->u8[6],
446 packetbuf_addr(PACKETBUF_ADDR_RECEIVER)->u8[7]);
448 PRINTDEBUG(
"cxmac: send unicast to %u.%u\n",
449 packetbuf_addr(PACKETBUF_ADDR_RECEIVER)->u8[0],
450 packetbuf_addr(PACKETBUF_ADDR_RECEIVER)->u8[1]);
453 len = NETSTACK_FRAMER.create();
454 strobe_len = len +
sizeof(
struct cxmac_hdr);
455 if(len < 0 || strobe_len > (
int)
sizeof(strobe)) {
457 PRINTF(
"cxmac: send failed, too large header\n");
458 return MAC_TX_ERR_FATAL;
461 strobe[len] = DISPATCH;
462 strobe[len + 1] = TYPE_STROBE;
465 packet = queuebuf_new_from_packetbuf();
468 PRINTF(
"cxmac: send failed, no queue buffer available (of %u)\n",
473 #if WITH_STREAMING && PACKETBUF_WITH_PACKET_TYPE
474 if(is_streaming == 1 &&
478 &is_streaming_to_too))) {
479 is_already_streaming = 1;
481 if(packetbuf_attr(PACKETBUF_ATTR_PACKET_TYPE) ==
482 PACKETBUF_ATTR_PACKET_TYPE_STREAM) {
485 linkaddr_copy(&is_streaming_to, packetbuf_addr(PACKETBUF_ADDR_RECEIVER));
486 }
else if(!
linkaddr_cmp(&is_streaming_to, packetbuf_addr(PACKETBUF_ADDR_RECEIVER))) {
487 linkaddr_copy(&is_streaming_to_too, packetbuf_addr(PACKETBUF_ADDR_RECEIVER));
489 stream_until =
RTIMER_NOW() + DEFAULT_STREAM_TIME;
495 #if WITH_ENCOUNTER_OPTIMIZATION
501 const linkaddr_t *neighbor = packetbuf_addr(PACKETBUF_ADDR_RECEIVER);
504 rtimer_clock_t wait, now, expected;
514 wait = ((rtimer_clock_t)(e->time - now)) % (DEFAULT_PERIOD);
515 expected = now + wait - 2 * DEFAULT_ON_TIME;
517 #if WITH_ACK_OPTIMIZATION && PACKETBUF_WITH_PACKET_TYPE
519 if(packetbuf_attr(PACKETBUF_ATTR_PACKET_TYPE) !=
520 PACKETBUF_ATTR_PACKET_TYPE_ACK &&
524 while(RTIMER_CLOCK_LT(
RTIMER_NOW(), expected));
528 while(RTIMER_CLOCK_LT(
RTIMER_NOW(), expected));
548 if(!is_already_streaming) {
552 for(strobes = 0, collisions = 0;
553 got_strobe_ack == 0 && collisions == 0 &&
554 RTIMER_CLOCK_LT(
RTIMER_NOW(), t0 + cxmac_config.strobe_time);
557 while(got_strobe_ack == 0 &&
558 RTIMER_CLOCK_LT(
RTIMER_NOW(), t + cxmac_config.strobe_wait_time)) {
566 if(NETSTACK_FRAMER.parse() >= 0) {
568 is_dispatch = hdr->dispatch == DISPATCH;
569 is_strobe_ack = hdr->type == TYPE_STROBE_ACK;
570 if(is_dispatch && is_strobe_ack) {
571 if(
linkaddr_cmp(packetbuf_addr(PACKETBUF_ADDR_RECEIVER),
576 encounter_time = now;
578 PRINTDEBUG(
"cxmac: strobe ack for someone else\n");
581 PRINTDEBUG(
"cxmac: strobe from someone else\n");
585 PRINTF(
"cxmac: send failed to parse %u\n", len);
592 if(got_strobe_ack == 0 && collisions == 0) {
594 #if WITH_STROBE_BROADCAST
595 NETSTACK_RADIO.send(strobe, strobe_len);
598 queuebuf_to_packetbuf(packet);
603 NETSTACK_RADIO.send(strobe, strobe_len);
610 while(RTIMER_CLOCK_LT(
RTIMER_NOW(), wt + WAIT_TIME_BEFORE_STROBE_ACK));
618 #if WITH_ACK_OPTIMIZATION
622 if(got_strobe_ack && (
623 #
if NETSTACK_CONF_WITH_RIME
624 packetbuf_attr(PACKETBUF_ATTR_RELIABLE) ||
625 packetbuf_attr(PACKETBUF_ATTR_ERELIABLE) ||
627 #
if PACKETBUF_WITH_PACKET_TYPE
628 packetbuf_attr(PACKETBUF_ATTR_PACKET_TYPE) ==
629 PACKETBUF_ATTR_PACKET_TYPE_STREAM ||
633 waiting_for_packet = 1;
642 queuebuf_to_packetbuf(packet);
643 queuebuf_free(packet);
646 if((is_broadcast || got_strobe_ack || is_streaming) && collisions == 0) {
650 #if WITH_ENCOUNTER_OPTIMIZATION
651 if(got_strobe_ack && !is_streaming) {
652 register_encounter(packetbuf_addr(PACKETBUF_ADDR_RECEIVER), encounter_time);
657 PRINTF(
"cxmac: send (strobes=%u,len=%u,%s), done\n", strobes,
660 #if CXMAC_CONF_COMPOWER
678 if(collisions == 0) {
679 if(!is_broadcast && !got_strobe_ack) {
685 someone_is_sending++;
692 qsend_packet(mac_callback_t sent,
void *ptr)
695 if(someone_is_sending) {
696 PRINTF(
"cxmac: should queue packet, now just dropping %d %d %d %d.\n",
697 waiting_for_packet, someone_is_sending, we_are_sending, radio_is_on);
698 RIMESTATS_ADD(sendingdrop);
701 PRINTF(
"cxmac: send immediately.\n");
705 mac_call_sent_callback(sent, ptr, ret, 1);
709 qsend_list(mac_callback_t sent,
void *ptr,
struct rdc_buf_list *buf_list)
711 if(buf_list !=
NULL) {
712 queuebuf_to_packetbuf(buf_list->buf);
713 qsend_packet(sent, ptr);
720 struct cxmac_hdr *hdr;
722 if(NETSTACK_FRAMER.parse() >= 0) {
725 if(hdr->dispatch != DISPATCH) {
726 someone_is_sending = 0;
727 if(
linkaddr_cmp(packetbuf_addr(PACKETBUF_ADDR_RECEIVER),
738 #if CXMAC_CONF_COMPOWER
752 waiting_for_packet = 0;
755 NETSTACK_MAC.input();
758 PRINTDEBUG(
"cxmac: data not for us\n");
761 }
else if(hdr->type == TYPE_STROBE) {
762 someone_is_sending = 2;
764 if(
linkaddr_cmp(packetbuf_addr(PACKETBUF_ADDR_RECEIVER),
772 hdr->type = TYPE_STROBE_ACK;
773 packetbuf_set_addr(PACKETBUF_ADDR_RECEIVER,
774 packetbuf_addr(PACKETBUF_ADDR_SENDER));
777 if(NETSTACK_FRAMER.create() >= 0) {
780 someone_is_sending = 1;
781 waiting_for_packet = 1;
786 PRINTF(
"cxmac: failed to send strobe ack\n");
788 }
else if(
linkaddr_cmp(packetbuf_addr(PACKETBUF_ADDR_RECEIVER),
794 waiting_for_packet = 1;
797 PRINTDEBUG(
"cxmac: strobe not for us\n");
803 #if CXMAC_CONF_ANNOUNCEMENTS
804 }
else if(hdr->type == TYPE_ANNOUNCEMENT) {
806 parse_announcements(packetbuf_addr(PACKETBUF_ADDR_SENDER));
808 }
else if(hdr->type == TYPE_STROBE_ACK) {
809 PRINTDEBUG(
"cxmac: stray strobe ack\n");
811 PRINTF(
"cxmac: unknown type %u (%u)\n", hdr->type,
819 #if CXMAC_CONF_ANNOUNCEMENTS
821 send_announcement(
void *ptr)
823 struct cxmac_hdr *hdr;
824 int announcement_len;
830 announcement_len = format_announcement((
char *)hdr +
831 sizeof(
struct cxmac_hdr));
833 if(announcement_len > 0) {
835 hdr->dispatch = DISPATCH;
836 hdr->type = TYPE_ANNOUNCEMENT;
840 packetbuf_set_attr(PACKETBUF_ATTR_RADIO_TXPOWER, announcement_radio_txpower);
841 if(NETSTACK_FRAMER.create() >= 0) {
848 cycle_announcement(
void *ptr)
850 ctimer_set(&announcement_ctimer, ANNOUNCEMENT_TIME,
851 send_announcement,
NULL);
852 ctimer_set(&announcement_cycle_ctimer, ANNOUNCEMENT_PERIOD,
853 cycle_announcement,
NULL);
854 if(is_listening > 0) {
861 listen_callback(
int periods)
863 is_listening = periods + 1;
868 cxmac_set_announcement_radio_txpower(
int txpower)
870 #if CXMAC_CONF_ANNOUNCEMENTS
871 announcement_radio_txpower = txpower;
879 waiting_for_packet = 0;
886 #if WITH_ENCOUNTER_OPTIMIZATION
891 #if CXMAC_CONF_ANNOUNCEMENTS
893 ctimer_set(&announcement_cycle_ctimer, ANNOUNCEMENT_TIME,
894 cycle_announcement,
NULL);
897 CSCHEDULE_POWERCYCLE(DEFAULT_OFF_TIME);
906 CSCHEDULE_POWERCYCLE(DEFAULT_OFF_TIME);
911 turn_off(
int keep_radio_on)
915 return NETSTACK_RADIO.on();
917 return NETSTACK_RADIO.off();
921 static unsigned short
922 channel_check_interval(
void)
924 return (1ul *
CLOCK_SECOND * DEFAULT_PERIOD) / RTIMER_ARCH_SECOND;
void * packetbuf_dataptr(void)
Get a pointer to the data in the packetbuf.
Header file for the real-time timer module.
unsigned short(* channel_check_interval)(void)
Returns the channel check interval, expressed in clock_time_t ticks.
Protothreads implementation.
Representation of an announcement.
#define PT_INIT(pt)
Initialize a protothread.
The MAC layer deferred the transmission for a later time.
The MAC layer did not get an acknowledgement for the packet.
Header file for the radio API
#define LIST(name)
Declare a linked list.
void compower_attrconv(struct compower_activity *e)
Convert power contumption information to packet attributes.
void compower_clear(struct compower_activity *e)
Clear power consumption information for a communication activity.
A simple power saving MAC protocol based on X-MAC [SenSys 2006]
#define MEMB(name, structure, num)
Declare a memory block.
#define RTIMER_NOW()
Get the current clock time.
void packetbuf_clear(void)
Clear and reset the packetbuf.
Default definitions of C compiler quirk work-arounds.
#define PT_YIELD(pt)
Yield from the current protothread.
void * list_item_next(void *item)
Get the next item following this item.
An activity record that contains power consumption information for a specific communication activity...
Header file for the communication power accounting module
#define PACKETBUF_SIZE
The size of the packetbuf, in bytes.
uint16_t packetbuf_totlen(void)
Get the total length of the header and data in the packetbuf.
const linkaddr_t linkaddr_null
The null Rime address.
void watchdog_stop(void)
Stops the WDT such that it won't timeout and cause MCU reset.
void compower_accumulate(struct compower_activity *e)
Accumulate power contumption for a communication activity.
int packetbuf_holds_broadcast(void)
Checks whether the current packet is a broadcast.
void * packetbuf_hdrptr(void)
Get a pointer to the header in the packetbuf, for outbound packets.
void announcement_heard(const linkaddr_t *from, uint16_t id, uint16_t value)
Inform the announcement module of an incoming announcement.
struct compower_activity compower_idle_activity
The default idle communication activity.
void packetbuf_compact(void)
Compact the packetbuf.
The MAC layer transmission could not be performed because of a fatal error.
void list_init(list_t list)
Initialize a list.
void memb_init(struct memb *m)
Initialize a memory block that was declared with MEMB().
void * list_head(list_t list)
Get a pointer to the first element of a list.
Header file for the Rime stack
The MAC layer transmission was OK.
#define NULL
The null pointer.
struct announcement * announcement_list(void)
Get the list of registered announcements.
#define PT_BEGIN(pt)
Declare the start of a protothread inside the C function implementing the protothread.
void list_add(list_t list, void *item)
Add an item at the end of a list.
#define CLOCK_SECOND
A second, measured in system clock time.
Linked list manipulation routines.
void announcement_register_listen_callback(void(*callback)(int time))
Register a listen callback with the announcement module.
void packetbuf_set_datalen(uint16_t len)
Set the length of the data in the packetbuf.
The structure of a RDC (radio duty cycling) driver in Contiki.
void ctimer_set(struct ctimer *c, clock_time_t t, void(*f)(void *), void *ptr)
Set a callback timer.
#define PT_END(pt)
Declare the end of a protothread.
uint16_t packetbuf_datalen(void)
Get the length of the data in the packetbuf.
Memory block allocation routines.
int linkaddr_cmp(const linkaddr_t *addr1, const linkaddr_t *addr2)
Compare two Rime addresses.
void linkaddr_copy(linkaddr_t *dest, const linkaddr_t *src)
Copy a Rime address.
linkaddr_t linkaddr_node_addr
The Rime address of the node.
void watchdog_start(void)
Starts the WDT in watchdog mode if enabled by user configuration, maximum interval.
Header file for a simple time synchronization mechanism
void * memb_alloc(struct memb *m)
Allocate a memory block from a block of memory declared with MEMB().
int packetbuf_hdrreduce(int size)
Reduce the header in the packetbuf, for incoming packets.
Include file for the Contiki low-layer network stack (NETSTACK)