Arduino thermometer using DS18B20 and OLED display

This article describes a Arduino based thermometer using a 1-wire DS18B20 digital temperature sensor using a SDD1306 or SH1106 OLED display.

The DS18B20 is a digital sensor, used for relative noise immunity, especially given the choice of an OLED display.

This is a basis for tinkering, for modification and a vehicle for learning.

Above is the sample display.

The code is written to support multiple sensors on the 1-wire bus, it cycles through each of the sensors displaying them for 1s each.

Parasitic power

The DS18B20 can be connected using a two wire connection and using “parasitic power”.

Above is a simple scheme for parasitic power which should work with one master and one sensor at the other end of the cable for tens of metres. For longer cables and multiple sensors, see (Maxim 2014).

Note that the Vdd pin is tied to ground.

1-Wire

Above is a capture of the DQ line for search and read of a single DS18B20.

Above is a zoomed in view of the 1-wire encoding format.

I2C display

The display uses I2C.

It takes just under 25ms to paint the display using the example code.

Source code

Here is source code that compiles in the Arduino IDE v2.3.2.

#define VERSION "0.02"
#include 
#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 32 // OLED display height, in pixels
#define TEMPMIN -20
#define BARGRAPH
#define PPD 2 //pixels per degree, must be +ve integer
#define TICKMIN 5
#define TICKMAJ 10
#define SSD1306_DISPLAY
//#define SH1106G_DISPLAY
#if defined(SSD1306_DISPLAY)
#define OLED_RESET    -1 // Reset pin # (or -1 if sharing Arduino reset pin)
#include 
Adafruit_SSD1306 display=Adafruit_SSD1306(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
#endif
#if defined(SH1106G_DISPLAY)
#include 
#define WHITE SH110X_WHITE
#define BLACK SH110X_BLACK
Adafruit_SH1106G display=Adafruit_SH1106G(SCREEN_WIDTH,SCREEN_HEIGHT,&Wire);
#endif
#include 
DS18B20 ds(2);

#if defined(__AVR_ATmega328P__)
HardwareSerial &MySerial=Serial;
#endif

int i;
int barh=SCREEN_HEIGHT/2-2;
int basey=display.height()-1;
int tickh=barh/4;

void setup(){
  float adcref;
  long adcfs;

  #if defined(__AVR_ATmega328P__)
  analogReference(INTERNAL);
  adcref=1.10;
  adcfs=1024;
  #endif
  analogRead(A2); //Read ADC2
  delay (500); // Allow ADC to settle
  float vbat=analogRead(A2); //Read ADC again
  vbat=16*(vbat + 0.5)/(float)adcfs*adcref; //Calculate battery voltage scaled by 150k & 10k
  // Display startup screen
  MySerial.begin(9600);
  MySerial.println(F("Starting..."));
  #if defined(SSD1306_DISPLAY)
  display.begin(SSD1306_SWITCHCAPVCC, 0x3C);  //Initialize with the I2C address 0x3C.
  #endif
  #if defined(SH1106G_DISPLAY)
  display.begin(0x3C, true); // Address 0x3C default
  #endif
  display.setTextColor(WHITE);
  display.clearDisplay();
  display.setTextSize(1);
  display.setCursor(0, 0);
  display.print("DS18B20 thermometer");
  display.setCursor(0, 12);
  display.print("ardds18b20 ver: ");
  display.println(VERSION);
  display.print("vbat: ");
  display.println(vbat,1);
  display.display();
  delay(1000);
}

void loop(){
  int i,j;
  float temp;
  uint8_t id[8];
  char buf[27];

  j=1;
  while (ds.selectNext()){
    //for each sensor
    ds.getAddress(id);
    sprintf(buf," %02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X ",id[0],id[1],id[2],id[3],id[4],id[5],id[6],id[7]);
    temp=(ds.getTempC());
    MySerial.print(j);
    MySerial.print(buf);
    MySerial.print(temp,2);
    MySerial.println(F(" °"));
    display.clearDisplay();
    display.setCursor (0,0);
    display.setTextSize(2);
    display.print(j);
    display.print(F(": "));
    display.print(temp,1);
    display.print((char)247);
    
    #if defined(BARGRAPH)
    int w=(temp-TEMPMIN)*PPD;
    //draw bar starting from left of screen:
    display.fillRect(0,display.height()-1-barh,w,barh,WHITE);
    display.fillRect(w+1,display.height()-barh-1,display.width()-w,barh,BLACK);
    //draw tick marks
    for(int i=0;i<SCREEN_WIDTH;i=i+PPD*TICKMIN) display.fillRect(i,basey-barh+3*tickh,1,barh-3*tickh,i>w?WHITE:BLACK);
    for(int i=0;i<SCREEN_WIDTH;i=i+PPD*TICKMAJ) display.fillRect(i,basey-barh+2*tickh,1,barh-2*tickh,i>w?WHITE:BLACK);
    if(TEMPMIN<0) display.fillRect((0-TEMPMIN)*PPD,basey-barh+tickh,1,barh-tickh,i>w?WHITE:BLACK);
    #endif

    display.display();
    j++;
    delay(1000);
    }
  delay(100);
  }

This code suits a 128*32 pixel display. Changes will be needed to optimise other display resolution.

Github repository

See https://github.com/owenduffy/ardds18b20 for code updates.

See https://github.com/owenduffy/tds18b20 for a platformio implementation.

References

Maxim. 2014. Guidelines for long 1-wire networks.