Jump to content
43oh

nRF24 Temperature Sensor Bug


Recommended Posts

I think I mentioned this in another thread (can't find it atm) that I had built a tiny board for tossing little temperature sensor bugs everywhere, and @@yyrkoon mentioned on IRC yesterday he'd like to see the details.

 

Of course by "bug" I mean a tiny board embedded in hidden places, not "software bug" :smile:

 

The theme of this board is "minimalistic" design.  It's based on the MSP430G2452 since I happened to have a bunch of TSSOP's in my bin.  It includes a TSSOP-20 footprint with only a handful of pins used so a G2452 TSSOP-14 or -20 could be employed.

 

Unfortunately I forgot to take pics last night when I hooked it all up & calibrated it

Pics:

post-15991-0-57097700-1368842835_thumb.jpg

post-15991-0-02490700-1368842843_thumb.jpg

 

The board breaks out P1.0 and P1.1 to some pin headers and there's GND in the SBW header set.  One thing I'd like to explore is doing a soil moisture level sensor for my wife's planters, this should work as I could supply +Vcc with P1.0 and ADC reading with P1.1 on a small board that plugs on top for the voltage divider.  With +Vcc assigned to an I/O pin the MSP430 can shut off the voltage divider for ultra-low-power sleeping.

 

Power is supplied by 2xAA batteries, pinout spaced for this battery holder: http://www.mouser.com/Search/ProductDetail.aspx?R=12BH321P-GRvirtualkey56100000virtualkey12BH321P-GR

The battery holder is way bigger than the board ;-)

 

 

Top:

post-15991-0-30133200-1368795593_thumb.png

Bottom:

post-15991-0-38484000-1368795600_thumb.png

Schematic: DipTrace Schematic - Wireless_ADCTempSender_draft1.pdf

OSHpark gerbers: Wireless_TempSender_nRF24.zip

Link to post
Share on other sites
  • Replies 36
  • Created
  • Last Reply

Top Posters In This Topic

Top Posters In This Topic

Popular Posts

I think I mentioned this in another thread (can't find it atm) that I had built a tiny board for tossing little temperature sensor bugs everywhere, and @@yyrkoon mentioned on IRC yesterday he'd like t

Lol, it just occurred to me that I misused the ADC_STATIC_OFFSET variable vs. the ADC_CALIBRATION_OFFSET in my tempbug.h file.  Might have to go back and fix that... haha.  Doesn't really matter thoug

Looks right to me, you'll get an int value that's 100x greater than the actual float value (+/- whatever errors are caused by not using floating point math). Really curious to see what kind of results

Posted Images

Firmware written for MSPGCC for my msprf24 library -- https://github.com/spirilis/msprf24

 

Using my packet_processor.c system for managing the OTA packet format, and this uses the same Thermocouple packet ID as my grill monitor (just sets both ambient temp + thermocouple temp to the same value)

 

 

tempbug.h:

/* TempBug - nRF24L01+-based Wireless Temperature Sending Unit */

#ifndef TEMPBUG_H
#define TEMPBUG_H




#define RF_CHANNEL 10
#define RF_SPEED RF24_SPEED_MIN

#define DEVICE_ID 0x03

#define SLEEP_BETWEEN_SAMPLES 161
// ^ 161 * 8192/VLOCLK = ~2 minutes

// If the onboard temp sensor seems off, adjust ADC value by this much:
#define ADC_CALIBRATION_OFFSET 0




#endif 

Main C code - main.c:

/* TempBug
 * Temperature sending unit for reporting MSP430 ADC thermistor temperature
 * over the nRF24L01+ RF with my Thermocouple packet format.
 */
#include <msp430.h>
#include "tempbug.h"
#include "msprf24.h"
#include "nrf_userconfig.h"
#include "packet_processor.h"
#include <sys/cdefs.h>
#include <string.h>
#include <stdint.h>

const char dummyaddr[] = { 0xE7, 0xE7, 0xE7, 0xE7, 0xE7 };
const char tempbug_rxaddr[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0x03 };
const char basestation_rxaddr[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0x01 };

volatile unsigned int sleep_counter;
unsigned int total_sends;

#define ADCVAL_STATIC_OFFSET -7
int adc_temp_read();
void debug_packet(struct packet_task *);

int main()
{
        uint8_t do_lpm, pktlen, pipeid;
        int temp, i;
        char rfbuf[32];

        WDTCTL = WDTPW | WDTHOLD;
        DCOCTL = CALDCO_1MHZ;
        BCSCTL1 = CALBC1_1MHZ;
        BCSCTL2 = 0;  // SMCLK = DCO/1
        BCSCTL3 = LFXT1S_2;  // VLOCLK
        while (BCSCTL3 & LFXT1OF) {  // Wait for VLO to stabilize
                BCSCTL3 &= ~LFXT1OF;
                __delay_cycles(1000);
        }

        // GPIO configuration
        P1DIR = 0;
        P1OUT = 0;
        P1REN |= BIT0 | BIT1;  // P1.0, P1.1 pull-down to GND; they're application-expandable I/O pins

        P2SEL = 0;
        P2SEL2 = 0;
        P2DIR = BIT6;  // P2.6 has the LED
        P2OUT = 0;
        P2REN = BIT0 | BIT1 | BIT2 | BIT3 | BIT4 | BIT5 | BIT7;

        // nRF24L01+ configuration & initialization
        rf_crc = RF24_EN_CRC | RF24_CRCO; // CRC enabled, 16-bit
        rf_addr_width      = 5;
        rf_speed_power     = RF_SPEED | RF24_POWER_MAX;  // RF_SPEED from tcsender.h
        rf_channel         = RF_CHANNEL;  // This #define is located in tcsender.h
        msprf24_init();  // Interrupts get enabled here
        msprf24_open_pipe(0, 0);  // Open pipe#0, AutoACK disabled (250Kbps doesn't do autoack anyway)
        msprf24_open_pipe(1, 0);  // Pipe#1 is the primary receiver for base->slave traffic
        msprf24_set_pipe_packetsize(0, 0);  // Dynamic packet sizes
        msprf24_set_pipe_packetsize(1, 0);  // Dynamic packet sizes
        w_rx_addr(0, (char*)dummyaddr);
        w_rx_addr(1, (char*)tempbug_rxaddr);  // For receiving base->slave packets (not used)

        sleep_counter = 0;
        WDTCTL = WDT_ADLY_250;
        IFG1 &= ~WDTIFG;
        IE1 |= WDTIE;

        // Main loop
        packet_init_tasklist();
        total_sends = 0;
        while(1) {
                do_lpm = 1;

                // Handle RF acknowledgements & RX packets
                if (rf_irq & RF24_IRQ_FLAGGED) {
                        msprf24_get_irq_reason();
                        if (rf_irq & RF24_IRQ_TX) {
                                // Acknowledge
                                msprf24_irq_clear(RF24_IRQ_TX);
                                flush_tx();

                                // Load dummy addr into pipe#0 (this is only needed for autoack, but harmless to do anyway)
                                w_rx_addr(0, (char*)dummyaddr);

                                // Go into deep sleep
                                msprf24_powerdown();
                        }

                        // This should never happen, at 250Kbps.
                        if (rf_irq & RF24_IRQ_TXFAILED) {
                                msprf24_irq_clear(RF24_IRQ_TXFAILED);
                                flush_tx();
                                w_rx_addr(0, (char*)dummyaddr);
                                msprf24_powerdown();
                        }

                        // Handle RX packets
                        if (rf_irq & RF24_IRQ_RX) {
                                pktlen = r_rx_peek_payload_size();
                                pipeid = r_rx_payload(pktlen, rfbuf);
                                msprf24_irq_clear(RF24_IRQ_RX);

                                if (pktlen && pktlen < 33 && pipeid == 1) {  // Ignore 0, >32-byte packets
                                        for (i=0; i<pktlen; i++) {
                                                if (rfbuf[i]) {
                                                        // Process valid packet
                                                        if ((i + (unsigned char)rfbuf[i+1] + 1) < pktlen) {
                                                                packet_processor((unsigned char)rfbuf[i],
                                                                                 (unsigned char)rfbuf[i+1],
                                                                                &rfbuf[i+2]);
                                                                i += (unsigned char)rfbuf[i+1] + 1;
                                                        } else {
                                                                i = pktlen;  // Invalid packet length = we're done
                                                        }
                                                }
                                        }
                                } else {
                                        flush_rx();
                                } // End of payload parsing/handling

                        }  // End of RX handling

                        do_lpm = 0;  // Prefer to always loop once between RF handling events
                }  // End of RF handling

                // Did we just finish a long sleep?  Sample the temperature and send it out.
                if (!sleep_counter) {
                        temp = adc_temp_read();  // Degrees C
                        msprf24_standby();

                        struct packet_task tpkt;
                        tpkt.program = 0x10;  // Thermocouple
                        tpkt.size = 6;
                        tpkt.data[0] = DEVICE_ID;
                        memcpy(&(tpkt.rfaddr[0]), basestation_rxaddr, 5);
                        memcpy(&(tpkt.data[1]), &temp, sizeof(int));  // Stuff value in both TC and Ambient temp fields
                        memcpy(&(tpkt.data[3]), &temp, sizeof(int));  // This isn't a thermocouple, but we're reusing the packet type.
                        tpkt.data[5] = 0;  // Fault codes not applicable here

                        packet_task_append(&tpkt);
                        // Toss the total # of samples sent in there too
                        debug_packet(&tpkt);
                        packet_task_append(&tpkt);

                        sleep_counter = SLEEP_BETWEEN_SAMPLES;

                        total_sends++;
                }

                if (packet_task_next(NULL) != NULL)
                        packet_process_txqueue();

                if (do_lpm)
                        LPM3;
        }  // End of main loop
}

uint8_t adc_done;

int adc_temp_read()
{
        int adcval;
        long tempC, tempval;

        // Select temp sensor with ADC10CLK/4
        ADC10CTL1 = INCH_10 | ADC10DIV_3;
        ADC10CTL0 = SREF_1 | ADC10SHT_3 | REFON | ADC10ON | ADC10IE;  // 1.5V Vref

        sleep_counter = 1;
        WDTCTL = WDT_ADLY_16;  // Wait 46ms for REF voltage to settle
        while (sleep_counter)
                LPM3;
        WDTCTL = WDT_ADLY_250; // Resume original setting

        adc_done = 0;
        ADC10CTL0 |= ENC | ADC10SC;  // Start conversion
        while (!adc_done)
                LPM0;
        ADC10CTL0 &= ~ADC10IFG;

        adcval = ADC10MEM + ADC_CALIBRATION_OFFSET;  // Calibration offset defined in "typebug.h"
        ADC10CTL0 &= ~ADC10ON;
        tempval = (long) adcval + ADCVAL_STATIC_OFFSET;
        tempC = (tempval * 27069 - 18169625) >> 16;  // oPossum's formula:
                                                   // http://forum.43oh.com/topic/1954-using-the-internal-temperature-sensor/

        return (int)tempC;
}

// ADC10 conversion complete
#pragma vector=ADC10_VECTOR
__interrupt void adc_isr(void)
{
        ADC10CTL0 &= ~ADC10IE;
        adc_done = 1;
        __bic_SR_register_on_exit(LPM4_bits);
}

void debug_packet(struct packet_task *taskbuf)
{
        unsigned char *devidptr = (unsigned char *) &(taskbuf->data[0]);  /* making sure the (unsigned) DEVID isn't clobbered by the
                                                                           *   signed char nature of the data[] array
                                                                           */
        *devidptr = DEVICE_ID;
        memcpy(&(taskbuf->data[1]), &total_sends, 2);  // # times we've woken up to read/xmit the temperature data

        taskbuf->program = 0xFE;
        taskbuf->size = 3;
        memcpy(&taskbuf->rfaddr[0], basestation_rxaddr, 5);
}


// WDT overflow/timer
#pragma vector=WDT_VECTOR
__interrupt void WDT_ISR(void)
{
        IFG1 &= ~WDTIFG;
        if (sleep_counter)
                sleep_counter--;
        else
                __bic_SR_register_on_exit(LPM3_bits);
}

Packet_processor.c:

/* packet_processor.c
 * Handle slave node packets (TX or RX)
 */

#include <msp430.h>
#include "packet_processor.h"
#include "msprf24.h"
#include "tempbug.h"
#include <sys/cdefs.h>
#include <string.h>
#include <stdlib.h>

struct packet_task txtasks[PACKET_TASKLIST_DEPTH];

char packet_task_append(struct packet_task *task)
{
        int i=0;

        if (task == NULL)
                return 0;  // Invalid request
        while (txtasks[i].active && i < PACKET_TASKLIST_DEPTH)
                i++;
        if (i == PACKET_TASKLIST_DEPTH)
                return 0;  // No free slots in the tasklist queue
        memcpy(&(txtasks[i]), task, sizeof(struct packet_task));
        txtasks[i].active = 1;

        return 1;
}

struct packet_task *packet_task_next(struct packet_task *cur)
{
        int i;

        if (cur == NULL) {
                i = 0;
        } else {
                for (i=0; i < PACKET_TASKLIST_DEPTH; i++) {
                        if (cur == &(txtasks[i])) {
                                i += 1;
                                break;
                        }
                }
        }
        for (; i < PACKET_TASKLIST_DEPTH; i++) {
                if (txtasks[i].active)
                        return &txtasks[i];
        }
        return NULL;
}

void packet_init_tasklist()
{
        int i;

        for (i=0; i < PACKET_TASKLIST_DEPTH; i++) {
                txtasks[i].active = 0;
                txtasks[i].program = 0;
                txtasks[i].size = 0;
        }
}

/* Process RX packet payloads
 *
 * Some commands may result in TX replies, which are appended to the
 * packet task list.
 */
char packet_processor(unsigned char program, unsigned char size, char *data)
{
        unsigned char *udata = (unsigned char*)data;

        switch (program) {
                case 0x03:  // LED test reply
                        // Data: ID(1), LED_EN(1)
                        if (size != 2)
                                return 0;  // Packet length invalid
                        if (udata[0] != DEVICE_ID)
                                return 0;  // Packet was sent to the wrong device
                        if (udata[1])
                                P2OUT |= BIT6;
                        else
                                P2OUT &= ~BIT6;
                        return 1;

                default:
                        return 0;
        }
        return 1;
}

/* Process all outstanding TX packet payloads of a similar RF address */
char packet_process_txqueue()
{
        char packet[32], *curaddr;
        int plen=0;
        struct packet_task *ct;

        // No point in sending a packet until the TX queue is empty
        if ( msprf24_queue_state() & RF24_QUEUE_TXFULL )
                return 0;

        if ((ct = packet_task_next(NULL)) == NULL)
                return 0;
        curaddr = ct->rfaddr;
        do {
                if (ct->active && !memcmp(ct->rfaddr, curaddr, 5)) {
                        if ( (ct->size + 2 + plen) < 32 ) {
                                packet[plen++] = ct->program;
                                packet[plen++] = ct->size;
                                memcpy(&packet[plen], ct->data, ct->size);
                                plen += ct->size;
                                ct->active = 0;  // Delete task; it's handled
                        }
                        if (ct->size > 16)  // Cull any invalid/faulty-sized packets
                                ct->active = 0;
                }
                ct = packet_task_next(ct);
        } while (ct != NULL);
        if (!plen)
                return 0;

        w_tx_addr(curaddr);
        w_rx_addr(0, curaddr);
        w_tx_payload(plen, packet);

        // Let'er rip
        msprf24_activate_tx();
        return 1;
}

packet_processor.h:

/* packet_processor.h
 *
 * Handle slave node packets (TX or RX)
 *
 * Headers & reference documentation
 * Struct for managing packet task list
 */

/* Command/packet reference:
 * +---------+-----------+----------------+
 * | PROGRAM | PACKETLEN | PACKETCONTENTS |
 * +---------+-----------+----------------+
 *
 * Programs:
 * 0x01 : Hello (slave->base)
 * 0x02 : LEDtest request (slave->base)
 * 0x03 : LEDtest reply (base->slave)
 * 0x04 : GPIO set (base->slave)
 * 0x05 : GPIO get request (base->slave)
 * 0x06 : GPIO get reply (slave->base)
 * 0x10 : Thermocouple status (slave->base)
 */

#define PACKET_TASKLIST_DEPTH 3

// Packet TX task; an array of these comprise the TX queue
struct packet_task {
        unsigned char active;
        unsigned char program;
        unsigned char size;
        char rfaddr[5];
        char data[16];
};

extern struct packet_task txtasks[PACKET_TASKLIST_DEPTH];  // Task list

void packet_init_tasklist();
char packet_task_append(struct packet_task *task);
struct packet_task *packet_task_next(struct packet_task *cur);

char packet_processor(unsigned char program, unsigned char size, char *data); // Processes an RX packet, one at a time, returning 1 if successful

char packet_process_txqueue();          /* Submits a TX packet stuffing as many packets with the same RF address as can fit
                                         * inside a 32-byte packet; returns 1 if successful or 0 if no packets found in the task
                                         * queue.
                                         * This function runs msprf24_activate_tx() and the MCU should enter LPM sleep shortly after
                                         * successful completion of this function, handling IRQ upon wakeup.  After waking up it's
                                         * prudent to clear RX pipe#0's address (w_rx_addr(0, <some_dummy_array_of_zeroes>)) to avoid
                                         * inadvertently capturing other traffic destined to that remote module if we enter RX mode later.
                                         */
Link to post
Share on other sites

Lol, it just occurred to me that I misused the ADC_STATIC_OFFSET variable vs. the ADC_CALIBRATION_OFFSET in my tempbug.h file.  Might have to go back and fix that... haha.  Doesn't really matter though, it works with that -7 offset.

 

edit: And I found a logic flow bug in packet_process_txqueue() ... ct->size > 16 gets stuffed into the packet before it even has a chance to cull it.  I should just post all my code to the forum since I seem to find all my bugs only after I've posted it for the whole world to see!

Link to post
Share on other sites

Grill monitor base station's output (DEVICE_ID of 0x03 shows as "TC3", as opposed to "TC1" and "TC2" which are my grill monitor thermocouples)-

TC3 temp 18 degC 64 degF  ambient 18 degC 64 degF
DEBUG3 95 01

Larger dump of data:

TC3 temp 18 degC 64 degF  ambient 18 degC 64 degF
DEBUG3 93 01
BaseStn: RX IRQ packet=10 06 03 12 00 12 00 00 FE 03 03 93 01
TC1 temp 17 degC 62 degF  ambient 21 degC 69 degF
TC2 temp 17 degC 62 degF  ambient 20 degC 68 degF
DEBUG1 29 C7 00 00 F0 02
BaseStn: RX IRQ packet=10 06 01 11 00 15 00 00 10 06 02 11 00 14 00 00 FE 07 01 29 C7 00 00 F0 02
TC1 temp 17 degC 62 degF  ambient 21 degC 69 degF
TC2 temp 17 degC 62 degF  ambient 20 degC 68 degF
DEBUG1 2A C7 00 00 F0 02
BaseStn: RX IRQ packet=10 06 01 11 00 15 00 00 10 06 02 11 00 14 00 00 FE 07 01 2A C7 00 00 F0 02
TC3 temp 18 degC 64 degF  ambient 18 degC 64 degF
DEBUG3 94 01
BaseStn: RX IRQ packet=10 06 03 12 00 12 00 00 FE 03 03 94 01
TC1 temp 17 degC 62 degF  ambient 21 degC 69 degF
TC2 temp 18 degC 64 degF  ambient 20 degC 68 degF
DEBUG1 2B C7 00 00 F0 02
BaseStn: RX IRQ packet=10 06 01 11 00 15 00 00 10 06 02 12 00 14 00 00 FE 07 01 2B C7 00 00 F0 02
TC1 temp 17 degC 62 degF  ambient 21 degC 69 degF
TC2 temp 18 degC 64 degF  ambient 20 degC 68 degF
DEBUG1 2C C7 00 00 F0 02
BaseStn: RX IRQ packet=10 06 01 11 00 15 00 00 10 06 02 12 00 14 00 00 FE 07 01 2C C7 00 00 F0 02
TC3 temp 18 degC 64 degF  ambient 18 degC 64 degF
DEBUG3 95 01
BaseStn: RX IRQ packet=10 06 03 12 00 12 00 00 FE 03 03 95 01
TC1 temp 17 degC 62 degF  ambient 21 degC 69 degF
TC2 temp 18 degC 64 degF  ambient 20 degC 68 degF
DEBUG1 2D C7 00 00 F0 02
BaseStn: RX IRQ packet=10 06 01 11 00 15 00 00 10 06 02 12 00 14 00 00 FE 07 01 2D C7 00 00 F0 02
TC1 temp 17 degC 62 degF  ambient 21 degC 69 degF
TC2 temp 18 degC 64 degF  ambient 20 degC 68 degF
DEBUG1 2E C7 00 00 F0 02
BaseStn: RX IRQ packet=10 06 01 11 00 15 00 00 10 06 02 12 00 14 00 00 FE 07 01 2E C7 00 00 F0 02
TC3 temp 18 degC 64 degF  ambient 18 degC 64 degF
DEBUG3 96 01
BaseStn: RX IRQ packet=10 06 03 12 00 12 00 00 FE 03 03 96 01

Sun must be out and just starting to shine on the grill outside, so the ambient (cold-junction PCB) temp for those thermocouples is rising... if it's hot out today it should see ~100-120F temps from the IR gain of the sun beating down on the grill monitor box (and steadily fall after mid-day when the sun moves to the west; grill is located on the east side of the house).  TC3 should be 64F all day long being in the cold basement.

Link to post
Share on other sites

Another slightly off topic/half related post:

TC3 temp 18 degC 64 degF  ambient 18 degC 64 degF
DEBUG3 CA 01
BaseStn: RX IRQ packet=10 06 03 12 00 12 00 00 FE 03 03 CA 01
TC1 temp 27 degC 80 degF  ambient 49 degC 120 degF
TC2 temp 35 degC 95 degF  ambient 48 degC 118 degF
DEBUG1 9C C7 00 00 F0 02
BaseStn: RX IRQ packet=10 06 01 1B 00 31 00 00 10 06 02 23 00 30 00 00 FE 07 01 9C C7 00 00 F0 02
TC1 temp 27 degC 80 degF  ambient 49 degC 120 degF
TC2 temp 35 degC 95 degF  ambient 48 degC 118 degF
DEBUG1 9D C7 00 00 F0 02
BaseStn: RX IRQ packet=10 06 01 1B 00 31 00 00 10 06 02 23 00 30 00 00 FE 07 01 9D C7 00 00 F0 02
TC3 temp 18 degC 64 degF  ambient 18 degC 64 degF
DEBUG3 CB 01
BaseStn: RX IRQ packet=10 06 03 12 00 12 00 00 FE 03 03 CB 01
TC1 temp 28 degC 82 degF  ambient 49 degC 120 degF
TC2 temp 35 degC 95 degF  ambient 48 degC 118 degF
DEBUG1 9E C7 00 00 F0 02
BaseStn: RX IRQ packet=10 06 01 1C 00 31 00 00 10 06 02 23 00 30 00 00 FE 07 01 9E C7 00 00 F0 02
TC1 temp 28 degC 82 degF  ambient 49 degC 120 degF
TC2 temp 35 degC 95 degF  ambient 48 degC 118 degF
DEBUG1 9F C7 00 00 F0 02
BaseStn: RX IRQ packet=10 06 01 1C 00 31 00 00 10 06 02 23 00 30 00 00 FE 07 01 9F C7 00 00 F0 02
TC3 temp 18 degC 64 degF  ambient 18 degC 64 degF
DEBUG3 CC 01
BaseStn: RX IRQ packet=10 06 03 12 00 12 00 00 FE 03 03 CC 01
TC1 temp 28 degC 82 degF  ambient 49 degC 120 degF
TC2 temp 36 degC 96 degF  ambient 48 degC 118 degF
DEBUG1 A0 C7 00 00 F0 02
BaseStn: RX IRQ packet=10 06 01 1C 00 31 00 00 10 06 02 24 00 30 00 00 FE 07 01 A0 C7 00 00 F0 02

Pretty cool, sun shining on the grill monitor weatherproof box shows 120F temps while the heavy ceramics of the Big Green Egg buffer the thermocouples themselves... only showing 96F on the dome reading and 82F for the interior thermocouple (sitting on the grill, it's the one you stick into the meat to monitor cooking status)

Weather Underground reports the outside temp as 75F for my area.

Link to post
Share on other sites

I think you mentioned it originally in my thread on the 2955 temperature sensor.

 

Funny thing is I switched over to the 2452 since I found a bunch of them cheap on avnet and they seem a better fit for a lightweight application like this. Wasn't aware of the battery holder that you are using, so I put in holes for m2 screws. Also put in a bunch of extra IOs with optional pull up resistors in case I wanted to interface with i2c, buzzers, etc...

 

How did you calibrate your sensors? I've just used the calibration data within the 2995 (which I believe is also there on the 2452) and seems to get within 1 degree C of a desktop thermometer.

 

Also do you find that the 10uF cap for the nrf24 is needed? I just assumed they had something on the board for that and seems to work fine without it.

 

Waiting for Oshpark to get these out to me so I can keep playing.

post-31956-0-52621600-1368809705_thumb.png

post-31956-0-51652300-1368809715_thumb.png

Link to post
Share on other sites

I think you mentioned it originally in my thread on the 2955 temperature sensor.

 

Funny thing is I switched over to the 2452 since I found a bunch of them cheap on avnet and they seem a better fit for a lightweight application like this. Wasn't aware of the battery holder that you are using, so I put in holes for m2 screws. Also put in a bunch of extra IOs with optional pull up resistors in case I wanted to interface with i2c, buzzers, etc...

 

How did you calibrate your sensors? I've just used the calibration data within the 2995 (which I believe is also there on the 2452) and seems to get within 1 degree C of a desktop thermometer.

 

Also do you find that the 10uF cap for the nrf24 is needed? I just assumed they had something on the board for that and seems to work fine without it.

 

Waiting for Oshpark to get these out to me so I can keep playing.

I haven't tried it without the 10uF cap.  I have several more of these boards so I might give it a shot...

 

Mine was pretty far off but I'm not sure, is there calibration data built into Info_A somewhere?  I should go look for that, might make life easier!  For now I just use a static offset in my code and use an Infrared thermometer to check the temperature of the board.

Link to post
Share on other sites

So I looked over that formula and I don't know if its right. I believe the formula is dependent on the individual calibration constants within the MCU. What I do is use the slope intercept formula and the two constants to figure out the linear formula and for that particular MCU and just use that. Likely could be optimized, but this seems to work fast enough and reasonably accurate.

 

 

#define TAG_ADC10_1_ 0x10DA /* ADC10_1 calibration tag */
#define CAL_ADC_25T85 (*(unsigned short *)(TAG_ADC10_1_+0x0010))
#define CAL_ADC_25T30 (*(unsigned short *)(TAG_ADC10_1_+0x000E))
#define CAL_ADC_25VREF_FACTOR (*(unsigned short *)(TAG_ADC10_1_+0x000C))
#define CAL_ADC_15T85 (*(unsigned short *)(TAG_ADC10_1_+0x000A))
#define CAL_ADC_15T30 (*(unsigned short *)(TAG_ADC10_1_+0x0008))
#define CAL_ADC_15VREF_FACTOR (*(unsigned short *)(TAG_ADC10_1_+0x0006))
#define CAL_ADC_OFFSET (*(short *)(TAG_ADC10_1_+0x0004))
#define CAL_ADC_GAIN_FACTOR (*(unsigned short *)(TAG_ADC10_1_+0x0002))

short getCalibratedADC10MEM()
{
return (short)((((long)ADC10MEM * (long)CAL_ADC_GAIN_FACTOR) >> 15l) + CAL_ADC_OFFSET);
}



short voltage = getCalibratedADC10MEM();
long temperature = voltage;
temperature = 55l * temperature + 30l*CAL_ADC_15T85 - 85l*CAL_ADC_15T30;
temperature = (temperature * 100l)/(CAL_ADC_15T85 - CAL_ADC_15T30);
Link to post
Share on other sites

It's not in any of the include files (for gcc, that's why I have the #defines) but it is listed in the data sheet on page 12. So pretty sure it should be on the devices.

Aaah!  Good catch... I found it.  (And finally realized that you had #define's in your code, not reposting from the .h file or something LOL ... need more coffee)

 

Definitely gonna give your code a spin later!

 

So would this look right?

        tempval = ( ( (long)ADC10MEM * (long)CAL_ADC_GAIN_FACTOR ) >> 15 ) + CAL_ADC_OFFSET;
        tempval = 55L * tempval + 30L*CAL_ADC_15T85 - 85L*CAL_ADC_15T30;
        tempC = (tempval * 100L) / (CAL_ADC_15T85 - CAL_ADC_15T30);
Link to post
Share on other sites

Looks right to me, you'll get an int value that's 100x greater than the actual float value (+/- whatever errors are caused by not using floating point math). Really curious to see what kind of results you get with this, again hoping I didn't screw anything up...

 

 

Have you found the 46 ms delay between turning on the ADC and reading the value to actually be needed? I've found

	ADC10CTL1 = INCH_10 | ADC10DIV_4;
	ADC10CTL0 = SREF_1 | ADC10SHT_3 | ADC10ON | ADC10IE | ENC | ADC10SC | REFON;
	LPM3;
	ADC10CTL0 = 0;

to work well, but can't remember if I've tried a delay to see if it improved things.

 

And totally unrelated, but did you know you could define interrupts as the following in GCC, seems IMO a bit cleaner as far as wakeups go.

__attribute__((interrupt(ADC10_VECTOR), wakeup))
	void adc10_isr(void)
{
}

Link to post
Share on other sites

 

Looks right to me, you'll get an int value that's 100x greater than the actual float value (+/- whatever errors are caused by not using floating point math). Really curious to see what kind of results you get with this, again hoping I didn't screw anything up...

 

 

Have you found the 46 ms delay between turning on the ADC and reading the value to actually be needed? I've found

	ADC10CTL1 = INCH_10 | ADC10DIV_4;
	ADC10CTL0 = SREF_1 | ADC10SHT_3 | ADC10ON | ADC10IE | ENC | ADC10SC | REFON;
	LPM3;
	ADC10CTL0 = 0;

to work well, but can't remember if I've tried a delay to see if it improved things.

 

And totally unrelated, but did you know you could define interrupts as the following in GCC, seems IMO a bit cleaner as far as wakeups go.

__attribute__((interrupt(ADC10_VECTOR), wakeup))
	void adc10_isr(void)
{
}

How 'bout that!  Fancy.  Think I'll stick to my current method just because (I think it's compatible with CCS?)

Anyway, my settings are ADC10DIV_3 and ADC10SHT_3 and I think they work well enough, but I may try ADC10DIV_4 for kicks later.

Link to post
Share on other sites

@@paulpthcom yeah spirilis and I were both talking about this on IRC 2-3 maybe even 4 months ago too. I was commenting on his our door remote BBQ project, 

 

Originally atleast for a moment or two I was thinking to use the max31855 as the sensor until I remembered the msp430 had an on die temp sensor.

 

@@spirilis Yeah looks great man, I have not looked at it in depth, but will take a good long hard look when the time presents its self. This is one of those "simple" projects I would definitely be interested in personally.

Link to post
Share on other sites

@@paulpthcom yeah spirilis and I were both talking about this on IRC 2-3 maybe even 4 months ago too. I was commenting on his our door remote BBQ project, 

 

Originally atleast for a moment or two I was thinking to use the max31855 as the sensor until I remembered the msp430 had an on die temp sensor.

A thermistor seems like a good choice if you need a bit more accuracy than the die sensor, it's cheap, very low power and very easy to drive. I'm using it in a few places where I want to measure the temperature separately from the board.

Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.


×
×
  • Create New...