blob: a4dde1dfc4d1d88efdac60d42bf934ae923873b4 [file] [log] [blame]
/*
* ==== mon_AP.c ========
*/
#include <xdc/runtime/System.h>
#include <xdc/runtime/Gate.h>
#include <xdc/runtime/Diags.h>
#include <xdc/runtime/Log.h>
#include <xdc/runtime/Types.h>
#include <local/runtime/SysUart.h>
#include <local/runtime/utils/Stack.h>
#include <local/rf/Radio.h>
#include <local/rf/Bsp.h>
#include "msp430x22x4.h"
#define NUM_CONNECTIONS 2 /* should match (or exceed) rf config */
#define MAX_APP_PAYLOAD 20 /* should match (or exceed) rf config */
#define RED 1
#define GREEN 2
#define MESSAGE_LENGTH 3 /* should be <= MAX_APP_PAYLOAD */
#define TRACE 0x01
#define STACK 0x02
#define RADIO 0x04
#define SMPLT 0x08
#define HELP 0x01
void createRandomAddress(void);
int getDelta(void);
uint16_t getTime(void);
Radio_Rssi getRssi(void);
void mcuInit(void);
void printMessage(int addr, signed char rssi, char msg[MESSAGE_LENGTH]);
Void printEDEvent(char msg[], int len);
void printTempData(int node, int temp, int volt, int rssi_pct);
/* usage message for terminal input */
const char help[] = {
"Commands:\r\n"
" c - display tempature in Celcius\r\n"
" f - display tempature in Fahrenheit\r\n"
" s - toggle stack trace\r\n"
" r - toggle radio trace\r\n"
" n - disable all trace\r\n"
" 0-3 - switch to specified channel\r\n"
" ? - display this menu\r\n"
" ! - reset\r\n"};
#define TEMP_OFFSET (*(int *)0x10F4) /* tempature calibration offset */
#define FLASH_ADDR ((char *)0x10F0) /* Tag-Length-Value Table address */
#define INVALID_CHAN ((char)-1)
/* reserve space for the maximum possible peer Link IDs */
static Radio_LinkId sLID[NUM_CONNECTIONS];
static uint8_t sNumCurrentPeers;
/* callback handler */
static uint8_t sCB(Radio_LinkId);
/* work loop semaphores */
static uint8_t sPeerFrameSem;
static uint8_t sJoinSem;
static uint8_t sSelfMeasureSem;
static uint16_t sTime = 0;
/* mode data */
char degCMode = 0; /* use deg F by default */
char traceMask = 0; /* disable all trace by default */
char helpMask = 0; /* don't display help initially */
char radioChan = INVALID_CHAN;
/*
* ======== main ========
*/
void main(void)
{
Radio_Addr lAddr;
IArg intState;
volatile int i;
WDTCTL = WDTPW + WDTHOLD; /* Stop WDT */
/* delay loop to ensure proper startup before SimpliciTI increases DCO */
/* This is typically tailored to the power supply used, and in this case */
/* is overkill for safety due to wide distribution. */
for (i = 0; i < 0xFFFF; i++){}
if (CALBC1_8MHZ == 0xFF) { /* Do not run if cal values are erased */
volatile int i;
P1DIR |= 0x03;
Bsp_turnOnLed(RED);
Bsp_turnOffLed(GREEN);
while (1) {
for (i = 0; i < 0x5FFF; i++){}
Bsp_toggleLed(GREEN);
Bsp_toggleLed(RED);
}
}
if (FLASH_ADDR[0] == 0xFF && FLASH_ADDR[1] == 0xFF
&& FLASH_ADDR[2] == 0xFF && FLASH_ADDR[3] == 0xFF) {
/* flash a Random device address at startup */
createRandomAddress();
}
/* copy our address (from flash) into network RAM */
lAddr.addr[0] = FLASH_ADDR[0];
lAddr.addr[1] = FLASH_ADDR[1];
lAddr.addr[2] = FLASH_ADDR[2];
lAddr.addr[3] = FLASH_ADDR[3];
mcuInit();
for (i = 0; i < 0xFFFF; i++){}
/* initialize the radio network */
getDelta();
Radio_start(sCB, &lAddr);
System_printf("Network initialization done (%d)\n", getDelta());
/* main work loop */
while (1) {
if (helpMask & HELP) {
helpMask ^= HELP;
System_printf((String)help);
}
if (traceMask & TRACE) {
Bits16 mask;
traceMask ^= TRACE;
mask = ((traceMask & RADIO) ? Diags_ENTRY : 0)
| ((traceMask & SMPLT) ? Diags_USER1 : 0);
Radio_Module_setMask(mask);
Stack_Module_setMask((traceMask & STACK) ? Diags_EXIT : 0);
Log_print1(Diags_USER1, "trace mask set to 0x%x", traceMask);
}
/* Wait for the Join semaphore to be set by the receipt of a
* Join frame from a device that supports an End Device.
*/
if (sJoinSem && (sNumCurrentPeers < NUM_CONNECTIONS)) {
/* listen for a new connection */
Radio_listen(&sLID[sNumCurrentPeers]);
sNumCurrentPeers++;
intState = Gate_enterSystem();
if (sJoinSem) {
sJoinSem--;
}
Gate_leaveSystem(intState);
}
/* change channel if requested */
if (radioChan != INVALID_CHAN) {
Radio_setChannel((Int)radioChan);
radioChan = INVALID_CHAN;
}
/* if it is time to measure our own temperature... */
if (sSelfMeasureSem) {
char msg[MESSAGE_LENGTH];
int degC, volt;
volatile long temp;
int results[2];
ADC10CTL1 = INCH_10 + ADC10DIV_4; /* Temp Sensor ADC10CLK/5 */
ADC10CTL0 = SREF_1 + ADC10SHT_3 + REFON + ADC10ON + ADC10IE + ADC10SR;
for (degC = 240; degC > 0; degC--); /* allow reference to settle */
ADC10CTL0 |= ENC + ADC10SC; /* Sample & convertion start */
__bis_SR_register(CPUOFF + GIE); /* LPM0 with interupts on */
results[0] = ADC10MEM;
ADC10CTL0 &= ~ENC;
ADC10CTL1 = INCH_11; /* AVcc/2 */
ADC10CTL0 = SREF_1 + ADC10SHT_2 + REFON + ADC10ON + ADC10IE + REF2_5V;
for (degC = 240; degC > 0; degC--); /* allow reference to settle */
ADC10CTL0 |= ENC + ADC10SC; /* Sample & convertion start */
__bis_SR_register(CPUOFF + GIE); /* LPM0 with interrupts on */
results[1] = ADC10MEM;
ADC10CTL0 &= ~ENC;
ADC10CTL0 &= ~(REFON + ADC10ON); /* turn off A/D; saves power */
/* oC = ((A10/1024)*1500mV)-986mV)*1/3.55mV = A10*423/1024 - 278 */
/* the temperature is printed as an integer where 32.1 = 321 */
/* hence 4230 instead of 423 */
temp = results[0];
degC = ((temp - 673) * 4230) / 1024;
if (TEMP_OFFSET != 0xFFFF) {
degC += TEMP_OFFSET;
}
temp = results[1];
volt = (temp * 25) / 512;
msg[0] = degC & 0xFF;
msg[1] = (degC >> 8) & 0xFF;
msg[2] = volt;
printMessage(-1, getRssi(), msg);
Bsp_toggleLed(RED);
sSelfMeasureSem = 0;
}
/* Have we received a frame on one of the ED connections?
* No critical section -- it doesn't really matter much if we miss a
* poll
*/
if (sPeerFrameSem) {
uint8_t msg[MAX_APP_PAYLOAD], len, i;
/* process all frames waiting */
for (i = 0; i < sNumCurrentPeers; ++i) {
if (Radio_receive(sLID[i], msg, &len) == Radio_SUCCESS) {
Radio_RxMetrics metrics;
Radio_getMetrics(sLID[i], &metrics);
printMessage(i, metrics.rssi, (char *)msg);
Bsp_toggleLed(GREEN);
intState = Gate_enterSystem();
sPeerFrameSem--;
Gate_leaveSystem(intState);
}
}
}
}
}
/*
* ======== createRandomAddress ========
*/
void createRandomAddress(void)
{
unsigned int rand, rand2;
extern int TI_getRandomIntegerFromVLO(void);
do { /* first byte can't be 0x00 or 0xFF */
rand = TI_getRandomIntegerFromVLO();
} while((rand & 0xFF00) == 0xFF00 || (rand & 0xFF00) == 0x0000);
rand2 = TI_getRandomIntegerFromVLO();
/* write the random address to Flash */
BCSCTL1 = CALBC1_1MHZ; /* Set DCO to 1MHz */
DCOCTL = CALDCO_1MHZ;
FCTL2 = FWKEY + FSSEL0 + FN1; /* MCLK/3 for Flash Timing Generator */
FCTL3 = FWKEY + LOCKA; /* Clear LOCK & LOCKA bits */
FCTL1 = FWKEY + WRT; /* Set WRT bit for write operation */
FLASH_ADDR[0] = (rand >> 8) & 0xFF;
FLASH_ADDR[1] = rand & 0xFF;
FLASH_ADDR[2] = (rand2 >> 8) & 0xFF;
FLASH_ADDR[3] = rand2 & 0xFF;
FCTL1 = FWKEY; /* Clear WRT bit */
FCTL3 = FWKEY + LOCKA + LOCK; /* Set LOCK & LOCKA bit */
}
/*
* ======== printMessage ========
*/
void printMessage(int addr, signed char rssi, char msg[MESSAGE_LENGTH])
{
int rssi_pct;
int temp;
/* Convert raw rssi value into a "percent"
* We could scale the rssi value based on the full range of the
* data type ([-128,127]):
* rssi_pct = (int)rssi_pct + 128;
* rssi_pct = (rssi_pct * 100) / 256;
*
* But since rssi is dBm, it's real range is [-110, 0) and, in
* practice never seems to go below -98. So, we simply add 100 to
* the raw value.
*/
rssi_pct = rssi + 100;
temp = msg[0] + (msg[1] << 8);
if (msg[1] == 0x80) {
if (traceMask & RADIO) {
Log_print2(Diags_USER1,
"trace event received: node: %0.2d, strength: %3d",
addr + 1, rssi_pct);
}
printEDEvent(msg + 2, msg[0]);
}
else {
printTempData(addr + 1, temp, msg[2], rssi_pct);
if (Stack_getUnused() <= 0) {
IArg intState;
System_printf("warning: stack overrun\r\n");
intState = Gate_enterSystem();
Stack_fill();
Gate_leaveSystem(intState);
}
}
}
#define EVENT_SUFFIX_SIZE (sizeof(Log_EventRec)-sizeof(Types_Timestamp64))
/*
* ======== printEDEvent ========
*/
Void printEDEvent(char msg[], int len)
{
Log_EventRec evtRec;
Char *src, *dst;
Int i;
if (len > EVENT_SUFFIX_SIZE) {
len = EVENT_SUFFIX_SIZE;
}
/* "unpack" message into a Log_EventRec */
evtRec.tstamp.hi = 0;
evtRec.tstamp.lo = 0;
src = msg;
dst = (Char *)&evtRec.serial;
for (i = len; i > 0; i--) {
*dst++ = *src++;
}
/* push ED event out the serial port */
/* send header */
SysUart_putch('\01');
/* send prog id */
SysUart_putch('\01');
/* send payload */
for (src = (Char *)&evtRec, i = 0; i < sizeof(evtRec); i++) {
SysUart_putch(*src++);
}
}
/*
* ======== printTempData ========
*/
void printTempData(int node, int temp, int volt, int rssi_pct)
{
int t1, t2;
char *format = "Node:%0.2d, Temp: %d.%dC, Battery: %d.%dV, Strength: %d\n";
if (!degCMode) {
temp = (((float)temp) * 1.8) + 320;
format = "Node:%0.2d, Temp: %d.%dF, Battery: %d.%dV, Strength: %d\n";
}
t1 = temp / 10;
t2 = temp % 10;
if (t2 < 0) {
t2 = -1 * t2;
}
System_printf("time: 0x%x\n", getTime());
System_printf(format, node, t1, t2, volt / 10, volt % 10, rssi_pct);
}
/*
* ======== mcuInit ========
*/
void mcuInit(void)
{
BCSCTL1 = CALBC1_8MHZ; /* Set DCO */
DCOCTL = CALDCO_8MHZ;
BCSCTL3 |= LFXT1S_2; /* LFXT1 = VLO */
TBCCTL0 = CCIE; /* Timer_B interrupt enabled */
TBCCR0 = 12000; /* ~1 second @ 8 MHz */
TBCTL = TASSEL_1 + MC_1; /* ACLK, upmode */
}
/*
* ======== getTime ========
*/
uint16_t getTime(void)
{
uint16_t time;
int intState = Gate_enterSystem();
time = TBR;
if (time == 0 && (TBCCTL0 & CCIFG)) {
time += (sTime + 1) * 12001;
}
else {
time += sTime * 12001;
}
Gate_leaveSystem(intState);
return (time);
}
/*
* ======== getDelta ========
*/
int getDelta(void)
{
static int t0 = 0;
int t1, delta;
t1 = TBR; /* read the current timer counter */
if (t0 <= t1) {
delta = t1 - t0;
}
else {
delta = 12001 - (t1 - t0); /* handle wrap situation */
}
t0 = t1;
return (delta);
}
/*
* ======== getRssi ========
* Compute a measure of the signal strength on the current channel. This
* gives a measure of interference from other wireless devices and can be
* used to decide to switch channels to avoid interference.
*/
#define WLEN 2
#define NUMSAMPLES (24 / WLEN)
Radio_Rssi getRssi()
{
Int max = -100;
Int total;
Int i, j;
Radio_Rssi window[WLEN];
for (i = 1; i <= NUMSAMPLES; i++) {
if (sPeerFrameSem || sJoinSem) {
return ((Radio_Rssi)max);
}
window[i % WLEN] = Radio_getRssi();
if (i >= WLEN) {
for (total = j = 0; j < WLEN; j++) {
total += window[j];
}
total = total / WLEN;
if (max < total) {
max = total;
}
}
Bsp_delay(1);
}
return ((Radio_Rssi)max);
}
/*
* ======== sCB ========
* Runs in ISR context. Reading the frame should be done in the
* application thread not in the ISR thread.
*/
static uint8_t sCB(Radio_LinkId lid)
{
if (lid) {
sPeerFrameSem++;
}
else {
sJoinSem++;
}
/* leave frame to be read by application. */
return (0);
}
/*------------------------------------------------------------------------------
* ADC10 interrupt service routine
------------------------------------------------------------------------------*/
__asm("\t.global ADC10_ISR");
__asm("\t.sect \".adc10\"");
__asm("\t.align 2");
__asm("_ADC10_vector:\n\t.field ADC10_ISR, 16");
__interrupt void ADC10_ISR(void)
{
__bic_SR_register_on_exit(CPUOFF); /* Clear CPUOFF bit from 0(SR) */
}
/*------------------------------------------------------------------------------
* Timer A0 interrupt service routine
------------------------------------------------------------------------------*/
__asm("\t.global TIMER_A0_ISR");
__asm("\t.sect \".timer_a0\"");
__asm("\t.align 2");
__asm("_TIMER_A0_vector:\n\t.field TIMER_A0_ISR, 16");
__interrupt void TIMER_A0_ISR (void)
{
sSelfMeasureSem = 1;
}
/*------------------------------------------------------------------------------
* Timer B0 interrupt service routine
------------------------------------------------------------------------------*/
__asm("\t.global TIMER_B0_ISR");
__asm("\t.sect \".timer_b0\"");
__asm("\t.align 2");
__asm("_TIMER_B0_vector:\n\t.field TIMER_B0_ISR, 16");
__interrupt void TIMER_B0_ISR (void)
{
sSelfMeasureSem = 1;
sTime++;
}
/*
* ======== uartRxCallback ========
*/
void uartRxCallback(char *buf, int len)
{
char rx = buf[0];
extern void _c_int00(void);
if (rx == 'F' || rx == 'f') {
degCMode = 0;
}
else if (rx == 'C' || rx == 'c') {
degCMode = 1;
}
else if (rx == 'r') {
traceMask ^= (RADIO | TRACE);
}
else if (rx == 'R') {
traceMask ^= (SMPLT | TRACE);
}
else if (rx == 'N' || rx == 'n') {
traceMask = TRACE;
}
if (rx == 'S' || rx == 's') {
traceMask ^= (STACK | TRACE);
}
if (rx == '?') {
helpMask = HELP;
}
if (rx >= '0' && rx <= '3') {
radioChan = rx - '0';
}
if (rx == '!') {
Bsp_reset();
}
}