MultipleSensorsOneMicroLan.htm

Arduino + Dallas / Maxim 1-Wire chips

Putting several sensors on one pin...

I've LONG been a fan of the Dallas 1-Wire chips.

They are VERY easy to use with Arduino... in a simple way: one sensor to a pin. And there's nothing wrong with using them thus.

They are, however, designed to be deployed as networks of sensors. If your project requires multiple sensors, there's no need to use more than one pin to read all of them! Dallas has a trademark on the term "MicroLan", which is a name for such a network.

The following arose out of a longstanding "itch" to conquer this particular "Everest" on the Arduino. I'd done it many times under Windows.

It turned out that, due to the excellent work by the Arduino OneWire Library team, to be remarkably easy. (Beware: There is a OneWire library (an optional download) and a "Wire" library... part of the basic IDE. We need the former to play with 1-Wire chips. "Wire" is for something else.)

The "breakthrough moment" came while scanning the excellent...

https://www.pjrc.com/teensy/td_libs_OneWire.html

... so THANK YOU, PJRC/ Teensy Team. (But what I needed was, actually, in the "basic" 1-Wire demo program, all along. The PJRC page opened my eyes to see it.)

The code below is a Dog's Dinner. The comments are in a mess. Some are still true, others, not so. What was once a clean, robust program, using "return" to "give up" when appropriate is now a jumble. There are scraps, fit only for deletion.

But it is also a program which WILL read a MicroLan with two DS18B20s on it!

You can see the readings from the sensors via the serial monitor OR the serial plotter... but, as is usual, you can only have one OR the other open at any given time. You have to close one before opening the other, but you don't need to recompile the program between the two viewing modes. Note however: The Arduino will be reset when either monitor opens, so you won't, in the new display, have the data collected prior to the switch-over.

Hardware

You'll attache your 1-Wire chips via a data line of the Arduino. If you use line 4, set the constant pOneWireMicroLan to 4.

On that pin: The Data line of the 1-Wire chips, and a 4.7K resistor to pull the line up to the system's Vcc. The 1-Wire chip's Vcc and Gnd pins to be connected to system's. See internet for discussion of right value for resistor. Just one, regardless of how many 1-Wire chips. Look a little way down http://sheepdogguides.com/arduino/DS18B20-HowTo.htm for a diagram giving help with DS18B20 pinout.

One part of the code is particularly crude.

When you run it, and open the serial monitor, you should see something like...

ROM = 28 FF 50 72 0 16 2 88
ROM = 28 FF 1E 5F 0 16 2 2E

These were the IDs of the chips I was using. You should see different numbers, but they should start with "28".

In the code, you will find a block that looks like this...

  addr2[0]=0x28;
  addr2[1]=0xFF;
  addr2[2]=0x50;
  addr2[3]=0x72;
  addr2[4]=0x00;
  addr2[5]=0x16;
  addr2[6]=0x02;
  addr2[7]=0x88;

That, rather crudely, puts the address of the FIRST chip into where it needs to be.

When this program is "perfect", doing that bit of hard-coding for the hardware won't be necessary. Note how it already "picks up" the address of the second chip, stores it for use as needed.

There are no great barriers to making the program pick up BOTH addresses... indeed, a variable number of addresses. Only the barrier of the time available to do it AND write it up.

For now, you will have to modify the program to reflect one of the 1-Wire chips you are using... the program will REPORT both... but only "take note of" the second.

That's it!... apart from the code...

//Ar795-v1-0
//N.B.: BE SURE TO ALSO EDIT PRGM NAME IN "Serial.print(..." in setup()

//ver 09 Feb 18
//Derived from first "DS18B20 on ESP8266" (not called that),
//which was created 3 Jan 17

//See also http://sheepdogguides.com/arduino/DS18B20-HowTo.htm

//Much of this comes from...
//http://www.elec-cafe.com/temperature-sensor-on-the-web-with-esp8266-and-ds18b20/
//.. apologies to the authors of that for the mess I've made of it,
//in adapting it to my wants. Their program was elegant.

/*Early version worked fine on Sparkfun ESP8266
  "Thing", "Dev Brd" version. (Part WRL-13711) Should work there
  still. Know to work on AruProMini*/

#include <OneWire.h>

long int liLoopCycleCount=0;
unsigned long ulMillisAtPrevTenthsBump;
unsigned long ulTmp;
long int liTenthsPassed=0;
long int liTenthsPassedPrev=0;
long int liTenthsPassedWhenConvertRequested;

byte const pOneWireMicroLan=4;
OneWire  ds(pOneWireMicroLan);  //Determines which pin the program
//will look at for all 1-Wire work.
//On that pin: The Data line of the 1-Wire chips, and a 4.7K resistor
//   to pull the line up to the system's Vcc. The 1-Wire chip's Vcc and  Gnd
//   pins to be connected to system's. See internet for discussion of
//   right value for resistor. Just one, regardless of how many 1-Wire chips.
//Look a little way down http://sheepdogguides.com/arduino/DS18B20-HowTo.htm
//   for a diagram giving help with DS18B20 pinout.

//(Program also makes trivial use of LED built into the Arduino...)
byte const pBuiltInLED=LED_BUILTIN;

byte i;
byte present = 0;
byte type_s;
byte data[12];
byte addr[8];
byte addr2[8];
byte bWhichSensor;

float celsius, fahrenheit, celsiusX10;

//(We won't be using celsiusx10 for a bit,
//nor what follows, but enter it now, and
//it shortens the tutorial! What follows
//is for "stuffing" the temperature into
//fixed with fields on a web-page...

int iTmp;
char cSTture0 ='+';
char cTturDC0Huns ='9';//Temperature, in Celcuis DECIdecgrees... i.e. 12.4 degC is 124 deg
char cTturDC0Tens ='9';
char cTturDC0Units ='9';
//Reports "+999" (DECIdegrees) until first reading done.

//END BLOCK ADDED BY TKB for read-DS18B20


void setup()
{ pinMode(pBuiltInLED, OUTPUT);

  Serial.begin(9600);
  Serial.println();
  Serial.println();
  Serial.println("Demo of using multiple DS18B20 tture sensors,");
  Serial.println("on one pin, with Arduino of your choice. Seen working on");
  Serial.println("Arduino Pro Mini, and very similar on Sparkfun ESP8266 Dev 'Thing'.");
  Serial.println("Prgm name: Ar795-v1-0, edit 09 Feb 18");
  //N.B.: BE SURE TO ALSO EDIT PRGM NAME IN REM AT TOP OF PAGE
  //"Edit" info is here only.

  Serial.println("For tutorial, see....");
  Serial.println("   http://sheepdogguides.com/arduino/DS18B20-HowTo.htm");
  Serial.println();
  Serial.println("Be patient. First reading should appear in about 20 seconds.");

  delay(1500);// Give reader a chance to see the output.

  ulMillisAtPrevTenthsBump=millis();

if (ds.search(addr)) { //addr can be empty when this called. It is for the
  //return of some data.
do {
Serial.print("ROM =");
for ( i = 0; i < 8; i++) {
Serial.write(' ');
Serial.print(addr[i], HEX);
}

if (OneWire::crc8(addr, 7) != addr[7]) {
Serial.print("CRC is not valid. May be noise on ");
Serial.print("line. Wire too long? Pull up resistor ");
Serial.print("forgotten? (Wires of at least 2 m should ");
Serial.println("be okay.)");
return;//Shuts app down if fetch of a ROM ID failed.
}
Serial.println();

} while (ds.search(addr));
};//end of if. No else.

/*Following can be done "automatically", when
 * addr2 made into an element into an array of byte <name>[8]

 *The "addr2... =..." lines that follow were the right ones for
 *       when the serial monitor reported...
 * ROM = 28 FF 50 72 0 16 2 88
 * ROM = 28 FF 1E 5F 0 16 2 2E <SEEMS TO READ THIS ONE, with auto fetch addr
 * when the program was run. (It can be run with bad values in addr2,
 * but only to get the report of "ROM" ("ROM ID" inside the 1-Wire chips
 * on the MicroLan.))
 */

  addr2[0]=0x28;
  addr2[1]=0xFF;
  addr2[2]=0x50;
  addr2[3]=0x72;
  addr2[4]=0x00;
  addr2[5]=0x16;
  addr2[6]=0x02;
  addr2[7]=0x88;


Serial.println("No more addresses.");
Serial.println();
ds.reset_search();
delay(250);

Serial.println();

Serial.println("There will be a delay of about 12 seconds");
Serial.println("between reading reports. (To keep your display sensible.)");
Serial.println("A reading every 2 seconds or so is possible.");

Serial.println();

Serial.println("This program can also drive the Arduino Serial Plotter");
Serial.println("There will be some ""noise"" at the start of the graph,");
Serial.println("arising from the text you are reading now. It won't recur.");


Serial.println();

//Serial.println("First two numbers are current values");
//Serial.println("of internal counters.");
//Serial.println();

//See orig code for a more flexible answer. The folloing line
//"locks" this implementation to serving only
//DS16B20s.
type_s = 0;


 bWhichSensor=0;//crude switch... an improved mechanism can be
    //used, and will be implemented in due course.

}//end setup()

void loop()
{
  liLoopCycleCount++;

  if ((liLoopCycleCount % 100)==0)
  {//"Once in a while..."

   ulTmp=millis();
   if (ulTmp<ulMillisAtPrevTenthsBump)
     {ulMillisAtPrevTenthsBump=ulTmp;};
     //this handles rollover of millis.
     //this "tenth of a second" will be up
     //to 2/10th of a second long. I can live
     //with this "extra" error in 50 days!

   if (ulTmp>ulMillisAtPrevTenthsBump+100)
     { //Tenth passed
     liTenthsPassed++;
     ulMillisAtPrevTenthsBump=ulTmp;
     if ((liTenthsPassed %100)==5)
      {//This block of code requests tture sensor to
       //take a reading, the first of the two parts of
       //getting a reading from the sensor.
       //Note: You must not reset 1-Wire MicroLan, nor ask
       //the chip for the conversion too soon after asking chip
       //to DO conversion. 1 second is a reasonably safe
       //"long enough". 2 seconds would be even better, if
       //circumstances allow.

       ds.reset();

       //Crude switch... to be improved when addresses in array. 1 of 2
       if (bWhichSensor==0) ds.select(addr);
       if (bWhichSensor==1) ds.select(addr2);

       ds.write(0x44, 1);  // start conversion, with parasite power on at the end

      }//end of block of code which requests tture sensor
       //to take a reading.

     //=================
     if ((liTenthsPassed %100)==16)
      {//This block of code asks tture sensor what
       //tture it "saw". Remember: It must not be excuted
       //too quickly after "part one" of getting a reading
       //from the sensor.

       present = ds.reset();
       //Crude switch... to be improved when addresses in array. 1 of 2
       if (bWhichSensor==0) ds.select(addr);
       if (bWhichSensor==1) ds.select(addr2);
       ds.write(0xBE);         // Read Scratchpad

       /*Scraps from original code, which may help you see what is going
          on here, if you re-enable...
              Serial.print("  Data = ");
              Serial.print(present, HEX);
              Serial.print(" ");
              */

       for ( i = 0; i < 9; i++) {// Yes, we need 9 bytes
          data[i] = ds.read();
          /*
          Serial.print(data[i], HEX);
          Serial.print(" ");
          */
          }//End of for i=0...
          /* Following scraps show how CRC can be CALCULATED....
          Really ought to USE it. (The calculated-from-data CRC
          should match the sent-from-chip CRC, which I pretty
          sure is what 9th sent byte was.

          Serial.print(" CRC=");
          Serial.print(OneWire::crc8(data, 8), HEX);
          Serial.println();
          */

       /*We now have "the temperature" in the data array... but in
       a rather arcane form. AQnd the form varies from chip to chip.
       What follows it the part of the original code which converts
       the "arcane form" for the DS18B20 to celsius. The "if (type_s)
       is a scrap of the switching code in the original to ensure
       the right conversion formulae are used.
        */

       //Calculate ttures, retaining code necessary for other chip
       //   types (type_s hold code for chuip type.)
       int16_t raw = (data[1] << 8) | data[0];
       if (type_s) {
          raw = raw << 3; // 9 bit resolution default
          if (data[7] == 0x10) {
              // "count remain" gives full 12 bit resolution
             raw = (raw & 0xFFF0) + 12 - data[6];
             }
        }
        else
        {
          byte cfg = (data[4] & 0x60);
          // at lower res, the low bits are undefined, so let's zero them
          if (cfg == 0x00) raw = raw & ~7;  // 9 bit resolution, 93.75 ms
          else if (cfg == 0x20) raw = raw & ~3; // 10 bit res, 187.5 ms
          else if (cfg == 0x40) raw = raw & ~1; // 11 bit res, 375 ms
          // default is 12 bit resolution, 750 ms conversion time
        }

    celsius = (float)raw / 16.0;//This finishes the prime "1-Wire to "normal" units" conversion

    //Now for some "trivial" conversions to other units...

    fahrenheit = celsius * 1.8 + 32.0;
    celsiusX10=celsius*10;
    /*"CelsiusX10" allows us, if happy with values only to tenths of degree
    as "good enough" to avoid the hassles of floating point numbers. Not only
    are we going to accept 15.7 for 15.72 degrees, but we are also going to
    accept 15.7 for 15.79 degrees. In other words, the values are truncated
    to tenths of degree, not rounded.

    Here begins clumsy, messy bit.
    The following values are for "stuffing" first tture into a web page....
    There MUST be a better way!... but I wanted the value to always have
    a + or a - followed by, always, 3 digits. 2.5 degrees shown as "+025"
    ... and this "ugly" answer was the first one I could make work!

    */
    cSTture0='+';
    if (celsiusX10<0)
       {
         celsiusX10=celsiusX10*-1;
         cSTture0='-';
        }
    iTmp=celsiusX10;
    iTmp=iTmp%1000;//Just in case tture was somehow >99.9 deg, i.e. 999 deci-degrees
       //for debug Serial.println(iTmp);
    cTturDC0Huns=(iTmp/100)+48;//Hundreds(x10) character, temperature, in Celcuis DECIdecgrees... i.e. 12.4 degC is 124 deg
       //for debug Serial.println(cTturDC0Huns);
    iTmp=iTmp%100;
    cTturDC0Tens=(iTmp/10)+48;
    iTmp=iTmp%10;
    cTturDC0Units=(iTmp%10)+48;

    //Here ENDS clumsy, messy bit. (The value hasn't been displayed, but
    //  the pieces for displaying the value are now in place.

    //Serial.print("  Temperature = ");
    //Serial.println(celsius);

    //Serial.print(" Celsius, ");
    //Serial.print(fahrenheit);
    //Serial.println(" Fahrenheit");

}   /*end of block of code which asks tture sensor what
          tture it "saw".*/


/*I have not... yet... dealt with what
     happens when liTenthsPassed rolls over.
     I will, someday.. sigh.. maybe.*/

     };  //end of "Tenth passed"

   //Serial.print(liLoopCycleCount);
   //Serial.print("  ");
   //Serial.print(ulMillisAtPrevTenthsBump);
   //Serial.print("  ");
   //Serial.println(liTenthsPassed);
  };//end of "Once in a while..."

  //Following is just to create a blinking LED to show program is running.
  //In not important in any other way.
  if ((liTenthsPassed % 8) <2)
    {digitalWrite(pBuiltInLED, HIGH);} // LED off
    else
    {digitalWrite(pBuiltInLED, LOW);} // LED on
  // end create blinking LED


  if ((liTenthsPassed %120)==110)
   {
   if (liTenthsPassed!=liTenthsPassedPrev)
     //This may execute more than once per "tenth %120==110" otherwise!
     {
     liTenthsPassedPrev=liTenthsPassed;
     //Serial.print(liLoopCycleCount);
     //Serial.print("  ");
     //Serial.print(liTenthsPassed);
     //Serial.print("  ");
     //Serial.print("Temperature (deg. C)=");
     Serial.print(celsius);

     if (bWhichSensor==0)
       {Serial.print(" ");
        bWhichSensor=1;}//No ; here. This serial plotter friendly at moment
       else
       {Serial.println();
        bWhichSensor=0;};
     //Serial.print("  ");

     String s = "";//N.B. String, not string. Capital on "S" matters.
                   //"string" also means something. Something different.

     if (cSTture0=='+')
       {
         s += "+";
       } else
       {
         s += "-";
       }
     //cTtureDC0Huns, etc, filled earlier, during read tture
     s +=cTturDC0Huns;//Temperature, in Celcuis DECIdecgrees... i.e. 12.4 degC is 124 deg
     s +=cTturDC0Tens;
     s +=cTturDC0Units;

     s += "  ";
     s += "<<tture in fixed format.";
    // Serial.println(s);

     }
   }
}//loop()



I hope that was useful. (At this point, thanks.. but no thanks.. in respect of how it could be cleaned up, finished.)


Here is how you can contact this page's author, Tom Boyd.