44 #include "dev/watchdog.h"
46 #include "lib/random.h"
54 #include "contiki-conf.h"
56 #ifdef EXPERIMENT_SETUP
57 #include "experiment-setup.h"
62 #ifndef WITH_ACK_OPTIMIZATION
63 #define WITH_ACK_OPTIMIZATION 1
65 #ifndef WITH_ENCOUNTER_OPTIMIZATION
66 #define WITH_ENCOUNTER_OPTIMIZATION 1
68 #ifndef WITH_STREAMING
69 #define WITH_STREAMING 1
71 #ifndef WITH_STROBE_BROADCAST
72 #define WITH_STROBE_BROADCAST 0
75 struct announcement_data {
82 #define ANNOUNCEMENT_MAX 10
85 struct announcement_msg {
87 struct announcement_data data[ANNOUNCEMENT_MAX];
92 #define ANNOUNCEMENT_MSG_HEADERLEN (sizeof (uint16_t))
95 #define TYPE_STROBE 0x10
97 #define TYPE_ANNOUNCEMENT 0x12
98 #define TYPE_STROBE_ACK 0x13
105 #define MAX_STROBE_SIZE 50
107 #ifdef CXMAC_CONF_ON_TIME
108 #define DEFAULT_ON_TIME (CXMAC_CONF_ON_TIME)
110 #define DEFAULT_ON_TIME (RTIMER_ARCH_SECOND / 160)
113 #ifdef CXMAC_CONF_OFF_TIME
114 #define DEFAULT_OFF_TIME (CXMAC_CONF_OFF_TIME)
116 #define DEFAULT_OFF_TIME (RTIMER_ARCH_SECOND / NETSTACK_RDC_CHANNEL_CHECK_RATE - DEFAULT_ON_TIME)
119 #define DEFAULT_PERIOD (DEFAULT_OFF_TIME + DEFAULT_ON_TIME)
121 #define WAIT_TIME_BEFORE_STROBE_ACK RTIMER_ARCH_SECOND / 1000
127 #if DEFAULT_PERIOD == 0
128 #undef DEFAULT_PERIOD
129 #define DEFAULT_PERIOD 1
133 #define ANNOUNCEMENT_PERIOD 4 * CLOCK_SECOND
137 #define ANNOUNCEMENT_TIME (random_rand() % (ANNOUNCEMENT_PERIOD))
139 #define DEFAULT_STROBE_WAIT_TIME (7 * DEFAULT_ON_TIME / 8)
141 struct cxmac_config cxmac_config = {
144 4 * DEFAULT_ON_TIME + DEFAULT_OFF_TIME,
145 DEFAULT_STROBE_WAIT_TIME
152 static volatile uint8_t cxmac_is_on = 0;
154 static volatile unsigned char waiting_for_packet = 0;
155 static volatile unsigned char someone_is_sending = 0;
156 static volatile unsigned char we_are_sending = 0;
157 static volatile unsigned char radio_is_on = 0;
163 #define LEDS_ON(x) leds_on(x)
164 #define LEDS_OFF(x) leds_off(x)
165 #define LEDS_TOGGLE(x) leds_toggle(x)
169 #define PRINTF(...) printf(__VA_ARGS__)
170 #define PRINTDEBUG(...) printf(__VA_ARGS__)
177 #define LEDS_TOGGLE(x)
179 #define PRINTDEBUG(...)
182 #if CXMAC_CONF_ANNOUNCEMENTS
184 static struct ctimer announcement_cycle_ctimer, announcement_ctimer;
186 static int announcement_radio_txpower;
191 static uint8_t is_listening;
193 #if CXMAC_CONF_COMPOWER
197 #if WITH_ENCOUNTER_OPTIMIZATION
203 struct encounter *next;
208 #define MAX_ENCOUNTERS 4
209 LIST(encounter_list);
210 MEMB(encounter_memb,
struct encounter, MAX_ENCOUNTERS);
213 static uint8_t is_streaming;
214 static linkaddr_t is_streaming_to, is_streaming_to_too;
215 static rtimer_clock_t stream_until;
216 #define DEFAULT_STREAM_TIME (RTIMER_ARCH_SECOND)
219 #define MIN(a, b) ((a) < (b)? (a) : (b))
226 if(cxmac_is_on && radio_is_on == 0) {
236 if(cxmac_is_on && radio_is_on != 0 && is_listening == 0 &&
239 NETSTACK_RADIO.off();
245 powercycle_turn_radio_off(
void)
247 if(we_are_sending == 0 &&
248 waiting_for_packet == 0) {
251 #if CXMAC_CONF_COMPOWER
256 powercycle_turn_radio_on(
void)
258 if(we_are_sending == 0 &&
259 waiting_for_packet == 0) {
264 static struct ctimer cpowercycle_ctimer;
265 #define CSCHEDULE_POWERCYCLE(rtime) cschedule_powercycle((1ul * CLOCK_SECOND * (rtime)) / RTIMER_ARCH_SECOND)
266 static char cpowercycle(
void *ptr);
268 cschedule_powercycle(clock_time_t time)
276 (
void (*)(
void *))cpowercycle,
NULL);
281 cpowercycle(
void *ptr)
284 if(!RTIMER_CLOCK_LT(
RTIMER_NOW(), stream_until)) {
295 if(someone_is_sending > 0) {
296 someone_is_sending--;
300 powercycle_turn_radio_on();
301 CSCHEDULE_POWERCYCLE(DEFAULT_ON_TIME);
304 if(cxmac_config.off_time > 0) {
305 powercycle_turn_radio_off();
306 if(waiting_for_packet != 0) {
307 waiting_for_packet++;
308 if(waiting_for_packet > 2) {
312 waiting_for_packet = 0;
313 powercycle_turn_radio_off();
316 CSCHEDULE_POWERCYCLE(DEFAULT_OFF_TIME);
324 #if CXMAC_CONF_ANNOUNCEMENTS
326 parse_announcements(
const linkaddr_t *from)
329 struct announcement_msg adata;
342 for(i = 0; i < adata.num; ++i) {
350 adata.data[i].value);
356 format_announcement(
char *hdr)
358 struct announcement_msg adata;
366 a !=
NULL && adata.num < ANNOUNCEMENT_MAX;
368 adata.data[adata.num].id = a->id;
369 adata.data[adata.num].value = a->value;
373 memcpy(hdr, &adata,
sizeof(
struct announcement_msg));
376 return ANNOUNCEMENT_MSG_HEADERLEN +
377 sizeof(
struct announcement_data) * adata.num;
384 #if WITH_ENCOUNTER_OPTIMIZATION
386 register_encounter(
const linkaddr_t *neighbor, rtimer_clock_t time)
416 rtimer_clock_t encounter_time = 0;
418 struct cxmac_hdr *hdr;
419 int got_strobe_ack = 0;
420 uint8_t strobe[MAX_STROBE_SIZE];
422 int is_broadcast = 0;
423 int is_dispatch, is_strobe_ack;
426 struct queuebuf *packet;
427 int is_already_streaming = 0;
432 #if !NETSTACK_CONF_BRIDGE_MODE
438 PRINTDEBUG(
"cxmac: send broadcast\n");
441 PRINTDEBUG(
"cxmac: send unicast to %02x%02x:%02x%02x:%02x%02x:%02x%02x\n",
442 packetbuf_addr(PACKETBUF_ADDR_RECEIVER)->u8[0],
443 packetbuf_addr(PACKETBUF_ADDR_RECEIVER)->u8[1],
444 packetbuf_addr(PACKETBUF_ADDR_RECEIVER)->u8[2],
445 packetbuf_addr(PACKETBUF_ADDR_RECEIVER)->u8[3],
446 packetbuf_addr(PACKETBUF_ADDR_RECEIVER)->u8[4],
447 packetbuf_addr(PACKETBUF_ADDR_RECEIVER)->u8[5],
448 packetbuf_addr(PACKETBUF_ADDR_RECEIVER)->u8[6],
449 packetbuf_addr(PACKETBUF_ADDR_RECEIVER)->u8[7]);
451 PRINTDEBUG(
"cxmac: send unicast to %u.%u\n",
452 packetbuf_addr(PACKETBUF_ADDR_RECEIVER)->u8[0],
453 packetbuf_addr(PACKETBUF_ADDR_RECEIVER)->u8[1]);
458 len = NETSTACK_FRAMER.create();
459 strobe_len = len +
sizeof(
struct cxmac_hdr);
460 if(len < 0 || strobe_len > (
int)
sizeof(strobe)) {
462 PRINTF(
"cxmac: send failed, too large header\n");
463 return MAC_TX_ERR_FATAL;
466 strobe[len] = DISPATCH;
467 strobe[len + 1] = TYPE_STROBE;
470 packet = queuebuf_new_from_packetbuf();
473 PRINTF(
"cxmac: send failed, no queue buffer available (of %u)\n",
479 if(is_streaming == 1 &&
483 &is_streaming_to_too))) {
484 is_already_streaming = 1;
486 if(packetbuf_attr(PACKETBUF_ATTR_PACKET_TYPE) ==
487 PACKETBUF_ATTR_PACKET_TYPE_STREAM) {
490 linkaddr_copy(&is_streaming_to, packetbuf_addr(PACKETBUF_ADDR_RECEIVER));
491 }
else if(!
linkaddr_cmp(&is_streaming_to, packetbuf_addr(PACKETBUF_ADDR_RECEIVER))) {
492 linkaddr_copy(&is_streaming_to_too, packetbuf_addr(PACKETBUF_ADDR_RECEIVER));
494 stream_until =
RTIMER_NOW() + DEFAULT_STREAM_TIME;
500 #if WITH_ENCOUNTER_OPTIMIZATION
506 const linkaddr_t *neighbor = packetbuf_addr(PACKETBUF_ADDR_RECEIVER);
509 rtimer_clock_t wait, now, expected;
519 wait = ((rtimer_clock_t)(e->time - now)) % (DEFAULT_PERIOD);
520 expected = now + wait - 2 * DEFAULT_ON_TIME;
522 #if WITH_ACK_OPTIMIZATION
524 if(packetbuf_attr(PACKETBUF_ATTR_PACKET_TYPE) !=
525 PACKETBUF_ATTR_PACKET_TYPE_ACK &&
529 while(RTIMER_CLOCK_LT(
RTIMER_NOW(), expected));
533 while(RTIMER_CLOCK_LT(
RTIMER_NOW(), expected));
553 if(!is_already_streaming) {
557 for(strobes = 0, collisions = 0;
558 got_strobe_ack == 0 && collisions == 0 &&
559 RTIMER_CLOCK_LT(
RTIMER_NOW(), t0 + cxmac_config.strobe_time);
562 while(got_strobe_ack == 0 &&
563 RTIMER_CLOCK_LT(
RTIMER_NOW(), t + cxmac_config.strobe_wait_time)) {
571 if(NETSTACK_FRAMER.parse() >= 0) {
573 is_dispatch = hdr->dispatch == DISPATCH;
574 is_strobe_ack = hdr->type == TYPE_STROBE_ACK;
575 if(is_dispatch && is_strobe_ack) {
576 if(
linkaddr_cmp(packetbuf_addr(PACKETBUF_ADDR_RECEIVER),
581 encounter_time = now;
583 PRINTDEBUG(
"cxmac: strobe ack for someone else\n");
586 PRINTDEBUG(
"cxmac: strobe from someone else\n");
590 PRINTF(
"cxmac: send failed to parse %u\n", len);
597 if(got_strobe_ack == 0 && collisions == 0) {
599 #if WITH_STROBE_BROADCAST
600 NETSTACK_RADIO.send(strobe, strobe_len);
603 queuebuf_to_packetbuf(packet);
608 NETSTACK_RADIO.send(strobe, strobe_len);
615 while(RTIMER_CLOCK_LT(
RTIMER_NOW(), wt + WAIT_TIME_BEFORE_STROBE_ACK));
623 #if WITH_ACK_OPTIMIZATION
627 if(got_strobe_ack && (packetbuf_attr(PACKETBUF_ATTR_RELIABLE) ||
628 packetbuf_attr(PACKETBUF_ATTR_ERELIABLE) ||
629 packetbuf_attr(PACKETBUF_ATTR_PACKET_TYPE) ==
630 PACKETBUF_ATTR_PACKET_TYPE_STREAM)) {
632 waiting_for_packet = 1;
641 queuebuf_to_packetbuf(packet);
642 queuebuf_free(packet);
645 if((is_broadcast || got_strobe_ack || is_streaming) && collisions == 0) {
649 #if WITH_ENCOUNTER_OPTIMIZATION
650 if(got_strobe_ack && !is_streaming) {
651 register_encounter(packetbuf_addr(PACKETBUF_ADDR_RECEIVER), encounter_time);
656 PRINTF(
"cxmac: send (strobes=%u,len=%u,%s), done\n", strobes,
659 #if CXMAC_CONF_COMPOWER
677 if(collisions == 0) {
678 if(!is_broadcast && !got_strobe_ack) {
684 someone_is_sending++;
691 qsend_packet(mac_callback_t sent,
void *ptr)
694 if(someone_is_sending) {
695 PRINTF(
"cxmac: should queue packet, now just dropping %d %d %d %d.\n",
696 waiting_for_packet, someone_is_sending, we_are_sending, radio_is_on);
697 RIMESTATS_ADD(sendingdrop);
700 PRINTF(
"cxmac: send immediately.\n");
704 mac_call_sent_callback(sent, ptr, ret, 1);
708 qsend_list(mac_callback_t sent,
void *ptr,
struct rdc_buf_list *buf_list)
710 if(buf_list !=
NULL) {
711 queuebuf_to_packetbuf(buf_list->buf);
712 qsend_packet(sent, ptr);
719 struct cxmac_hdr *hdr;
721 if(NETSTACK_FRAMER.parse() >= 0) {
724 if(hdr->dispatch != DISPATCH) {
725 someone_is_sending = 0;
726 if(
linkaddr_cmp(packetbuf_addr(PACKETBUF_ADDR_RECEIVER),
737 #if CXMAC_CONF_COMPOWER
751 waiting_for_packet = 0;
754 NETSTACK_MAC.input();
757 PRINTDEBUG(
"cxmac: data not for us\n");
760 }
else if(hdr->type == TYPE_STROBE) {
761 someone_is_sending = 2;
763 if(
linkaddr_cmp(packetbuf_addr(PACKETBUF_ADDR_RECEIVER),
771 hdr->type = TYPE_STROBE_ACK;
772 packetbuf_set_addr(PACKETBUF_ADDR_RECEIVER,
773 packetbuf_addr(PACKETBUF_ADDR_SENDER));
776 if(NETSTACK_FRAMER.create() >= 0) {
779 someone_is_sending = 1;
780 waiting_for_packet = 1;
785 PRINTF(
"cxmac: failed to send strobe ack\n");
787 }
else if(
linkaddr_cmp(packetbuf_addr(PACKETBUF_ADDR_RECEIVER),
793 waiting_for_packet = 1;
796 PRINTDEBUG(
"cxmac: strobe not for us\n");
802 #if CXMAC_CONF_ANNOUNCEMENTS
803 }
else if(hdr->type == TYPE_ANNOUNCEMENT) {
805 parse_announcements(packetbuf_addr(PACKETBUF_ADDR_SENDER));
807 }
else if(hdr->type == TYPE_STROBE_ACK) {
808 PRINTDEBUG(
"cxmac: stray strobe ack\n");
810 PRINTF(
"cxmac: unknown type %u (%u)\n", hdr->type,
818 #if CXMAC_CONF_ANNOUNCEMENTS
820 send_announcement(
void *ptr)
822 struct cxmac_hdr *hdr;
823 int announcement_len;
829 announcement_len = format_announcement((
char *)hdr +
830 sizeof(
struct cxmac_hdr));
832 if(announcement_len > 0) {
834 hdr->dispatch = DISPATCH;
835 hdr->type = TYPE_ANNOUNCEMENT;
839 packetbuf_set_attr(PACKETBUF_ATTR_RADIO_TXPOWER, announcement_radio_txpower);
840 if(NETSTACK_FRAMER.create() >= 0) {
847 cycle_announcement(
void *ptr)
849 ctimer_set(&announcement_ctimer, ANNOUNCEMENT_TIME,
850 send_announcement,
NULL);
851 ctimer_set(&announcement_cycle_ctimer, ANNOUNCEMENT_PERIOD,
852 cycle_announcement,
NULL);
853 if(is_listening > 0) {
860 listen_callback(
int periods)
862 is_listening = periods + 1;
867 cxmac_set_announcement_radio_txpower(
int txpower)
869 #if CXMAC_CONF_ANNOUNCEMENTS
870 announcement_radio_txpower = txpower;
878 waiting_for_packet = 0;
885 #if WITH_ENCOUNTER_OPTIMIZATION
890 #if CXMAC_CONF_ANNOUNCEMENTS
892 ctimer_set(&announcement_cycle_ctimer, ANNOUNCEMENT_TIME,
893 cycle_announcement,
NULL);
896 CSCHEDULE_POWERCYCLE(DEFAULT_OFF_TIME);
905 CSCHEDULE_POWERCYCLE(DEFAULT_OFF_TIME);
910 turn_off(
int keep_radio_on)
914 return NETSTACK_RADIO.on();
916 return NETSTACK_RADIO.off();
920 static unsigned short
921 channel_check_interval(
void)
923 return (1ul *
CLOCK_SECOND * DEFAULT_PERIOD) / RTIMER_ARCH_SECOND;
Linked list manipulation routines.
linkaddr_t linkaddr_node_addr
The Rime address of the node.
void compower_accumulate(struct compower_activity *e)
Accumulate power contumption for a communication activity.
void memb_init(struct memb *m)
Initialize a memory block that was declared with MEMB().
#define LEDS_RED
LED1 (Red) -> PC0.
void watchdog_start(void)
Starts the WDT in watchdog mode if enabled by user configuration, maximum interval.
#define PT_YIELD(pt)
Yield from the current protothread.
Header file for the radio API
const linkaddr_t linkaddr_null
The null Rime address.
The MAC layer deferred the transmission for a later time.
void * list_item_next(void *item)
Get the next item following this item.
void compower_attrconv(struct compower_activity *e)
Convert power contumption information to packet attributes.
void announcement_heard(const linkaddr_t *from, uint16_t id, uint16_t value)
Inform the announcement module of an incoming announcement.
void * memb_alloc(struct memb *m)
Allocate a memory block from a block of memory declared with MEMB().
#define NULL
The null pointer.
The MAC layer transmission could not be performed because of a fatal error.
#define PT_INIT(pt)
Initialize a protothread.
Header file for a simple time synchronization mechanism
struct compower_activity compower_idle_activity
The default idle communication activity.
void packetbuf_set_datalen(uint16_t len)
Set the length of the data in the packetbuf.
uint16_t packetbuf_totlen(void)
Get the total length of the header and data in the packetbuf.
Header file for the Rime stack
void compower_clear(struct compower_activity *e)
Clear power consumption information for a communication activity.
Header file for the communication power accounting module
uint16_t packetbuf_datalen(void)
Get the length of the data in the packetbuf.
struct announcement * announcement_list(void)
Get the list of registered announcements.
void linkaddr_copy(linkaddr_t *dest, const linkaddr_t *src)
Copy a Rime address.
void watchdog_stop(void)
In watchdog mode, the WDT can not be stopped.
void list_init(list_t list)
Initialize a list.
void ctimer_set(struct ctimer *c, clock_time_t t, void(*f)(void *), void *ptr)
Set a callback timer.
void * packetbuf_hdrptr(void)
Get a pointer to the header in the packetbuf, for outbound packets.
Header file for the real-time timer module.
void * list_head(list_t list)
Get a pointer to the first element of a list.
Protothreads implementation.
#define MEMB(name, structure, num)
Declare a memory block.
void packetbuf_compact(void)
Compact the packetbuf.
void list_add(list_t list, void *item)
Add an item at the end of a list.
#define PT_BEGIN(pt)
Declare the start of a protothread inside the C function implementing the protothread.
void announcement_register_listen_callback(void(*callback)(int time))
Register a listen callback with the announcement module.
#define LIST(name)
Declare a linked list.
unsigned short(* channel_check_interval)(void)
Returns the channel check interval, expressed in clock_time_t ticks.
The structure of a RDC (radio duty cycling) driver in Contiki.
void packetbuf_clear(void)
Clear and reset the packetbuf.
Representation of an announcement.
#define PT_END(pt)
Declare the end of a protothread.
The MAC layer transmission was OK.
#define RTIMER_NOW()
Get the current clock time.
int linkaddr_cmp(const linkaddr_t *addr1, const linkaddr_t *addr2)
Compare two Rime addresses.
A simple power saving MAC protocol based on X-MAC [SenSys 2006]
#define PACKETBUF_SIZE
The size of the packetbuf, in bytes.
The MAC layer did not get an acknowledgement for the packet.
void * packetbuf_dataptr(void)
Get a pointer to the data in the packetbuf.
Memory block allocation routines.
An activity record that contains power consumption information for a specific communication activity...
int packetbuf_hdrreduce(int size)
Reduce the header in the packetbuf, for incoming packets.
Include file for the Contiki low-layer network stack (NETSTACK)
#define CLOCK_SECOND
A second, measured in system clock time.