From 09c223f549ffbb4f351c2b27f722304538f561fc Mon Sep 17 00:00:00 2001 From: adamdunkels Date: Tue, 25 Jan 2011 14:31:09 +0000 Subject: [PATCH] Updated Contiki X-MAC to use hardware acks, which allows for tighter timing and results in a lower duty cycle. Hardware acks also make X-MAC more robust against packet losses, as there is a direct way for the CSMA layer to detect loss of data packets. Before, Contiki X-MAC only detected loss of strobes. --- core/net/mac/xmac.c | 83 ++++++++++++++++++++++++++++++++++++++------- 1 file changed, 70 insertions(+), 13 deletions(-) diff --git a/core/net/mac/xmac.c b/core/net/mac/xmac.c index 472a25ace..fcdb88924 100644 --- a/core/net/mac/xmac.c +++ b/core/net/mac/xmac.c @@ -28,7 +28,7 @@ * * This file is part of the Contiki operating system. * - * $Id: xmac.c,v 1.59 2010/10/03 22:46:53 joxe Exp $ + * $Id: xmac.c,v 1.60 2011/01/25 14:31:09 adamdunkels Exp $ */ /** @@ -137,7 +137,7 @@ struct xmac_hdr { cycle. */ #define ANNOUNCEMENT_TIME (random_rand() % (ANNOUNCEMENT_PERIOD)) -#define DEFAULT_STROBE_WAIT_TIME (7 * DEFAULT_ON_TIME / 8) +#define DEFAULT_STROBE_WAIT_TIME (5 * DEFAULT_ON_TIME / 8) struct xmac_config xmac_config = { DEFAULT_ON_TIME, @@ -415,6 +415,40 @@ register_encounter(const rimeaddr_t *neighbor, rtimer_clock_t time) #endif /* WITH_ENCOUNTER_OPTIMIZATION */ /*---------------------------------------------------------------------------*/ static int +detect_ack(void) +{ +#define INTER_PACKET_INTERVAL RTIMER_ARCH_SECOND / 5000 +#define ACK_LEN 3 +#define AFTER_ACK_DETECTECT_WAIT_TIME RTIMER_ARCH_SECOND / 1000 + rtimer_clock_t wt; + uint8_t ack_received = 0; + + wt = RTIMER_NOW(); + leds_on(LEDS_GREEN); + while(RTIMER_CLOCK_LT(RTIMER_NOW(), wt + INTER_PACKET_INTERVAL)) { } + leds_off(LEDS_GREEN); + /* Check for incoming ACK. */ + if((NETSTACK_RADIO.receiving_packet() || + NETSTACK_RADIO.pending_packet() || + NETSTACK_RADIO.channel_clear() == 0)) { + int len; + uint8_t ackbuf[ACK_LEN + 2]; + + wt = RTIMER_NOW(); + while(RTIMER_CLOCK_LT(RTIMER_NOW(), wt + AFTER_ACK_DETECTECT_WAIT_TIME)) { } + + len = NETSTACK_RADIO.read(ackbuf, ACK_LEN); + if(len == ACK_LEN) { + ack_received = 1; + } + } + if(ack_received) { + leds_toggle(LEDS_RED); + } + return ack_received; +} +/*---------------------------------------------------------------------------*/ +static int send_packet(void) { rtimer_clock_t t0; @@ -422,7 +456,8 @@ send_packet(void) rtimer_clock_t encounter_time = 0; int strobes; struct xmac_hdr *hdr; - int got_strobe_ack = 0; + uint8_t got_strobe_ack = 0; + uint8_t got_ack = 0; uint8_t strobe[MAX_STROBE_SIZE]; int strobe_len, len; int is_broadcast = 0; @@ -456,6 +491,8 @@ send_packet(void) } is_reliable = packetbuf_attr(PACKETBUF_ATTR_RELIABLE) || packetbuf_attr(PACKETBUF_ATTR_ERELIABLE); + + packetbuf_set_attr(PACKETBUF_ATTR_MAC_ACK, 1); len = NETSTACK_FRAMER.create(); strobe_len = len + sizeof(struct xmac_hdr); if(len == 0 || strobe_len > sizeof(strobe)) { @@ -518,6 +555,9 @@ send_packet(void) now = RTIMER_NOW(); wait = ((rtimer_clock_t)(e->time - now)) % (DEFAULT_PERIOD); + if(wait < 2 * DEFAULT_ON_TIME) { + wait = DEFAULT_PERIOD; + } expected = now + wait - 2 * DEFAULT_ON_TIME; #if WITH_ACK_OPTIMIZATION @@ -549,7 +589,7 @@ send_packet(void) /* Send a train of strobes until the receiver answers with an ACK. */ /* Turn on the radio to listen for the strobe ACK. */ - on(); + // on(); collisions = 0; if(!is_already_streaming) { watchdog_stop(); @@ -563,6 +603,8 @@ send_packet(void) while(got_strobe_ack == 0 && RTIMER_CLOCK_LT(RTIMER_NOW(), t + xmac_config.strobe_wait_time)) { rtimer_clock_t now = RTIMER_NOW(); + +#if 0 /* See if we got an ACK */ packetbuf_clear(); len = NETSTACK_RADIO.read(packetbuf_dataptr(), PACKETBUF_SIZE); @@ -588,8 +630,9 @@ send_packet(void) PRINTF("xmac: send failed to parse %u\n", len); } } +#endif /* 0 */ } - + t = RTIMER_NOW(); /* Send the strobe packet. */ if(got_strobe_ack == 0 && collisions == 0) { @@ -602,11 +645,12 @@ send_packet(void) queuebuf_to_packetbuf(packet); NETSTACK_RADIO.send(packetbuf_hdrptr(), packetbuf_totlen()); #endif - off(); + off(); } else { rtimer_clock_t wt; + on(); NETSTACK_RADIO.send(strobe, strobe_len); -#if 1 +#if 0 /* Turn off the radio for a while to let the other side respond. We don't need to keep our radio on when we know that the other side needs some time to produce a reply. */ @@ -614,8 +658,13 @@ send_packet(void) wt = RTIMER_NOW(); while(RTIMER_CLOCK_LT(RTIMER_NOW(), wt + WAIT_TIME_BEFORE_STROBE_ACK)); #endif /* 0 */ - on(); - } + + if(detect_ack()) { + got_strobe_ack = 1; + } else { + off(); + } + } } } } @@ -633,8 +682,6 @@ send_packet(void) } else { off(); } -#else /* WITH_ACK_OPTIMIZATION */ - off(); #endif /* WITH_ACK_OPTIMIZATION */ /* restore the packet to send */ @@ -644,7 +691,14 @@ send_packet(void) /* Send the data packet. */ if((is_broadcast || got_strobe_ack || is_streaming) && collisions == 0) { NETSTACK_RADIO.send(packetbuf_hdrptr(), packetbuf_totlen()); + + if(!is_broadcast) { + if(detect_ack()) { + got_ack = 1; + } + } } + off(); #if WITH_ENCOUNTER_OPTIMIZATION if(got_strobe_ack && !is_streaming) { @@ -675,7 +729,7 @@ send_packet(void) LEDS_OFF(LEDS_BLUE); if(collisions == 0) { - if(!is_broadcast && !got_strobe_ack) { + if(is_broadcast == 0 && got_ack == 0) { return MAC_TX_NOACK; } else { return MAC_TX_OK; @@ -781,6 +835,8 @@ input_packet(void) acknowledge the strobe and wait for the packet. By using the same address as both sender and receiver, we flag the message is a strobe ack. */ + waiting_for_packet = 1; +#if 0 hdr->type = TYPE_STROBE_ACK; packetbuf_set_addr(PACKETBUF_ADDR_RECEIVER, packetbuf_addr(PACKETBUF_ADDR_SENDER)); @@ -797,6 +853,7 @@ input_packet(void) } else { PRINTF("xmac: failed to send strobe ack\n"); } +#endif /* 0 */ } else if(rimeaddr_cmp(packetbuf_addr(PACKETBUF_ADDR_RECEIVER), &rimeaddr_null)) { /* If the receiver address is null, the strobe is sent to @@ -808,7 +865,7 @@ input_packet(void) } else { PRINTDEBUG("xmac: strobe not for us\n"); } - + /* We are done processing the strobe and we therefore return to the caller. */ return;