HOME  > >  ARDUINO MAIN PAGE > >  HINTS AND HOW-TOs > >  NuELEC DATALOG  t.o.c.
Delicious.Com Bookmark this on Delicious    StumbleUpon.Com Recommend to StumbleUpon

Up to five Dallas 1-Wire temperature sensors

on an Arduino


This tutorial shows you how to access two or more Dallas 1-Wire digital temperature sensors, DS18B20s. Don't be scared by the "DS"... yes, this is a chip from Dallas's 1-wire family , but you don't have to deal with the usual 1-Wire programming things. You don't need the general Arduino 1-Wire library.

If you feel you want to ease into this subject more gently, I have a "How to connect one DS18B20 to an Arduino" essay for you.

There's also my more general introduction, an overview of using 1-Wire chips

In this "solution", we are taking the "simple/ expensive" route: We will be using a separate data line for each of the sensors we want to read. To put that a different way: We won't be using the power of the 1-Wire chips to co-exist on a little "network" of their own. (Such networks are called "MicroLans" (a trademarked term, which is helpful for Google searching!)

It might not look like it, but "all" you need to understand of the code farther down the page is....

void loop()
{
  readTture(tture1);
       printTture();
       Serial.print("   ");

  delay(120);

  readTture(tture2);
      printTture();
      delay(200);
      Serial.print("\n");
}

Well... okay... and a few extra details. We'll come back to the above in a moment. The details are....

At the head of the code (after user's needs tweakings), we have:

#define tture1 33//no ; here
#define tture2 35//no ; here
... etc, one per sensor
... and a little later...

int tture[5] = {33,35...

int HighByte[5], LowByte[5], TReading[5], SignBit[5],
   Tc_100[5], Whole[5],Fract[5];

The defines tell the program which pins of the Arduino will be connected to the temperature sensors. The high numbers (33..) indicate that this particular instance of the code was running on a Mega, or higher, with their huge repertoire of pins. A modest Uno or similar would have the sensors on pins of lower numbers, e.g. 4,5...

The second "int" line creates some integer-type arrays for use in the program. (No rocket science there.)

And in the setup() function:

    pinMode(tture1, INPUT);
    pinMode(tture2, INPUT);

    digitalWrite(tture1, LOW);
    digitalWrite(tture2, LOW);

Again... These are just routine configuration commands. Writing to an input might seem strange. We are just, in the usual way, connecting an internal pull-up resistor to the input.

Everything else in the code is either basic, basic, general stuff, or "black box" stuff that you put in and leave alone, as is.

Details of what is in "loop()"...

I've done a Bad Thing in order to keep the code simple. I've used global variables: HighByte, LowByte, etc.

The command "readTture(xx)" tells the Arduino to go off and check one of the sensors, and place the answer in the global variables. (None of them has to be in any particular state before you called readTture.)

If you call "printTture();", and you have put sensible values in the global variable before calling it, you will get a temperature displayed via the serial monitor. (It is easy to write similar procedures to send the output someplace else.)"printTture();" sends just something like "15.0" to the monitor, so we have the Serial.print statements to put a space between the two readings and start a new line when both sensors have been read.

That use of global variables is "poor programming"... but it works. It is a Bad Idea because it is easy for bugs to creep in and hide in such opaque programming. But The Right Way To Do It requires that you understand a complex data type, and I want this essay accessible to many readers.

But that's really pretty well "it"!

Details of how to connect the sensor... really easy... and details of what goes on inside the "black box" parts of the program are covered in detail in my page about reading just one DS18B20.

An aside: You will see "one wire" and "one wire interface" in the nuelectronics.com documentation. This is not always connected with "1-Wire" (a Dallas trademark) at all, and even when you are connecting a 1-Wire device to a nuelectronics "one wire" connection point, you won't often (ever?) get dragged into some of the more complex 1-Wire issues. Don't get me wrong... I like 1-Wire... it is powerful. But to get everything you can from 1-Wire gets dangerously close to Serious Work. The datalogging shield from nuelectonics.com lets you use some of the (marvelous) 1-Wire devices without the work!

Code for reading up to five DS18B20's on an Arduino....

(It might be possible to read even more. It would certainly be possible to read fewer. It would be Good Programming to put the number of sensors you want the code to read into a constant, and use that whenever appropriate, rather than the hardcoded "5" which is present in the existing code.)

/*ReadDS18B20two
vers: 6Jly14- Modified by Brian (twice!) for 5 sensors,
better responsiveness. Derived from vers 18 July 2010
Started end of term eve WG/TA/LE/EW/JGB

Reading five DS18B20s (Changed to 5 from the 2
  allowed by the 18 July 2010 version of ReadDS18B20two)

See...
 http://sheepdogguides.com/arduino/ar3ne1tt2.htm
 ... for explanation of this code.

 Code adapted from code from nuelectronics.com demo

 Revise the next five lines, as necessary, for where
 you have your sensors connected. Remove pre-pended
 "//"s.   */

//#define tture1 33//no ; here
//#define tture2 35//no ; here
//#define tture3 37//no ; here
//#define tture4 39//no ; here
//#define tture5 41//no ; here

int tture[5] = {33,35,37,39,41};

int x = 0;
int count = 0;


/*Forward declarations. Only the last two need concern the user

Remmed out, as they seem unnecessary
void OneWireReset(int Pin);//Called by readTture
void OneWireOutByte(int Pin, byte d);//Called by readTture
byte OneWireInByte(int Pin);//Called by readTture
void readTture(byte Pin);//Of use to users
void printTture();//Of use to users
*/

//Following globals used to communicate results back
//from readTture(Pin), and to send data to printTture...

int HighByte[5], LowByte[5], TReading[5], SignBit[5],
   Tc_100[5], Whole[5],Fract[5];

unsigned long pause1;

void setup() {
   //For each tture sensor: Do a pinMode and a digitalWrite
   for (x = 0; x < 5; x++)
   {
      pinMode(tture[x], INPUT);
      digitalWrite(tture[x], LOW);//Disable internal pull-up.
   }

   pinMode(13,OUTPUT);

   Serial.begin(9600);
   delay(300);//Wait for newly restarted system to stabilize
   Serial.print("Temperature measurement, Five sensors:\n\n");
}

/*
The loop takes approximately 60uS
The reading and printing take about 5.9mS

One sensor is updated every 200mS

All 5 are updated once per second.
*/

void loop(){
   digitalWrite(13,!digitalRead(13));
   pause1 = millis() % 200;// pause1 will loop from 0 to 199
   if(pause1 == 0){
      readTture(tture[count]);//N.B.: Values passed back in globals
   }

   if(pause1 == 150){
      printTture();//N.B.: Takes values from globals. Also...
      Serial.print("   ");

   if(count == 0){
      Serial.print("\n");//Start new line
   }
   }
}

//Everything below here... just copy it into your program "as is".
//You are only likely to need to use readTture(pin) and printTture()
//   directly. Others are subordinate to those.
//These routine access the following global variables...
//   int HighByte, LowByte, TReading, SignBit, Tc_100, Whole, Fract;

void OneWireReset(int Pin) // reset.  Should improve to act as a presence pulse

{
   digitalWrite(Pin, LOW);
   pinMode(Pin, OUTPUT); // bring low for 500 us
   delayMicroseconds(500);
   pinMode(Pin, INPUT);
   delayMicroseconds(500);
}



void OneWireOutByte(int Pin, byte d) // output byte d (least sig bit first).

{
   byte n;
   for(n=8; n!=0; n--)
   {
      if ((d & 0x01) == 1)  // test least sig bit
      {
         digitalWrite(Pin, LOW);
         pinMode(Pin, OUTPUT);
         delayMicroseconds(5);
         pinMode(Pin, INPUT);
         delayMicroseconds(60);
      }

      else
      {
         digitalWrite(Pin, LOW);
         pinMode(Pin, OUTPUT);
         delayMicroseconds(60);
         pinMode(Pin, INPUT);
      }

      d=d>>1; // now the next bit is in
                     // the least sig bit position.
   }
}//end OneWireOutByte

byte OneWireInByte(int Pin) // read byte, least sig byte first

{
   byte d, n, b;

    d=0;//This critical line added 04 Oct 16
    //I hate to think how many derivatives of
    //this code exist elsewhere on my web pages
    //which have NOT HAD this. You may "get away"
    //with not setting d to zero here... but it
    //is A Very Bad Idea to trust to "hidden"
    //initializations!
    //The matter was brought to my attention by
    //a kind reader who was THINKING OF YOU!!!
    //If YOU spot an error, please write in, bring
    //it to my attention, to save the next person
    //grief.

   for (n=0; n<8; n++)
   {
      digitalWrite(Pin, LOW);
      pinMode(Pin, OUTPUT);
      delayMicroseconds(5);
      pinMode(Pin, INPUT);
      delayMicroseconds(5);
      b = digitalRead(Pin);
      delayMicroseconds(50);
      d = (d >> 1) | (b<<7); // shift d to right and
         //insert b in most sig bit position
   }

   return(d);

}

void readTture(byte Pin){

   //Pass WHICH pin you want to read in "Pin"
   //Returns values in... (See global declarations)

   OneWireReset(Pin);
   OneWireOutByte(Pin, 0xcc);
   OneWireOutByte(Pin, 0x44); // perform temperature conversion,
        //  strong pullup for one sec

   OneWireReset(Pin);
   OneWireOutByte(Pin, 0xcc);
   OneWireOutByte(Pin, 0xbe);

   LowByte[count] = OneWireInByte(Pin);
   HighByte[count] = OneWireInByte(Pin);

   TReading[count] = (HighByte[count] << 8) + LowByte[count];
   SignBit[count] = TReading[count] & 0x8000;  // test most sig bit

   if (SignBit[count]) // negative

   {
      TReading[count] = (TReading[count] ^ 0xffff) + 1; // 2's comp
   }

   Tc_100[count] = (6 * TReading[count]) + TReading[count] / 4;
   //multiply by (100 * 0.0625) or 6.25

   Whole[count] = Tc_100[count] / 100;  // separate off the whole
      //number and fractional portions

   Fract[count] = Tc_100[count] % 100;
}


void printTture(){//Uses values from global variables.

   //See global declarations.
   //N.B.: No new line inside printTture

   if (SignBit[count]) // If it's negative
   {
      Serial.print("-");
   }

   Serial.print(Whole[count]);
   Serial.print(".");

   if (Fract[count] < 10)
   {
      Serial.print("0");
   }

   Serial.print(Fract[count]);
   count++;

   if(count == 5){
      count = 0;
        }
}

I hope the above Just Works for you. Do please write and "complain", if not, as you may be saving the next person hassle you have had.

The history of the code above...

Quite a while ago (at 7/14), I posted a starting point, probably derived from demos from Nuelectronics.

A kind reader sent in an improved version. Said kind reader is a supporter of the Australian charity Very Special Kids, if you want to show appreciation for the reader's kindness to us all. (On its website, Very Special Kids says that its mission is to support families of children with life-threatening conditions.)

The kind reader's version of the code, which is all that remains here, is for up to five temperature sensors, and improved the responsiveness of the program over my original. (You can revise it to use fewer sensors.) The loop routine now takes about 60uS and the read-and-print routines take about 6mS combined.

A word about forward declarations...

/*Forward declarations. Only the last two need concern the user
Remmed out, as they seem unnecessary
void OneWireReset(int Pin);//Called by readTture
void OneWireOutByte(int Pin, byte d);//Called by readTture
byte OneWireInByte(int Pin);//Called by readTture

void readTture(byte Pin);//Of use to users
void printTture();//Of use to users
*/

I THINK you can just take that out, if you want to. Alternatively, I think it does no harm to take out the "/*" and "*/" which turn the block into a comment, or "rem".

There was a time when if you didn't put function declarations into your Arduino code in a particular order, or didn't use forward declarations, then you'd have problems when you tried to compile the code. I think those days are past. If any reader can comment from a secure, "expert", point of view, the comments would be welcome!

Give it a try! Let me know how you get on? If you contact me, it is very helpful if you cite the name of the page you are talking about; this one is "ar3ne1tt2.htm".

Hardware

Each sensor will connect to a separate data line. The sensor will also have to connect to ground. If it suits your circumstances, I would also eschew "parasitic powering"... but it should work, if you need it.

You will need to supply an external pull-up resistor on the data line. Getting the right value is a bit of a black art... consult other pages for details. Some will tell you "the right value", and if it works for you, great! But don't be surprised if you have to fiddle a bit to get good results. The good news: The chips will tell you if a reading is "bad". (One of the reasons a digital sensor is so much better than something analog!)

The End

That's pretty well it for "how to read one or more temperature sensors with a microprocessor, e.g. Arduino".

Back in the Bad Old Days when programming was Hard Work, I did quite a lot with Dallas 1-Wire chips. They have quite fantastic capabilities, of which the above code doesn't avail itself at all. If you are interested in learning more about 1-Wire chip programming, in a context probably more relevant to users of "big" computers, I offer you my tutorials about programming for the Dallas Semiconductor 1-Wire (tm) chips, as used on a MicroLan (tm). Those tutorials are written for Delphi (language) programmers, but they contain much information that would apply to other language environments. I also maintain pages which introduce MicroLans and explain the hardware.



Going back to Arduinos (and away from Dallas 1-Wire), see also...

The Arduino programming course from Sheepdog Guides:

I have written a series of essays which try to help you become a better Arduino programmer and engineer... but, for the best result, you will have to buckle down and work your way through them in sequence. The collection of "How To's" this page comes from can be accessed in whatever order you like.



Feel free to use this information in programming courses, etc, but a credit of the source would be appreciated. If you simply copy the pages to other web pages you will do your readers a disservice: Your copies won't stay current. Far better to link to these pages, and then your readers see up-to-date versions. For those who care- thank you- I have posted a page with more information on what copyright waivers I extend, and suggestions for those who wish to put this material on CDs, etc.





Editorial Philosophy

See the discussion near the bottom of the "top level" page covering the bulk of my Arduino contributions. There is information there, too, about things like "May I copy your material?", and the system of file names I am trying to work to.


   Search this site or the web        powered by FreeFind
 
  Site search Web search
Site Map    What's New    Search

The search engine is not intelligent. It merely seeks the words you specify. It will not do anything sensible with "What does the 'could not compile' error mean?" It will just return references to pages with "what", "does", "could", "not".... etc.
In addition to the information about the nuelectronics data shield of which this page is part, I have other sites with material you might find useful.....

Tutorials about the free database which is part of the free Open Office.
Sequenced set of tutorials on Pascal programming and electronics interfacing.
Some pages for programmers.
Using the parallel port of a Windows computer.

If you visit 1&1's site from here, it helps me. They host my website, and I wouldn't put this link up for them if I wasn't happy with their service... although I was less than pleased the other day to have what I was doing interrupted by a telephone call from their sales team, trying to get me to extend my involvement. Sigh. Hardly a rare event, but I'd thought 1&1 were a bit classier that some of the people who have my telephone number.



Ad from page's editor: Yes.. I do enjoy compiling these things for you... hope they are helpful. However.. this doesn't pay my bills!!! If you find this stuff useful, (and you run an MS-DOS or Windows PC) please visit my freeware and shareware page, download something, and circulate it for me? Links on your page to this page would also be appreciated!

Click here to visit editor's Sheepdog Software (tm) freeware, shareware pages.


And if you liked that, or want different things, here are some more pages from the editor of these tutorials....

Click here to visit the homepage of my biggest site.

Click here to visit the homepage of Sheepdogsoftware.co.uk. Apologies if the "?Frmar3ne1tt2" I added to that link causes your browser problems. Please let me know, if so?

Click here to visit editor's pages about using computers in Sensing and Control, e.g. weather logging.



To email this page's editor, Tom Boyd.... Editor's email address. Suggestions welcomed!


Valid HTML 4.01 Transitional Page tested for compliance with INDUSTRY (not MS-only) standards, using the free, publicly accessible validator at validator.w3.org. Passes, apart from two "unknown attributes" in Google+ button code. Sigh.


Why does this page cause a script to run? Because of the Google panels, and the code for the search button. Why do I mention the script? Be sure you know all you need to about spyware.

....... P a g e . . . E n d s .....