Go Down

Topic: [CODE] dual 1-wire temperature LCD display (Read 2277 times) previous topic - next topic


Feb 02, 2009, 06:59 pm Last Edit: Feb 03, 2009, 03:38 pm by agentsmith Reason: 1
I've written (most of it cut and paste from examples) code to get temperatures from two DS18B20 sensors and display them to a LCD module.
I put the code so might be useful to someone. However there are two drawbacks which I cooked my brain on without finding solution, but hey I'm just a n00b :)

Code: [Select]

//1-Wire temperature display on 4-bit LCD sketch

#include <LiquidCrystal.h>
#include <OneWire.h>
//create object to control an LCD.  
//number of lines in display=1
LiquidCrystal lcd(7, 0, 6, 2, 3, 4, 5);

OneWire ds(8);   // DS18B20 Temperature chip i/o

byte TempDisp;      //switch to alternate temperature display
unsigned long Time;    //time variable for millis function

void setup() {
 pinMode(13, OUTPUT);  //we'll use the debug LED to output a heartbeat
 //optionally, now set up our application-specific display settings, overriding whatever the lcd did in lcd.init()
 //lcd.commandWrite(0x0F);//cursor on, display on, blink on.  (nasty!)

void loop() {  
 int HighByte, LowByte, TReading, SignBit, Tc_100, Whole, Fract;
 byte i;
 byte present = 0;
 byte data[12];
 byte addr[8];
 char tempo[8];
 if ( !ds.search(addr)) {
     Serial.print("No more addresses.\n");

 for( i = 0; i < 8; i++) {
   Serial.print(addr[i], HEX);
   Serial.print(" ");

 if ( OneWire::crc8( addr, 7) != addr[7]) {
     Serial.print("CRC is not valid!\n");

 if ( addr[0] != 0x28) {
     Serial.print("Device is not a DS18S20 family device.\n");

 ds.write(0x44,1);         // start conversion, with parasite power on at the end
 if (millis()-Time > 1000) {          //non blocking refresh of temperature reading and LCD display
   Time = millis();
   present = ds.reset();
   ds.write(0xBE);         // Read Scratchpad

   Serial.print(" ");
   for ( i = 0; i < 9; i++) {           // we need 9 bytes
     data[i] = ds.read();
     Serial.print(data[i], HEX);
     Serial.print(" ");
   Serial.print(" CRC=");
   Serial.print( OneWire::crc8( data, 8), HEX);
   LowByte = data[0];
   HighByte = data[1];
   TReading = (HighByte << 8) + LowByte;
   SignBit = TReading & 0x8000;  // test most sig bit
   if (SignBit) // negative
     TReading = (TReading ^ 0xffff) + 1; // 2's comp
   Tc_100 = (6 * TReading) + TReading / 4;    // multiply by (100 * 0.0625) or 6.25

   Whole = Tc_100 / 100;  // separate off the whole and fractional portions
   Fract = Tc_100 % 100;

   if (SignBit) // If its negative
   if (Fract < 10)
   tempo[0]= (Whole)/10 +'0' ;
   tempo[1]= Whole-(10*(Whole/10)) +'0';
   tempo[3]= (Fract)/10 +'0';
   tempo[4]= Fract-(10*(Fract/10)) +'0';
   tempo[5]= ' ';
   tempo[6]= 223;
   tempo[7]= 'C';
   tempo[8]= '\0';
   if (TempDisp == 0) {
     lcd.setCursor(0, 0);
     lcd.print(" int");
     TempDisp = 1;
   } else {
     lcd.setCursor(0, 1);  //line=2, x=0.
     lcd.print(" ext");
     TempDisp = 0;

- i've put millis() instead of delay for avoiding to block code execution, since I plan to add other functions. But since the 1-wire routine isn't linked to a particular 1-wire device ID but displays all of them, with millis() it happens that the displayed temperature can get exchanged

- String conversion/manipulation in Arduino is really complicated! Don't know if it's due to space constraints or it's a C++ feature (I have always programmed in VB), but the only way I found so far is to put an ascii character in every item of the string array.
I'd like to write a subroutine that inputs an integer value like 2468 and a positive/negative bit and outputs a string like 24.68.
The temperatures could go from -99.99 to +99.99 so it needs a 6 character string.

I tried to write it but it's really a mess for me and doesn't work
Code: [Select]

char *Temp2String (int t, int signbit) {
 char tempo[5];
 int Whole, Fract;
 Whole = t / 100;  // separate off the whole and fractional portions
 Fract = t % 100;
 if (signbit) tempo[0]='-';
 tempo[1]= (Whole)/10 +'0' ;
 tempo[2]= Whole-(10*(Whole/10)) +'0';
 if (Fract < 10)  {
 } else {  
   tempo[4]= (Fract)/10 +'0';
 tempo[5]= Fract-(10*(Fract/10)) +'0';
 return tempo;

I hope someone will write a library to manipulate strings like in BASIC which is much easier i.e. strings aren't an array of characters, doesn't need null character at the end, and you can convert easily with val and str$ functions...


Any idea for the strings?

I thought I could put a 1-wire search routine at setup, then save the device IDs and poll them in the loop


With the current Onewire library I am able to detect 5 sensors at time. After that, it stays in indefinite loop with only last two sensors. I am not sure why it happens.
I was able to use 17 sensors (all I have) when I manually created two dimensional array of sensorIDs and get temperatures polling by sensorID.
Automatic search function for multiple sensors would be good. I am not sure are my coding skills good enough to do that.

I think lack of strings is a feature of c++. I worked around this problem using integers as LCD library does not like floats. After I got temp, I multiplied it with 100. passing an integer around the functions. (I think returning integers from functions is easier than arrays). and just before printing I divided temp back to Whole degrees and fractions. Well, it creates more code, but IMO less than using arrays.


Nice comment MaHa. I have 20 sensors and plan to use a dozen of them so I might encounter the wall you found.

I try to follow your integer function advice. But I can't understand what kind of number the routine outputs from the sensor.
I mean, look at the function Temp2String code I posted. I found code to display temperature which looks weird to me, though it works.
For example:
tempo[1]= (Whole)/10 +'0' ;
tempo[2]= Whole-(10*(Whole/10)) +'0';

Why add the zero if the number displayed is single digit?
Whole-(10*(Whole/10)) means to me for example 89-(10*(89/10)) = 0 - but it's output on the LCD isn't a zero but the correct value (the last decimal before the comma).


Why add the zero if the number displayed is single digit?
Whole-(10*(Whole/10)) means to me for example 89-(10*(89/10)) = 0 - but it's output on the LCD isn't a zero but the correct value (the last decimal before the comma).

89 - (10*(89/10)) = 9 not 0
89/10 = 8 ; tempo is a char


Thank you for explaination Patgadget.
So 89/10=8 result is truncated to the first digit. But then it should be 8*10=8, but to get 9 it should be 89-80

Go Up