| /* |
| * ==== 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); |
| 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; |
| |
| /* 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 */ |
| Radio_start(sCB, &lAddr); |
| System_printf("Network initialization done\n"); |
| |
| /* 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; |
| } |
| |
| t1 = temp / 10; |
| t2 = temp % 10; |
| if (t2 < 0) { |
| t2 = -1 * t2; |
| } |
| |
| if (!degCMode) { |
| format = "Node:%0.2d, Temp: %d.%dF, Battery: %d.%dV, Strength: %d\n"; |
| } |
| |
| 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 */ |
| TBCTL = TASSEL_1 + MC_1; /* ACLK, upmode */ |
| } |
| |
| /* |
| * ======== 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; |
| } |
| |
| /* |
| * ======== 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(); |
| } |
| } |