Project tutorial

Jens Temperature Sensor Coaster © GPL3+

This coaster detects the temperature of a cup. You get a signal when it is ready to drink.

  • 2,010 views
  • 0 comments
  • 11 respects

Components and supplies

Ard nano
Arduino Nano R3
×1
Blue LED 455nm 3.8v 20mA
×1
Red LED 625nm 1.8v 2..5mA
×1
Green LED 565nm 2.2v 2...5mA
×1
Resistor 560 ohm 0.4w
×1
Resistor 68 ohm 0.4w
×1
Resistor 680 ohm 0.4w
×1
Shrink Tube
×1
Rubber feets sticker 10 mm diameter 2 mm high
×4
10988 01
Temperature Sensor
×1

Necessary tools and machines

3D Printer, ABS Filament
09507 01
Soldering iron (generic)
Sidecutter

Apps and online services

About this project

Temperature Sensor Coaster

I was drinking hot coffee. First I burned my mouth on it. Then I forgot about it and it cooled down to room temperature. With a sore mouth and cold coffee I realized that I had failed to drink a cup of coffee. Then I asked myself: Could I, without any knowledge about Arduino, make a device solving this problem for me?

Challenge accepted!

How about creating a coaster with a temperature sensor making it possible to set the "too hot" and the "too cold" temperatures and then a red, green and blue LED displaying in what range the temperature is. The device should have a buzzer giving a sound when the temperature reached the drinkable temperature.

That was the basic idea but how should I make it easy to configure this device? Since Arduino comes with USB why not create a user interface on the computer that communicates with the coaster making it possible to configure it from there? When the configuration program starts it should establish the communication with the coaster and read the current values such as temperature etc. It should be possible to change the settings and these changes are then sent back to the coaster where they gets stored in the EEPROM.

First I bought a book about Arduino. I bought Arduino for dummies by John Nussey. In that book he suggested buying a starter kit. I bought a starter kit. Most starter kits comes with temperature sensors and LEDs and what not. I made many of the exercises in the book. Then I was ready for my own project. In small steps I created the circuit on the breadboard. I found information about how to use the TMP36 temperature sensor with Arduino and a sketch.

In the book there where exercises about how to use a piezo element (the buzzer). I combined the buzzer with the temperature sensor setup. Then I added the LEDs. At that point the raw circuit was finished.

Then I started working on the configuration program. For this I downloaded a community edition of Visual Studio and then from scratch I created the User Interface. This is the toughest part of the project. There are far more complex things going on in the configuration program compared to the Arduino. I will talk more about this on the software tab of this project.

Then it was time to put this together. For this purpose I decided to buy a smaller Arduino Nano so that the coaster could be thinner and a much nicer design.

When all the electronic parts for the coaster arrived I could start creating the housing for the coaster. I decided to make the coaster with my 3D printer. I would make in ABS plastic that can handle the temperatures of hot beverages and as an added bonus I can acetone smooth the surface of ABS plastic so that the coaster could get that shiny look. For this part I launched Fusion 360 and then I generated STL files that I sliced in Cura and printed on a Tevo Black Widow.

Here is a link to the Fusion 360 project. I also included STL files in the hardware section.

When I finished printing the coaster, it always takes a couple of times before you nail it. There is often that things do not fit into each other. For example I could not the Arduino to fit. The Arduino I bought had tabs so that it could be used in a breadboard but I cut these off with a side cutter so that it would fit.

Then it was time to assemble the thing.

I had ordered a 50 mm buzzer that I had to cut a little in the edges to get into the housing. The temperature sensor is in the middle of the coaster and it has a little feather that pushes up against the bottom of the cup. I used double sided tape for the buzzer to stick the buzzer to the coaster. The LEDs of the Arduino was shining though the plastic a little but I used black tape to hide them. I used shrink tubes to avoid shortcuts inside the coaster.

Then I screwed the coaster together and placed little rubber feeds on top of every screw. It looked nice indeed.

But is it working? It is! There are a few caveats though. One for example that I had not realized. Most people drink cold coffee! It sounds awful and it is but it is THE TRUTH. I had so many people trying the device with cold coffee and then it is not working properly? Huh, Jens what is up with this? Well, your coffee is cold how help that?

Another thing is ridiculously insulated cups. It will not work either. Then we have cups with almost no bottom. All the mass and hence heat is much higher up. Before the beverage heated the bottom the top is cold.

It works but it works for a "sensible" cup and it IS individual what settings suits you the best. That said, I made attempts to fulfill the "psychological" requirements but that is challenging, especially for almost cold beverages.

That is it. I mastered this, and I managed to make a device that helps me. That is all there is to it challenge accomplished!

The sketch to load into the Arduino

In the software section of this post you will find the program I made for the Arduino. When the temperature is rising the coaster will blink the LED of the current range. When the temperature stabilizes the LED will stop blink.

The coaster works internally with Celcious. The configuration program can convert this on the fly to Fahrenheit.

The configuration program

I made the configuration program in c# in Windows in Visual Studio 2017 community edition. The program will start the serial communication with the coaster and parse the values sent. When changing a range the configuration program writes a string to the serial interface with the new value. These are the 'commands' the coaster understands:

  • 'tt:' top temperature. After colon enter the top temperature in decimal with a dot as decimal separator. Temperature in Celsius.
  • 'bt:' bottom temperature. After colon enter the bottom temperature.
  • 'pt:' play top temperature sound. Enter 0 or 1.
  • 'pb:' play bottom temperature sound. Enter 0 or 1.
  • 'dc:' display Celsius. Enter 0 or 1.

The coaster sends a string of values each 450 milliseconds:

  • it: input temperature
  • tt: top temperature setting.
  • bt: bottom temperature setting.
  • dc: display Celsius setting.
  • at: average temperature
  • pt: play sound when transition from top temperature to drinkable temperature.
  • pb: play sound when transition from drinkable to too cold temperature

As you might understood from the cup I work for a Translation Agency. :)

Best Regards,

Jens Malmgren

Code

The sketch for the coasterArduino
This program is loded into the coaster it is reading the temperature sensor and lighting the LEDs based on the given ranges.
#include <EEPROM.h>

#define AREF 3.3         // we tie 3.3V to ARef and measure it with a multimeter!

//Vout TMP36 > A1
//3.3V > V+ TMP36 > AREF
//GND TMP36 > GND
//D6 > Piezo > GND
//D7 > Blue Led (+), Blue Led (-) > 60 Ohm > GND
//D8 > Green Led (+), Green Led (-) > 560 Ohm > GND
//D9 > Red Led (+), Red Led (-) > 640 Ohm > GND

int iPinTemperature = 1;
int iTemperatureReading;
int iPinLedRed = 9;
int iPinLedGreen = 8;
int iPinLedBlue = 7;
int iPinBuzzer = 6;
String strSerialInput = "";
bool bSerialInputComplete = false;
float fTopTemperature = 100;
float fBottomTemperature = 0;
int iEEPROMTopTemperature = 0;
int iEEPROMBottomTemperature = 4;
int iEEPROMDisplayCelcius = 8;
int iEEPROMPlayTopTemperatureSound = 9;
int iEEPROMPlayBottomTemperatureSound = 10;
bool bPlayTopTemperatureSound = false;
bool bPlayBottomTemperatureSound = false;
bool bTempDisplaysCelcius = true;
float fPreviousTemperature = 0;
// Number of average temperatures in celcius
#define AVGC 13
#define POSCOUNT 10
float fArrTemps[AVGC]; // Array used to calculate the average temperature
float fArrTempsD[AVGC]; // Array used to calculate the average delta temperature
int iLEDConfigCurrent = 0; // 1 = Red, 0 = Green, -1 = Blue
int iLEDConfigPrevious = 0; // 1 = Red, 0 = Green, -1 = Blue
bool bLEDsActive = false;
bool bLEDsBlink = false;
float fAverageTemp;
float fAverageTempD;
int iDeltaPositiveCount;
bool bInitAverage = true;
int iAvgIndex;
long lMilliSecondsSinceBeep = -1;
long iMilliSecondsSinceBeepCount = 50000;
long iMilliSecondsSinceLastSwitchCount = 900000;
long lMilliSecondsSinceLastSwitch = iMilliSecondsSinceLastSwitchCount;
   
void setup(void)
{
  // We'll send debugging information via the Serial monitor
  Serial.begin(9600);
  Serial.println("Setup");  
 
  // If you want to set the aref to something other than 5v
  analogReference(EXTERNAL);
  pinMode(iPinLedRed, OUTPUT);
  pinMode(iPinLedGreen, OUTPUT);
  pinMode(iPinLedBlue, OUTPUT);

  strSerialInput.reserve(20);

  long lTop = EEPROMReadlong(iEEPROMTopTemperature);
  if (lTop < 0 || lTop > 10000)
  {
    lTop = 4500;
  }
  long lBottom = EEPROMReadlong(iEEPROMBottomTemperature);
  if (lBottom < 0 || lBottom > 10000)
  {
    lBottom = 3900;
  }

  if (lTop < lBottom)
  {
    lTop = 4500;
    lBottom = 3900;
  }
  fTopTemperature = (float)lTop / (float)100;
  fBottomTemperature = (float)lBottom / (float)100;
  bTempDisplaysCelcius = EEPROM.read(iEEPROMDisplayCelcius) == 1;
  
  bPlayTopTemperatureSound = EEPROM.read(iEEPROMPlayTopTemperatureSound) == 1;
  bPlayBottomTemperatureSound = EEPROM.read(iEEPROMPlayBottomTemperatureSound) == 1;
}
 
 
void loop(void)
{
  if (bSerialInputComplete)
  {
    Serial.println(strSerialInput);
    strSerialInput = "";
    bSerialInputComplete = false;
  }
  
  iTemperatureReading = analogRead(iPinTemperature);  
  // Converting temperature voltage to temperature using AREF
  float fTemperatureVoltage = iTemperatureReading * AREF;
  fTemperatureVoltage /= 1024.0;
  float fTemperatureC = (fTemperatureVoltage - 0.5) * 100 ;  //converting from 10 mv per degree with 500 mV offset to degrees ((fTemperatureVoltage - 500mV) times 100

  // Initialize average arrays etc
  if (bInitAverage)
  {
    for (int j = 0; j < AVGC; j++)
    {
      fArrTemps[j] = fTemperatureC;
      fArrTempsD[j] = (float)-0.01;
    }
    bInitAverage = false;
    iAvgIndex = 0;
    fAverageTemp = fTemperatureC;
    fPreviousTemperature = fTemperatureC;
    bLEDsActive = true;
    lMilliSecondsSinceBeep = millis();
    iLEDConfigPrevious = -1;
  }

  // Point to next average in the arrays
  iAvgIndex++;
  if (iAvgIndex > AVGC - 1)
  {
    iAvgIndex = 0;
  }

  fArrTemps[iAvgIndex] = fTemperatureC;

  // Calculate averages
  fAverageTemp = 0;
  int iPosCount = 0;
  for (int j = 0; j < AVGC; j++)
  {
    if (fArrTempsD[j] > (float)0.0)
    {
      iPosCount++;
    }
    fAverageTemp += fArrTemps[j];
    fAverageTempD += fArrTempsD[j];
  }

  fAverageTemp = fAverageTemp / (float)AVGC;
  fAverageTempD = fAverageTempD / (float)AVGC;
 
  Serial.print("[it:"); Serial.print(fTemperatureC);Serial.print(",tt:");
  Serial.print(fTopTemperature);Serial.print(",bt:");
  Serial.print(fBottomTemperature);Serial.print(",dc:");
  Serial.print(bTempDisplaysCelcius);
  Serial.print(",at:");Serial.print(fAverageTemp);
  Serial.print(",pt:");Serial.print(bPlayTopTemperatureSound);
  Serial.print(",pb:");Serial.print(bPlayBottomTemperatureSound);
  Serial.println("]");

  if (fAverageTemp >= fTopTemperature)
  {
    iLEDConfigCurrent = 1; // Red
  }
  if (fAverageTemp <= fBottomTemperature)
  {
    iLEDConfigCurrent = -1; // Blue
  }
  if (fAverageTemp > fBottomTemperature && fAverageTemp < fTopTemperature)
  {
    iLEDConfigCurrent = 0; // Green
  }

  if (iLEDConfigCurrent >= 0 || (iLEDConfigCurrent == -1 && fAverageTempD > 0 && iPosCount >= POSCOUNT))
  {
    lMilliSecondsSinceLastSwitch = millis();
  }
  
  bLEDsActive = ((millis() - lMilliSecondsSinceLastSwitch) < iMilliSecondsSinceLastSwitchCount);

  if (iLEDConfigPrevious == 1 && iLEDConfigCurrent == 0)
  {
      PlayUp();
  }

  if (iLEDConfigPrevious == 0 && iLEDConfigCurrent == -1)
  {
      PlayDown();
  }

  if (!bLEDsActive || (fAverageTempD > 0 && iPosCount >= POSCOUNT && !bLEDsBlink))
  {
    digitalWrite(iPinLedRed, LOW);
    digitalWrite(iPinLedGreen, LOW);
    digitalWrite(iPinLedBlue, LOW);
  }
  else
  {
    if (iLEDConfigCurrent == 1)
    {
      digitalWrite(iPinLedRed, HIGH);
      digitalWrite(iPinLedGreen, LOW);
      digitalWrite(iPinLedBlue, LOW);
    }
    if (iLEDConfigCurrent == 0)
    {
      digitalWrite(iPinLedRed, LOW);
      digitalWrite(iPinLedGreen, HIGH);
      digitalWrite(iPinLedBlue, LOW);
    }
    if (iLEDConfigCurrent == -1)
    {
      digitalWrite(iPinLedRed, LOW);
      digitalWrite(iPinLedGreen, LOW);
      digitalWrite(iPinLedBlue, HIGH);
    }
  }

  fArrTempsD[iAvgIndex] = fAverageTemp - fPreviousTemperature;
  fPreviousTemperature = fAverageTemp;
  iLEDConfigPrevious = iLEDConfigCurrent;
  bLEDsBlink = !bLEDsBlink;
  delay(450);
}

void serialEvent()
{
  while (Serial.available())
  {
    // get the new byte:
    char inChar = (char)Serial.read();
    // add it to the strSerialInput:
    strSerialInput += inChar;
    // if the incoming character is a newline, set a flag so the main loop can
    // do something about it:
    if (inChar == '\n')
    {
      bSerialInputComplete = true;

      if (strSerialInput.startsWith("tt:"))
      {
          strSerialInput = strSerialInput.substring(3);
          fTopTemperature = strSerialInput.toFloat();
          EEPROMWritelong(iEEPROMTopTemperature, (long)(fTopTemperature * (float)100));
      }

      if (strSerialInput.startsWith("bt:"))
      {
          strSerialInput = strSerialInput.substring(3);
          fBottomTemperature = strSerialInput.toFloat();
          EEPROMWritelong(iEEPROMBottomTemperature, (long)(fBottomTemperature * (float)100));
      }

      bTempDisplaysCelcius = ParseSerialOfBoolIfMatchSetEEPROMAndReturnBool("dc:", bTempDisplaysCelcius, iEEPROMDisplayCelcius);
      bPlayTopTemperatureSound = ParseSerialOfBoolIfMatchSetEEPROMAndReturnBool("pt:", bPlayTopTemperatureSound, iEEPROMPlayTopTemperatureSound);
      bPlayBottomTemperatureSound = ParseSerialOfBoolIfMatchSetEEPROMAndReturnBool("pb:", bPlayBottomTemperatureSound, iEEPROMPlayBottomTemperatureSound);
    }
  }
}

bool ParseSerialOfBoolIfMatchSetEEPROMAndReturnBool(String ip_strCommand, bool ip_bOriginalValue, int ip_iEEPROM_Location)
{
  bool _bResult = ip_bOriginalValue;
  if (strSerialInput.startsWith(ip_strCommand))
  {
    strSerialInput = strSerialInput.substring(3);
    _bResult = (strSerialInput == "1\n");
    EEPROM.write(ip_iEEPROM_Location, _bResult);
  }
  return _bResult;
}

void EEPROMWritelong(int address, long value)
{
  byte by;
  for(int i=0;i< 4;i++) {
    by = (value >> ((3-i)*8)) & 0x000000ff; 
    EEPROM.write(address+i, by);
  }
}


long EEPROMReadlong(long address)
{
  long lo=0;

  for(int i=0;i< 3;i++){
    lo += EEPROM.read(address+i);
    lo = lo << 8;
  }
  lo += EEPROM.read(address+3);
  return lo;
}

void PlayUp()
{
  if (bPlayTopTemperatureSound && (millis() - lMilliSecondsSinceBeep > iMilliSecondsSinceBeepCount))
  { 
    tone(iPinBuzzer, 440, 100);
    delay(100);
    tone(iPinBuzzer, 880, 100);
    delay(100);
    tone(iPinBuzzer, 1760, 100);
    lMilliSecondsSinceBeep = millis();
  }
}

void PlayDown()
{
  if (bPlayBottomTemperatureSound && (millis() - lMilliSecondsSinceBeep > iMilliSecondsSinceBeepCount))
  { 
    tone(iPinBuzzer, 1760, 100);
    delay(100);
    tone(iPinBuzzer, 880, 100);
    delay(100);
    tone(iPinBuzzer, 440, 100);
    lMilliSecondsSinceBeep = millis();
  }
}
The configuration program made with c# and Visual Studio 2017 community editionC#
This program displays the settings of the coaster and makes it possible to change the settings. Here is an archive with the solution and the icons, c# etc
No preview (download only).

Custom parts and enclosures

STL of the bottom of the coaster
Run this file through your slicer to produce gcode to be printed with your 3d printer

Error uploading file to Sketchfab.

STL of top of the coaster
Run this file through your slicer to produce gcode to be printed with your 3d printer

Error uploading file to Sketchfab.

STL of the holder of the thermometer
Run this file through your slicer to produce gcode to be printed with your 3d printer

Error uploading file to Sketchfab.

Schematics

Circuit Diagram
Diagram of the circuit
Coaster schem rpxfpkwn3n
Bread Board Layout
Before putting together you can try on a breadboard
Coaster bb oodmtzuvuc

Comments

Similar projects you might like

Arduino LIDAR

Project tutorial by abhinav

  • 11,463 views
  • 4 comments
  • 18 respects

A DIY Smart Insole to Check Your Pressure Distribution

Project tutorial by Juliette van der Pas

  • 1,266 views
  • 6 comments
  • 13 respects

Arduino Mega Chess

Project showcase by Sergey_Urusov

  • 3,448 views
  • 14 comments
  • 24 respects

Sprout: Modern Indoor Self Watering Planter

Project tutorial by Jonathan Pereira

  • 3,449 views
  • 3 comments
  • 22 respects

VU Meter on Steroids: Arduino Nano and WS2812Bs

Project in progress by WannaDuino

  • 2,743 views
  • 4 comments
  • 13 respects

Arduino-Powered Smart Light (Works with Amazon Echo)

Project tutorial by Tinker Project

  • 5,147 views
  • 4 comments
  • 14 respects
Add projectSign up / Login