John Bradnam
Published © GPL3+

Contact Digital Thermometer

A small hand-held thermometer that shows temperature in Celsius and Fahrenheit all powered by a button battery.

AdvancedFull instructions provided8 hours460
Contact Digital Thermometer

Things used in this project

Hardware components

Microchip ATtiny1614
×1
DS18B20 Temperature Sensor 1m
HARDWARIO DS18B20 Temperature Sensor 1m
×1
Tactile Switch, Top Actuated
Tactile Switch, Top Actuated
12mmx12mm
×1
Passive Components
1 x 0.1uF 1206 capacitor 1 x 0 ohm 1206 resistor 1 x 4.7k 1206 resistor
×1
Battery Holder, 2032 x1
Battery Holder, 2032 x1
Search eBay for "CR2032 2032 3V Cell Coin Battery Socket Holder Case"
×1
128x32 OLED I2C module
×1

Software apps and online services

Arduino IDE
Arduino IDE

Hand tools and fabrication machines

3D Printer (generic)
3D Printer (generic)
Soldering iron (generic)
Soldering iron (generic)

Story

Read more

Custom parts and enclosures

STL Files

STL files for 3D printing

Schematics

Schematic

PCB

Eagle Files

Schematic and PCB in Eagle format

Code

Contact_Thermometer_V1.ino

C/C++
/**************************************************************************
 Contact Digital Thermometer With Deep Sleep

 Concept: Neutrino-1 - https://www.instructables.com/Contact-Digital-Thermometer-With-Deep-Sleep-Attiny/ - 23rd May 2021
 Portions of this code by David Johnson-Davies - http://www.technoblogy.com/show?2G8T

 Schematic & PCB at https://www.hackster.io/john-bradnam/contact-digital-thermometer-ed18d2
 
 2021-07-11 John Bradnam (jbrad2089@gmail.com)
   Create program for ATtiny1614

 --------------------------------------------------------------------------
 Arduino IDE:
 --------------------------------------------------------------------------
  BOARD: ATtiny1614/1604/814/804/414/404/214/204
  Chip: ATtiny1614
  Clock Speed: 20MHz
  millis()/micros(): "Enabled (default timer)"
  Programmer: jtag2updi (megaTinyCore)

  ATTiny1614 Pins mapped to Ardunio Pins
 
              +--------+
          VCC + 1   14 + GND
  (SS)  0 PA4 + 2   13 + PA3 10 (SCK)
        1 PA5 + 3   12 + PA2 9  (MISO)
  (DAC) 2 PA6 + 4   11 + PA1 8  (MOSI)
        3 PA7 + 5   10 + PA0 11 (UPDI)
  (RXD) 4 PB3 + 6    9 + PB0 7  (SCL)
  (TXD) 5 PB2 + 7    8 + PB1 6  (SDA)
              +--------+
  
 **************************************************************************/

#include <OneWire.h>
#include <U8g2lib.h>
#include <avr/sleep.h>

#define MEASUREMENT_TIME 15000 //Set measurement time in milliseconds

#define TXD_PIN 5     //PB2
#define RXD_PIN 4     //PB3
#define SCA_PIN 6     //PB1
#define SCL_PIN 7     //PB0
#define TEMP_PIN 10   //PA3
#define SWITCH_PIN 1  //PA5

#define TEMP_PORT PORTA
#define TEMP_BM PIN3_bm

U8G2_SSD1306_128X32_UNIVISION_F_HW_I2C u8g2(U8G2_R0, U8X8_PIN_NONE);

#define MEASURE_INTERVAL_TIME 500
long measureTimeout;

char line1[32]; 
char line2[32]; 

//--------------------------------------------------------------------
// One Wire Protocol 

#define READ_ROM 0x33
#define MATCH_ROM 0x55
#define SKIP_ROM 0xCC
#define CONVERT_T 0x44
#define READ_SCRATCH_PAD 0xBE

// Buffer to read data or ROM code
static union {
  uint8_t DataBytes[9];
  unsigned int DataWords[4];
};

//-------------------------------------------------------------------------
// Initialise Hardware
void setup(void)
{
  pinMode(SWITCH_PIN, INPUT_PULLUP);

  OneWireSetup();
  
  u8g2.begin();
  u8g2.clearBuffer();         // clear the internal memory
  u8g2.sendBuffer();          // transfer internal memory to the display

  goToSleep();  
}

//--------------------------------------------------------------------
// Handle pin change interrupt when SWITCH is pressed
void switchInterrupt()
{
}

//--------------------------------------------------------------------
// Main program loop
void loop(void)
{ 
  if (millis() >= measureTimeout)
  { 
    float sum = 0.0;
    byte counter = 0;
    int width1;
    int width2;
    bool err = false;
  
    line1[0] = '\0';
    line2[0] = '\0';
    if (OneWireReset() != 0) 
    {
      strcat(line1,"DS18B20");
      strcat(line2,"Reset Failed");
      err = true;
    } 
    else 
    {
      OneWireWrite(SKIP_ROM);
      OneWireWrite(CONVERT_T);
      while (OneWireRead() != 0xFF);
      OneWireReset();
      OneWireWrite(SKIP_ROM);
      OneWireWrite(READ_SCRATCH_PAD);
      OneWireReadBytes(9);
      if (OneWireCRC(9) == 0) 
      {
        int temp = DataWords[0];
        int celsius = (temp+8) >> 4;        // Round to nearest degree
        int fahrenheit = (celsius * 9) / 5 + 32;
        sprintf(line1,"%dC",celsius);
        sprintf(line2,"%dF",fahrenheit);
      } 
      else
      {
        strcat(line1,"DS18B20");
        strcat(line2,"CRC error");
        err = true;
      }
    }

    u8g2.clearBuffer();
    u8g2.setFont(u8g2_font_logisoso28_tr); // helvetica bold
    width1 = u8g2.getStrWidth(line1);
    width2 = u8g2.getStrWidth(line2);
    if (err || (width1 + width2) > 120)
    {
      //Display over two lines
      u8g2.setFont(u8g2_font_helvB12_tr); // helvetica bold
      width1 = u8g2.getStrWidth(line1);
      width2 = u8g2.getStrWidth(line2);
      u8g2.drawStr((128-width1)>>1,14,line1);
      u8g2.drawStr((128-width2)>>1,29,line2);
    }
    else
    {
      //Single line with large font
      u8g2.drawStr(0,30,line1);
      u8g2.drawStr(128-width2,30,line2);
    }
    u8g2.sendBuffer();

    measureTimeout = millis() + MEASURE_INTERVAL_TIME;
  }
  
  if (digitalRead(SWITCH_PIN) == LOW)
  {
    delay(10);  //Debounce
    if (digitalRead(SWITCH_PIN) == LOW)
    {
      //wait until release
      while (digitalRead(SWITCH_PIN) == LOW);
      goToSleep();
    }
  }
}

//--------------------------------------------------------------------
// Show splash screen

void showSplashScreen()
{
  u8g2.setFont(u8g2_font_helvB12_tr); // helvetica bold
    
  //Splash screen 1
  u8g2.clearBuffer();
  u8g2.drawStr(20,14,"ATtiny1614");   // x,y,text
  u8g2.drawStr(8,29,"Thermometer!");
  u8g2.sendBuffer();                // transfer internal memory to the display
  delay(5000);
  
  u8g2.clearBuffer();
  u8g2.drawStr(8,14,"Measuring in");
  u8g2.drawStr(20,29,"3 seconds");
  u8g2.sendBuffer();
  delay(3000);
  
  u8g2.clearBuffer();
  u8g2.drawStr(16,21,"Measuring...");
  u8g2.sendBuffer();
  measureTimeout = millis();
}

//--------------------------------------------------------------------
//Shut down OLED and put ATtiny to sleep
//Will wake up when LEFT button is pressed
void goToSleep() 
{
  attachInterrupt(SWITCH_PIN, switchInterrupt, CHANGE);
  u8g2.clearBuffer();         // clear the internal memory
  u8g2.sendBuffer();          // transfer internal memory to the display
  u8g2.setPowerSave(true);
  set_sleep_mode(SLEEP_MODE_PWR_DOWN);  // sleep mode is set here
  sleep_enable();
  sleep_mode();                         // System actually sleeps here
  sleep_disable();                      // System continues execution here when watchdog timed out
  u8g2.setPowerSave(false);
  detachInterrupt(SWITCH_PIN);
  //wait until release
  while (digitalRead(SWITCH_PIN) == LOW);
  showSplashScreen();
}

//--------------------------------------------------------------------
// One Wire Protocol 

inline void PinLow () 
{
  TEMP_PORT.DIRSET = TEMP_BM;
  TEMP_PORT.OUTCLR = TEMP_BM;
}

inline void PinRelease () 
{
  TEMP_PORT.DIRCLR = TEMP_BM;
}

// Returns 0 or 1
inline uint8_t PinRead () 
{
  return (TEMP_PORT.IN & TEMP_BM) ? 1 : 0;
}

void DelayMicros(unsigned int micro) 
{
  delayMicroseconds(micro);
}

void LowRelease(int low, int high) 
{
  PinLow();
  DelayMicros(low);
  PinRelease();
  DelayMicros(high);
}

void OneWireSetup() 
{
}

uint8_t OneWireReset() 
{
  uint8_t data = 1;
  LowRelease(480, 70);
  data = PinRead();
  DelayMicros(410);
  return data;                            // 0 = device present
}

void OneWireWrite(uint8_t data) 
{
  int del;
  for (int i = 0; i<8; i++) {
    if ((data & 1) == 1) del = 6; else del = 60;
    LowRelease(del, 70 - del);
    data = data >> 1;
  }
}

uint8_t OneWireRead () 
{
  uint8_t data = 0;
  for (int i = 0; i<8; i++) 
  {
    LowRelease(6, 9);
    data = data | PinRead()<<i;
    DelayMicros(55);
  }
  return data;
}

// Read bytes into array, least significant byte first
void OneWireReadBytes (int bytes) 
{
  for (int i=0; i<bytes; i++) 
  {
    DataBytes[i] = OneWireRead();
  }
}

// Calculate CRC over buffer - 0x00 is correct
uint8_t OneWireCRC (int bytes) 
{
  uint8_t crc = 0;
  for (int j=0; j<bytes; j++) 
  {
    crc = crc ^ DataBytes[j];
    for (int i=0; i<8; i++) crc = crc>>1 ^ ((crc & 1) ? 0x8c : 0);
  }
  return crc;
}

Credits

John Bradnam
151 projects • 194 followers
Contact

Comments

Please log in or sign up to comment.