/* * Copyright (c) 2010, Loughborough University - Computer Science * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the Institute nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * This file is part of the Contiki operating system. */ /** * \file * Example demonstrating the cc2431 location engine. * * This file contains code for the blind node. The blind node must be * equipped with a cc2431 SoC (as opposed to reference nodes which * don't need to have a Loc. Eng.) * * The blind node receives co-ordinates of reference nodes over * broadcast rime. Once it has enough data (3+ reference nodes), it * will calculate its own position. * * We calculate with all potential values for parameter 'n' to * demonstrate how 'n' influences the result of the calculation. * * Optionally, send the result of the calculation to a collection node * * More information on the cc2431 Location Engine can be found in: * - cc2431 Datasheet * - Texas Instruments Application Note 42 * * \author * George Oikonomou - */ #include "contiki.h" #include "net/rime/rime.h" #include "cc2431_loc_eng.h" #include "cc2430_sfr.h" #include #include #define MAX_REF_NODES 16 /* Do not change */ #define SAMPLE_RSSI 100 /* Used for testing */ #define SAMPLE_ALPHA 101 static struct meas_params parameters; static struct refcoords ref_coords[MAX_REF_NODES]; /* Store our current location here to be transmitted to a collector node */ static uint8_t coords[2]; /*---------------------------------------------------------------------------*/ PROCESS(blindnode_bcast_rec, "Blind Node"); AUTOSTART_PROCESSES(&blindnode_bcast_rec); /*---------------------------------------------------------------------------*/ /* * This handles the calculation cycle. Returns non-zero on error, 0 on success. * * When we move this outside the example, we will perhaps want to pass * struct refcoords *, struct meas_params * * instead of exposing our own data structures. If this happens, we will need * to add checks to our code to detect non-sane values */ static uint8_t calculate() { static int j, x; uint8_t valid_rssi = 0; /* Turn on the Engine */ LOCENG = LOCENG_EN; while(!(LOCENG & LOCENG_EN)); /* Reference Coordinate Load Stage */ LOCENG |= LOCENG_REFLD; while(!(LOCENG & LOCENG_REFLD)); for(j = 0; j < MAX_REF_NODES; j++) { /* Write the Reference Node x,y into the engine */ REFCOORD = ref_coords[j].x; REFCOORD = ref_coords[j].y; } /* Reference Coordinate Load Stage Done. Proceed with measured params */ LOCENG &= ~LOCENG_REFLD; LOCENG |= LOCENG_PARLD; /* Load Parameters */ MEASPARM = parameters.alpha; MEASPARM = parameters.n; MEASPARM = parameters.x_min; MEASPARM = parameters.x_delta; MEASPARM = parameters.y_min; MEASPARM = parameters.y_delta; /* Load Neighbor RSSIs */ for(j = 0; j < MAX_REF_NODES; j++) { if(parameters.rssi[j] != 0) { /* Range-check for the RSSI here, can only be in [-95 dBm , -40 dBm] * so we only accept 80 <= rssi <= 190*/ if(parameters.rssi[j] >= 80 && parameters.rssi[j] <= 190) { valid_rssi++; } } /* Write the value, even if it's zero */ MEASPARM = parameters.rssi[j]; } /* Done with measured parameters too */ LOCENG &= ~LOCENG_PARLD; /* Only Calculate if we have 3+ reference nodes (non-zero RSSIs) */ if(valid_rssi >= 3) { LOCENG |= LOCENG_RUN; } else { LOCENG = 0; printf("some error\n"); return 1; } /* Block on the calculation, between 50us and 13ms */ while(!(LOCENG & LOCENG_DONE)); /* * LOCX contains an offset. Remove it to obtain our actual X value. * cc2431 datasheet, section 2.1.3 */ x = (LOCX - parameters.x_min + 1) % (parameters.x_delta + 1) + parameters.x_min; coords[0] = x; coords[1] = LOCY; /* No offset here */ printf("n=%2u: X=%3u, Y=%3u\n", parameters.n, LOCX, LOCY); /* Turn it off */ LOCENG = 0; return 0; } /*---------------------------------------------------------------------------*/ /* * We receive X, Y from reference nodes. * We store this in location J of the ref_coords array, where J is the LSB * of the reference node's rime address. So we can only accept data from nodes * with rime address ending in [0 , 15] */ static void broadcast_recv(struct broadcast_conn *c, const linkaddr_t *from) { packetbuf_attr_t rssi; /* Careful here, this is uint16_t */ if(from->u8[1] < MAX_REF_NODES) { memset(&ref_coords[from->u8[1] - 1], 0, sizeof(struct refcoords)); /* Obtain incoming message's RSSI from contiki */ rssi = packetbuf_attr(PACKETBUF_ATTR_RSSI); /* Convert RSSI to the loc. eng. format */ parameters.rssi[from->u8[1] - 1] = (-2 * rssi); /* Raw dump the packetbuf into the ref_coords struct */ memcpy(&ref_coords[from->u8[1] - 1], packetbuf_dataptr(), 2 * sizeof(uint8_t)); } return; } /* * Imaginary nodes to test functionality * All nodes at 1 meter distance, rssi = -40 (80) * Since the rssi at 1 meter = -40 (A), the blind node should think it's at * 5,5 */ /*---------------------------------------------------------------------------*/ static void set_imaginary_ref_nodes() { ref_coords[0].x = 1; ref_coords[0].y = 5; parameters.rssi[0] = SAMPLE_RSSI; ref_coords[1].x = 5; ref_coords[1].y = 1; parameters.rssi[1] = SAMPLE_RSSI; ref_coords[2].x = 5; ref_coords[2].y = 9; parameters.rssi[2] = SAMPLE_RSSI; ref_coords[3].x = 9; ref_coords[3].y = 5; parameters.rssi[3] = SAMPLE_RSSI; } /*---------------------------------------------------------------------------*/ static const struct broadcast_callbacks broadcast_call = { broadcast_recv }; static struct broadcast_conn broadcast; /*---------------------------------------------------------------------------*/ PROCESS_THREAD(blindnode_bcast_rec, ev, data) { static struct etimer et; static uint8_t n; int i; PROCESS_EXITHANDLER(broadcast_close(&broadcast)); PROCESS_BEGIN(); printf("Reading Chip ID: 0x%02x\n", CHIPID); /* Read our chip ID. If we are not cc2431, bail out */ if(CHIPID != CC2431_CHIP_ID) { printf("Hardware does not have a location engine. Exiting.\n"); PROCESS_EXIT(); } /* OK, we are cc2431. Do stuff */ n = 0; /* Initalise our structs and parameters */ memset(ref_coords, 0, sizeof(struct refcoords) * MAX_REF_NODES); memset(¶meters, 0, sizeof(struct meas_params)); /* * Just hard-coding measurement parameters here. * Ideally, this should be part of a calibration mechanism */ parameters.alpha = SAMPLE_ALPHA; parameters.x_min = 0; parameters.x_delta = 255; parameters.y_min = 0; parameters.y_delta = 255; set_imaginary_ref_nodes(); broadcast_open(&broadcast, 129, &broadcast_call); while(1) { etimer_set(&et, CLOCK_SECOND); PROCESS_WAIT_EVENT_UNTIL(etimer_expired(&et)); /* * With the hard-coded parameters and locations, we will calculate * for all possible values of n [0 , 31] */ parameters.n = n; calculate(); n++; if(n == 32) { n = 0; } /* Send our calculated location to some monitoring node */ packetbuf_copyfrom(&coords, 2 * sizeof(uint8_t)); broadcast_send(&broadcast); } PROCESS_END(); }