Esempio n. 1
0
void handle_discover_request(pkt_t *pkt)
{
    node_id_t origin;
    uint8_t seq, depth;
    nrk_time_t delay;
    uint8_t attempt;
    int8_t rc;

    origin = pkt->payload[PKT_REQUEST_ORIGIN_OFFSET];
    seq = pkt->payload[PKT_REQUEST_SEQ_OFFSET];
    depth = pkt->payload[PKT_REQUEST_DEPTH_OFFSET];

    if (!IS_VALID_NODE_ID(origin)) {
        LOG("WARN: invalid origin node id in response: ");
        LOGP("%d\r\n", origin);
        return;
    }

    LOG("request:");
    LOGA(" src "); LOGP("%u", pkt->src);
    LOGA(" orig "); LOGP("%u", origin);
    LOGA(" seq "); LOGP("%d (%d)", seq, discovered_seq);
    LOGA(" depth "); LOGP("%d\r\n", depth);

    nrk_time_get(&last_activity);

    choose_delay(&delay, &discover_req_delay);
    nrk_wait(delay);

    /* Discover this node: mark and broadcast to neighbors */
    if (discovered_seq != seq) {
        discovered_seq = seq;
        route_to_origin = pkt->src;

        LOG("discovered: orig "); LOGP("%d", origin);
        LOGA(" seq "); LOGP("%d", discovered_seq);
        LOGA(" hop "); LOGP("%d\r\n", route_to_origin);

        if (depth == 0 || pkt->hops < depth) {
            attempt = 0;
            do {
                rc = broadcast_request(origin, seq, depth, attempt);
                if (rc != NRK_OK)
                    LOG("WARN: failed to broadcast req\r\n");
                nrk_wait(delay);
            } while (++attempt < discover_send_attempts);
        } else {
            LOG("max depth reached: "); LOGP("%d\r\n", depth);
        }
    } else {
        LOG("already discovered: seq "); LOGP("%u\r\n", discovered_seq);
    }

    attempt = 0;
    do {
        send_response(origin, pkt->src, seq, attempt);
        nrk_wait(delay);
    } while (++attempt < discover_send_attempts);
}
Esempio n. 2
0
// This function will put the node into a low-duty checking mode to save power
void tdma_snooze()
{
    int8_t v;
    uint8_t i;

// stop the software watchdog timer so it doesn't interrupt
    nrk_sw_wdt_stop(0);
// This flag is cleared only by the button interrupt
    snoozing=1;
// Setup the button interrupt
    nrk_ext_int_configure( NRK_EXT_INT_1,NRK_LEVEL_TRIGGER, &wakeup_func);
// Clear it so it doesn't fire instantly
    EIFR=0xff;
    nrk_ext_int_enable( NRK_EXT_INT_1);
// Now loop and periodically check for packets
    while(1)
    {
        nrk_led_clr(RED_LED);
        nrk_led_clr(GREEN_LED);
        rf_power_up();
        rf_rx_on ();
// check return from button interrupt on next cycle
        if(snoozing==0 ) return;
        tmp_time.secs=0;
        tmp_time.nano_secs=10*NANOS_PER_MS;
        nrk_led_set(RED_LED);
        // Leave radio on for two loops of 10ms for a total of 20ms every 10 seconds
        for(i=0; i<2; i++ )
        {
            v = _tdma_rx ();
            if(v==NRK_OK) {
                nrk_led_set(RED_LED);
                nrk_ext_int_disable( NRK_EXT_INT_1);
                nrk_sw_wdt_update(0);
                nrk_sw_wdt_start(0);
                return NRK_OK;
            }
            nrk_wait(tmp_time);
        }
        nrk_led_clr(RED_LED);
        rf_power_down();
        tmp_time.secs=10;
        tmp_time.nano_secs=0;
        nrk_sw_wdt_stop(0);
        nrk_wait(tmp_time);

    }

}
Esempio n. 3
0
void handle_discover_response(pkt_t *pkt)
{
    node_id_t origin = pkt->payload[PKT_RESPONSE_ORIGIN_OFFSET];
    uint8_t seq = pkt->payload[PKT_RESPONSE_SEQ_OFFSET];
    nrk_time_t delay;
    uint8_t attempt;

    if (!IS_VALID_NODE_ID(origin)) {
        LOG("WARN: invalid origin in response: ");
        LOGP("%d\r\n", origin);
        return;
    }

    LOG("response: orig "); LOGP("%u", origin);
    LOGA(" seq "); LOGP("%u", seq);
    LOGA(" src "); LOGP("%u", pkt->src);
    LOGA(": ");

    nrk_time_get(&last_activity);

    if (origin == this_node_id) {
        LOGA("reached origin\r\n");

        add_path_to_graph(pkt);
        print_graph(&network);
    } else { /* we're not the destination: forward to gateway */
        attempt = 0;
        do {
            forward_response(pkt, attempt);
            choose_delay(&delay, &discover_req_delay);
            nrk_wait(delay);
        } while (++attempt < discover_send_attempts);
    }
}
Esempio n. 4
0
int8_t nrk_wait_until (nrk_time_t t)
{
    nrk_time_t ct;
    int8_t v;
//    uint8_t c;

    //c = _nrk_os_timer_get ();
    //do{
    //}while(_nrk_os_timer_get()==c);

    //ttt=c+1;
    nrk_time_get (&ct);

    v = nrk_time_sub (&t, t, ct);
    //nrk_time_compact_nanos(&t);
    if (v == NRK_ERROR)
    {
        return NRK_ERROR;
    }
//if(t.secs<ct.secs) return 0;
//if(t.secs==ct.secs && t.nano_secs<ct.nano_secs) return 0;

//t.secs-=ct.secs;
//t.nano_secs-=ct.nano_secs;
//
    nrk_wait (t);

    return NRK_OK;
}
Esempio n. 5
0
static int8_t proc_ping(node_id_t requester,
                        uint8_t *req_buf, uint8_t req_len,
                        uint8_t *reply_buf, uint8_t reply_size,
                        uint8_t *reply_len)
{
    uint8_t token;

    if (req_len != RPC_PING_REQ_LEN) {
        LOG("WARN: req of unexpected length: ");
        LOGP("%u/%u\r\n", req_len, RPC_PING_REQ_LEN);
        return NRK_ERROR;
    }

    if (reply_size < RPC_PING_REPLY_LEN) {
        LOG("WARN: reply buf too small: ");
        LOGP("%u/%u\r\n", reply_size, RPC_PING_REPLY_LEN);
        return NRK_ERROR;
    }

    nrk_led_set(led_proc_ping);
    nrk_wait(pong_delay);
    nrk_led_clr(led_proc_ping);

    token = req_buf[RPC_PING_REQ_TOKEN_OFFSET];

    reply_buf[RPC_PING_REPLY_TOKEN_OFFSET] = token;
    *reply_len = RPC_PING_REPLY_LEN;

    return NRK_OK;
}
Esempio n. 6
0
int8_t _bmac_rx()
{
int8_t n;
uint8_t cnt;

	rf_set_rx (&bmac_rfRxInfo, g_chan);
        rf_polling_rx_on ();
	cnt=0;
	while ((n = rf_rx_check_fifop()) == 0)
	{
	cnt++;
	nrk_wait(_bmac_check_period);
	if(cnt>2) { 
			#ifdef DEBUG
			printf( "rx timeout 1 %d\r\n",cnt );
			#endif
			if(rx_failure_cnt<65535) rx_failure_cnt++;
			rf_rx_off();
			return 0;
			} 
	}
        if (n != 0) {
                n = 0;
                // Packet on its way
                cnt=0;
                while ((n = rf_polling_rx_packet ()) == 0) {
                cnt++;
                nrk_spin_wait_us(100);
                if (cnt > 50) { 
			#ifdef DEBUG
			printf( "rx timeout 2\r\n" ); 
			#endif
			rx_failure_cnt++;
			rf_rx_off(); 
			return 0;
			}        
                }
        }
        rf_rx_off();
        if (n == 1) {
                // CRC and checksum passed
    		rx_buf_empty=0;
		#ifdef DEBUG
                printf( "BMAC: SNR= %d [",bmac_rfRxInfo.rssi );
                for(uint8_t i=0; i<bmac_rfRxInfo.length; i++ )
                        printf( "%c", bmac_rfRxInfo.pPayload[i]);
                printf( "]\r\n" );
		#endif
		return 1;
        } else 
	{
	#ifdef DEBUG 
	printf( "CRC failed!\r\n" );
	#endif 
	rx_failure_cnt++;
	return 0; 
	}
rx_failure_cnt++;
return 0;
}
Esempio n. 7
0
void bmac_nw_task ()
{
int8_t v;
int8_t e;
uint8_t backoff;
nrk_sig_mask_t event;

while(bmac_started()==0) nrk_wait_until_next_period();

//register the signal after bmac_init has been called
v=nrk_signal_register(bmac_enable_signal); 
if(v==NRK_ERROR) nrk_kprintf( PSTR("Failed to register signal\r\n"));
backoff=0;
    while (1) {
    #ifdef NRK_SW_WDT
	#ifdef BMAC_SW_WDT_ID
	nrk_sw_wdt_update(BMAC_SW_WDT_ID);
	#endif
    #endif
	if(is_enabled ) { 
	v=1;
	if(rx_buf_empty==1) v=_bmac_channel_check();
	// If the buffer is full, signal the receiving task again.
	else e=nrk_event_signal (bmac_rx_pkt_signal);
	// bmac_channel check turns on radio, don't turn off if
	// data is coming.
		if(v==0)
			{
			if(_bmac_rx()==1)
			  {
				e=nrk_event_signal (bmac_rx_pkt_signal);
				//if(e==NRK_ERROR) {
				//	nrk_kprintf( PSTR("bmac rx pkt signal failed\r\n"));
				//	printf( "errno: %u \r\n",nrk_errno_get() );
				//}
			  }
			  //else nrk_kprintf( PSTR("Pkt failed, buf could be corrupt\r\n" ));
			} 
		if(/*rx_buf_empty==1 &&*/ tx_data_ready==1)
			{
				rf_rx_off(); 
				_bmac_tx();
			}
	//do {
		nrk_wait(_bmac_check_period); 
	//	if(rx_buf_empty!=1)  nrk_event_signal (bmac_rx_pkt_signal);
	//} while(rx_buf_empty!=1);
	} else {
		event=0;
		do {
		v=nrk_signal_register(bmac_enable_signal); 
    		event=nrk_event_wait (SIG(bmac_enable_signal));
		} while((event & SIG(bmac_enable_signal))==0);
	}
	//nrk_wait_until_next_period();
	}

}
Esempio n. 8
0
int8_t _bmac_tx()
{
uint8_t v,backoff, backoff_count;
uint16_t b;

#ifdef DEBUG
nrk_kprintf( PSTR("_bmac_tx()\r\n"));
#endif
if(cca_active)
{

// Add random time here to stop nodes from synchronizing with eachother
b=_nrk_time_to_ticks(&_bmac_check_period);
b=b/((rand()%10)+1);
//printf( "waiting %d\r\n",b );
nrk_wait_until_ticks(b);
//nrk_wait_ticks(b);

	backoff_count=1;
	do{
	v=_bmac_channel_check();
	rf_rx_off(); 
	if(v==1) break;
	// Channel is busy
	backoff=rand()%(_b_pow(backoff_count));
			#ifdef DEBUG
			printf( "backoff %d\r\n",backoff );
			#endif
//	printf( "backoff %d\r\n",backoff );
	nrk_wait_until_next_n_periods(backoff);
	backoff_count++;
	if(backoff_count>6) backoff_count=6; // cap it at 64	
	b=_nrk_time_to_ticks(&_bmac_check_period);
	b=b/((rand()%10)+1);
//	printf( "waiting %d\r\n",b );
	nrk_wait_until_ticks(b);
//	nrk_wait_ticks(b);

	} while(v==0);
}

	rf_test_mode();
	rf_carrier_on(); 
	nrk_wait(_bmac_check_period);
	//nrk_wait_until_next_period();
	rf_carrier_off(); 
	rf_data_mode();
	// send packet
	rf_rx_off();
	pkt_got_ack=rf_tx_packet (&bmac_rfTxInfo);
	rf_rx_off(); 	
tx_data_ready=0;
nrk_event_signal (bmac_tx_pkt_done_signal);
return NRK_OK;
}
Esempio n. 9
0
void oled_on()
{
nrk_time_t t;
nrk_gpio_set(NRK_DEBUG_1);
t.secs=1;
t.nano_secs=500*NANOS_PER_MS;
nrk_kprintf( PSTR( "start wait\r\n" ));
nrk_wait(t);
nrk_kprintf( PSTR( "stop wait\r\n" ));
putc0(0x55);
}
Esempio n. 10
0
//*************************PING MODE************************************************
void getDestMac()
{

  mac_addr[0] = '\0';
  
  nrk_kprintf(PSTR("\r\n*************************************************************\r\n"));
  nrk_kprintf(PSTR("         Please Enter Address of Node to Program             \r\n"));
  nrk_kprintf(PSTR("     MAC (Subnet + Dest) 4 Bytes Hex E.g. 00000004           \r\n"));
  nrk_kprintf(PSTR("*************************************************************\r\n"));
  printf("Enter MAC:");

  charCount = 0;

  // Enter Mac
  do
  {
    // Wait for UART signal
    while(1)
    {
      if(nrk_uart_data_ready(NRK_DEFAULT_UART)!=0)
      {
        while(nrk_uart_data_ready(NRK_DEFAULT_UART)!=0)
        {
          charCount++;
          // Read Character
          c=getchar();
          printf( "%c",c);
          sprintf(mac_addr,"%s%c",mac_addr,c);
        }
        break;
      }
      timeout.secs = 0;
      timeout.nano_secs = 20 * NANOS_PER_MS;
      nrk_wait(timeout);
    }
  }while(charCount < 8);

  
  buffer0[0]=mac_addr[0]; buffer0[1]=mac_addr[1]; buffer0[3]='\0';
  buffer1[0]=mac_addr[2]; buffer1[1]=mac_addr[3]; buffer1[3]='\0';
  buffer2[0]=mac_addr[4]; buffer2[1]=mac_addr[5]; buffer2[3]='\0';
  buffer3[0]=mac_addr[6]; buffer3[1]=mac_addr[7]; buffer3[3]='\0';
  val=sscanf( buffer0,"%x",&dest_mac[3]);
  val+=sscanf( buffer1,"%x",&dest_mac[2]);
  val+=sscanf( buffer2,"%x",&dest_mac[1]);
  val+=sscanf( buffer3,"%x",&dest_mac[0]);


  nrk_kprintf(PSTR("\r\n*************************************************************\r\n"));
  nrk_kprintf(PSTR("                       Node MAC Accepted                     \r\n"));
            printf("       Initiating Update for Node 0 x %X %X %X %X        \r\n", dest_mac[3],dest_mac[2],dest_mac[1], dest_mac[0]);
  nrk_kprintf(PSTR("*************************************************************\r\n"));

}
Esempio n. 11
0
void bmac_nw_task()
{
	int8_t v;
	int8_t e;
	uint8_t backoff;
	nrk_sig_mask_t event;
	
	while(bmac_started() == 0) {
		nrk_wait_until_next_period();
	}
	
	while(1) {
		
		if(is_enabled)
		{
		v = 1;
		rf_rx_on();
		if(rx_buf_empty) {
				v = _bmac_channel_check();
		} else {
			e = nrk_event_signal(bmac_rx_pkt_signal); // Mb
			// Should signal user task here as a "reminder"
		}
		
		if(v == 0) {
				// Channel detected as busy, attept to Rx
			_bmac_rx();
				// Should signal user task here to notify of Rx 
		}
		else if(tx_data_ready) {
			// Only try to Tx if the channel is free
			rf_rx_off(); // Mb
			_bmac_tx();
		}
		
		//rf_rx_off(); // Mb
		nrk_wait(_bmac_check_period);
		
		}
	else
	{
			event =0;
			do
			{
				v = nrk_signal_register(bmac_enable_signal);
				event = nrk_event_wait(SIG(bmac_enable_signal));
			}
			while((event & SIG(bmac_enable_signal))==0);
	}
			
		
	}
}
Esempio n. 12
0
int8_t _bmac_rx()
{
	int8_t result;
	uint8_t cnt;
	
	rf_rx_on();
	
	cnt = 0;
	while((result = rf_rx_check_cca()) != 0) {
		cnt++;
		nrk_wait(_bmac_check_period);
		if(cnt > 2) {
			if(rx_failure_cnt < 65535) rx_failure_cnt++;
			rf_rx_off();
			//printf("failure1=%d\r\n",rx_failure_cnt);
			return 0;
		}
	}
	

	// Packet on its way
	cnt = 0;
	while((result = rf_rx_packet()) == 0) {
		cnt++;
		nrk_wait(_bmac_check_period);
		//nrk_spin_wait_us(100);
		if(cnt > 1) {
			if(rx_failure_cnt < 65535) rx_failure_cnt++;
				rf_rx_off();
			//printf("failure2=%d\r\n",rx_failure_cnt);
				return 0;	
			}
		}
	//printf("failure3=%d\r\n",rx_failure_cnt);
	rf_rx_off();
	rx_buf_empty = 0;
	return 1;
	// Note: CRC is checked in hardware
	
}
Esempio n. 13
0
int8_t _bmac_tx()
{
	uint8_t v,result, backoff, backoff_count;
	uint16_t b;
	
	if(cca_active) {
				// Add random time here to stop nodes from synchronizing with eachother
				b=_nrk_time_to_ticks(&_bmac_check_period);
        b=b/((rand()%10)+1);
				//printf( "waiting %d\r\n",b );
        nrk_wait_until_ticks(b);
				//nrk_wait_ticks(b);

        backoff_count=1;
        do
        {
            v=_bmac_channel_check();
            rf_rx_off();
            if(v==1) break;
            // Channel is busy
            backoff=rand()%(_b_pow(backoff_count));
						
						//	printf( "backoff %d\r\n",backoff );
            nrk_wait_until_next_n_periods(backoff);
            backoff_count++;
            if(backoff_count>6) backoff_count=6; // cap it at 64
            b=_nrk_time_to_ticks(&_bmac_check_period);
            b=b/((rand()%10)+1);
						//	printf( "waiting %d\r\n",b );
            nrk_wait_until_ticks(b);
						//	nrk_wait_ticks(b);
					}
        while(v==0);
    }
	
	// Give check_period worth of channel activity
	rf_test_mode();
	rf_carrier_on();
	nrk_wait(_bmac_check_period);
	rf_carrier_off();
	rf_data_mode();
		
		rf_rx_off();
		pkt_got_ack = rf_tx_packet(&bmac_rfTxInfo);
		rf_rx_off();
		tx_data_ready = 0;
	// Should signal user task here that Tx is done
	return NRK_OK;
}
Esempio n. 14
0
// Assuming that CCA returned 1 and a packet is on its way
// Receive the packet or timeout and error
int8_t _bmac_rx ()
{
  int8_t n;
  uint8_t cnt;

  rf_rx_on ();
  cnt = 0;
//printf( "calling rx\r\n" );
  dummy_t.secs = 0;
  dummy_t.nano_secs = 5 * NANOS_PER_MS;
  nrk_wait (dummy_t);

  n = rf_rx_packet_nonblock ();

  if (n != NRK_OK) {
    if (rx_failure_cnt < 65535)
      rx_failure_cnt++;
    rf_rx_off ();
    return 0;
  }

/*while ( rf_rx_packet_nonblock() != NRK_OK )
	{
	cnt++;
	nrk_wait(_bmac_check_period);
	if(cnt>2) { 
			#ifdef DEBUG
			printf( "rx timeout 1 %d\r\n",cnt );
			#endif
			if(rx_failure_cnt<65535) rx_failure_cnt++;
			rf_rx_off();
			return 0;
			} 
	}
*/


  rx_buf_empty = 0;
#ifdef DEBUG
  printf ("BMAC: SNR= %d [", bmac_rfRxInfo.rssi);
  for (uint8_t i = 0; i < bmac_rfRxInfo.length; i++)
    printf ("%c", bmac_rfRxInfo.pPayload[i]);
  printf ("]\r\n");
#endif
  rf_rx_off ();
  return 1;
}
Esempio n. 15
0
int8_t nrk_wait_until(nrk_time_t t) {
	nrk_time_t ct;
	uint8_t v;

	nrk_time_get(&ct);

	v = nrk_time_sub(&t, t, ct);
	if (v == 0)
		return NRK_ERROR;
	//if(t.secs<ct.secs) return 0;
	//if(t.secs==ct.secs && t.nano_secs<ct.nano_secs) return 0;

	//t.secs-=ct.secs;
	//t.nano_secs-=ct.nano_secs;
	nrk_wait(t);

	return NRK_OK;
}
Esempio n. 16
0
void tx_task ()
{
  // Get the signal for UART RX
  //uart_rx_signal=nrk_uart_rx_signal_get();
  // Register your task to wakeup on RX Data 
  //if(uart_rx_signal==NRK_ERROR) nrk_kprintf( PSTR("Get Signal ERROR!\r\n") );
  //nrk_signal_register(uart_rx_signal);

  //printf ("tx_task PID=%d\r\n", nrk_get_pid ());

  // Wait until the tx_task starts up bmac
  // This should be called by all tasks using bmac that
  // do not call bmac_init()...
  bmac_init (26);

  bmac_encryption_set_key(aes_key,16);
  bmac_encryption_enable();

  bmac_rx_pkt_set_buffer (rx_buf, RF_MAX_PAYLOAD_SIZE);

  //nrk_kprintf (PSTR ("bmac_started()\r\n"));
  bmac_set_cca_thresh (-45);

  check_period.secs = 0;
  check_period.nano_secs = 100 * NANOS_PER_MS;
  val = bmac_set_rx_check_rate (check_period);

  // Get and register the tx_done_signal if you want to
  // do non-blocking transmits
  tx_done_signal = bmac_get_tx_done_signal ();
  nrk_signal_register (tx_done_signal);

  rx_signal = bmac_get_rx_pkt_signal ();
  nrk_signal_register (rx_signal);

  cnt = 0;

  while (1)
  {
      nrk_kprintf(PSTR("\r\n*************************************************************\r\n"));
      nrk_kprintf(PSTR("               PHOENIX WIRELESS UPDATE SYSTEM                \r\n"));
      nrk_kprintf(PSTR("*************************************************************\r\n"));
      nrk_kprintf(PSTR("Press 'p' : To PING Nodes in Vicinity                        \r\n"));
      nrk_kprintf(PSTR("Press 'u' : To Begin Node Update                             \r\n"));
      nrk_kprintf(PSTR("                                                             \r\n"));
      nrk_kprintf(PSTR("*************************************************************\r\n"));

      printf("Enter Choice: ");
      
      //sm=nrk_event_wait(SIG(uart_rx_signal));
    
      //if(sm != SIG(uart_rx_signal))
      //{
      //  nrk_kprintf( PSTR("UART signal error\r\n") );
      //  while(1);
      //}
      // Wait for UART signal
      while(1)
      {
        if(nrk_uart_data_ready(NRK_DEFAULT_UART)!=0)
        {
          // Read Character
          c=getchar();
          printf( "%c\r\n",c);
          break;
        }
        timeout.secs = 0;
        timeout.nano_secs = 20 * NANOS_PER_MS;
        nrk_wait(timeout);
      }
      // Choose mode
      switch(c){
        case 'p':
          programState = PING;
          break;
        case 'u':
          getDestMac();
          phoenix_init();
          programState = UPDATE;
          break;
        default:
          programState = NONE;
          nrk_kprintf(PSTR("Invalid Command! Please Try Again\r\n"));
      }

      // Reset c
      c = 0;

      nrk_wait_until_next_period();

      // Execute protocol
      switch(programState)
      {
        case PING:
          pingMode();
          break;
        case UPDATE:
          updateMode();
          break;
        case NONE:;// Do nothing
          break;
        default:
          nrk_kprintf(PSTR("Invalid Program State\r\n"));
          break;
      }
      nrk_wait_until_next_period ();
  }
}
Esempio n. 17
0
int8_t get_heading(int16_t *heading)
{
    uint8_t status = 0;
    nrk_time_t start, elapsed, now;
    int8_t rc;
    uint8_t i;
    uint8_t lsb, msb;
    float heading_rad;

    if (!have_compass) {
        LOG("ERROR: no compass\r\n");
        return NRK_ERROR;
    }
 
    /* trigger a measurement */
    rc = twi_write(COMPASS_ADDR, REG_MODE, 0x01);
    if (rc != NRK_OK) {
        LOG("ERROR: failed to set mode\r\n");
        return rc;
    }

    /* wait for data ready */
    nrk_time_get(&start);
    do {
        nrk_wait(compass_poll_interval);

        status = 0;
        rc = twi_read(COMPASS_ADDR, REG_STATUS, &status);
        if (rc != NRK_OK) {
            LOG("ERROR: failed to read status\r\n");
            return rc;
        }
        nrk_time_get(&now);
        nrk_time_sub(&elapsed, now, start);
    } while (!(status & BIT_STATUS_READY) &&
            time_cmp(&elapsed, &compass_measure_timeout) < 0);

    if (!(status & BIT_STATUS_READY)) {
        LOG("ERROR: compass measure timed out\r\n");
        return NRK_ERROR;
    }

    for (i = 0; i < NUM_AXES; ++i) {
        rc = twi_read(COMPASS_ADDR, REG_DATA_OUT_X_MSB + 2 * i, &msb);
        if (rc != NRK_OK) break;
        rc = twi_read(COMPASS_ADDR, REG_DATA_OUT_X_LSB + 2 * i, &lsb);
        if (rc != NRK_OK) break;
        mag[i] = (msb << 8) | lsb;
    }

    if (rc != NRK_OK) {
        LOG("ERROR: compass read failed\r\n");
        return rc;
    }

    /* Re-order to (X, Y, Z) and cast to float */
    mag_uT[AXIS_X] = mag[RAW_AXIS_X];
    mag_uT[AXIS_Y] = mag[RAW_AXIS_Y];
    mag_uT[AXIS_Z] = mag[RAW_AXIS_Z];

    /* Convert to uT units */
    for (i = 0; i < NUM_AXES; ++i)
        mag_uT[i] = mag_uT[i] / GAUSS_LSB * GAUSS_TO_MICROTESLA;

    heading_rad = atan2(mag_uT[AXIS_Y], mag_uT[AXIS_X]);
    if (heading_rad < 0)
        heading_rad += 2.0 * M_PI;
    heading_rad *= 180.0 / M_PI;

    *heading  = heading_rad;

    LOG("raw: ");
    for (i = 0; i < NUM_AXES; ++i)
        LOGP("%x:%x ", (mag[i] >> 8) & 0xFF, mag[i] & 0xFF);
    LOGA("dec: ");
    for (i = 0; i < NUM_AXES; ++i)
        LOGP("%d ", mag[i]);
    LOGA("\r\n");

    LOG("uT: ");
    for (i = 0; i < NUM_AXES; ++i)
        LOGP("%d ", (int16_t)mag_uT[i]);
    LOGP("=> %d deg\r\n", *heading);

    return NRK_OK;
}