0
\$\begingroup\$

I'm using a Dallas DS18B20 sensor to measure temperature in conjunction with an AVR ATMega328P microcontroller (using this AVR C library to implement the code). Unfortunately, the value always returns two or three degrees celsius (while it is around 20 degrees celsius here). I'm pretty sure I wired my breadboard correctly. I know this because:

  • The code for the 7-segment 4-digit display (3461AS) that I'm using to view the temperature is tested extensively.
  • To check for errors, I implemented the error codes available in the library (see wiki of AVR C library, bottom of page). When I pull out the jumper cable between the data pin and microcontroller pin I got an DS18B20_ERROR_COMM and when I try to pull out the 4.7KΩ resistor I got an DS18B20_ERROR_PULL error
  • With the same breadboard setup, the sensor works fine with an Arduino Uno (I used the library available in the Arduino IDE)
  • I checked the pinout setup which should be correct (segments on port D (cathode), digit slots on port B (anode) and data pin of DS18B20 to Port C (first pin))

I'm pretty sure the sensor is okay:

  • When I rub the sensor, the temperature changes
  • I have a second identical sensor which gives the same results

Therefore, I expect that there is something wrong with the code. Here is my Makefile:

PORT_ID=/dev/ttyACM0
MCU=atmega328p
F_CPU=1200000UL
CC=avr-gcc
PROGRAMMER_ID=stk500v1
OBJCOPY=avr-objcopy
CFLAGS=-Wall -g -Os -mmcu=${MCU} -DF_CPU=${F_CPU} -I.
TARGET=main
SRCS=main.c
BAUD_RATE=19200
PATH_DS18B20=./lib/libds18b20.a

all:
    ${CC} ${CFLAGS} -o ${TARGET}.bin ${SRCS} ${PATH_DS18B20}
    ${OBJCOPY} -j .text -j .data -O ihex ${TARGET}.bin ${TARGET}.hex

flash:
    avrdude -v -P ${PORT_ID} -b ${BAUD_RATE} -c ${PROGRAMMER_ID} -p ${MCU} -U flash:w:${TARGET}.hex

clean:
    rm -f *.bin *.hex

The static library is generated by the Makefile in the source of the library. For both compilations, I used a F_CPU value of 1200000UL (also tried different values).

Here is the C program:

#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#include <math.h>
#include <stdlib.h>
#include <ds18b20/ds18b20.h>

static const double MINIMUM_TEMPERATURE_DISPLAY = -9.9;
static const double MAXIMUM_TEMPERATURE_DISPLAY = 99.9;

#define DDR_DIGIT_POS_PORT DDRB
#define DDR_SEGMENT_PORT DDRD
#define DDR_TEMPERATURE_SENSOR DDRC

#define DIGIT_POS_PORT PORTB
#define SEGMENT_PORT PORTD
#define TEMPERATURE_PORT PORTC

#define TEMPERATURE_PIN_REGISTER PINC
#define TEMPERATURE_PIN PC0

#define SEGMENT_A PD0
#define SEGMENT_B PD1
#define SEGMENT_C PD2
#define SEGMENT_D PD3
#define SEGMENT_E PD4
#define SEGMENT_F PD5
#define SEGMENT_G PD6
#define SEGMENT_DP PD7

#define DIGIT_POS_ONE PB0
#define DIGIT_POS_TWO PB1
#define DIGIT_POS_THREE PB2
#define DIGIT_POS_FOUR PB6

uint8_t segments[] = {SEGMENT_A, SEGMENT_B, SEGMENT_C, SEGMENT_D, SEGMENT_E, SEGMENT_F, SEGMENT_G, SEGMENT_DP};
uint8_t digits[] = {DIGIT_POS_ONE, DIGIT_POS_TWO, DIGIT_POS_THREE, DIGIT_POS_FOUR};

uint8_t segsMinus[] = {SEGMENT_G};
uint8_t segsZero[] = {SEGMENT_A, SEGMENT_F, SEGMENT_B, SEGMENT_E, SEGMENT_C, SEGMENT_D};
uint8_t segsOne[] = {SEGMENT_B, SEGMENT_C};
uint8_t segsTwo[] = {SEGMENT_A, SEGMENT_B, SEGMENT_G, SEGMENT_E, SEGMENT_D};
uint8_t segsThree[] = {SEGMENT_A, SEGMENT_B, SEGMENT_G, SEGMENT_C, SEGMENT_D};
uint8_t segsFour[] = {SEGMENT_F, SEGMENT_G, SEGMENT_B, SEGMENT_C};
uint8_t segsFive[] = {SEGMENT_A, SEGMENT_F, SEGMENT_G, SEGMENT_C, SEGMENT_D};
uint8_t segsSix[] = {SEGMENT_A, SEGMENT_F, SEGMENT_G, SEGMENT_E, SEGMENT_C, SEGMENT_D};
uint8_t segsSeven[] = {SEGMENT_A, SEGMENT_B, SEGMENT_C};
uint8_t segsEight[] = {SEGMENT_A, SEGMENT_F, SEGMENT_B, SEGMENT_G, SEGMENT_E, SEGMENT_C, SEGMENT_D};
uint8_t segsNine[] = {SEGMENT_A, SEGMENT_F, SEGMENT_B, SEGMENT_G, SEGMENT_C, SEGMENT_D};
uint8_t segsDp[] = {SEGMENT_DP};
uint8_t segsLetterE[] = {SEGMENT_A, SEGMENT_F, SEGMENT_G, SEGMENT_E, SEGMENT_D};
uint8_t segsLetterR[] = {SEGMENT_A, SEGMENT_F, SEGMENT_E};

uint8_t segsCelsius[] = {SEGMENT_A, SEGMENT_F, SEGMENT_E, SEGMENT_D};
uint8_t segsFahrenheit[] = {SEGMENT_A, SEGMENT_F, SEGMENT_G, SEGMENT_E};

void setupIoSegmentDisplay(){
  for(int i = 0; i < sizeof(digits); i++){
    DDR_DIGIT_POS_PORT |= (1<<digits[i]);
  }

  for(int i = 0; i < sizeof(segments); i++){
    DDR_SEGMENT_PORT |= (1<<segments[i]);
  }
}

void displayDigit(uint8_t *segs, uint8_t size){
  for(int i = 0; i < size; i++){
    SEGMENT_PORT |= (1<<segs[i]);
  }
}

void resetDigitSlots(){
  for(int i = 0; i < sizeof(digits); i++){
    DIGIT_POS_PORT &= ~(1<<digits[i]);
  }

  for(int i = 0; i < sizeof(segments); i++){
    SEGMENT_PORT &= ~(1<<segments[i]);
  }
}

void loadDigitSlot(uint8_t digitPosition){
  switch(digitPosition){
    case DIGIT_POS_ONE:
      DIGIT_POS_PORT |= (1<<DIGIT_POS_ONE);
      break;
    case DIGIT_POS_TWO:
      DIGIT_POS_PORT |= (1<<DIGIT_POS_TWO);
      break;
    case DIGIT_POS_THREE:
      DIGIT_POS_PORT |= (1<<DIGIT_POS_THREE);
      break;
    case DIGIT_POS_FOUR:
      DIGIT_POS_PORT |= (1<<DIGIT_POS_FOUR);
      break;

  }
}

char* parseTemperature(char* buffer, double temperature){
  double roundedTemperature = roundf(temperature * 10) / 10;
  if(roundedTemperature < MINIMUM_TEMPERATURE_DISPLAY){
    roundedTemperature = MINIMUM_TEMPERATURE_DISPLAY;
  }

  if(roundedTemperature > MAXIMUM_TEMPERATURE_DISPLAY){
    roundedTemperature = MAXIMUM_TEMPERATURE_DISPLAY;
  }

  dtostrf(roundedTemperature, 4, 1, buffer);
  return buffer;
}

uint8_t checkErrors(int status){
  return (DS18B20_ERROR_OK != status) ? 1 : 0;
}

void displayError(uint8_t status){
  while(1){
    resetDigitSlots();
    loadDigitSlot(DIGIT_POS_ONE);
    displayDigit(segsLetterE, sizeof(segsLetterE));

    resetDigitSlots();
    loadDigitSlot(DIGIT_POS_TWO);
    displayDigit(segsLetterR, sizeof(segsLetterR));

    resetDigitSlots();
    loadDigitSlot(DIGIT_POS_THREE);
    displayDigit(segsLetterR, sizeof(segsLetterR));

    resetDigitSlots();
    loadDigitSlot(DIGIT_POS_FOUR);
    switch(status){
      case DS18B20_ERROR_COMM:
        displayDigit(segsOne, sizeof(segsOne));
        break;
      case DS18B20_ERROR_CRC:
        displayDigit(segsTwo, sizeof(segsTwo));
        break;
      case DS18B20_ERROR_PULL:
        displayDigit(segsThree, sizeof(segsThree));
        break;
      case DS18B20_ERROR_OTHER:
        displayDigit(segsFour, sizeof(segsFour));
        break;
    }
  }
 }

 void printTemp(char digit){
   switch(digit){
     case '-':
       displayDigit(segsMinus, sizeof(segsMinus));
       break;
     case '0':
       displayDigit(segsZero, sizeof(segsZero));
       break;
     case '1':
       displayDigit(segsOne, sizeof(segsOne));
       break;
     case '2':
       displayDigit(segsTwo, sizeof(segsTwo));
       break;
     case '3':
       displayDigit(segsThree, sizeof(segsThree));
       break;
     case '4':
       displayDigit(segsFour, sizeof(segsFour));
       break;
     case '5':
       displayDigit(segsFive, sizeof(segsFive));
       break;
     case '6':
       displayDigit(segsSix, sizeof(segsSix));
       break;
     case '7':
       displayDigit(segsSeven, sizeof(segsSeven));
       break;
     case '8':
       displayDigit(segsEight, sizeof(segsEight));
       break;
     case '9':
       displayDigit(segsNine, sizeof(segsNine));
       break;
     case '.':
       displayDigit(segsDp, sizeof(segsDp));
       break;
     case ' ':
       displayDigit(segsCelsius, sizeof(segsCelsius));
       break;
     default:
       displayDigit(segsLetterE, sizeof(segsLetterE));
       break;
    }
 }

 int main(void){
   cli();
   setupIoSegmentDisplay();
   sei();

   // Stores temperature (times 16 see ds18b20.h)
   int16_t temperature;
   uint8_t status;
   while(1){
     //Start conversion (without ROM matching)
     status = ds18b20convert( 
       &TEMPERATURE_PORT, 
       &DDR_TEMPERATURE_SENSOR, 
       &TEMPERATURE_PIN_REGISTER, 
       (1<<TEMPERATURE_PIN), 
       NULL 
     );

     if(checkErrors(status)){
       displayError(status);
     }

     //Delay (sensor needs time to perform conversion)
     _delay_ms( 3000 );

     //Read temperature (without ROM matching)
     status = ds18b20read( 
       &TEMPERATURE_PORT, 
       &DDR_TEMPERATURE_SENSOR, 
       &TEMPERATURE_PIN_REGISTER, 
       (1<<TEMPERATURE_PIN), 
       NULL, 
       &temperature 
     );

     if(checkErrors(status)){
       displayError(status);
     }

     char buffer[4];
     // Divide temperature again
     char *parsedTemp = parseTemperature(buffer, temperature/DS18B20_MUL);
     int i = 0;
     while(*parsedTemp){
       resetDigitSlots();
       loadDigitSlot(digits[i]);
       char digit = *parsedTemp;
       printTemp(digit);
       ++parsedTemp;
       i++;

       _delay_ms(30);
     }
   }
  }

Is there anything obliviously wrong with the code?

\$\endgroup\$
  • \$\begingroup\$ Does the temperature change? Put your fingers on the sensor. Temperature should change. \$\endgroup\$ – StainlessSteelRat Sep 12 '19 at 20:48
  • \$\begingroup\$ @StainlessSteelRat Yes, I rubbed the sensor and it changes (although not that much). I also tried a second identical sensor which also shows exactly the same temperature. \$\endgroup\$ – markvdlaan93 Sep 12 '19 at 20:50
  • \$\begingroup\$ I've had trouble with integer types with the AVR chips before. It's a long shot, but maybe the 10 values in the line double roundedTemperature = roundf(temperature * 10) / 10; should be changed to 10.0 to ensure floating point arithmetic is used (and also temperature/DS18B20_MUL further down). \$\endgroup\$ – Sean Sep 12 '19 at 20:58
  • \$\begingroup\$ So sensor works. Error checking works. As Sam says, this is probably code related. \$\endgroup\$ – StainlessSteelRat Sep 12 '19 at 21:00
  • 2
    \$\begingroup\$ I would avoid using floating point arithmetic entirely; store the temperature to the desired resolution (0.1°C) in an int16_t and then deal with the decimal point in the display code. It will run much faster and generally be more reliable. \$\endgroup\$ – Caleb Reister Sep 13 '19 at 0:41

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.